1 /* 2 * QEMU Crypto hmac algorithms 3 * 4 * Copyright (c) 2021 Red Hat, Inc. 5 * 6 * Derived from hmac-gcrypt.c: 7 * 8 * Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD. 9 * 10 * This work is licensed under the terms of the GNU GPL, version 2 or 11 * (at your option) any later version. See the COPYING file in the 12 * top-level directory. 13 * 14 */ 15 16 #include "qemu/osdep.h" 17 #include <gnutls/crypto.h> 18 19 #include "qapi/error.h" 20 #include "crypto/hmac.h" 21 #include "hmacpriv.h" 22 23 static int qcrypto_hmac_alg_map[QCRYPTO_HASH_ALG__MAX] = { 24 [QCRYPTO_HASH_ALG_MD5] = GNUTLS_MAC_MD5, 25 [QCRYPTO_HASH_ALG_SHA1] = GNUTLS_MAC_SHA1, 26 [QCRYPTO_HASH_ALG_SHA224] = GNUTLS_MAC_SHA224, 27 [QCRYPTO_HASH_ALG_SHA256] = GNUTLS_MAC_SHA256, 28 [QCRYPTO_HASH_ALG_SHA384] = GNUTLS_MAC_SHA384, 29 [QCRYPTO_HASH_ALG_SHA512] = GNUTLS_MAC_SHA512, 30 [QCRYPTO_HASH_ALG_RIPEMD160] = GNUTLS_MAC_RMD160, 31 }; 32 33 typedef struct QCryptoHmacGnutls QCryptoHmacGnutls; 34 struct QCryptoHmacGnutls { 35 gnutls_hmac_hd_t handle; 36 }; 37 38 bool qcrypto_hmac_supports(QCryptoHashAlgorithm alg) 39 { 40 size_t i; 41 const gnutls_digest_algorithm_t *algs; 42 if (alg >= G_N_ELEMENTS(qcrypto_hmac_alg_map) || 43 qcrypto_hmac_alg_map[alg] == GNUTLS_DIG_UNKNOWN) { 44 return false; 45 } 46 algs = gnutls_digest_list(); 47 for (i = 0; algs[i] != GNUTLS_DIG_UNKNOWN; i++) { 48 if (algs[i] == qcrypto_hmac_alg_map[alg]) { 49 return true; 50 } 51 } 52 return false; 53 } 54 55 void *qcrypto_hmac_ctx_new(QCryptoHashAlgorithm alg, 56 const uint8_t *key, size_t nkey, 57 Error **errp) 58 { 59 QCryptoHmacGnutls *ctx; 60 int err; 61 62 if (!qcrypto_hmac_supports(alg)) { 63 error_setg(errp, "Unsupported hmac algorithm %s", 64 QCryptoHashAlgorithm_str(alg)); 65 return NULL; 66 } 67 68 ctx = g_new0(QCryptoHmacGnutls, 1); 69 70 err = gnutls_hmac_init(&ctx->handle, 71 qcrypto_hmac_alg_map[alg], 72 (const void *)key, nkey); 73 if (err != 0) { 74 error_setg(errp, "Cannot initialize hmac: %s", 75 gnutls_strerror(err)); 76 goto error; 77 } 78 79 return ctx; 80 81 error: 82 g_free(ctx); 83 return NULL; 84 } 85 86 static void 87 qcrypto_gnutls_hmac_ctx_free(QCryptoHmac *hmac) 88 { 89 QCryptoHmacGnutls *ctx; 90 91 ctx = hmac->opaque; 92 gnutls_hmac_deinit(ctx->handle, NULL); 93 94 g_free(ctx); 95 } 96 97 static int 98 qcrypto_gnutls_hmac_bytesv(QCryptoHmac *hmac, 99 const struct iovec *iov, 100 size_t niov, 101 uint8_t **result, 102 size_t *resultlen, 103 Error **errp) 104 { 105 QCryptoHmacGnutls *ctx; 106 uint32_t ret; 107 int i; 108 109 ctx = hmac->opaque; 110 111 for (i = 0; i < niov; i++) { 112 gnutls_hmac(ctx->handle, iov[i].iov_base, iov[i].iov_len); 113 } 114 115 ret = gnutls_hmac_get_len(qcrypto_hmac_alg_map[hmac->alg]); 116 if (ret <= 0) { 117 error_setg(errp, "Unable to get hmac length: %s", 118 gnutls_strerror(ret)); 119 return -1; 120 } 121 122 if (*resultlen == 0) { 123 *resultlen = ret; 124 *result = g_new0(uint8_t, *resultlen); 125 } else if (*resultlen != ret) { 126 error_setg(errp, "Result buffer size %zu is smaller than hmac %d", 127 *resultlen, ret); 128 return -1; 129 } 130 131 gnutls_hmac_output(ctx->handle, *result); 132 133 return 0; 134 } 135 136 QCryptoHmacDriver qcrypto_hmac_lib_driver = { 137 .hmac_bytesv = qcrypto_gnutls_hmac_bytesv, 138 .hmac_free = qcrypto_gnutls_hmac_ctx_free, 139 }; 140