From 48b8583698d96d6290726400789fcd51c55691b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 29 Aug 2024 11:58:51 +0100 Subject: [PATCH 01/10] iotests: fix expected output from gnutls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Error reporting from gnutls was improved by: commit 57941c9c86357a6a642f9ee3279d881df4043b6d Author: Daniel P. Berrangé Date: Fri Mar 15 14:07:58 2024 +0000 crypto: push error reporting into TLS session I/O APIs This has the effect of changing the output from one of the NBD tests. Reported-by: Thomas Huth Signed-off-by: Daniel P. Berrangé --- tests/qemu-iotests/233.out | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/qemu-iotests/233.out b/tests/qemu-iotests/233.out index 1910f7df20..d498d55e0e 100644 --- a/tests/qemu-iotests/233.out +++ b/tests/qemu-iotests/233.out @@ -69,8 +69,8 @@ read 1048576/1048576 bytes at offset 1048576 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) == check TLS with authorization == -qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=PORT,tls-creds=tls0': Failed to read option reply: Cannot read from TLS channel: Software caused connection abort -qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=PORT,tls-creds=tls0': Failed to read option reply: Cannot read from TLS channel: Software caused connection abort +qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=PORT,tls-creds=tls0': Failed to read option reply: Cannot read from TLS channel: The TLS connection was non-properly terminated. +qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=PORT,tls-creds=tls0': Failed to read option reply: Cannot read from TLS channel: The TLS connection was non-properly terminated. == check TLS fail over UNIX with no hostname == qemu-img: Could not open 'driver=nbd,path=SOCK_DIR/qemu-nbd.sock,tls-creds=tls0': No hostname for certificate validation @@ -103,14 +103,14 @@ qemu-img: Could not open 'driver=nbd,path=SOCK_DIR/qemu-nbd.sock,tls-creds=tls0' qemu-nbd: TLS handshake failed: The TLS connection was non-properly terminated. == final server log == -qemu-nbd: option negotiation failed: Failed to read opts magic: Cannot read from TLS channel: Software caused connection abort -qemu-nbd: option negotiation failed: Failed to read opts magic: Cannot read from TLS channel: Software caused connection abort +qemu-nbd: option negotiation failed: Failed to read opts magic: Cannot read from TLS channel: The TLS connection was non-properly terminated. +qemu-nbd: option negotiation failed: Failed to read opts magic: Cannot read from TLS channel: The TLS connection was non-properly terminated. qemu-nbd: option negotiation failed: Verify failed: No certificate was found. qemu-nbd: option negotiation failed: Verify failed: No certificate was found. qemu-nbd: option negotiation failed: TLS x509 authz check for DISTINGUISHED-NAME is denied qemu-nbd: option negotiation failed: TLS x509 authz check for DISTINGUISHED-NAME is denied -qemu-nbd: option negotiation failed: Failed to read opts magic: Cannot read from TLS channel: Software caused connection abort -qemu-nbd: option negotiation failed: Failed to read opts magic: Cannot read from TLS channel: Software caused connection abort +qemu-nbd: option negotiation failed: Failed to read opts magic: Cannot read from TLS channel: The TLS connection was non-properly terminated. +qemu-nbd: option negotiation failed: Failed to read opts magic: Cannot read from TLS channel: The TLS connection was non-properly terminated. qemu-nbd: option negotiation failed: TLS handshake failed: An illegal parameter has been received. qemu-nbd: option negotiation failed: TLS handshake failed: An illegal parameter has been received. *** done From c72cab5ad9f849bbcfcf4be7952b8b8946cc626e Mon Sep 17 00:00:00 2001 From: Tiago Pasqualini Date: Wed, 4 Sep 2024 20:52:30 -0300 Subject: [PATCH 02/10] crypto: run qcrypto_pbkdf2_count_iters in a new thread MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CPU time accounting in the kernel has been demonstrated to have a sawtooth pattern[1][2]. This can cause the getrusage system call to not be as accurate as we are expecting, which can cause this calculation to stall. The kernel discussions shows that this inaccuracy happens when CPU time gets big enough, so this patch changes qcrypto_pbkdf2_count_iters to run in a fresh thread to avoid this inaccuracy. It also adds a sanity check to fail the process if CPU time is not accounted. [1] https://lore.kernel.org/lkml/159231011694.16989.16351419333851309713.tip-bot2@tip-bot2/ [2] https://lore.kernel.org/lkml/20221226031010.4079885-1-maxing.lan@bytedance.com/t/#m1c7f2fdc0ea742776a70fd1aa2a2e414c437f534 Resolves: #2398 Signed-off-by: Tiago Pasqualini Signed-off-by: Daniel P. Berrangé --- crypto/pbkdf.c | 53 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 7 deletions(-) diff --git a/crypto/pbkdf.c b/crypto/pbkdf.c index 8d198c152c..d1c06ef3ed 100644 --- a/crypto/pbkdf.c +++ b/crypto/pbkdf.c @@ -19,6 +19,7 @@ */ #include "qemu/osdep.h" +#include "qemu/thread.h" #include "qapi/error.h" #include "crypto/pbkdf.h" #ifndef _WIN32 @@ -85,12 +86,28 @@ static int qcrypto_pbkdf2_get_thread_cpu(unsigned long long *val_ms, #endif } -uint64_t qcrypto_pbkdf2_count_iters(QCryptoHashAlgorithm hash, - const uint8_t *key, size_t nkey, - const uint8_t *salt, size_t nsalt, - size_t nout, - Error **errp) +typedef struct CountItersData { + QCryptoHashAlgorithm hash; + const uint8_t *key; + size_t nkey; + const uint8_t *salt; + size_t nsalt; + size_t nout; + uint64_t iterations; + Error **errp; +} CountItersData; + +static void *threaded_qcrypto_pbkdf2_count_iters(void *data) { + CountItersData *iters_data = (CountItersData *) data; + QCryptoHashAlgorithm hash = iters_data->hash; + const uint8_t *key = iters_data->key; + size_t nkey = iters_data->nkey; + const uint8_t *salt = iters_data->salt; + size_t nsalt = iters_data->nsalt; + size_t nout = iters_data->nout; + Error **errp = iters_data->errp; + uint64_t ret = -1; g_autofree uint8_t *out = g_new(uint8_t, nout); uint64_t iterations = (1 << 15); @@ -114,7 +131,10 @@ uint64_t qcrypto_pbkdf2_count_iters(QCryptoHashAlgorithm hash, delta_ms = end_ms - start_ms; - if (delta_ms > 500) { + if (delta_ms == 0) { /* sanity check */ + error_setg(errp, "Unable to get accurate CPU usage"); + goto cleanup; + } else if (delta_ms > 500) { break; } else if (delta_ms < 100) { iterations = iterations * 10; @@ -129,5 +149,24 @@ uint64_t qcrypto_pbkdf2_count_iters(QCryptoHashAlgorithm hash, cleanup: memset(out, 0, nout); - return ret; + iters_data->iterations = ret; + return NULL; +} + +uint64_t qcrypto_pbkdf2_count_iters(QCryptoHashAlgorithm hash, + const uint8_t *key, size_t nkey, + const uint8_t *salt, size_t nsalt, + size_t nout, + Error **errp) +{ + CountItersData data = { + hash, key, nkey, salt, nsalt, nout, 0, errp + }; + QemuThread thread; + + qemu_thread_create(&thread, "pbkdf2", threaded_qcrypto_pbkdf2_count_iters, + &data, QEMU_THREAD_JOINABLE); + qemu_thread_join(&thread); + + return data.iterations; } From e6c09ea4f9e5f8af92a6453642b84b9efd52892f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 29 Aug 2024 14:47:42 +0100 Subject: [PATCH 03/10] crypto: check gnutls & gcrypt support the requested pbkdf hash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both gnutls and gcrypt can be configured to exclude support for certain algorithms via a runtime check against system crypto policies. Thus it is not sufficient to have a compile time test for hash support in their pbkdf implementations. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Daniel P. Berrangé --- crypto/pbkdf-gcrypt.c | 2 +- crypto/pbkdf-gnutls.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crypto/pbkdf-gcrypt.c b/crypto/pbkdf-gcrypt.c index a8d8e64f4d..bc0719c831 100644 --- a/crypto/pbkdf-gcrypt.c +++ b/crypto/pbkdf-gcrypt.c @@ -33,7 +33,7 @@ bool qcrypto_pbkdf2_supports(QCryptoHashAlgorithm hash) case QCRYPTO_HASH_ALG_SHA384: case QCRYPTO_HASH_ALG_SHA512: case QCRYPTO_HASH_ALG_RIPEMD160: - return true; + return qcrypto_hash_supports(hash); default: return false; } diff --git a/crypto/pbkdf-gnutls.c b/crypto/pbkdf-gnutls.c index 2dfbbd382c..911b565bea 100644 --- a/crypto/pbkdf-gnutls.c +++ b/crypto/pbkdf-gnutls.c @@ -33,7 +33,7 @@ bool qcrypto_pbkdf2_supports(QCryptoHashAlgorithm hash) case QCRYPTO_HASH_ALG_SHA384: case QCRYPTO_HASH_ALG_SHA512: case QCRYPTO_HASH_ALG_RIPEMD160: - return true; + return qcrypto_hash_supports(hash); default: return false; } From b0fe44b56b6d02e6f57577cdf69dfb54fd6ada99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 29 Aug 2024 14:50:33 +0100 Subject: [PATCH 04/10] tests/unit: always build the pbkdf crypto unit test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The meson rules were excluding the pbkdf crypto test when gnutls was the crypto backend. It was then excluded again in #if statements in the test file. Rather than update these conditions, remove them all, and use the result of the qcrypto_pbkdf_supports() function to determine whether to skip test registration. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Daniel P. Berrangé --- tests/unit/meson.build | 4 +--- tests/unit/test-crypto-pbkdf.c | 13 ++++++++----- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/tests/unit/meson.build b/tests/unit/meson.build index 490ab8182d..972d792883 100644 --- a/tests/unit/meson.build +++ b/tests/unit/meson.build @@ -121,9 +121,7 @@ if have_block if config_host_data.get('CONFIG_REPLICATION') tests += {'test-replication': [testblock]} endif - if nettle.found() or gcrypt.found() - tests += {'test-crypto-pbkdf': [io]} - endif + tests += {'test-crypto-pbkdf': [io]} endif if have_system diff --git a/tests/unit/test-crypto-pbkdf.c b/tests/unit/test-crypto-pbkdf.c index 43c417f6b4..241e1c2cf0 100644 --- a/tests/unit/test-crypto-pbkdf.c +++ b/tests/unit/test-crypto-pbkdf.c @@ -25,8 +25,7 @@ #include #endif -#if ((defined(CONFIG_NETTLE) || defined(CONFIG_GCRYPT)) && \ - (defined(_WIN32) || defined(RUSAGE_THREAD))) +#if defined(_WIN32) || defined(RUSAGE_THREAD) #include "crypto/pbkdf.h" typedef struct QCryptoPbkdfTestData QCryptoPbkdfTestData; @@ -394,7 +393,7 @@ static void test_pbkdf(const void *opaque) } -static void test_pbkdf_timing(void) +static void test_pbkdf_timing_sha256(void) { uint8_t key[32]; uint8_t salt[32]; @@ -422,14 +421,18 @@ int main(int argc, char **argv) g_assert(qcrypto_init(NULL) == 0); for (i = 0; i < G_N_ELEMENTS(test_data); i++) { + if (!qcrypto_pbkdf2_supports(test_data[i].hash)) { + continue; + } + if (!test_data[i].slow || g_test_slow()) { g_test_add_data_func(test_data[i].path, &test_data[i], test_pbkdf); } } - if (g_test_slow()) { - g_test_add_func("/crypt0/pbkdf/timing", test_pbkdf_timing); + if (g_test_slow() && qcrypto_pbkdf2_supports(QCRYPTO_HASH_ALG_SHA256)) { + g_test_add_func("/crypt0/pbkdf/timing/sha256", test_pbkdf_timing_sha256); } return g_test_run(); From ebe0302ac822da214f347f6cc37a73139660bed8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 4 Sep 2024 09:58:19 +0100 Subject: [PATCH 05/10] tests/unit: build pbkdf test on macOS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add CONFIG_DARWIN to the pbkdf test build condition, since we have a way to measure CPU time on this platform since commit bf98afc75efedf1. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Daniel P. Berrangé --- tests/unit/test-crypto-pbkdf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/test-crypto-pbkdf.c b/tests/unit/test-crypto-pbkdf.c index 241e1c2cf0..39264cb662 100644 --- a/tests/unit/test-crypto-pbkdf.c +++ b/tests/unit/test-crypto-pbkdf.c @@ -25,7 +25,7 @@ #include #endif -#if defined(_WIN32) || defined(RUSAGE_THREAD) +#if defined(_WIN32) || defined(RUSAGE_THREAD) || defined(CONFIG_DARWNI) #include "crypto/pbkdf.h" typedef struct QCryptoPbkdfTestData QCryptoPbkdfTestData; From 586ac2c67d707c2588766c5195d94fa553cc25af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 10 Jun 2024 10:26:18 +0100 Subject: [PATCH 06/10] crypto: avoid leak of ctx when bad cipher mode is given MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: Coverity CID 1546884 Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Daniel P. Berrangé --- crypto/cipher-nettle.c.inc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crypto/cipher-nettle.c.inc b/crypto/cipher-nettle.c.inc index 42b39e18a2..766de036ba 100644 --- a/crypto/cipher-nettle.c.inc +++ b/crypto/cipher-nettle.c.inc @@ -734,16 +734,19 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, #ifdef CONFIG_CRYPTO_SM4 case QCRYPTO_CIPHER_ALG_SM4: { - QCryptoNettleSm4 *ctx = g_new0(QCryptoNettleSm4, 1); + QCryptoNettleSm4 *ctx; + const QCryptoCipherDriver *drv; switch (mode) { case QCRYPTO_CIPHER_MODE_ECB: - ctx->base.driver = &qcrypto_nettle_sm4_driver_ecb; + drv = &qcrypto_nettle_sm4_driver_ecb; break; default: goto bad_cipher_mode; } + ctx = g_new0(QCryptoNettleSm4, 1); + ctx->base.driver = drv; sm4_set_encrypt_key(&ctx->key[0], key); sm4_set_decrypt_key(&ctx->key[1], key); From 41162dd6ee5bc8bfd73f549d6bddd24e9b205c18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 10 Jun 2024 10:37:33 +0100 Subject: [PATCH 07/10] crypto: use consistent error reporting pattern for unsupported cipher modes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not all paths in qcrypto_cipher_ctx_new() were correctly distinguishing between valid user input for cipher mode (which should report a user facing error), vs program logic errors (which should assert). Reported-by: Peter Maydell Signed-off-by: Daniel P. Berrangé --- crypto/cipher-nettle.c.inc | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/crypto/cipher-nettle.c.inc b/crypto/cipher-nettle.c.inc index 766de036ba..2654b439c1 100644 --- a/crypto/cipher-nettle.c.inc +++ b/crypto/cipher-nettle.c.inc @@ -525,8 +525,10 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, case QCRYPTO_CIPHER_MODE_CTR: drv = &qcrypto_nettle_des_driver_ctr; break; - default: + case QCRYPTO_CIPHER_MODE_XTS: goto bad_cipher_mode; + default: + g_assert_not_reached(); } ctx = g_new0(QCryptoNettleDES, 1); @@ -551,8 +553,10 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, case QCRYPTO_CIPHER_MODE_CTR: drv = &qcrypto_nettle_des3_driver_ctr; break; - default: + case QCRYPTO_CIPHER_MODE_XTS: goto bad_cipher_mode; + default: + g_assert_not_reached(); } ctx = g_new0(QCryptoNettleDES3, 1); @@ -663,8 +667,10 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, case QCRYPTO_CIPHER_MODE_CTR: drv = &qcrypto_nettle_cast128_driver_ctr; break; - default: + case QCRYPTO_CIPHER_MODE_XTS: goto bad_cipher_mode; + default: + g_assert_not_reached(); } ctx = g_new0(QCryptoNettleCAST128, 1); @@ -741,8 +747,12 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, case QCRYPTO_CIPHER_MODE_ECB: drv = &qcrypto_nettle_sm4_driver_ecb; break; - default: + case QCRYPTO_CIPHER_MODE_CBC: + case QCRYPTO_CIPHER_MODE_CTR: + case QCRYPTO_CIPHER_MODE_XTS: goto bad_cipher_mode; + default: + g_assert_not_reached(); } ctx = g_new0(QCryptoNettleSm4, 1); From 5d04de7de54e163b056980be10ee1c281a600276 Mon Sep 17 00:00:00 2001 From: Dorjoy Chowdhury Date: Fri, 6 Sep 2024 01:57:28 +0600 Subject: [PATCH 08/10] crypto: Define macros for hash algorithm digest lengths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Daniel P. Berrangé Signed-off-by: Dorjoy Chowdhury Signed-off-by: Daniel P. Berrangé --- crypto/hash.c | 14 +++++++------- include/crypto/hash.h | 8 ++++++++ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/crypto/hash.c b/crypto/hash.c index b0f8228bdc..8087f5dae6 100644 --- a/crypto/hash.c +++ b/crypto/hash.c @@ -23,13 +23,13 @@ #include "hashpriv.h" static size_t qcrypto_hash_alg_size[QCRYPTO_HASH_ALG__MAX] = { - [QCRYPTO_HASH_ALG_MD5] = 16, - [QCRYPTO_HASH_ALG_SHA1] = 20, - [QCRYPTO_HASH_ALG_SHA224] = 28, - [QCRYPTO_HASH_ALG_SHA256] = 32, - [QCRYPTO_HASH_ALG_SHA384] = 48, - [QCRYPTO_HASH_ALG_SHA512] = 64, - [QCRYPTO_HASH_ALG_RIPEMD160] = 20, + [QCRYPTO_HASH_ALG_MD5] = QCRYPTO_HASH_DIGEST_LEN_MD5, + [QCRYPTO_HASH_ALG_SHA1] = QCRYPTO_HASH_DIGEST_LEN_SHA1, + [QCRYPTO_HASH_ALG_SHA224] = QCRYPTO_HASH_DIGEST_LEN_SHA224, + [QCRYPTO_HASH_ALG_SHA256] = QCRYPTO_HASH_DIGEST_LEN_SHA256, + [QCRYPTO_HASH_ALG_SHA384] = QCRYPTO_HASH_DIGEST_LEN_SHA384, + [QCRYPTO_HASH_ALG_SHA512] = QCRYPTO_HASH_DIGEST_LEN_SHA512, + [QCRYPTO_HASH_ALG_RIPEMD160] = QCRYPTO_HASH_DIGEST_LEN_RIPEMD160, }; size_t qcrypto_hash_digest_len(QCryptoHashAlgorithm alg) diff --git a/include/crypto/hash.h b/include/crypto/hash.h index 54d87aa2a1..a113cc3b04 100644 --- a/include/crypto/hash.h +++ b/include/crypto/hash.h @@ -23,6 +23,14 @@ #include "qapi/qapi-types-crypto.h" +#define QCRYPTO_HASH_DIGEST_LEN_MD5 16 +#define QCRYPTO_HASH_DIGEST_LEN_SHA1 20 +#define QCRYPTO_HASH_DIGEST_LEN_SHA224 28 +#define QCRYPTO_HASH_DIGEST_LEN_SHA256 32 +#define QCRYPTO_HASH_DIGEST_LEN_SHA384 48 +#define QCRYPTO_HASH_DIGEST_LEN_SHA512 64 +#define QCRYPTO_HASH_DIGEST_LEN_RIPEMD160 20 + /* See also "QCryptoHashAlgorithm" defined in qapi/crypto.json */ /** From 9893da71b274cc16f9a5fc18b94896f4887bcc2d Mon Sep 17 00:00:00 2001 From: Dorjoy Chowdhury Date: Fri, 6 Sep 2024 01:57:29 +0600 Subject: [PATCH 09/10] crypto: Support SHA384 hash when using glib MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QEMU requires minimum glib version 2.66.0 as per the root meson.build file and per glib documentation[1] G_CHECKSUM_SHA384 is available since 2.51. [1] https://docs.gtk.org/glib/enum.ChecksumType.html Reviewed-by: Daniel P. Berrangé Signed-off-by: Dorjoy Chowdhury Signed-off-by: Daniel P. Berrangé --- crypto/hash-glib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/hash-glib.c b/crypto/hash-glib.c index 82de9db705..18e64faa9c 100644 --- a/crypto/hash-glib.c +++ b/crypto/hash-glib.c @@ -29,7 +29,7 @@ static int qcrypto_hash_alg_map[QCRYPTO_HASH_ALG__MAX] = { [QCRYPTO_HASH_ALG_SHA1] = G_CHECKSUM_SHA1, [QCRYPTO_HASH_ALG_SHA224] = -1, [QCRYPTO_HASH_ALG_SHA256] = G_CHECKSUM_SHA256, - [QCRYPTO_HASH_ALG_SHA384] = -1, + [QCRYPTO_HASH_ALG_SHA384] = G_CHECKSUM_SHA384, [QCRYPTO_HASH_ALG_SHA512] = G_CHECKSUM_SHA512, [QCRYPTO_HASH_ALG_RIPEMD160] = -1, }; From 10a1d34fc0d4dfe0dd6f5ec73f62dc1afa04af6c Mon Sep 17 00:00:00 2001 From: Dorjoy Chowdhury Date: Fri, 6 Sep 2024 01:57:30 +0600 Subject: [PATCH 10/10] crypto: Introduce x509 utils MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An utility function for getting fingerprint from X.509 certificate has been introduced. Implementation only provided using gnutls. Signed-off-by: Dorjoy Chowdhury [DB: fixed missing gnutls_x509_crt_deinit in success path] Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Daniel P. Berrangé --- crypto/meson.build | 4 ++ crypto/x509-utils.c | 76 +++++++++++++++++++++++++++++++++++++ include/crypto/x509-utils.h | 22 +++++++++++ 3 files changed, 102 insertions(+) create mode 100644 crypto/x509-utils.c create mode 100644 include/crypto/x509-utils.h diff --git a/crypto/meson.build b/crypto/meson.build index c46f9c22a7..735635de1f 100644 --- a/crypto/meson.build +++ b/crypto/meson.build @@ -24,6 +24,10 @@ crypto_ss.add(files( 'rsakey.c', )) +if gnutls.found() + crypto_ss.add(files('x509-utils.c')) +endif + if nettle.found() crypto_ss.add(nettle, files('hash-nettle.c', 'hmac-nettle.c', 'pbkdf-nettle.c')) if hogweed.found() diff --git a/crypto/x509-utils.c b/crypto/x509-utils.c new file mode 100644 index 0000000000..6e157af76b --- /dev/null +++ b/crypto/x509-utils.c @@ -0,0 +1,76 @@ +/* + * X.509 certificate related helpers + * + * Copyright (c) 2024 Dorjoy Chowdhury + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "crypto/x509-utils.h" +#include +#include +#include + +static const int qcrypto_to_gnutls_hash_alg_map[QCRYPTO_HASH_ALG__MAX] = { + [QCRYPTO_HASH_ALG_MD5] = GNUTLS_DIG_MD5, + [QCRYPTO_HASH_ALG_SHA1] = GNUTLS_DIG_SHA1, + [QCRYPTO_HASH_ALG_SHA224] = GNUTLS_DIG_SHA224, + [QCRYPTO_HASH_ALG_SHA256] = GNUTLS_DIG_SHA256, + [QCRYPTO_HASH_ALG_SHA384] = GNUTLS_DIG_SHA384, + [QCRYPTO_HASH_ALG_SHA512] = GNUTLS_DIG_SHA512, + [QCRYPTO_HASH_ALG_RIPEMD160] = GNUTLS_DIG_RMD160, +}; + +int qcrypto_get_x509_cert_fingerprint(uint8_t *cert, size_t size, + QCryptoHashAlgorithm alg, + uint8_t *result, + size_t *resultlen, + Error **errp) +{ + int ret = -1; + int hlen; + gnutls_x509_crt_t crt; + gnutls_datum_t datum = {.data = cert, .size = size}; + + if (alg >= G_N_ELEMENTS(qcrypto_to_gnutls_hash_alg_map)) { + error_setg(errp, "Unknown hash algorithm"); + return -1; + } + + if (result == NULL) { + error_setg(errp, "No valid buffer given"); + return -1; + } + + gnutls_x509_crt_init(&crt); + + if (gnutls_x509_crt_import(crt, &datum, GNUTLS_X509_FMT_PEM) != 0) { + error_setg(errp, "Failed to import certificate"); + goto cleanup; + } + + hlen = gnutls_hash_get_len(qcrypto_to_gnutls_hash_alg_map[alg]); + if (*resultlen < hlen) { + error_setg(errp, + "Result buffer size %zu is smaller than hash %d", + *resultlen, hlen); + goto cleanup; + } + + if (gnutls_x509_crt_get_fingerprint(crt, + qcrypto_to_gnutls_hash_alg_map[alg], + result, resultlen) != 0) { + error_setg(errp, "Failed to get fingerprint from certificate"); + goto cleanup; + } + + ret = 0; + + cleanup: + gnutls_x509_crt_deinit(crt); + return ret; +} diff --git a/include/crypto/x509-utils.h b/include/crypto/x509-utils.h new file mode 100644 index 0000000000..4210dfbcfc --- /dev/null +++ b/include/crypto/x509-utils.h @@ -0,0 +1,22 @@ +/* + * X.509 certificate related helpers + * + * Copyright (c) 2024 Dorjoy Chowdhury + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ + +#ifndef QCRYPTO_X509_UTILS_H +#define QCRYPTO_X509_UTILS_H + +#include "crypto/hash.h" + +int qcrypto_get_x509_cert_fingerprint(uint8_t *cert, size_t size, + QCryptoHashAlgorithm hash, + uint8_t *result, + size_t *resultlen, + Error **errp); + +#endif