| /* | 
 |  * QEMU Crypto af_alg-backend hash/hmac support | 
 |  * | 
 |  * Copyright (c) 2017 HUAWEI TECHNOLOGIES CO., LTD. | 
 |  * | 
 |  * Authors: | 
 |  *    Longpeng(Mike) <longpeng2@huawei.com> | 
 |  * | 
 |  * 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 "qemu/iov.h" | 
 | #include "qemu/sockets.h" | 
 | #include "qapi/error.h" | 
 | #include "crypto/hash.h" | 
 | #include "crypto/hmac.h" | 
 | #include "hashpriv.h" | 
 | #include "hmacpriv.h" | 
 |  | 
 | static char * | 
 | qcrypto_afalg_hash_format_name(QCryptoHashAlgorithm alg, | 
 |                                bool is_hmac, | 
 |                                Error **errp) | 
 | { | 
 |     char *name; | 
 |     const char *alg_name; | 
 |  | 
 |     switch (alg) { | 
 |     case QCRYPTO_HASH_ALG_MD5: | 
 |         alg_name = "md5"; | 
 |         break; | 
 |     case QCRYPTO_HASH_ALG_SHA1: | 
 |         alg_name = "sha1"; | 
 |         break; | 
 |     case QCRYPTO_HASH_ALG_SHA224: | 
 |         alg_name = "sha224"; | 
 |         break; | 
 |     case QCRYPTO_HASH_ALG_SHA256: | 
 |         alg_name = "sha256"; | 
 |         break; | 
 |     case QCRYPTO_HASH_ALG_SHA384: | 
 |         alg_name = "sha384"; | 
 |         break; | 
 |     case QCRYPTO_HASH_ALG_SHA512: | 
 |         alg_name = "sha512"; | 
 |         break; | 
 |     case QCRYPTO_HASH_ALG_RIPEMD160: | 
 |         alg_name = "rmd160"; | 
 |         break; | 
 |  | 
 |     default: | 
 |         error_setg(errp, "Unsupported hash algorithm %d", alg); | 
 |         return NULL; | 
 |     } | 
 |  | 
 |     if (is_hmac) { | 
 |         name = g_strdup_printf("hmac(%s)", alg_name); | 
 |     } else { | 
 |         name = g_strdup_printf("%s", alg_name); | 
 |     } | 
 |  | 
 |     return name; | 
 | } | 
 |  | 
 | static QCryptoAFAlg * | 
 | qcrypto_afalg_hash_hmac_ctx_new(QCryptoHashAlgorithm alg, | 
 |                                 const uint8_t *key, size_t nkey, | 
 |                                 bool is_hmac, Error **errp) | 
 | { | 
 |     QCryptoAFAlg *afalg; | 
 |     char *name; | 
 |  | 
 |     name = qcrypto_afalg_hash_format_name(alg, is_hmac, errp); | 
 |     if (!name) { | 
 |         return NULL; | 
 |     } | 
 |  | 
 |     afalg = qcrypto_afalg_comm_alloc(AFALG_TYPE_HASH, name, errp); | 
 |     if (!afalg) { | 
 |         g_free(name); | 
 |         return NULL; | 
 |     } | 
 |  | 
 |     g_free(name); | 
 |  | 
 |     /* HMAC needs setkey */ | 
 |     if (is_hmac) { | 
 |         if (setsockopt(afalg->tfmfd, SOL_ALG, ALG_SET_KEY, | 
 |                        key, nkey) != 0) { | 
 |             error_setg_errno(errp, errno, "Set hmac key failed"); | 
 |             qcrypto_afalg_comm_free(afalg); | 
 |             return NULL; | 
 |         } | 
 |     } | 
 |  | 
 |     return afalg; | 
 | } | 
 |  | 
 | static QCryptoAFAlg * | 
 | qcrypto_afalg_hash_ctx_new(QCryptoHashAlgorithm alg, | 
 |                            Error **errp) | 
 | { | 
 |     return qcrypto_afalg_hash_hmac_ctx_new(alg, NULL, 0, false, errp); | 
 | } | 
 |  | 
 | QCryptoAFAlg * | 
 | qcrypto_afalg_hmac_ctx_new(QCryptoHashAlgorithm alg, | 
 |                            const uint8_t *key, size_t nkey, | 
 |                            Error **errp) | 
 | { | 
 |     return qcrypto_afalg_hash_hmac_ctx_new(alg, key, nkey, true, errp); | 
 | } | 
 |  | 
 | static int | 
 | qcrypto_afalg_hash_hmac_bytesv(QCryptoAFAlg *hmac, | 
 |                                QCryptoHashAlgorithm alg, | 
 |                                const struct iovec *iov, | 
 |                                size_t niov, uint8_t **result, | 
 |                                size_t *resultlen, | 
 |                                Error **errp) | 
 | { | 
 |     QCryptoAFAlg *afalg; | 
 |     struct iovec outv; | 
 |     int ret = 0; | 
 |     bool is_hmac = (hmac != NULL) ? true : false; | 
 |     const int expect_len = qcrypto_hash_digest_len(alg); | 
 |  | 
 |     if (*resultlen == 0) { | 
 |         *resultlen = expect_len; | 
 |         *result = g_new0(uint8_t, *resultlen); | 
 |     } else if (*resultlen != expect_len) { | 
 |         error_setg(errp, | 
 |                    "Result buffer size %zu is not match hash %d", | 
 |                    *resultlen, expect_len); | 
 |         return -1; | 
 |     } | 
 |  | 
 |     if (is_hmac) { | 
 |         afalg = hmac; | 
 |     } else { | 
 |         afalg = qcrypto_afalg_hash_ctx_new(alg, errp); | 
 |         if (!afalg) { | 
 |             return -1; | 
 |         } | 
 |     } | 
 |  | 
 |     /* send data to kernel's crypto core */ | 
 |     ret = iov_send_recv(afalg->opfd, iov, niov, | 
 |                         0, iov_size(iov, niov), true); | 
 |     if (ret < 0) { | 
 |         error_setg_errno(errp, errno, "Send data to afalg-core failed"); | 
 |         goto out; | 
 |     } | 
 |  | 
 |     /* hash && get result */ | 
 |     outv.iov_base = *result; | 
 |     outv.iov_len = *resultlen; | 
 |     ret = iov_send_recv(afalg->opfd, &outv, 1, | 
 |                         0, iov_size(&outv, 1), false); | 
 |     if (ret < 0) { | 
 |         error_setg_errno(errp, errno, "Recv result from afalg-core failed"); | 
 |     } else { | 
 |         ret = 0; | 
 |     } | 
 |  | 
 | out: | 
 |     if (!is_hmac) { | 
 |         qcrypto_afalg_comm_free(afalg); | 
 |     } | 
 |     return ret; | 
 | } | 
 |  | 
 | static int | 
 | qcrypto_afalg_hash_bytesv(QCryptoHashAlgorithm alg, | 
 |                           const struct iovec *iov, | 
 |                           size_t niov, uint8_t **result, | 
 |                           size_t *resultlen, | 
 |                           Error **errp) | 
 | { | 
 |     return qcrypto_afalg_hash_hmac_bytesv(NULL, alg, iov, niov, result, | 
 |                                           resultlen, errp); | 
 | } | 
 |  | 
 | static int | 
 | qcrypto_afalg_hmac_bytesv(QCryptoHmac *hmac, | 
 |                           const struct iovec *iov, | 
 |                           size_t niov, uint8_t **result, | 
 |                           size_t *resultlen, | 
 |                           Error **errp) | 
 | { | 
 |     return qcrypto_afalg_hash_hmac_bytesv(hmac->opaque, hmac->alg, | 
 |                                           iov, niov, result, resultlen, | 
 |                                           errp); | 
 | } | 
 |  | 
 | static void qcrypto_afalg_hmac_ctx_free(QCryptoHmac *hmac) | 
 | { | 
 |     QCryptoAFAlg *afalg; | 
 |  | 
 |     afalg = hmac->opaque; | 
 |     qcrypto_afalg_comm_free(afalg); | 
 | } | 
 |  | 
 | QCryptoHashDriver qcrypto_hash_afalg_driver = { | 
 |     .hash_bytesv = qcrypto_afalg_hash_bytesv, | 
 | }; | 
 |  | 
 | QCryptoHmacDriver qcrypto_hmac_afalg_driver = { | 
 |     .hmac_bytesv = qcrypto_afalg_hmac_bytesv, | 
 |     .hmac_free = qcrypto_afalg_hmac_ctx_free, | 
 | }; |