diff options
Diffstat (limited to 'qemu/crypto/hash.c')
-rw-r--r-- | qemu/crypto/hash.c | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/qemu/crypto/hash.c b/qemu/crypto/hash.c new file mode 100644 index 000000000..81e74de86 --- /dev/null +++ b/qemu/crypto/hash.c @@ -0,0 +1,200 @@ +/* + * QEMU Crypto hash algorithms + * + * Copyright (c) 2015 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 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 <http://www.gnu.org/licenses/>. + * + */ + +#include "crypto/hash.h" + +#ifdef CONFIG_GNUTLS_HASH +#include <gnutls/gnutls.h> +#include <gnutls/crypto.h> + +static int qcrypto_hash_alg_map[QCRYPTO_HASH_ALG_LAST] = { + [QCRYPTO_HASH_ALG_MD5] = GNUTLS_DIG_MD5, + [QCRYPTO_HASH_ALG_SHA1] = GNUTLS_DIG_SHA1, + [QCRYPTO_HASH_ALG_SHA256] = GNUTLS_DIG_SHA256, +}; + +gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg) +{ + if (alg < G_N_ELEMENTS(qcrypto_hash_alg_map)) { + return true; + } + return false; +} + +int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg, + const struct iovec *iov, + size_t niov, + uint8_t **result, + size_t *resultlen, + Error **errp) +{ + int i, ret; + gnutls_hash_hd_t dig; + + if (alg >= G_N_ELEMENTS(qcrypto_hash_alg_map)) { + error_setg(errp, + "Unknown hash algorithm %d", + alg); + return -1; + } + + ret = gnutls_hash_init(&dig, qcrypto_hash_alg_map[alg]); + + if (ret < 0) { + error_setg(errp, + "Unable to initialize hash algorithm: %s", + gnutls_strerror(ret)); + return -1; + } + + for (i = 0; i < niov; i++) { + ret = gnutls_hash(dig, iov[i].iov_base, iov[i].iov_len); + if (ret < 0) { + error_setg(errp, + "Unable process hash data: %s", + gnutls_strerror(ret)); + goto error; + } + } + + ret = gnutls_hash_get_len(qcrypto_hash_alg_map[alg]); + if (ret <= 0) { + error_setg(errp, + "Unable to get hash length: %s", + gnutls_strerror(ret)); + goto error; + } + 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 hash %d", + *resultlen, ret); + goto error; + } + + gnutls_hash_deinit(dig, *result); + return 0; + + error: + gnutls_hash_deinit(dig, NULL); + return -1; +} + +#else /* ! CONFIG_GNUTLS_HASH */ + +gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg G_GNUC_UNUSED) +{ + return false; +} + +int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg, + const struct iovec *iov G_GNUC_UNUSED, + size_t niov G_GNUC_UNUSED, + uint8_t **result G_GNUC_UNUSED, + size_t *resultlen G_GNUC_UNUSED, + Error **errp) +{ + error_setg(errp, + "Hash algorithm %d not supported without GNUTLS", + alg); + return -1; +} + +#endif /* ! CONFIG_GNUTLS_HASH */ + +int qcrypto_hash_bytes(QCryptoHashAlgorithm alg, + const char *buf, + size_t len, + uint8_t **result, + size_t *resultlen, + Error **errp) +{ + struct iovec iov = { .iov_base = (char *)buf, + .iov_len = len }; + return qcrypto_hash_bytesv(alg, &iov, 1, result, resultlen, errp); +} + +static const char hex[] = "0123456789abcdef"; + +int qcrypto_hash_digestv(QCryptoHashAlgorithm alg, + const struct iovec *iov, + size_t niov, + char **digest, + Error **errp) +{ + uint8_t *result = NULL; + size_t resultlen = 0; + size_t i; + + if (qcrypto_hash_bytesv(alg, iov, niov, &result, &resultlen, errp) < 0) { + return -1; + } + + *digest = g_new0(char, (resultlen * 2) + 1); + for (i = 0 ; i < resultlen ; i++) { + (*digest)[(i * 2)] = hex[(result[i] >> 4) & 0xf]; + (*digest)[(i * 2) + 1] = hex[result[i] & 0xf]; + } + (*digest)[resultlen * 2] = '\0'; + g_free(result); + return 0; +} + +int qcrypto_hash_digest(QCryptoHashAlgorithm alg, + const char *buf, + size_t len, + char **digest, + Error **errp) +{ + struct iovec iov = { .iov_base = (char *)buf, .iov_len = len }; + + return qcrypto_hash_digestv(alg, &iov, 1, digest, errp); +} + +int qcrypto_hash_base64v(QCryptoHashAlgorithm alg, + const struct iovec *iov, + size_t niov, + char **base64, + Error **errp) +{ + uint8_t *result = NULL; + size_t resultlen = 0; + + if (qcrypto_hash_bytesv(alg, iov, niov, &result, &resultlen, errp) < 0) { + return -1; + } + + *base64 = g_base64_encode(result, resultlen); + g_free(result); + return 0; +} + +int qcrypto_hash_base64(QCryptoHashAlgorithm alg, + const char *buf, + size_t len, + char **base64, + Error **errp) +{ + struct iovec iov = { .iov_base = (char *)buf, .iov_len = len }; + + return qcrypto_hash_base64v(alg, &iov, 1, base64, errp); +} |