| /* | 
 |  * QEMU Crypto hmac algorithms | 
 |  * | 
 |  * Copyright (c) 2021 Red Hat, Inc. | 
 |  * | 
 |  * Derived from hmac-gcrypt.c: | 
 |  * | 
 |  *   Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD. | 
 |  * | 
 |  * This work is licensed under the terms of the GNU GPL, version 2 or | 
 |  * (at your option) any later version.  See the COPYING file in the | 
 |  * top-level directory. | 
 |  * | 
 |  */ | 
 |  | 
 | #include "qemu/osdep.h" | 
 | #include <gnutls/crypto.h> | 
 |  | 
 | #include "qapi/error.h" | 
 | #include "crypto/hmac.h" | 
 | #include "hmacpriv.h" | 
 |  | 
 | static int qcrypto_hmac_alg_map[QCRYPTO_HASH_ALG__MAX] = { | 
 |     [QCRYPTO_HASH_ALG_MD5] = GNUTLS_MAC_MD5, | 
 |     [QCRYPTO_HASH_ALG_SHA1] = GNUTLS_MAC_SHA1, | 
 |     [QCRYPTO_HASH_ALG_SHA224] = GNUTLS_MAC_SHA224, | 
 |     [QCRYPTO_HASH_ALG_SHA256] = GNUTLS_MAC_SHA256, | 
 |     [QCRYPTO_HASH_ALG_SHA384] = GNUTLS_MAC_SHA384, | 
 |     [QCRYPTO_HASH_ALG_SHA512] = GNUTLS_MAC_SHA512, | 
 |     [QCRYPTO_HASH_ALG_RIPEMD160] = GNUTLS_MAC_RMD160, | 
 | }; | 
 |  | 
 | typedef struct QCryptoHmacGnutls QCryptoHmacGnutls; | 
 | struct QCryptoHmacGnutls { | 
 |     gnutls_hmac_hd_t handle; | 
 | }; | 
 |  | 
 | bool qcrypto_hmac_supports(QCryptoHashAlgorithm alg) | 
 | { | 
 |     size_t i; | 
 |     const gnutls_digest_algorithm_t *algs; | 
 |     if (alg >= G_N_ELEMENTS(qcrypto_hmac_alg_map) || | 
 |         qcrypto_hmac_alg_map[alg] == GNUTLS_DIG_UNKNOWN) { | 
 |         return false; | 
 |     } | 
 |     algs = gnutls_digest_list(); | 
 |     for (i = 0; algs[i] != GNUTLS_DIG_UNKNOWN; i++) { | 
 |         if (algs[i] == qcrypto_hmac_alg_map[alg]) { | 
 |             return true; | 
 |         } | 
 |     } | 
 |     return false; | 
 | } | 
 |  | 
 | void *qcrypto_hmac_ctx_new(QCryptoHashAlgorithm alg, | 
 |                            const uint8_t *key, size_t nkey, | 
 |                            Error **errp) | 
 | { | 
 |     QCryptoHmacGnutls *ctx; | 
 |     int err; | 
 |  | 
 |     if (!qcrypto_hmac_supports(alg)) { | 
 |         error_setg(errp, "Unsupported hmac algorithm %s", | 
 |                    QCryptoHashAlgorithm_str(alg)); | 
 |         return NULL; | 
 |     } | 
 |  | 
 |     ctx = g_new0(QCryptoHmacGnutls, 1); | 
 |  | 
 |     err = gnutls_hmac_init(&ctx->handle, | 
 |                            qcrypto_hmac_alg_map[alg], | 
 |                            (const void *)key, nkey); | 
 |     if (err != 0) { | 
 |         error_setg(errp, "Cannot initialize hmac: %s", | 
 |                    gnutls_strerror(err)); | 
 |         goto error; | 
 |     } | 
 |  | 
 |     return ctx; | 
 |  | 
 | error: | 
 |     g_free(ctx); | 
 |     return NULL; | 
 | } | 
 |  | 
 | static void | 
 | qcrypto_gnutls_hmac_ctx_free(QCryptoHmac *hmac) | 
 | { | 
 |     QCryptoHmacGnutls *ctx; | 
 |  | 
 |     ctx = hmac->opaque; | 
 |     gnutls_hmac_deinit(ctx->handle, NULL); | 
 |  | 
 |     g_free(ctx); | 
 | } | 
 |  | 
 | static int | 
 | qcrypto_gnutls_hmac_bytesv(QCryptoHmac *hmac, | 
 |                            const struct iovec *iov, | 
 |                            size_t niov, | 
 |                            uint8_t **result, | 
 |                            size_t *resultlen, | 
 |                            Error **errp) | 
 | { | 
 |     QCryptoHmacGnutls *ctx; | 
 |     uint32_t ret; | 
 |     int i; | 
 |  | 
 |     ctx = hmac->opaque; | 
 |  | 
 |     for (i = 0; i < niov; i++) { | 
 |         gnutls_hmac(ctx->handle, iov[i].iov_base, iov[i].iov_len); | 
 |     } | 
 |  | 
 |     ret = gnutls_hmac_get_len(qcrypto_hmac_alg_map[hmac->alg]); | 
 |     if (ret <= 0) { | 
 |         error_setg(errp, "Unable to get hmac length: %s", | 
 |                    gnutls_strerror(ret)); | 
 |         return -1; | 
 |     } | 
 |  | 
 |     if (*resultlen == 0) { | 
 |         *resultlen = ret; | 
 |         *result = g_new0(uint8_t, *resultlen); | 
 |     } else if (*resultlen != ret) { | 
 |         error_setg(errp, "Result buffer size %zu is smaller than hmac %d", | 
 |                    *resultlen, ret); | 
 |         return -1; | 
 |     } | 
 |  | 
 |     gnutls_hmac_output(ctx->handle, *result); | 
 |  | 
 |     return 0; | 
 | } | 
 |  | 
 | QCryptoHmacDriver qcrypto_hmac_lib_driver = { | 
 |     .hmac_bytesv = qcrypto_gnutls_hmac_bytesv, | 
 |     .hmac_free = qcrypto_gnutls_hmac_ctx_free, | 
 | }; |