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:
commit
7e3b6d8063
@ -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 = {
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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,
|
||||
};
|
||||
|
163
crypto/hash.c
163
crypto/hash.c
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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,
|
||||
|
25
util/iov.c
25
util/iov.c
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user