crypto: switch hash code to use nettle/gcrypt directly

Currently the internal hash code is using the gnutls hash APIs.
GNUTLS in turn is wrapping either nettle or gcrypt. Not only
were the GNUTLS hash APIs not added until GNUTLS 2.9.10, but
they don't expose support for all the algorithms QEMU needs
to use with LUKS.

Address this by directly wrapping nettle/gcrypt in QEMU and
avoiding GNUTLS's extra layer of indirection. This gives us
support for hash functions on a much wider range of platforms
and opens up ability to support more hash functions. It also
avoids a GNUTLS bug which would not correctly handle hashing
of large data blocks if int != size_t.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
This commit is contained in:
Daniel P. Berrange
2016-03-11 18:09:22 +00:00
parent 8cbfc94269
commit 0c16c056a4
8 changed files with 299 additions and 122 deletions

View File

@@ -22,12 +22,6 @@
#include "qapi/error.h"
#include "crypto/hash.h"
#ifdef CONFIG_GNUTLS_HASH
#include <gnutls/gnutls.h>
#include <gnutls/crypto.h>
#endif
static size_t qcrypto_hash_alg_size[QCRYPTO_HASH_ALG__MAX] = {
[QCRYPTO_HASH_ALG_MD5] = 16,
[QCRYPTO_HASH_ALG_SHA1] = 20,
@@ -41,105 +35,6 @@ size_t qcrypto_hash_digest_len(QCryptoHashAlgorithm alg)
}
#ifdef CONFIG_GNUTLS_HASH
static int qcrypto_hash_alg_map[QCRYPTO_HASH_ALG__MAX] = {
[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,