Skip to content

Commit

Permalink
crypto: add support for accumulative hashing
Browse files Browse the repository at this point in the history
This change adds an accumulative hashing function
(qcrypto_hash_accumulate_bytesv) and implementation
for each of the crypto library backends that QEMU supports.

The QCrypto API did not support hashing in an accumulative mode.
As such, hardware hash modules (like the HACE from Aspeed's SoCs) are
unable to perform such operations correctly when the guest requires it.

The creation and freeing of each library's context is abstracted by
the qcrypto_hash_accumulate_new_ctx and qcrypto_hash_accumulate_free_ctx
functions.

Changes in V2:
* Fixed error checking bug in libgcrypt backend

Signed-off-by: Alejandro Zeise <[email protected]>
  • Loading branch information
Alejandro Zeise authored and legoater committed Jul 31, 2024
1 parent 689378f commit c477691
Show file tree
Hide file tree
Showing 7 changed files with 487 additions and 0 deletions.
105 changes: 105 additions & 0 deletions crypto/hash-gcrypt.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/*
* 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
Expand Down Expand Up @@ -110,7 +111,111 @@ qcrypto_gcrypt_hash_bytesv(QCryptoHashAlgorithm alg,
return -1;
}

static
int qcrypto_gcrypt_hash_accumulate_new_ctx(QCryptoHashAlgorithm alg,
qcrypto_hash_accumulate_ctx_t **accumulate_ctx,
Error **errp)
{
int ret;

if (!qcrypto_hash_supports(alg)) {
error_setg(errp,
"Unknown hash algorithm %d",
alg);
return -1;
}

ret = gcry_md_open((gcry_md_hd_t *) accumulate_ctx, qcrypto_hash_alg_map[alg], 0);

if (ret < 0) {
error_setg(errp,
"Unable to initialize hash algorithm: %s",
gcry_strerror(ret));
return -1;
}

return 0;
}

static
int qcrypto_gcrypt_hash_accumulate_free_ctx(qcrypto_hash_accumulate_ctx_t *hash_ctx,
Error **errp)
{
if (hash_ctx != NULL) {
gcry_md_close((gcry_md_hd_t) hash_ctx);
}

return 0;
}

static
int qcrypto_gcrypt_hash_accumulate_bytesv(QCryptoHashAlgorithm alg,
qcrypto_hash_accumulate_ctx_t *accumulate_ctx,
const struct iovec *iov,
size_t niov,
uint8_t **result,
size_t *resultlen,
Error **errp)
{
int i, ret;
gcry_md_hd_t ctx_copy;
unsigned char *digest;

if (!qcrypto_hash_supports(alg)) {
error_setg(errp,
"Unknown hash algorithm %d",
alg);
return -1;
}

for (i = 0; i < niov; i++) {
gcry_md_write((gcry_md_hd_t) accumulate_ctx, iov[i].iov_base, iov[i].iov_len);
}

ret = gcry_md_get_algo_dlen(qcrypto_hash_alg_map[alg]);
if (ret <= 0) {
error_setg(errp,
"Unable to get hash length: %s",
gcry_strerror(ret));
return -1;
}

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);
return -1;
}

/*
* Make a copy so we don't distort the main context
* by calculating the intermediate hash
*/
ret = gcry_md_copy(&ctx_copy, (gcry_md_hd_t) accumulate_ctx);
if (ret) {
error_setg(errp, "Unable to make copy: %s", gcry_strerror(ret));
return -1;
}

digest = gcry_md_read(ctx_copy, 0);
if (!digest) {
error_setg(errp,
"No digest produced");
return -1;
}
memcpy(*result, digest, *resultlen);
gcry_md_close(ctx_copy);

return 0;
}


QCryptoHashDriver qcrypto_hash_lib_driver = {
.hash_bytesv = qcrypto_gcrypt_hash_bytesv,
.hash_accumulate_bytesv = qcrypto_gcrypt_hash_accumulate_bytesv,
.accumulate_new_ctx = qcrypto_gcrypt_hash_accumulate_new_ctx,
.accumulate_free_ctx = qcrypto_gcrypt_hash_accumulate_free_ctx,
};
89 changes: 89 additions & 0 deletions crypto/hash-glib.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/*
* 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
Expand Down Expand Up @@ -95,6 +96,94 @@ qcrypto_glib_hash_bytesv(QCryptoHashAlgorithm alg,
}


static
int qcrypto_glib_hash_accumulate_new_ctx(QCryptoHashAlgorithm alg,
qcrypto_hash_accumulate_ctx_t **accumulate_ctx,
Error **errp)
{
if (!qcrypto_hash_supports(alg)) {
error_setg(errp,
"Unknown hash algorithm %d",
alg);
return -1;
}

*accumulate_ctx = g_checksum_new(qcrypto_hash_alg_map[alg]);

return 0;
}

static
int qcrypto_glib_hash_accumulate_free_ctx(qcrypto_hash_accumulate_ctx_t *hash_ctx,
Error **errp)
{
if (hash_ctx != NULL) {
g_checksum_free((GChecksum *) hash_ctx);
}

return 0;
}


static
int qcrypto_glib_hash_accumulate_bytesv(QCryptoHashAlgorithm alg,
qcrypto_hash_accumulate_ctx_t *accumulate_ctx,
const struct iovec *iov,
size_t niov,
uint8_t **result,
size_t *resultlen,
Error **errp)
{
int i, ret;
GChecksum *ctx_copy;

if (!qcrypto_hash_supports(alg)) {
error_setg(errp,
"Unknown hash algorithm %d",
alg);
return -1;
}

for (i = 0; i < niov; i++) {
g_checksum_update((GChecksum *) accumulate_ctx, iov[i].iov_base, iov[i].iov_len);
}

ret = g_checksum_type_get_length(qcrypto_hash_alg_map[alg]);
if (ret < 0) {
error_setg(errp, "%s",
"Unable to get hash length");
return -1;
}
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);
return -1;
}

/*
Make a copy so we don't distort the main context
by calculating the intermediate hash.
*/
ctx_copy = g_checksum_copy((GChecksum *) accumulate_ctx);
if (ctx_copy == NULL) {
error_setg(errp, "Unable to make copy: %s", __func__);
return -1;
}

g_checksum_get_digest((GChecksum *) ctx_copy, *result, resultlen);
g_checksum_free(ctx_copy);

return 0;
}


QCryptoHashDriver qcrypto_hash_lib_driver = {
.hash_bytesv = qcrypto_glib_hash_bytesv,
.hash_accumulate_bytesv = qcrypto_glib_hash_accumulate_bytesv,
.accumulate_new_ctx = qcrypto_glib_hash_accumulate_new_ctx,
.accumulate_free_ctx = qcrypto_glib_hash_accumulate_free_ctx,
};
82 changes: 82 additions & 0 deletions crypto/hash-gnutls.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/*
* QEMU Crypto hash algorithms
*
* Copyright (c) 2024 Seagate Technology LLC and/or its Affiliates
* Copyright (c) 2021 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
Expand Down Expand Up @@ -99,6 +100,87 @@ qcrypto_gnutls_hash_bytesv(QCryptoHashAlgorithm alg,
}


static
int qcrypto_gnutls_hash_accumulate_new_ctx(QCryptoHashAlgorithm alg,
qcrypto_hash_accumulate_ctx_t **hash_ctx,
Error **errp)
{
int ret;

if (!qcrypto_hash_supports(alg)) {
error_setg(errp,
"Unknown hash algorithm %d",
alg);
return -1;
}

ret = gnutls_hash_init((gnutls_hash_hd_t *) hash_ctx, qcrypto_hash_alg_map[alg]);
if (ret < 0) {
error_setg(errp,
"Unable to initialize hash algorithm: %s",
gnutls_strerror(ret));
return -1;
}

return 0;
}

static
int qcrypto_gnutls_hash_accumulate_free_ctx(qcrypto_hash_accumulate_ctx_t *hash_ctx,
Error **errp)
{
if (hash_ctx != NULL) {
gnutls_hash_deinit((gnutls_hash_hd_t) hash_ctx, NULL);
}

return 0;
}

static
int qcrypto_gnutls_hash_accumulate_bytesv(QCryptoHashAlgorithm alg,
qcrypto_hash_accumulate_ctx_t *hash_ctx,
const struct iovec *iov,
size_t niov,
uint8_t **result,
size_t *resultlen,
Error **errp)
{
int i, ret;

if (!qcrypto_hash_supports(alg)) {
error_setg(errp,
"Unknown hash algorithm %d",
alg);
return -1;
}

ret = gnutls_hash_get_len(qcrypto_hash_alg_map[alg]);
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);
return -1;
}

for (i = 0; i < niov; i++) {
gnutls_hash((gnutls_hash_hd_t) hash_ctx,
iov[i].iov_base, iov[i].iov_len);
}

/* Make a copy so we don't distort the main context */
gnutls_hash_hd_t copy = gnutls_hash_copy((gnutls_hash_hd_t) hash_ctx);
gnutls_hash_deinit(copy, *result);

return 0;
}


QCryptoHashDriver qcrypto_hash_lib_driver = {
.hash_bytesv = qcrypto_gnutls_hash_bytesv,
.hash_accumulate_bytesv = qcrypto_gnutls_hash_accumulate_bytesv,
.accumulate_new_ctx = qcrypto_gnutls_hash_accumulate_new_ctx,
.accumulate_free_ctx = qcrypto_gnutls_hash_accumulate_free_ctx,
};
Loading

0 comments on commit c477691

Please sign in to comment.