diff options
-rw-r--r-- | crypto/hmac-gnutls.c | 139 |
1 files changed, 139 insertions, 0 deletions
diff --git a/crypto/hmac-gnutls.c b/crypto/hmac-gnutls.c new file mode 100644 index 0000000000..24db383322 --- /dev/null +++ b/crypto/hmac-gnutls.c @@ -0,0 +1,139 @@ +/* + * 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, +}; |