1 /* 2 * QEMU Crypto af_alg-backend hash/hmac support 3 * 4 * Copyright (c) 2024 Seagate Technology LLC and/or its Affiliates 5 * Copyright (c) 2017 HUAWEI TECHNOLOGIES CO., LTD. 6 * 7 * Authors: 8 * Longpeng(Mike) <longpeng2@huawei.com> 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 #include "qemu/osdep.h" 15 #include "qemu/iov.h" 16 #include "qemu/sockets.h" 17 #include "qapi/error.h" 18 #include "crypto/hash.h" 19 #include "crypto/hmac.h" 20 #include "hashpriv.h" 21 #include "hmacpriv.h" 22 23 static char * 24 qcrypto_afalg_hash_format_name(QCryptoHashAlgo alg, 25 bool is_hmac, 26 Error **errp) 27 { 28 char *name; 29 const char *alg_name; 30 31 switch (alg) { 32 case QCRYPTO_HASH_ALGO_MD5: 33 alg_name = "md5"; 34 break; 35 case QCRYPTO_HASH_ALGO_SHA1: 36 alg_name = "sha1"; 37 break; 38 case QCRYPTO_HASH_ALGO_SHA224: 39 alg_name = "sha224"; 40 break; 41 case QCRYPTO_HASH_ALGO_SHA256: 42 alg_name = "sha256"; 43 break; 44 case QCRYPTO_HASH_ALGO_SHA384: 45 alg_name = "sha384"; 46 break; 47 case QCRYPTO_HASH_ALGO_SHA512: 48 alg_name = "sha512"; 49 break; 50 case QCRYPTO_HASH_ALGO_RIPEMD160: 51 alg_name = "rmd160"; 52 break; 53 54 default: 55 error_setg(errp, "Unsupported hash algorithm %d", alg); 56 return NULL; 57 } 58 59 if (is_hmac) { 60 name = g_strdup_printf("hmac(%s)", alg_name); 61 } else { 62 name = g_strdup_printf("%s", alg_name); 63 } 64 65 return name; 66 } 67 68 static QCryptoAFAlgo * 69 qcrypto_afalg_hash_hmac_ctx_new(QCryptoHashAlgo alg, 70 const uint8_t *key, size_t nkey, 71 bool is_hmac, Error **errp) 72 { 73 QCryptoAFAlgo *afalg; 74 char *name; 75 76 name = qcrypto_afalg_hash_format_name(alg, is_hmac, errp); 77 if (!name) { 78 return NULL; 79 } 80 81 afalg = qcrypto_afalg_comm_alloc(AFALG_TYPE_HASH, name, errp); 82 if (!afalg) { 83 g_free(name); 84 return NULL; 85 } 86 87 g_free(name); 88 89 /* HMAC needs setkey */ 90 if (is_hmac) { 91 if (setsockopt(afalg->tfmfd, SOL_ALG, ALG_SET_KEY, 92 key, nkey) != 0) { 93 error_setg_errno(errp, errno, "Set hmac key failed"); 94 qcrypto_afalg_comm_free(afalg); 95 return NULL; 96 } 97 } 98 99 return afalg; 100 } 101 102 static QCryptoAFAlgo * 103 qcrypto_afalg_hash_ctx_new(QCryptoHashAlgo alg, 104 Error **errp) 105 { 106 return qcrypto_afalg_hash_hmac_ctx_new(alg, NULL, 0, false, errp); 107 } 108 109 QCryptoAFAlgo * 110 qcrypto_afalg_hmac_ctx_new(QCryptoHashAlgo alg, 111 const uint8_t *key, size_t nkey, 112 Error **errp) 113 { 114 return qcrypto_afalg_hash_hmac_ctx_new(alg, key, nkey, true, errp); 115 } 116 117 static 118 QCryptoHash *qcrypto_afalg_hash_new(QCryptoHashAlgo alg, Error **errp) 119 { 120 /* Check if hash algorithm is supported */ 121 char *alg_name = qcrypto_afalg_hash_format_name(alg, false, NULL); 122 QCryptoHash *hash; 123 124 if (alg_name == NULL) { 125 error_setg(errp, "Unknown hash algorithm %d", alg); 126 return NULL; 127 } 128 129 g_free(alg_name); 130 131 hash = g_new(QCryptoHash, 1); 132 hash->alg = alg; 133 hash->opaque = qcrypto_afalg_hash_ctx_new(alg, errp); 134 if (!hash->opaque) { 135 free(hash); 136 return NULL; 137 } 138 139 return hash; 140 } 141 142 static 143 void qcrypto_afalg_hash_free(QCryptoHash *hash) 144 { 145 QCryptoAFAlg *ctx = hash->opaque; 146 147 if (ctx) { 148 qcrypto_afalg_comm_free(ctx); 149 } 150 151 g_free(hash); 152 } 153 154 /** 155 * Send data to the kernel's crypto core. 156 * 157 * The more_data parameter is used to notify the crypto engine 158 * that this is an "update" operation, and that more data will 159 * be provided to calculate the final hash. 160 */ 161 static 162 int qcrypto_afalg_send_to_kernel(QCryptoAFAlg *afalg, 163 const struct iovec *iov, 164 size_t niov, 165 bool more_data, 166 Error **errp) 167 { 168 int ret = 0; 169 int flags = (more_data ? MSG_MORE : 0); 170 171 /* send data to kernel's crypto core */ 172 ret = iov_send_recv_with_flags(afalg->opfd, flags, iov, niov, 173 0, iov_size(iov, niov), true); 174 if (ret < 0) { 175 error_setg_errno(errp, errno, "Send data to afalg-core failed"); 176 ret = -1; 177 } else { 178 /* No error, so return 0 */ 179 ret = 0; 180 } 181 182 return ret; 183 } 184 185 static 186 int qcrypto_afalg_recv_from_kernel(QCryptoAFAlg *afalg, 187 QCryptoHashAlgo alg, 188 uint8_t **result, 189 size_t *result_len, 190 Error **errp) 191 { 192 struct iovec outv; 193 int ret; 194 const int expected_len = qcrypto_hash_digest_len(alg); 195 196 if (*result_len == 0) { 197 *result_len = expected_len; 198 *result = g_new0(uint8_t, *result_len); 199 } else if (*result_len != expected_len) { 200 error_setg(errp, 201 "Result buffer size %zu is not match hash %d", 202 *result_len, expected_len); 203 return -1; 204 } 205 206 /* hash && get result */ 207 outv.iov_base = *result; 208 outv.iov_len = *result_len; 209 ret = iov_send_recv(afalg->opfd, &outv, 1, 210 0, iov_size(&outv, 1), false); 211 if (ret < 0) { 212 error_setg_errno(errp, errno, "Recv result from afalg-core failed"); 213 return -1; 214 } 215 216 return 0; 217 } 218 219 static 220 int qcrypto_afalg_hash_update(QCryptoHash *hash, 221 const struct iovec *iov, 222 size_t niov, 223 Error **errp) 224 { 225 return qcrypto_afalg_send_to_kernel((QCryptoAFAlg *) hash->opaque, 226 iov, niov, true, errp); 227 } 228 229 static 230 int qcrypto_afalg_hash_finalize(QCryptoHash *hash, 231 uint8_t **result, 232 size_t *result_len, 233 Error **errp) 234 { 235 return qcrypto_afalg_recv_from_kernel((QCryptoAFAlg *) hash->opaque, 236 hash->alg, result, result_len, errp); 237 } 238 239 static int 240 qcrypto_afalg_hash_hmac_bytesv(QCryptoAFAlgo *hmac, 241 QCryptoHashAlgo alg, 242 const struct iovec *iov, 243 size_t niov, uint8_t **result, 244 size_t *resultlen, 245 Error **errp) 246 { 247 int ret = 0; 248 249 ret = qcrypto_afalg_send_to_kernel(hmac, iov, niov, false, errp); 250 if (ret == 0) { 251 ret = qcrypto_afalg_recv_from_kernel(hmac, alg, result, 252 resultlen, errp); 253 } 254 255 return ret; 256 } 257 258 static int 259 qcrypto_afalg_hmac_bytesv(QCryptoHmac *hmac, 260 const struct iovec *iov, 261 size_t niov, uint8_t **result, 262 size_t *resultlen, 263 Error **errp) 264 { 265 return qcrypto_afalg_hash_hmac_bytesv(hmac->opaque, hmac->alg, 266 iov, niov, result, resultlen, 267 errp); 268 } 269 270 static void qcrypto_afalg_hmac_ctx_free(QCryptoHmac *hmac) 271 { 272 QCryptoAFAlgo *afalg; 273 274 afalg = hmac->opaque; 275 qcrypto_afalg_comm_free(afalg); 276 } 277 278 QCryptoHashDriver qcrypto_hash_afalg_driver = { 279 .hash_new = qcrypto_afalg_hash_new, 280 .hash_free = qcrypto_afalg_hash_free, 281 .hash_update = qcrypto_afalg_hash_update, 282 .hash_finalize = qcrypto_afalg_hash_finalize 283 }; 284 285 QCryptoHmacDriver qcrypto_hmac_afalg_driver = { 286 .hmac_bytesv = qcrypto_afalg_hmac_bytesv, 287 .hmac_free = qcrypto_afalg_hmac_ctx_free, 288 }; 289