Introduce new cryptography hashing APIs

-----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEE2vOm/bJrYpEtDo4/vobrtBUQT98FAmcH/iIACgkQvobrtBUQ
 T9+Yjg/+NReYV5BDjOLk6vfgTsK6Ku0/hdis2cf9OS8Ud1VXzKaxfhwkchtw9QVI
 kuAthesQNocEPfQfl2K4+f4oaKfysO7awDwYto/JhY/m1iCZ8iqofZWehOITszvM
 EvWlNBr83NtpGFIwQWIxFEVZo42gaUnA69iAjBo7YQnE5xufJuPIbgMjB/O4/zar
 Xlo15A69TP9dBJTvIDdrhkt3Quiysa7a68BW+piAAKvplOjOfugCEo3ebLwlZYOh
 dK0Cg9v24+BMAqQ7kDMroS4uHC+OEs2AOvfYh01QqWxNkk7RsPjb9VAA60Ng89eC
 6BU4jw17zUAqL67of+M1cTTX4UPGBWGIUXt8CtO1DpByxiGXXfEkBrBmIyDJvxn9
 EzB4WpAXpVo2AG6vYpYSBGyxycWQs33ljfBb/qR6xu5PnA+Jc/jfJkVv5iYP96wW
 F6pJm6FoK69aTJU7K4kAJPjD2fZum+iHVWc283NIkq9HQJLz2EYE0LIfOOY5feJK
 S0tjEE5ZLqKG5JAdpsaCe5V/vExc512/D56Xb5fY4mC2DPb/b6fM66Oc5M7DTuK1
 LxCgnEuqm1Lo3CMR0k4W8Xezs7hWp+u3tr+i705l5qFxklYkmFeVAzTWdQ56JOGk
 Z1XKUbcPUnweormPMxMQXyxXpey4DBwUGbjC98iqE8tjUg6NA3o=
 =yVgk
 -----END PGP SIGNATURE-----

Merge tag 'crypto-fixes-pull-request' of https://gitlab.com/berrange/qemu into staging

Introduce new cryptography hashing APIs

# -----BEGIN PGP SIGNATURE-----
#
# iQIzBAABCAAdFiEE2vOm/bJrYpEtDo4/vobrtBUQT98FAmcH/iIACgkQvobrtBUQ
# T9+Yjg/+NReYV5BDjOLk6vfgTsK6Ku0/hdis2cf9OS8Ud1VXzKaxfhwkchtw9QVI
# kuAthesQNocEPfQfl2K4+f4oaKfysO7awDwYto/JhY/m1iCZ8iqofZWehOITszvM
# EvWlNBr83NtpGFIwQWIxFEVZo42gaUnA69iAjBo7YQnE5xufJuPIbgMjB/O4/zar
# Xlo15A69TP9dBJTvIDdrhkt3Quiysa7a68BW+piAAKvplOjOfugCEo3ebLwlZYOh
# dK0Cg9v24+BMAqQ7kDMroS4uHC+OEs2AOvfYh01QqWxNkk7RsPjb9VAA60Ng89eC
# 6BU4jw17zUAqL67of+M1cTTX4UPGBWGIUXt8CtO1DpByxiGXXfEkBrBmIyDJvxn9
# EzB4WpAXpVo2AG6vYpYSBGyxycWQs33ljfBb/qR6xu5PnA+Jc/jfJkVv5iYP96wW
# F6pJm6FoK69aTJU7K4kAJPjD2fZum+iHVWc283NIkq9HQJLz2EYE0LIfOOY5feJK
# S0tjEE5ZLqKG5JAdpsaCe5V/vExc512/D56Xb5fY4mC2DPb/b6fM66Oc5M7DTuK1
# LxCgnEuqm1Lo3CMR0k4W8Xezs7hWp+u3tr+i705l5qFxklYkmFeVAzTWdQ56JOGk
# Z1XKUbcPUnweormPMxMQXyxXpey4DBwUGbjC98iqE8tjUg6NA3o=
# =yVgk
# -----END PGP SIGNATURE-----
# gpg: Signature made Thu 10 Oct 2024 17:17:38 BST
# gpg:                using RSA key DAF3A6FDB26B62912D0E8E3FBE86EBB415104FDF
# gpg: Good signature from "Daniel P. Berrange <dan@berrange.com>" [full]
# gpg:                 aka "Daniel P. Berrange <berrange@redhat.com>" [full]
# Primary key fingerprint: DAF3 A6FD B26B 6291 2D0E  8E3F BE86 EBB4 1510 4FDF

* tag 'crypto-fixes-pull-request' of https://gitlab.com/berrange/qemu:
  tests/unit: Add a assert for test_io_channel_unix_listen_cleanup
  crypto: drop obsolete back compat logic for old nettle
  crypto/hashpriv: Remove old hash API function
  crypto/hash-afalg: Remove old hash API functions
  crypto/hash-nettle: Remove old hash API functions
  crypto/hash-gnutls: Remove old hash API functions
  crypto/hash-gcrypt: Remove old hash API functions
  crypto/hash-glib: Remove old hash API functions
  tests/unit/test-crypto-hash: accumulative hashing
  crypto/hash: Implement and use new hash API
  crypto/hash-afalg: Implement new hash API
  util/iov: Introduce iov_send_recv_with_flags()
  crypto/hash-nettle: Implement new hash API
  crypto/hash-gnutls: Implement new hash API
  crypto/hash-gcrypt: Implement new hash API
  crypto/hash-glib: Implement new hash API
  crypto: accumulative hashing API

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2024-10-10 18:05:43 +01:00
commit 7e3b6d8063
12 changed files with 733 additions and 282 deletions

View File

@ -1,6 +1,7 @@
/*
* QEMU Crypto af_alg-backend hash/hmac support
*
* Copyright (c) 2024 Seagate Technology LLC and/or its Affiliates
* Copyright (c) 2017 HUAWEI TECHNOLOGIES CO., LTD.
*
* Authors:
@ -113,6 +114,128 @@ qcrypto_afalg_hmac_ctx_new(QCryptoHashAlgo alg,
return qcrypto_afalg_hash_hmac_ctx_new(alg, key, nkey, true, errp);
}
static
QCryptoHash *qcrypto_afalg_hash_new(QCryptoHashAlgo alg, Error **errp)
{
/* Check if hash algorithm is supported */
char *alg_name = qcrypto_afalg_hash_format_name(alg, false, NULL);
QCryptoHash *hash;
if (alg_name == NULL) {
error_setg(errp, "Unknown hash algorithm %d", alg);
return NULL;
}
g_free(alg_name);
hash = g_new(QCryptoHash, 1);
hash->alg = alg;
hash->opaque = qcrypto_afalg_hash_ctx_new(alg, errp);
if (!hash->opaque) {
free(hash);
return NULL;
}
return hash;
}
static
void qcrypto_afalg_hash_free(QCryptoHash *hash)
{
QCryptoAFAlg *ctx = hash->opaque;
if (ctx) {
qcrypto_afalg_comm_free(ctx);
}
g_free(hash);
}
/**
* Send data to the kernel's crypto core.
*
* The more_data parameter is used to notify the crypto engine
* that this is an "update" operation, and that more data will
* be provided to calculate the final hash.
*/
static
int qcrypto_afalg_send_to_kernel(QCryptoAFAlg *afalg,
const struct iovec *iov,
size_t niov,
bool more_data,
Error **errp)
{
int ret = 0;
int flags = (more_data ? MSG_MORE : 0);
/* send data to kernel's crypto core */
ret = iov_send_recv_with_flags(afalg->opfd, flags, iov, niov,
0, iov_size(iov, niov), true);
if (ret < 0) {
error_setg_errno(errp, errno, "Send data to afalg-core failed");
ret = -1;
} else {
/* No error, so return 0 */
ret = 0;
}
return ret;
}
static
int qcrypto_afalg_recv_from_kernel(QCryptoAFAlg *afalg,
QCryptoHashAlgo alg,
uint8_t **result,
size_t *result_len,
Error **errp)
{
struct iovec outv;
int ret;
const int expected_len = qcrypto_hash_digest_len(alg);
if (*result_len == 0) {
*result_len = expected_len;
*result = g_new0(uint8_t, *result_len);
} else if (*result_len != expected_len) {
error_setg(errp,
"Result buffer size %zu is not match hash %d",
*result_len, expected_len);
return -1;
}
/* hash && get result */
outv.iov_base = *result;
outv.iov_len = *result_len;
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");
return -1;
}
return 0;
}
static
int qcrypto_afalg_hash_update(QCryptoHash *hash,
const struct iovec *iov,
size_t niov,
Error **errp)
{
return qcrypto_afalg_send_to_kernel((QCryptoAFAlg *) hash->opaque,
iov, niov, true, errp);
}
static
int qcrypto_afalg_hash_finalize(QCryptoHash *hash,
uint8_t **result,
size_t *result_len,
Error **errp)
{
return qcrypto_afalg_recv_from_kernel((QCryptoAFAlg *) hash->opaque,
hash->alg, result, result_len, errp);
}
static int
qcrypto_afalg_hash_hmac_bytesv(QCryptoAFAlgo *hmac,
QCryptoHashAlgo alg,
@ -121,68 +244,17 @@ qcrypto_afalg_hash_hmac_bytesv(QCryptoAFAlgo *hmac,
size_t *resultlen,
Error **errp)
{
QCryptoAFAlgo *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;
ret = qcrypto_afalg_send_to_kernel(hmac, iov, niov, false, errp);
if (ret == 0) {
ret = qcrypto_afalg_recv_from_kernel(hmac, alg, result,
resultlen, errp);
}
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(QCryptoHashAlgo 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,
@ -204,7 +276,10 @@ static void qcrypto_afalg_hmac_ctx_free(QCryptoHmac *hmac)
}
QCryptoHashDriver qcrypto_hash_afalg_driver = {
.hash_bytesv = qcrypto_afalg_hash_bytesv,
.hash_new = qcrypto_afalg_hash_new,
.hash_free = qcrypto_afalg_hash_free,
.hash_update = qcrypto_afalg_hash_update,
.hash_finalize = qcrypto_afalg_hash_finalize
};
QCryptoHmacDriver qcrypto_hmac_afalg_driver = {

View File

@ -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
@ -44,73 +45,84 @@ gboolean qcrypto_hash_supports(QCryptoHashAlgo alg)
return false;
}
static int
qcrypto_gcrypt_hash_bytesv(QCryptoHashAlgo alg,
const struct iovec *iov,
size_t niov,
uint8_t **result,
size_t *resultlen,
Error **errp)
static
QCryptoHash *qcrypto_gcrypt_hash_new(QCryptoHashAlgo alg, Error **errp)
{
int i, ret;
gcry_md_hd_t md;
unsigned char *digest;
QCryptoHash *hash;
int ret;
if (!qcrypto_hash_supports(alg)) {
error_setg(errp,
"Unknown hash algorithm %d",
alg);
return -1;
}
ret = gcry_md_open(&md, qcrypto_hash_alg_map[alg], 0);
hash = g_new(QCryptoHash, 1);
hash->alg = alg;
hash->opaque = g_new(gcry_md_hd_t, 1);
ret = gcry_md_open((gcry_md_hd_t *) hash->opaque,
qcrypto_hash_alg_map[alg], 0);
if (ret < 0) {
error_setg(errp,
"Unable to initialize hash algorithm: %s",
gcry_strerror(ret));
return -1;
g_free(hash->opaque);
g_free(hash);
return NULL;
}
return hash;
}
static
void qcrypto_gcrypt_hash_free(QCryptoHash *hash)
{
gcry_md_hd_t *ctx = hash->opaque;
if (ctx) {
gcry_md_close(*ctx);
g_free(ctx);
}
for (i = 0; i < niov; i++) {
gcry_md_write(md, 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));
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;
}
digest = gcry_md_read(md, 0);
if (!digest) {
error_setg(errp,
"No digest produced");
goto error;
}
memcpy(*result, digest, *resultlen);
gcry_md_close(md);
return 0;
error:
gcry_md_close(md);
return -1;
g_free(hash);
}
static
int qcrypto_gcrypt_hash_update(QCryptoHash *hash,
const struct iovec *iov,
size_t niov,
Error **errp)
{
gcry_md_hd_t *ctx = hash->opaque;
for (int i = 0; i < niov; i++) {
gcry_md_write(*ctx, iov[i].iov_base, iov[i].iov_len);
}
return 0;
}
static
int qcrypto_gcrypt_hash_finalize(QCryptoHash *hash,
uint8_t **result,
size_t *result_len,
Error **errp)
{
unsigned char *digest;
gcry_md_hd_t *ctx = hash->opaque;
*result_len = gcry_md_get_algo_dlen(qcrypto_hash_alg_map[hash->alg]);
if (*result_len == 0) {
error_setg(errp, "Unable to get hash length");
return -1;
}
*result = g_new(uint8_t, *result_len);
/* Digest is freed by gcry_md_close(), copy it */
digest = gcry_md_read(*ctx, 0);
memcpy(*result, digest, *result_len);
return 0;
}
QCryptoHashDriver qcrypto_hash_lib_driver = {
.hash_bytesv = qcrypto_gcrypt_hash_bytesv,
.hash_new = qcrypto_gcrypt_hash_new,
.hash_update = qcrypto_gcrypt_hash_update,
.hash_finalize = qcrypto_gcrypt_hash_finalize,
.hash_free = qcrypto_gcrypt_hash_free,
};

View File

@ -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
@ -43,58 +44,71 @@ gboolean qcrypto_hash_supports(QCryptoHashAlgo alg)
return false;
}
static int
qcrypto_glib_hash_bytesv(QCryptoHashAlgo alg,
const struct iovec *iov,
size_t niov,
uint8_t **result,
size_t *resultlen,
Error **errp)
static
QCryptoHash *qcrypto_glib_hash_new(QCryptoHashAlgo alg,
Error **errp)
{
int i, ret;
GChecksum *cs;
QCryptoHash *hash;
if (!qcrypto_hash_supports(alg)) {
error_setg(errp,
"Unknown hash algorithm %d",
alg);
return -1;
hash = g_new(QCryptoHash, 1);
hash->alg = alg;
hash->opaque = g_checksum_new(qcrypto_hash_alg_map[alg]);
return hash;
}
static
void qcrypto_glib_hash_free(QCryptoHash *hash)
{
if (hash->opaque) {
g_checksum_free(hash->opaque);
}
cs = g_checksum_new(qcrypto_hash_alg_map[alg]);
for (i = 0; i < niov; i++) {
g_checksum_update(cs, 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");
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;
}
g_checksum_get_digest(cs, *result, resultlen);
g_checksum_free(cs);
return 0;
error:
g_checksum_free(cs);
return -1;
g_free(hash);
}
static
int qcrypto_glib_hash_update(QCryptoHash *hash,
const struct iovec *iov,
size_t niov,
Error **errp)
{
GChecksum *ctx = hash->opaque;
for (int i = 0; i < niov; i++) {
g_checksum_update(ctx, iov[i].iov_base, iov[i].iov_len);
}
return 0;
}
static
int qcrypto_glib_hash_finalize(QCryptoHash *hash,
uint8_t **result,
size_t *result_len,
Error **errp)
{
int ret;
GChecksum *ctx = hash->opaque;
ret = g_checksum_type_get_length(qcrypto_hash_alg_map[hash->alg]);
if (ret < 0) {
error_setg(errp, "Unable to get hash length");
*result_len = 0;
return -1;
}
*result_len = ret;
*result = g_new(uint8_t, *result_len);
g_checksum_get_digest(ctx, *result, result_len);
return 0;
}
QCryptoHashDriver qcrypto_hash_lib_driver = {
.hash_bytesv = qcrypto_glib_hash_bytesv,
.hash_new = qcrypto_glib_hash_new,
.hash_update = qcrypto_glib_hash_update,
.hash_finalize = qcrypto_glib_hash_finalize,
.hash_free = qcrypto_glib_hash_free,
};

View File

@ -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
@ -52,53 +53,83 @@ gboolean qcrypto_hash_supports(QCryptoHashAlgo alg)
return false;
}
static int
qcrypto_gnutls_hash_bytesv(QCryptoHashAlgo alg,
const struct iovec *iov,
size_t niov,
uint8_t **result,
size_t *resultlen,
Error **errp)
static
QCryptoHash *qcrypto_gnutls_hash_new(QCryptoHashAlgo alg, Error **errp)
{
int i, ret;
gnutls_hash_hd_t hash;
QCryptoHash *hash;
int ret;
if (!qcrypto_hash_supports(alg)) {
error_setg(errp,
"Unknown hash algorithm %d",
alg);
return -1;
}
hash = g_new(QCryptoHash, 1);
hash->alg = alg;
hash->opaque = g_new(gnutls_hash_hd_t, 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;
}
ret = gnutls_hash_init(&hash, qcrypto_hash_alg_map[alg]);
ret = gnutls_hash_init(hash->opaque, qcrypto_hash_alg_map[alg]);
if (ret < 0) {
error_setg(errp,
"Unable to initialize hash algorithm: %s",
gnutls_strerror(ret));
return -1;
g_free(hash->opaque);
g_free(hash);
return NULL;
}
for (i = 0; i < niov; i++) {
gnutls_hash(hash, iov[i].iov_base, iov[i].iov_len);
}
return hash;
}
gnutls_hash_deinit(hash, *result);
return 0;
static
void qcrypto_gnutls_hash_free(QCryptoHash *hash)
{
gnutls_hash_hd_t *ctx = hash->opaque;
gnutls_hash_deinit(*ctx, NULL);
g_free(ctx);
g_free(hash);
}
static
int qcrypto_gnutls_hash_update(QCryptoHash *hash,
const struct iovec *iov,
size_t niov,
Error **errp)
{
int ret = 0;
gnutls_hash_hd_t *ctx = hash->opaque;
for (int i = 0; i < niov; i++) {
ret = gnutls_hash(*ctx, iov[i].iov_base, iov[i].iov_len);
if (ret != 0) {
error_setg(errp, "Failed to hash data: %s",
gnutls_strerror(ret));
return -1;
}
}
return 0;
}
static
int qcrypto_gnutls_hash_finalize(QCryptoHash *hash,
uint8_t **result,
size_t *result_len,
Error **errp)
{
gnutls_hash_hd_t *ctx = hash->opaque;
*result_len = gnutls_hash_get_len(qcrypto_hash_alg_map[hash->alg]);
if (*result_len == 0) {
error_setg(errp, "Unable to get hash length");
return -1;
}
*result = g_new(uint8_t, *result_len);
gnutls_hash_output(*ctx, *result);
return 0;
}
QCryptoHashDriver qcrypto_hash_lib_driver = {
.hash_bytesv = qcrypto_gnutls_hash_bytesv,
.hash_new = qcrypto_gnutls_hash_new,
.hash_update = qcrypto_gnutls_hash_update,
.hash_finalize = qcrypto_gnutls_hash_finalize,
.hash_free = qcrypto_gnutls_hash_free,
};

View File

@ -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
@ -103,59 +104,64 @@ gboolean qcrypto_hash_supports(QCryptoHashAlgo alg)
return false;
}
static int
qcrypto_nettle_hash_bytesv(QCryptoHashAlgo alg,
const struct iovec *iov,
size_t niov,
uint8_t **result,
size_t *resultlen,
Error **errp)
static
QCryptoHash *qcrypto_nettle_hash_new(QCryptoHashAlgo alg, Error **errp)
{
size_t i;
union qcrypto_hash_ctx ctx;
QCryptoHash *hash;
if (!qcrypto_hash_supports(alg)) {
error_setg(errp,
"Unknown hash algorithm %d",
alg);
return -1;
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++) {
qcrypto_hash_alg_map[hash->alg].write(ctx,
iov[i].iov_len,
iov[i].iov_base);
}
qcrypto_hash_alg_map[alg].init(&ctx);
for (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[alg].write(&ctx, len, base);
len -= shortlen;
base += len;
}
}
if (*resultlen == 0) {
*resultlen = qcrypto_hash_alg_map[alg].len;
*result = g_new0(uint8_t, *resultlen);
} else if (*resultlen != qcrypto_hash_alg_map[alg].len) {
error_setg(errp,
"Result buffer size %zu is smaller than hash %zu",
*resultlen, qcrypto_hash_alg_map[alg].len);
return -1;
}
qcrypto_hash_alg_map[alg].result(&ctx, *resultlen, *result);
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_bytesv = qcrypto_nettle_hash_bytesv,
.hash_new = qcrypto_nettle_hash_new,
.hash_update = qcrypto_nettle_hash_update,
.hash_finalize = qcrypto_nettle_hash_finalize,
.hash_free = qcrypto_nettle_hash_free,
};

View File

@ -1,6 +1,7 @@
/*
* QEMU Crypto hash algorithms
*
* Copyright (c) 2024 Seagate Technology LLC and/or its Affiliates
* Copyright (c) 2015 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
@ -19,6 +20,8 @@
*/
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "qapi-types-crypto.h"
#include "crypto/hash.h"
#include "hashpriv.h"
@ -45,23 +48,18 @@ int qcrypto_hash_bytesv(QCryptoHashAlgo alg,
size_t *resultlen,
Error **errp)
{
#ifdef CONFIG_AF_ALG
int ret;
/*
* TODO:
* Maybe we should treat some afalg errors as fatal
*/
ret = qcrypto_hash_afalg_driver.hash_bytesv(alg, iov, niov,
result, resultlen,
NULL);
if (ret == 0) {
return ret;
}
#endif
g_autoptr(QCryptoHash) ctx = qcrypto_hash_new(alg, errp);
return qcrypto_hash_lib_driver.hash_bytesv(alg, iov, niov,
result, resultlen,
errp);
if (!ctx) {
return -1;
}
if (qcrypto_hash_updatev(ctx, iov, niov, errp) < 0 ||
qcrypto_hash_finalize_bytes(ctx, result, resultlen, errp) < 0) {
return -1;
}
return 0;
}
@ -77,29 +75,130 @@ int qcrypto_hash_bytes(QCryptoHashAlgo alg,
return qcrypto_hash_bytesv(alg, &iov, 1, result, resultlen, errp);
}
int qcrypto_hash_updatev(QCryptoHash *hash,
const struct iovec *iov,
size_t niov,
Error **errp)
{
QCryptoHashDriver *drv = hash->driver;
return drv->hash_update(hash, iov, niov, errp);
}
int qcrypto_hash_update(QCryptoHash *hash,
const char *buf,
size_t len,
Error **errp)
{
struct iovec iov = { .iov_base = (char *)buf, .iov_len = len };
return qcrypto_hash_updatev(hash, &iov, 1, errp);
}
QCryptoHash *qcrypto_hash_new(QCryptoHashAlgo alg, Error **errp)
{
QCryptoHash *hash = NULL;
if (!qcrypto_hash_supports(alg)) {
error_setg(errp, "Unsupported hash algorithm %s",
QCryptoHashAlgo_str(alg));
return NULL;
}
#ifdef CONFIG_AF_ALG
hash = qcrypto_hash_afalg_driver.hash_new(alg, NULL);
if (hash) {
hash->driver = &qcrypto_hash_afalg_driver;
return hash;
}
#endif
hash = qcrypto_hash_lib_driver.hash_new(alg, errp);
if (!hash) {
return NULL;
}
hash->driver = &qcrypto_hash_lib_driver;
return hash;
}
void qcrypto_hash_free(QCryptoHash *hash)
{
QCryptoHashDriver *drv;
if (hash) {
drv = hash->driver;
drv->hash_free(hash);
}
}
int qcrypto_hash_finalize_bytes(QCryptoHash *hash,
uint8_t **result,
size_t *result_len,
Error **errp)
{
QCryptoHashDriver *drv = hash->driver;
return drv->hash_finalize(hash, result, result_len, errp);
}
static const char hex[] = "0123456789abcdef";
int qcrypto_hash_finalize_digest(QCryptoHash *hash,
char **digest,
Error **errp)
{
int ret;
g_autofree uint8_t *result = NULL;
size_t resultlen = 0;
size_t i;
ret = qcrypto_hash_finalize_bytes(hash, &result, &resultlen, errp);
if (ret == 0) {
*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';
}
return ret;
}
int qcrypto_hash_finalize_base64(QCryptoHash *hash,
char **base64,
Error **errp)
{
int ret;
g_autofree uint8_t *result = NULL;
size_t resultlen = 0;
ret = qcrypto_hash_finalize_bytes(hash, &result, &resultlen, errp);
if (ret == 0) {
*base64 = g_base64_encode(result, resultlen);
}
return ret;
}
int qcrypto_hash_digestv(QCryptoHashAlgo alg,
const struct iovec *iov,
size_t niov,
char **digest,
Error **errp)
{
uint8_t *result = NULL;
size_t resultlen = 0;
size_t i;
g_autoptr(QCryptoHash) ctx = qcrypto_hash_new(alg, errp);
if (qcrypto_hash_bytesv(alg, iov, niov, &result, &resultlen, errp) < 0) {
if (!ctx) {
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];
if (qcrypto_hash_updatev(ctx, iov, niov, errp) < 0 ||
qcrypto_hash_finalize_digest(ctx, digest, errp) < 0) {
return -1;
}
(*digest)[resultlen * 2] = '\0';
g_free(result);
return 0;
}
@ -120,15 +219,17 @@ int qcrypto_hash_base64v(QCryptoHashAlgo alg,
char **base64,
Error **errp)
{
uint8_t *result = NULL;
size_t resultlen = 0;
g_autoptr(QCryptoHash) ctx = qcrypto_hash_new(alg, errp);
if (qcrypto_hash_bytesv(alg, iov, niov, &result, &resultlen, errp) < 0) {
if (!ctx) {
return -1;
}
if (qcrypto_hash_updatev(ctx, iov, niov, errp) < 0 ||
qcrypto_hash_finalize_base64(ctx, base64, errp) < 0) {
return -1;
}
*base64 = g_base64_encode(result, resultlen);
g_free(result);
return 0;
}

View File

@ -1,6 +1,7 @@
/*
* QEMU Crypto hash driver supports
*
* Copyright (c) 2024 Seagate Technology LLC and/or its Affiliates
* Copyright (c) 2017 HUAWEI TECHNOLOGIES CO., LTD.
*
* Authors:
@ -15,15 +16,21 @@
#ifndef QCRYPTO_HASHPRIV_H
#define QCRYPTO_HASHPRIV_H
#include "crypto/hash.h"
typedef struct QCryptoHashDriver QCryptoHashDriver;
struct QCryptoHashDriver {
int (*hash_bytesv)(QCryptoHashAlgo alg,
QCryptoHash *(*hash_new)(QCryptoHashAlgo alg, Error **errp);
int (*hash_update)(QCryptoHash *hash,
const struct iovec *iov,
size_t niov,
uint8_t **result,
size_t *resultlen,
Error **errp);
int (*hash_finalize)(QCryptoHash *hash,
uint8_t **result,
size_t *resultlen,
Error **errp);
void (*hash_free)(QCryptoHash *hash);
};
extern QCryptoHashDriver qcrypto_hash_lib_driver;

View File

@ -1,6 +1,7 @@
/*
* QEMU Crypto hash algorithms
*
* Copyright (c) 2024 Seagate Technology LLC and/or its Affiliates
* Copyright (c) 2015 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
@ -33,6 +34,13 @@
/* See also "QCryptoHashAlgo" defined in qapi/crypto.json */
typedef struct QCryptoHash QCryptoHash;
struct QCryptoHash {
QCryptoHashAlgo alg;
void *opaque;
void *driver;
};
/**
* qcrypto_hash_supports:
* @alg: the hash algorithm
@ -128,6 +136,117 @@ int qcrypto_hash_digestv(QCryptoHashAlgo alg,
char **digest,
Error **errp);
/**
* qcrypto_hash_updatev:
* @hash: hash object from qcrypto_hash_new
* @iov: the array of memory regions to hash
* @niov: the length of @iov
* @errp: pointer to a NULL-initialized error object
*
* Updates the given hash object with all the memory regions
* present in @iov.
*
* Returns: 0 on success, -1 on error
*/
int qcrypto_hash_updatev(QCryptoHash *hash,
const struct iovec *iov,
size_t niov,
Error **errp);
/**
* qcrypto_hash_update:
* @hash: hash object from qcrypto_hash_new
* @buf: the memory region to hash
* @len: the length of @buf
* @errp: pointer to a NULL-initialized error object
*
* Updates the given hash object with the data from
* the given buffer.
*
* Returns: 0 on success, -1 on error
*/
int qcrypto_hash_update(QCryptoHash *hash,
const char *buf,
size_t len,
Error **errp);
/**
* qcrypto_hash_finalize_digest:
* @hash: the hash object to finalize
* @digest: pointer to hold output hash
* @errp: pointer to a NULL-initialized error object
*
* Computes the hash from the given hash object. Hash object
* is expected to have its data updated from the qcrypto_hash_update function.
* The @digest pointer will be filled with the printable hex digest of the
* computed hash, which will be terminated by '\0'. The memory pointer
* in @digest must be released with a call to g_free() when
* no longer required.
*
* Returns: 0 on success, -1 on error
*/
int qcrypto_hash_finalize_digest(QCryptoHash *hash,
char **digest,
Error **errp);
/**
* qcrypto_hash_finalize_base64:
* @hash_ctx: hash object to finalize
* @base64: pointer to store the hash result in
* @errp: pointer to a NULL-initialized error object
*
* Computes the hash from the given hash object. Hash object
* is expected to have it's data updated from the qcrypto_hash_update function.
* The @base64 pointer will be filled with the base64 encoding of the computed
* hash, which will be terminated by '\0'. The memory pointer in @base64
* must be released with a call to g_free() when no longer required.
*
* Returns: 0 on success, -1 on error
*/
int qcrypto_hash_finalize_base64(QCryptoHash *hash,
char **base64,
Error **errp);
/**
* qcrypto_hash_finalize_bytes:
* @hash_ctx: hash object to finalize
* @result: pointer to store the hash result in
* @result_len: Pointer to store the length of the result in
* @errp: pointer to a NULL-initialized error object
*
* Computes the hash from the given hash object. Hash object
* is expected to have it's data updated from the qcrypto_hash_update function.
* The memory pointer in @result must be released with a call to g_free()
* when no longer required.
*
* Returns: 0 on success, -1 on error
*/
int qcrypto_hash_finalize_bytes(QCryptoHash *hash,
uint8_t **result,
size_t *result_len,
Error **errp);
/**
* qcrypto_hash_new:
* @alg: the hash algorithm
* @errp: pointer to a NULL-initialized error object
*
* Creates a new hashing context for the chosen algorithm for
* usage with qcrypto_hash_update.
*
* Returns: New hash object with the given algorithm, or NULL on error.
*/
QCryptoHash *qcrypto_hash_new(QCryptoHashAlgo alg, Error **errp);
/**
* qcrypto_hash_free:
* @hash: hash object to free
*
* Frees a hashing context for the chosen algorithm.
*/
void qcrypto_hash_free(QCryptoHash *hash);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoHash, qcrypto_hash_free)
/**
* qcrypto_hash_digest:
* @alg: the hash algorithm

View File

@ -1,6 +1,7 @@
/*
* Helpers for using (partial) iovecs.
*
* Copyright (c) 2024 Seagate Technology LLC and/or its Affiliates
* Copyright (C) 2010 Red Hat, Inc.
*
* Author(s):
@ -75,6 +76,32 @@ iov_to_buf(const struct iovec *iov, const unsigned int iov_cnt,
size_t iov_memset(const struct iovec *iov, const unsigned int iov_cnt,
size_t offset, int fillc, size_t bytes);
/*
* Send/recv data from/to iovec buffers directly, with the provided
* socket flags.
*
* `offset' bytes in the beginning of iovec buffer are skipped and
* next `bytes' bytes are used, which must be within data of iovec.
*
* r = iov_send_recv_with_flags(sockfd, sockflags, iov, iovcnt,
* offset, bytes, true);
*
* is logically equivalent to
*
* char *buf = malloc(bytes);
* iov_to_buf(iov, iovcnt, offset, buf, bytes);
* r = send(sockfd, buf, bytes, sockflags);
* free(buf);
*
* For iov_send_recv_with_flags() _whole_ area being sent or received
* should be within the iovec, not only beginning of it.
*/
ssize_t iov_send_recv_with_flags(int sockfd, int sockflags,
const struct iovec *iov,
unsigned iov_cnt, size_t offset,
size_t bytes,
bool do_send);
/*
* Send/recv data from/to iovec buffers directly
*

View File

@ -1,6 +1,7 @@
/*
* QEMU Crypto hash algorithms
*
* Copyright (c) 2024 Seagate Technology LLC and/or its Affiliates
* Copyright (c) 2015 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
@ -241,6 +242,50 @@ static void test_hash_base64(void)
}
}
static void test_hash_accumulate(void)
{
size_t i;
for (i = 0; i < G_N_ELEMENTS(expected_outputs) ; i++) {
g_autoptr(QCryptoHash) hash = NULL;
struct iovec iov[] = {
{ .iov_base = (char *)INPUT_TEXT1, .iov_len = strlen(INPUT_TEXT1) },
{ .iov_base = (char *)INPUT_TEXT2, .iov_len = strlen(INPUT_TEXT2) },
{ .iov_base = (char *)INPUT_TEXT3, .iov_len = strlen(INPUT_TEXT3) },
};
g_autofree uint8_t *result = NULL;
size_t resultlen = 0;
int ret;
size_t j;
if (!qcrypto_hash_supports(i)) {
continue;
}
hash = qcrypto_hash_new(i, &error_fatal);
g_assert(hash != NULL);
/* Add each iovec to the hash context separately */
for (j = 0; j < G_N_ELEMENTS(iov); j++) {
ret = qcrypto_hash_updatev(hash,
&iov[j], 1,
&error_fatal);
g_assert(ret == 0);
}
ret = qcrypto_hash_finalize_bytes(hash, &result, &resultlen,
&error_fatal);
g_assert(ret == 0);
g_assert(resultlen == expected_lens[i]);
for (j = 0; j < resultlen; j++) {
g_assert(expected_outputs[i][j * 2] == hex[(result[j] >> 4) & 0xf]);
g_assert(expected_outputs[i][j * 2 + 1] == hex[result[j] & 0xf]);
}
}
}
int main(int argc, char **argv)
{
int ret = qcrypto_init(&error_fatal);
@ -252,5 +297,6 @@ int main(int argc, char **argv)
g_test_add_func("/crypto/hash/prealloc", test_hash_prealloc);
g_test_add_func("/crypto/hash/digest", test_hash_digest);
g_test_add_func("/crypto/hash/base64", test_hash_base64);
g_test_add_func("/crypto/hash/accumulate", test_hash_accumulate);
return g_test_run();
}

View File

@ -506,7 +506,7 @@ static void test_io_channel_unix_listen_cleanup(void)
{
QIOChannelSocket *ioc;
struct sockaddr_un un;
int sock;
int sock, ret = 0;
#define TEST_SOCKET "test-io-channel-socket.sock"
@ -519,7 +519,9 @@ static void test_io_channel_unix_listen_cleanup(void)
un.sun_family = AF_UNIX;
snprintf(un.sun_path, sizeof(un.sun_path), "%s", TEST_SOCKET);
unlink(TEST_SOCKET);
bind(sock, (struct sockaddr *)&un, sizeof(un));
ret = bind(sock, (struct sockaddr *)&un, sizeof(un));
g_assert_cmpint(ret, ==, 0);
ioc->fd = sock;
ioc->localAddrLen = sizeof(ioc->localAddr);
getsockname(sock, (struct sockaddr *)&ioc->localAddr,

View File

@ -3,6 +3,7 @@
*
* Copyright IBM, Corp. 2007, 2008
* Copyright (C) 2010 Red Hat, Inc.
* Copyright (c) 2024 Seagate Technology LLC and/or its Affiliates
*
* Author(s):
* Anthony Liguori <aliguori@us.ibm.com>
@ -92,7 +93,8 @@ size_t iov_size(const struct iovec *iov, const unsigned int iov_cnt)
/* helper function for iov_send_recv() */
static ssize_t
do_send_recv(int sockfd, struct iovec *iov, unsigned iov_cnt, bool do_send)
do_send_recv(int sockfd, int flags, struct iovec *iov, unsigned iov_cnt,
bool do_send)
{
#ifdef CONFIG_POSIX
ssize_t ret;
@ -102,8 +104,8 @@ do_send_recv(int sockfd, struct iovec *iov, unsigned iov_cnt, bool do_send)
msg.msg_iovlen = iov_cnt;
do {
ret = do_send
? sendmsg(sockfd, &msg, 0)
: recvmsg(sockfd, &msg, 0);
? sendmsg(sockfd, &msg, flags)
: recvmsg(sockfd, &msg, flags);
} while (ret < 0 && errno == EINTR);
return ret;
#else
@ -114,8 +116,8 @@ do_send_recv(int sockfd, struct iovec *iov, unsigned iov_cnt, bool do_send)
ssize_t off = 0;
while (i < iov_cnt) {
ssize_t r = do_send
? send(sockfd, iov[i].iov_base + off, iov[i].iov_len - off, 0)
: recv(sockfd, iov[i].iov_base + off, iov[i].iov_len - off, 0);
? send(sockfd, iov[i].iov_base + off, iov[i].iov_len - off, flags)
: recv(sockfd, iov[i].iov_base + off, iov[i].iov_len - off, flags);
if (r > 0) {
ret += r;
off += r;
@ -144,6 +146,15 @@ do_send_recv(int sockfd, struct iovec *iov, unsigned iov_cnt, bool do_send)
ssize_t iov_send_recv(int sockfd, const struct iovec *_iov, unsigned iov_cnt,
size_t offset, size_t bytes,
bool do_send)
{
return iov_send_recv_with_flags(sockfd, 0, _iov, iov_cnt, offset, bytes,
do_send);
}
ssize_t iov_send_recv_with_flags(int sockfd, int sockflags,
const struct iovec *_iov,
unsigned iov_cnt, size_t offset,
size_t bytes, bool do_send)
{
ssize_t total = 0;
ssize_t ret;
@ -192,11 +203,11 @@ ssize_t iov_send_recv(int sockfd, const struct iovec *_iov, unsigned iov_cnt,
assert(iov[niov].iov_len > tail);
orig_len = iov[niov].iov_len;
iov[niov++].iov_len = tail;
ret = do_send_recv(sockfd, iov, niov, do_send);
ret = do_send_recv(sockfd, sockflags, iov, niov, do_send);
/* Undo the changes above before checking for errors */
iov[niov-1].iov_len = orig_len;
} else {
ret = do_send_recv(sockfd, iov, niov, do_send);
ret = do_send_recv(sockfd, sockflags, iov, niov, do_send);
}
if (offset) {
iov[0].iov_base -= offset;