/* * QEMU Crypto hash algorithms * * Copyright (c) 2024 Seagate Technology LLC and/or its Affiliates * Copyright (c) 2016 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * */ #include "qemu/osdep.h" #include "qapi/error.h" #include "crypto/hash.h" #include "hashpriv.h" #include #include #include typedef void (*qcrypto_nettle_init)(void *ctx); typedef void (*qcrypto_nettle_write)(void *ctx, size_t len, const uint8_t *buf); typedef void (*qcrypto_nettle_result)(void *ctx, size_t len, uint8_t *buf); union qcrypto_hash_ctx { struct md5_ctx md5; struct sha1_ctx sha1; struct sha224_ctx sha224; struct sha256_ctx sha256; struct sha384_ctx sha384; struct sha512_ctx sha512; struct ripemd160_ctx ripemd160; }; struct qcrypto_hash_alg { qcrypto_nettle_init init; qcrypto_nettle_write write; qcrypto_nettle_result result; size_t len; } qcrypto_hash_alg_map[] = { [QCRYPTO_HASH_ALGO_MD5] = { .init = (qcrypto_nettle_init)md5_init, .write = (qcrypto_nettle_write)md5_update, .result = (qcrypto_nettle_result)md5_digest, .len = MD5_DIGEST_SIZE, }, [QCRYPTO_HASH_ALGO_SHA1] = { .init = (qcrypto_nettle_init)sha1_init, .write = (qcrypto_nettle_write)sha1_update, .result = (qcrypto_nettle_result)sha1_digest, .len = SHA1_DIGEST_SIZE, }, [QCRYPTO_HASH_ALGO_SHA224] = { .init = (qcrypto_nettle_init)sha224_init, .write = (qcrypto_nettle_write)sha224_update, .result = (qcrypto_nettle_result)sha224_digest, .len = SHA224_DIGEST_SIZE, }, [QCRYPTO_HASH_ALGO_SHA256] = { .init = (qcrypto_nettle_init)sha256_init, .write = (qcrypto_nettle_write)sha256_update, .result = (qcrypto_nettle_result)sha256_digest, .len = SHA256_DIGEST_SIZE, }, [QCRYPTO_HASH_ALGO_SHA384] = { .init = (qcrypto_nettle_init)sha384_init, .write = (qcrypto_nettle_write)sha384_update, .result = (qcrypto_nettle_result)sha384_digest, .len = SHA384_DIGEST_SIZE, }, [QCRYPTO_HASH_ALGO_SHA512] = { .init = (qcrypto_nettle_init)sha512_init, .write = (qcrypto_nettle_write)sha512_update, .result = (qcrypto_nettle_result)sha512_digest, .len = SHA512_DIGEST_SIZE, }, [QCRYPTO_HASH_ALGO_RIPEMD160] = { .init = (qcrypto_nettle_init)ripemd160_init, .write = (qcrypto_nettle_write)ripemd160_update, .result = (qcrypto_nettle_result)ripemd160_digest, .len = RIPEMD160_DIGEST_SIZE, }, }; gboolean qcrypto_hash_supports(QCryptoHashAlgo alg) { if (alg < G_N_ELEMENTS(qcrypto_hash_alg_map) && qcrypto_hash_alg_map[alg].init != NULL) { return true; } return false; } static QCryptoHash *qcrypto_nettle_hash_new(QCryptoHashAlgo alg, Error **errp) { QCryptoHash *hash; hash = g_new(QCryptoHash, 1); hash->alg = alg; hash->opaque = g_new(union qcrypto_hash_ctx, 1); qcrypto_hash_alg_map[alg].init(hash->opaque); return hash; } static void qcrypto_nettle_hash_free(QCryptoHash *hash) { union qcrypto_hash_ctx *ctx = hash->opaque; g_free(ctx); g_free(hash); } static int qcrypto_nettle_hash_update(QCryptoHash *hash, const struct iovec *iov, size_t niov, Error **errp) { union qcrypto_hash_ctx *ctx = hash->opaque; for (int i = 0; i < niov; i++) { /* * Some versions of nettle have functions * declared with 'int' instead of 'size_t' * so to be safe avoid writing more than * UINT_MAX bytes at a time */ size_t len = iov[i].iov_len; uint8_t *base = iov[i].iov_base; while (len) { size_t shortlen = MIN(len, UINT_MAX); qcrypto_hash_alg_map[hash->alg].write(ctx, len, base); len -= shortlen; base += len; } } return 0; } static int qcrypto_nettle_hash_finalize(QCryptoHash *hash, uint8_t **result, size_t *result_len, Error **errp) { union qcrypto_hash_ctx *ctx = hash->opaque; *result_len = qcrypto_hash_alg_map[hash->alg].len; *result = g_new(uint8_t, *result_len); qcrypto_hash_alg_map[hash->alg].result(ctx, *result_len, *result); return 0; } QCryptoHashDriver qcrypto_hash_lib_driver = { .hash_new = qcrypto_nettle_hash_new, .hash_update = qcrypto_nettle_hash_update, .hash_finalize = qcrypto_nettle_hash_finalize, .hash_free = qcrypto_nettle_hash_free, };