From 20d9e396cc8667e7e0ea85cee0650ecc38199ba7 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 10 Mar 2026 17:49:54 +0100 Subject: [PATCH 01/25] Guard NULL in KDF capacity getter F/011 --- src/psa_key_derivation.c | 3 ++- test/psa_server/psa_api_test.c | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/psa_key_derivation.c b/src/psa_key_derivation.c index 43a0c07..6e9ae02 100644 --- a/src/psa_key_derivation.c +++ b/src/psa_key_derivation.c @@ -440,12 +440,13 @@ psa_status_t psa_key_derivation_set_capacity(psa_key_derivation_operation_t *ope psa_status_t psa_key_derivation_get_capacity(const psa_key_derivation_operation_t *operation, size_t *capacity) { - const wolfpsa_kdf_ctx_t *ctx = (const wolfpsa_kdf_ctx_t *)(uintptr_t)operation->opaque; + const wolfpsa_kdf_ctx_t *ctx; if (operation == NULL || capacity == NULL) { return PSA_ERROR_INVALID_ARGUMENT; } + ctx = (const wolfpsa_kdf_ctx_t *)(uintptr_t)operation->opaque; if (ctx == NULL) { return PSA_ERROR_BAD_STATE; } diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index da263ce..4c97b12 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -388,6 +388,20 @@ static int test_asym_ecc(void) return TEST_OK; } +static int test_kdf_null_capacity(void) +{ + size_t capacity = 0; + psa_status_t st; + + st = psa_key_derivation_get_capacity(NULL, &capacity); + if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, + "psa_key_derivation_get_capacity(NULL)") != TEST_OK) { + return TEST_FAIL; + } + + return TEST_OK; +} + static int run_named_test(const char* name, test_fn_t fn) { fprintf(stderr, "RUN %s\n", name); @@ -420,6 +434,9 @@ int main(int argc, char** argv) if (only == NULL || strcmp(only, "asym_ecc") == 0) { if (run_named_test("asym_ecc", test_asym_ecc) != TEST_OK) return TEST_FAIL; } + if (only == NULL || strcmp(only, "kdf_null_capacity") == 0) { + if (run_named_test("kdf_null_capacity", test_kdf_null_capacity) != TEST_OK) return TEST_FAIL; + } printf("PSA API test: OK\n"); return TEST_OK; From b04e6c3e141f7810ba18893d66f18620231c5b4c Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 10 Mar 2026 17:51:16 +0100 Subject: [PATCH 02/25] Reject AES keys for ChaCha20-Poly1305 F/003 --- src/psa_aead.c | 3 +- test/psa_server/psa_api_test.c | 54 ++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/src/psa_aead.c b/src/psa_aead.c index 42990ab..c085155 100644 --- a/src/psa_aead.c +++ b/src/psa_aead.c @@ -124,8 +124,7 @@ static psa_status_t wolfpsa_aead_check_key(psa_key_id_t key, } } else if (PSA_ALG_AEAD_EQUAL(alg, PSA_ALG_CHACHA20_POLY1305)) { - if (attributes->type != PSA_KEY_TYPE_CHACHA20 && - attributes->type != PSA_KEY_TYPE_AES) { + if (attributes->type != PSA_KEY_TYPE_CHACHA20) { wolfpsa_free_key_data(*key_data); *key_data = NULL; *key_data_length = 0; diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index 4c97b12..7f38764 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -298,6 +298,54 @@ static int test_aead_gcm(void) return TEST_OK; } +static int test_chacha20_poly1305_rejects_aes_key(void) +{ + static const uint8_t key[32] = { + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, + 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f + }; + static const uint8_t nonce[12] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00 + }; + static const uint8_t plaintext[16] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + uint8_t out[sizeof(plaintext) + 16]; + size_t out_len = 0; + psa_key_id_t key_id = 0; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_status_t st; + + psa_set_key_type(&attrs, PSA_KEY_TYPE_AES); + psa_set_key_bits(&attrs, 256); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_ENCRYPT); + psa_set_key_algorithm(&attrs, PSA_ALG_CHACHA20_POLY1305); + + st = psa_import_key(&attrs, key, sizeof(key), &key_id); + if (check_status(st, "psa_import_key(AES for ChaCha20)") != TEST_OK) return TEST_FAIL; + + st = psa_aead_encrypt(key_id, PSA_ALG_CHACHA20_POLY1305, + nonce, sizeof(nonce), + NULL, 0, + plaintext, sizeof(plaintext), + out, sizeof(out), &out_len); + if (st != PSA_ERROR_INVALID_ARGUMENT) { + (void)psa_destroy_key(key_id); + printf("FAIL: psa_aead_encrypt accepted AES key for ChaCha20-Poly1305 (status=%d)\n", + (int)st); + return TEST_FAIL; + } + + st = psa_destroy_key(key_id); + if (check_status(st, "psa_destroy_key(AES for ChaCha20)") != TEST_OK) return TEST_FAIL; + + return TEST_OK; +} + static int test_asym_ecc(void) { static const uint8_t msg[] = "psa ecc sign"; @@ -431,6 +479,12 @@ int main(int argc, char** argv) if (only == NULL || strcmp(only, "aead_gcm") == 0) { if (run_named_test("aead_gcm", test_aead_gcm) != TEST_OK) return TEST_FAIL; } + if (only == NULL || strcmp(only, "chacha20_aes_reject") == 0) { + if (run_named_test("chacha20_aes_reject", + test_chacha20_poly1305_rejects_aes_key) != TEST_OK) { + return TEST_FAIL; + } + } if (only == NULL || strcmp(only, "asym_ecc") == 0) { if (run_named_test("asym_ecc", test_asym_ecc) != TEST_OK) return TEST_FAIL; } From 04f5550663fd61a07ac3ae32863dfb63986d3af2 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 10 Mar 2026 17:53:31 +0100 Subject: [PATCH 03/25] Fix EdDSA length handling on 64-bit F/014 --- src/psa_ed25519_ed448.c | 50 ++++++++++++++++++++++------ test/psa_server/psa_api_test.c | 60 ++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 10 deletions(-) diff --git a/src/psa_ed25519_ed448.c b/src/psa_ed25519_ed448.c index a573321..cb85617 100644 --- a/src/psa_ed25519_ed448.c +++ b/src/psa_ed25519_ed448.c @@ -59,6 +59,7 @@ psa_status_t psa_asymmetric_sign_ed25519(psa_key_type_t key_type, { int ret; ed25519_key ed_key; + word32 sig_len32; if (alg != PSA_ALG_ED25519 && alg != PSA_ALG_ED25519PH) { return PSA_ERROR_NOT_SUPPORTED; @@ -85,15 +86,16 @@ psa_status_t psa_asymmetric_sign_ed25519(psa_key_type_t key_type, } /* Sign message */ + sig_len32 = (word32)signature_size; if (alg == PSA_ALG_ED25519PH) { /* Sign hash */ ret = wc_ed25519ph_sign_hash(hash, (word32)hash_length, signature, - (word32*)signature_length, &ed_key, NULL, 0); + &sig_len32, &ed_key, NULL, 0); } else if (alg == PSA_ALG_ED25519) { /* Sign message */ ret = wc_ed25519_sign_msg(hash, (word32)hash_length, signature, - (word32*)signature_length, &ed_key); + &sig_len32, &ed_key); } wc_ed25519_free(&ed_key); @@ -101,6 +103,8 @@ psa_status_t psa_asymmetric_sign_ed25519(psa_key_type_t key_type, if (ret != 0) { return wc_error_to_psa_status(ret); } + + *signature_length = (size_t)sig_len32; return PSA_SUCCESS; } @@ -184,6 +188,8 @@ psa_status_t psa_asymmetric_generate_key_ed25519(psa_key_type_t key_type, int ret; ed25519_key ed_key; WC_RNG rng; + word32 priv_len32; + word32 pub_len32; /* Check if key type is ED25519 key pair */ if (key_type != PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_TWISTED_EDWARDS) || @@ -213,7 +219,8 @@ psa_status_t psa_asymmetric_generate_key_ed25519(psa_key_type_t key_type, } /* Export private key */ - ret = wc_ed25519_export_private_only(&ed_key, private_key, (word32*)private_key_length); + priv_len32 = (word32)private_key_size; + ret = wc_ed25519_export_private_only(&ed_key, private_key, &priv_len32); if (ret != 0) { wc_FreeRng(&rng); wc_ed25519_free(&ed_key); @@ -221,7 +228,8 @@ psa_status_t psa_asymmetric_generate_key_ed25519(psa_key_type_t key_type, } /* Export public key */ - ret = wc_ed25519_export_public(&ed_key, public_key, (word32*)public_key_length); + pub_len32 = (word32)public_key_size; + ret = wc_ed25519_export_public(&ed_key, public_key, &pub_len32); if (ret != 0) { wc_FreeRng(&rng); wc_ed25519_free(&ed_key); @@ -230,6 +238,9 @@ psa_status_t psa_asymmetric_generate_key_ed25519(psa_key_type_t key_type, wc_FreeRng(&rng); wc_ed25519_free(&ed_key); + + *private_key_length = (size_t)priv_len32; + *public_key_length = (size_t)pub_len32; return PSA_SUCCESS; } @@ -245,6 +256,7 @@ psa_status_t psa_asymmetric_export_public_key_ed25519(psa_key_type_t key_type, { int ret; ed25519_key ed_key; + word32 out_len32; /* Check if key type is ED25519 */ if ((key_type != PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_TWISTED_EDWARDS) && @@ -274,13 +286,16 @@ psa_status_t psa_asymmetric_export_public_key_ed25519(psa_key_type_t key_type, } /* Export public key */ - ret = wc_ed25519_export_public(&ed_key, output, (word32*)output_length); + out_len32 = (word32)output_size; + ret = wc_ed25519_export_public(&ed_key, output, &out_len32); wc_ed25519_free(&ed_key); if (ret != 0) { return wc_error_to_psa_status(ret); } + + *output_length = (size_t)out_len32; return PSA_SUCCESS; } @@ -301,6 +316,7 @@ psa_status_t psa_asymmetric_sign_ed448(psa_key_type_t key_type, { int ret; ed448_key ed_key; + word32 sig_len32; if (alg != PSA_ALG_ED448 && alg != PSA_ALG_ED448PH) { return PSA_ERROR_NOT_SUPPORTED; @@ -327,15 +343,16 @@ psa_status_t psa_asymmetric_sign_ed448(psa_key_type_t key_type, } /* Sign message */ + sig_len32 = (word32)signature_size; if (alg == PSA_ALG_ED448PH) { /* Sign hash */ ret = wc_ed448ph_sign_hash(hash, (word32)hash_length, signature, - (word32*)signature_length, &ed_key, NULL, 0); + &sig_len32, &ed_key, NULL, 0); } else if (alg == PSA_ALG_ED448) { /* Sign message */ ret = wc_ed448_sign_msg(hash, (word32)hash_length, signature, - (word32*)signature_length, &ed_key, NULL, 0); + &sig_len32, &ed_key, NULL, 0); } wc_ed448_free(&ed_key); @@ -343,6 +360,8 @@ psa_status_t psa_asymmetric_sign_ed448(psa_key_type_t key_type, if (ret != 0) { return wc_error_to_psa_status(ret); } + + *signature_length = (size_t)sig_len32; return PSA_SUCCESS; } @@ -426,6 +445,8 @@ psa_status_t psa_asymmetric_generate_key_ed448(psa_key_type_t key_type, int ret; ed448_key ed_key; WC_RNG rng; + word32 priv_len32; + word32 pub_len32; /* Check if key type is ED448 key pair */ if (key_type != PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_TWISTED_EDWARDS) || @@ -455,7 +476,8 @@ psa_status_t psa_asymmetric_generate_key_ed448(psa_key_type_t key_type, } /* Export private key */ - ret = wc_ed448_export_private_only(&ed_key, private_key, (word32*)private_key_length); + priv_len32 = (word32)private_key_size; + ret = wc_ed448_export_private_only(&ed_key, private_key, &priv_len32); if (ret != 0) { wc_FreeRng(&rng); wc_ed448_free(&ed_key); @@ -463,7 +485,8 @@ psa_status_t psa_asymmetric_generate_key_ed448(psa_key_type_t key_type, } /* Export public key */ - ret = wc_ed448_export_public(&ed_key, public_key, (word32*)public_key_length); + pub_len32 = (word32)public_key_size; + ret = wc_ed448_export_public(&ed_key, public_key, &pub_len32); if (ret != 0) { wc_FreeRng(&rng); wc_ed448_free(&ed_key); @@ -472,6 +495,9 @@ psa_status_t psa_asymmetric_generate_key_ed448(psa_key_type_t key_type, wc_FreeRng(&rng); wc_ed448_free(&ed_key); + + *private_key_length = (size_t)priv_len32; + *public_key_length = (size_t)pub_len32; return PSA_SUCCESS; } @@ -487,6 +513,7 @@ psa_status_t psa_asymmetric_export_public_key_ed448(psa_key_type_t key_type, { int ret; ed448_key ed_key; + word32 out_len32; /* Check if key type is ED448 */ if ((key_type != PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_TWISTED_EDWARDS) && @@ -516,13 +543,16 @@ psa_status_t psa_asymmetric_export_public_key_ed448(psa_key_type_t key_type, } /* Export public key */ - ret = wc_ed448_export_public(&ed_key, output, (word32*)output_length); + out_len32 = (word32)output_size; + ret = wc_ed448_export_public(&ed_key, output, &out_len32); wc_ed448_free(&ed_key); if (ret != 0) { return wc_error_to_psa_status(ret); } + + *output_length = (size_t)out_len32; return PSA_SUCCESS; } diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index 7f38764..80c5598 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -436,6 +436,61 @@ static int test_asym_ecc(void) return TEST_OK; } +static int test_ed25519_signature_length(void) +{ + static const uint8_t hash[64] = { + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, + 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, + 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, + 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f + }; + uint8_t sig[128]; + size_t sig_len = ~(size_t)0; + psa_key_id_t key_id = 0; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_status_t st; + + psa_set_key_type(&attrs, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_TWISTED_EDWARDS)); + psa_set_key_bits(&attrs, 255); + psa_set_key_usage_flags(&attrs, + PSA_KEY_USAGE_SIGN_HASH | PSA_KEY_USAGE_VERIFY_HASH); + psa_set_key_algorithm(&attrs, PSA_ALG_ED25519PH); + + st = psa_generate_key(&attrs, &key_id); + if (st == PSA_ERROR_NOT_SUPPORTED) { + return TEST_OK; + } + if (check_status(st, "psa_generate_key(ED25519)") != TEST_OK) return TEST_FAIL; + + st = psa_sign_hash(key_id, PSA_ALG_ED25519PH, + hash, sizeof(hash), + sig, sizeof(sig), &sig_len); + if (check_status(st, "psa_sign_hash(ED25519PH)") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + if (check_true(sig_len == 64u, "psa_sign_hash(ED25519PH) length") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + + st = psa_verify_hash(key_id, PSA_ALG_ED25519PH, + hash, sizeof(hash), sig, sig_len); + if (check_status(st, "psa_verify_hash(ED25519PH)") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + + st = psa_destroy_key(key_id); + if (check_status(st, "psa_destroy_key(ED25519)") != TEST_OK) return TEST_FAIL; + + return TEST_OK; +} + static int test_kdf_null_capacity(void) { size_t capacity = 0; @@ -488,6 +543,11 @@ int main(int argc, char** argv) if (only == NULL || strcmp(only, "asym_ecc") == 0) { if (run_named_test("asym_ecc", test_asym_ecc) != TEST_OK) return TEST_FAIL; } + if (only == NULL || strcmp(only, "ed25519_sig_len") == 0) { + if (run_named_test("ed25519_sig_len", test_ed25519_signature_length) != TEST_OK) { + return TEST_FAIL; + } + } if (only == NULL || strcmp(only, "kdf_null_capacity") == 0) { if (run_named_test("kdf_null_capacity", test_kdf_null_capacity) != TEST_OK) return TEST_FAIL; } From e9b00d29c49ebf5a84e7caa4b05af39dfca6b6f5 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 10 Mar 2026 17:54:37 +0100 Subject: [PATCH 04/25] Fix LMS and XMSS output lengths on 64-bit F/009 --- src/psa_lms_xmss.c | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/src/psa_lms_xmss.c b/src/psa_lms_xmss.c index 1d7c367..045e650 100644 --- a/src/psa_lms_xmss.c +++ b/src/psa_lms_xmss.c @@ -50,6 +50,8 @@ psa_status_t psa_lms_generate_key(uint8_t *private_key, int ret; LmsKey key; WC_RNG rng; + word32 priv_len32; + word32 pub_len32; /* Initialize LMS key */ ret = wc_LmsKey_Init(&key, NULL, INVALID_DEVID); @@ -80,7 +82,8 @@ psa_status_t psa_lms_generate_key(uint8_t *private_key, } /* Export private key */ - ret = wc_LmsKey_ExportPrivate(&key, private_key, (word32*)private_key_length); + priv_len32 = (word32)private_key_size; + ret = wc_LmsKey_ExportPrivate(&key, private_key, &priv_len32); if (ret != 0) { wc_FreeRng(&rng); wc_LmsKey_Free(&key); @@ -88,7 +91,8 @@ psa_status_t psa_lms_generate_key(uint8_t *private_key, } /* Export public key */ - ret = wc_LmsKey_ExportPublic(&key, public_key, (word32*)public_key_length); + pub_len32 = (word32)public_key_size; + ret = wc_LmsKey_ExportPublic(&key, public_key, &pub_len32); if (ret != 0) { wc_FreeRng(&rng); wc_LmsKey_Free(&key); @@ -97,6 +101,9 @@ psa_status_t psa_lms_generate_key(uint8_t *private_key, wc_FreeRng(&rng); wc_LmsKey_Free(&key); + + *private_key_length = (size_t)priv_len32; + *public_key_length = (size_t)pub_len32; return PSA_SUCCESS; } @@ -112,6 +119,7 @@ psa_status_t psa_lms_sign(const uint8_t *private_key, { int ret; LmsKey key; + word32 sig_len32; /* Initialize LMS key */ ret = wc_LmsKey_Init(&key, NULL, INVALID_DEVID); @@ -127,7 +135,8 @@ psa_status_t psa_lms_sign(const uint8_t *private_key, } /* Sign message */ - ret = wc_LmsKey_Sign(&key, signature, (word32*)signature_length, + sig_len32 = (word32)signature_size; + ret = wc_LmsKey_Sign(&key, signature, &sig_len32, message, (word32)message_length); if (ret != 0) { wc_LmsKey_Free(&key); @@ -135,6 +144,8 @@ psa_status_t psa_lms_sign(const uint8_t *private_key, } wc_LmsKey_Free(&key); + + *signature_length = (size_t)sig_len32; return PSA_SUCCESS; } @@ -196,6 +207,8 @@ psa_status_t psa_xmss_generate_key(uint8_t *private_key, int ret; XmssKey key; WC_RNG rng; + word32 priv_len32; + word32 pub_len32; /* Initialize XMSS key */ ret = wc_XmssKey_Init(&key, NULL, INVALID_DEVID); @@ -226,7 +239,8 @@ psa_status_t psa_xmss_generate_key(uint8_t *private_key, } /* Export private key */ - ret = wc_XmssKey_ExportPrivate(&key, private_key, (word32*)private_key_length); + priv_len32 = (word32)private_key_size; + ret = wc_XmssKey_ExportPrivate(&key, private_key, &priv_len32); if (ret != 0) { wc_FreeRng(&rng); wc_XmssKey_Free(&key); @@ -234,7 +248,8 @@ psa_status_t psa_xmss_generate_key(uint8_t *private_key, } /* Export public key */ - ret = wc_XmssKey_ExportPublic(&key, public_key, (word32*)public_key_length); + pub_len32 = (word32)public_key_size; + ret = wc_XmssKey_ExportPublic(&key, public_key, &pub_len32); if (ret != 0) { wc_FreeRng(&rng); wc_XmssKey_Free(&key); @@ -243,6 +258,9 @@ psa_status_t psa_xmss_generate_key(uint8_t *private_key, wc_FreeRng(&rng); wc_XmssKey_Free(&key); + + *private_key_length = (size_t)priv_len32; + *public_key_length = (size_t)pub_len32; return PSA_SUCCESS; } @@ -258,6 +276,7 @@ psa_status_t psa_xmss_sign(const uint8_t *private_key, { int ret; XmssKey key; + word32 sig_len32; /* Initialize XMSS key */ ret = wc_XmssKey_Init(&key, NULL, INVALID_DEVID); @@ -273,7 +292,8 @@ psa_status_t psa_xmss_sign(const uint8_t *private_key, } /* Sign message */ - ret = wc_XmssKey_Sign(&key, signature, (word32*)signature_length, + sig_len32 = (word32)signature_size; + ret = wc_XmssKey_Sign(&key, signature, &sig_len32, message, (word32)message_length); if (ret != 0) { wc_XmssKey_Free(&key); @@ -281,6 +301,8 @@ psa_status_t psa_xmss_sign(const uint8_t *private_key, } wc_XmssKey_Free(&key); + + *signature_length = (size_t)sig_len32; return PSA_SUCCESS; } From 44005a60a23436040e895e92842f620b9e7aa1e1 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 10 Mar 2026 17:57:10 +0100 Subject: [PATCH 05/25] Zeroize AEAD keys before free F/001 --- src/psa_aead.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/psa_aead.c b/src/psa_aead.c index c085155..ac9f186 100644 --- a/src/psa_aead.c +++ b/src/psa_aead.c @@ -858,6 +858,7 @@ psa_status_t psa_aead_abort(psa_aead_operation_t *operation) XFREE(ctx->input, NULL, DYNAMIC_TYPE_TMP_BUFFER); } if (ctx->key != NULL) { + wc_ForceZero(ctx->key, ctx->key_length); XFREE(ctx->key, NULL, DYNAMIC_TYPE_TMP_BUFFER); } XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER); From 8cf6e69b3e607f2720fccb5a539b1dd076addf10 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 10 Mar 2026 17:57:48 +0100 Subject: [PATCH 06/25] Zeroize KDF secret buffers before free F/002 --- src/psa_key_derivation.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/psa_key_derivation.c b/src/psa_key_derivation.c index 6e9ae02..7eca3f8 100644 --- a/src/psa_key_derivation.c +++ b/src/psa_key_derivation.c @@ -82,6 +82,7 @@ static wolfpsa_kdf_ctx_t* wolfpsa_kdf_get_ctx(psa_key_derivation_operation_t *op static void wolfpsa_kdf_free_buf(uint8_t **buf, size_t *len) { if (buf != NULL && *buf != NULL) { + wc_ForceZero(*buf, len != NULL ? *len : 0); XFREE(*buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); *buf = NULL; } From 7d6b0ac006c9f65d5450aeaf87b28405d466a146 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 10 Mar 2026 17:58:11 +0100 Subject: [PATCH 07/25] Guard AEAD buffer growth overflow F/007 --- src/psa_aead.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/psa_aead.c b/src/psa_aead.c index ac9f186..fa46c1b 100644 --- a/src/psa_aead.c +++ b/src/psa_aead.c @@ -72,6 +72,9 @@ static psa_status_t wolfpsa_aead_append(uint8_t **buf, size_t *len, if (data_length == 0) { return PSA_SUCCESS; } + if (*len > SIZE_MAX - data_length) { + return PSA_ERROR_INSUFFICIENT_MEMORY; + } new_buf = (uint8_t *)XMALLOC(*len + data_length, NULL, DYNAMIC_TYPE_TMP_BUFFER); From 3f80c287b36a9d114b3eb968a751223d936fdd96 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 10 Mar 2026 17:58:28 +0100 Subject: [PATCH 08/25] Guard KDF buffer growth overflow F/008 --- src/psa_key_derivation.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/psa_key_derivation.c b/src/psa_key_derivation.c index 7eca3f8..dfdb5ae 100644 --- a/src/psa_key_derivation.c +++ b/src/psa_key_derivation.c @@ -103,6 +103,9 @@ static psa_status_t wolfpsa_kdf_append(uint8_t **buf, size_t *len, if (data_length == 0) { return PSA_SUCCESS; } + if (*len > SIZE_MAX - data_length) { + return PSA_ERROR_INSUFFICIENT_MEMORY; + } new_buf = (uint8_t *)XMALLOC(*len + data_length, NULL, DYNAMIC_TYPE_TMP_BUFFER); From 8070a3a9b6efaa9da45a70f32f056143d67f8d55 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 10 Mar 2026 17:58:54 +0100 Subject: [PATCH 09/25] Free volatile key data on export rejection F/013 --- src/psa_key_storage.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/psa_key_storage.c b/src/psa_key_storage.c index eafd5d2..469037b 100644 --- a/src/psa_key_storage.c +++ b/src/psa_key_storage.c @@ -1134,6 +1134,9 @@ psa_status_t psa_export_public_key( if (!PSA_KEY_TYPE_IS_RSA(attributes.type) && !PSA_KEY_TYPE_IS_ECC(attributes.type)) { + if (use_volatile) { + wolfpsa_free_key_data(key_data); + } return PSA_ERROR_INVALID_ARGUMENT; } From 003627c9813528fd8a1a6304807fa39d80632321 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 10 Mar 2026 17:59:29 +0100 Subject: [PATCH 10/25] Fix PQ key-type switch fallthrough F/015 --- src/psa_pq.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/psa_pq.c b/src/psa_pq.c index 9668364..0128fef 100644 --- a/src/psa_pq.c +++ b/src/psa_pq.c @@ -52,6 +52,7 @@ psa_status_t psa_pq_check_key_type_supported(psa_key_type_t type) #endif #if defined(WOLFSSL_HAVE_LMS) && defined(PSA_KEY_TYPE_LMS_KEY_PAIR) case PSA_KEY_TYPE_LMS_KEY_PAIR: + return PSA_SUCCESS; #endif #if defined(WOLFSSL_HAVE_LMS) && defined(PSA_KEY_TYPE_LMS_PUBLIC_KEY) case PSA_KEY_TYPE_LMS_PUBLIC_KEY: @@ -59,6 +60,7 @@ psa_status_t psa_pq_check_key_type_supported(psa_key_type_t type) #endif #if defined(WOLFSSL_HAVE_XMSS) && defined(PSA_KEY_TYPE_XMSS_KEY_PAIR) case PSA_KEY_TYPE_XMSS_KEY_PAIR: + return PSA_SUCCESS; #endif #if defined(WOLFSSL_HAVE_XMSS) && defined(PSA_KEY_TYPE_XMSS_PUBLIC_KEY) case PSA_KEY_TYPE_XMSS_PUBLIC_KEY: @@ -96,6 +98,8 @@ psa_status_t psa_pq_check_key_size_valid(psa_key_type_t type, size_t bits) #endif #if defined(WOLFSSL_HAVE_LMS) && defined(PSA_KEY_TYPE_LMS_KEY_PAIR) case PSA_KEY_TYPE_LMS_KEY_PAIR: + /* LMS doesn't have specific key sizes in the same way */ + return PSA_SUCCESS; #endif #if defined(WOLFSSL_HAVE_LMS) && defined(PSA_KEY_TYPE_LMS_PUBLIC_KEY) case PSA_KEY_TYPE_LMS_PUBLIC_KEY: @@ -104,6 +108,8 @@ psa_status_t psa_pq_check_key_size_valid(psa_key_type_t type, size_t bits) #endif #if defined(WOLFSSL_HAVE_XMSS) && defined(PSA_KEY_TYPE_XMSS_KEY_PAIR) case PSA_KEY_TYPE_XMSS_KEY_PAIR: + /* XMSS doesn't have specific key sizes in the same way */ + return PSA_SUCCESS; #endif #if defined(WOLFSSL_HAVE_XMSS) && defined(PSA_KEY_TYPE_XMSS_PUBLIC_KEY) case PSA_KEY_TYPE_XMSS_PUBLIC_KEY: From b964bd915f2674b27c2ecc319ae2485b153ef1f2 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 10 Mar 2026 18:38:48 +0100 Subject: [PATCH 11/25] Fix RSA-PSS MGF and verify handling F/012 --- src/psa_rsa.c | 40 +++- test/Makefile | 6 +- test/psa_server/psa_rsa_pss_interop_test.c | 211 +++++++++++++++++++++ 3 files changed, 250 insertions(+), 7 deletions(-) create mode 100644 test/psa_server/psa_rsa_pss_interop_test.c diff --git a/src/psa_rsa.c b/src/psa_rsa.c index 1cc85d3..5222fef 100644 --- a/src/psa_rsa.c +++ b/src/psa_rsa.c @@ -63,6 +63,31 @@ static int wc_psa_get_mgf(int hash_type) } } +#ifdef WC_RSA_PSS +static int wc_psa_rsa_pss_check_padding(const byte* hash, word32 hash_length, + const byte* encoded, word32 encoded_length, + int hash_type, int salt_len, + RsaKey* rsa_key) +{ +#if (defined(HAVE_SELFTEST) && \ + (!defined(HAVE_SELFTEST_VERSION) || (HAVE_SELFTEST_VERSION < 2))) || \ + (defined(HAVE_FIPS) && defined(HAVE_FIPS_VERSION) && \ + (HAVE_FIPS_VERSION < 2)) + return wc_RsaPSS_CheckPadding_ex(hash, hash_length, encoded, + encoded_length, hash_type, salt_len); +#elif (defined(HAVE_SELFTEST) && (HAVE_SELFTEST_VERSION == 2)) || \ + (defined(HAVE_FIPS) && defined(HAVE_FIPS_VERSION) && \ + (HAVE_FIPS_VERSION == 2)) + return wc_RsaPSS_CheckPadding_ex(hash, hash_length, encoded, + encoded_length, hash_type, salt_len, 0); +#else + return wc_RsaPSS_CheckPadding_ex2(hash, hash_length, encoded, + encoded_length, hash_type, salt_len, + mp_count_bits(&rsa_key->n), NULL); +#endif +} +#endif + /* Sign a hash or short message with an RSA private key */ psa_status_t psa_asymmetric_sign_rsa(psa_key_type_t key_type, size_t key_bits, @@ -150,7 +175,7 @@ psa_status_t psa_asymmetric_sign_rsa(psa_key_type_t key_type, #ifdef WC_RSA_PSS ret = wc_RsaPSS_Sign(hash, (word32)hash_length, signature, (word32)signature_size, hash_type, - WC_MGF1NONE, &rsa_key, &rng); + wc_psa_get_mgf(hash_type), &rsa_key, &rng); #else ret = NOT_COMPILED_IN; #endif @@ -251,7 +276,7 @@ psa_status_t psa_asymmetric_verify_rsa(psa_key_type_t key_type, } else if (padding == WC_RSA_PSS_PAD) { #ifdef WC_RSA_PSS - byte decoded[PSA_HASH_MAX_SIZE]; + byte decoded[RSA_MAX_SIZE/8]; int salt_len = RSA_PSS_SALT_LEN_DEFAULT; #ifdef WOLFSSL_PSS_SALT_LEN_DISCOVER @@ -268,11 +293,14 @@ psa_status_t psa_asymmetric_verify_rsa(psa_key_type_t key_type, ret = wc_RsaPSS_Verify_ex(signature, (word32)signature_length, decoded, (word32)sizeof(decoded), - hash_type, WC_MGF1NONE, salt_len, &rsa_key); + hash_type, wc_psa_get_mgf(hash_type), + salt_len, &rsa_key); if (ret > 0) { - if ((size_t)ret != hash_length || - XMEMCMP(decoded, hash, hash_length) != 0) { - ret = SIG_VERIFY_E; + ret = wc_psa_rsa_pss_check_padding(hash, (word32)hash_length, + decoded, (word32)ret, + hash_type, salt_len, &rsa_key); + if (ret == 0) { + ret = (int)hash_length; } } #else diff --git a/test/Makefile b/test/Makefile index 46e7bf9..1e85e69 100644 --- a/test/Makefile +++ b/test/Makefile @@ -55,13 +55,14 @@ WOLFSSL_LOCAL_SYMBOLS := $(if $(WOLFSSL_LOCAL_LIB),$(shell \ $(NM) $(WOLFSSL_LOCAL_LIB) 2>/dev/null)) WOLFSSL_HAS_PSA_TLS := $(findstring wolfSSL_CTX_psa_enable,$(WOLFSSL_LOCAL_SYMBOLS)) -BINARIES = psa_api_test psa_tls_client wolfcrypt-psa-benchmark +BINARIES = psa_api_test psa_rsa_pss_interop_test psa_tls_client wolfcrypt-psa-benchmark ifdef WOLFSSL_HAS_PSA_TLS BINARIES += psa_tls_server endif PSA_API_TEST_OBJS = psa_server/psa_api_test.o +PSA_RSA_PSS_TEST_OBJS = psa_server/psa_rsa_pss_interop_test.o .PHONY: all clean rebuild-wolfssl-psa require-wolfssl-lib @@ -77,6 +78,9 @@ endif psa_api_test: require-wolfssl-lib $(PSA_API_TEST_OBJS) $(CC) $(CFLAGS) -o $@ $(PSA_API_TEST_OBJS) $(LDFLAGS) $(LDLIBS) $(RPATH_WOLFPSA) $(RPATH_WOLFSSL) +psa_rsa_pss_interop_test: require-wolfssl-lib $(PSA_RSA_PSS_TEST_OBJS) + $(CC) $(CFLAGS) -o $@ $(PSA_RSA_PSS_TEST_OBJS) $(LDFLAGS) $(LDLIBS) $(RPATH_WOLFPSA) $(RPATH_WOLFSSL) + ifdef WOLFSSL_HAS_PSA_TLS psa_tls_server: psa_server/psa_tls_server.o $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LDLIBS) $(RPATH_WOLFPSA) $(RPATH_WOLFSSL) diff --git a/test/psa_server/psa_rsa_pss_interop_test.c b/test/psa_server/psa_rsa_pss_interop_test.c new file mode 100644 index 0000000..692d859 --- /dev/null +++ b/test/psa_server/psa_rsa_pss_interop_test.c @@ -0,0 +1,211 @@ +#include + +#include +#include +#include + +#include + +#include +#include + +#ifndef INVALID_DEVID +#define INVALID_DEVID -2 +#endif + +#define WOLFSSL_CERTS_DIR "../../wolfssl/certs" +#define RSA_PRIVATE_KEY_PATH WOLFSSL_CERTS_DIR "/server-key.der" + +static int load_file(const char* path, uint8_t** data, size_t* len) +{ + FILE* f; + long size; + uint8_t* buf; + + if (data == NULL || len == NULL) { + return -1; + } + + f = fopen(path, "rb"); + if (f == NULL) { + return -1; + } + if (fseek(f, 0, SEEK_END) != 0) { + fclose(f); + return -1; + } + size = ftell(f); + if (size < 0) { + fclose(f); + return -1; + } + rewind(f); + + buf = (uint8_t*)malloc((size_t)size); + if (buf == NULL) { + fclose(f); + return -1; + } + if (fread(buf, 1, (size_t)size, f) != (size_t)size) { + free(buf); + fclose(f); + return -1; + } + fclose(f); + + *data = buf; + *len = (size_t)size; + return 0; +} + +static int check_status(const char* what, psa_status_t st) +{ + if (st != PSA_SUCCESS) { + printf("FAIL: %s (status=%d)\n", what, (int)st); + return 1; + } + return 0; +} + +static int check_pss_padding(const uint8_t* hash, size_t hash_len, + const uint8_t* encoded, size_t encoded_len, + RsaKey* rsa_key) +{ +#if (defined(HAVE_SELFTEST) && \ + (!defined(HAVE_SELFTEST_VERSION) || (HAVE_SELFTEST_VERSION < 2))) || \ + (defined(HAVE_FIPS) && defined(HAVE_FIPS_VERSION) && \ + (HAVE_FIPS_VERSION < 2)) + return wc_RsaPSS_CheckPadding_ex(hash, (word32)hash_len, encoded, + (word32)encoded_len, + WC_HASH_TYPE_SHA256, + RSA_PSS_SALT_LEN_DEFAULT); +#elif (defined(HAVE_SELFTEST) && (HAVE_SELFTEST_VERSION == 2)) || \ + (defined(HAVE_FIPS) && defined(HAVE_FIPS_VERSION) && \ + (HAVE_FIPS_VERSION == 2)) + return wc_RsaPSS_CheckPadding_ex(hash, (word32)hash_len, encoded, + (word32)encoded_len, + WC_HASH_TYPE_SHA256, + RSA_PSS_SALT_LEN_DEFAULT, 0); +#else + return wc_RsaPSS_CheckPadding_ex2(hash, (word32)hash_len, encoded, + (word32)encoded_len, + WC_HASH_TYPE_SHA256, + RSA_PSS_SALT_LEN_DEFAULT, + wc_RsaEncryptSize(rsa_key) * 8, NULL); +#endif +} + +int main(void) +{ +#ifndef WC_RSA_PSS + puts("SKIP: WC_RSA_PSS not enabled"); + return 0; +#else + static const uint8_t hash[32] = { + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, + 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f + }; + psa_key_attributes_t priv_attrs = psa_key_attributes_init(); + psa_key_id_t priv_key = 0; + uint8_t* priv_der = NULL; + size_t priv_der_len = 0; + uint8_t psa_sig[256]; + size_t psa_sig_len = 0; + byte wc_sig[256]; + int wc_sig_len; + byte decoded[256]; + RsaKey rsa_key; + WC_RNG rng; + word32 idx = 0; + int ret; + psa_status_t st; + + st = psa_crypto_init(); + if (check_status("psa_crypto_init", st) != 0) return 1; + + if (load_file(RSA_PRIVATE_KEY_PATH, &priv_der, &priv_der_len) != 0) { + printf("FAIL: load %s\n", RSA_PRIVATE_KEY_PATH); + return 1; + } + psa_set_key_type(&priv_attrs, PSA_KEY_TYPE_RSA_KEY_PAIR); + psa_set_key_bits(&priv_attrs, 2048); + psa_set_key_usage_flags(&priv_attrs, + PSA_KEY_USAGE_SIGN_HASH | PSA_KEY_USAGE_VERIFY_HASH); + psa_set_key_algorithm(&priv_attrs, PSA_ALG_RSA_PSS(PSA_ALG_SHA_256)); + st = psa_import_key(&priv_attrs, priv_der, priv_der_len, &priv_key); + if (check_status("psa_import_key(private)", st) != 0) goto fail; + + ret = wc_InitRsaKey(&rsa_key, NULL); + if (ret != 0) { + printf("FAIL: wc_InitRsaKey (%d)\n", ret); + goto fail; + } + ret = wc_InitRng(&rng); + if (ret != 0) { + printf("FAIL: wc_InitRng (%d)\n", ret); + wc_FreeRsaKey(&rsa_key); + goto fail; + } + ret = wc_RsaPrivateKeyDecode(priv_der, &idx, &rsa_key, (word32)priv_der_len); + if (ret != 0) { + printf("FAIL: wc_RsaPrivateKeyDecode (%d)\n", ret); + wc_FreeRng(&rng); + wc_FreeRsaKey(&rsa_key); + goto fail; + } + + wc_sig_len = wc_RsaPSS_Sign(hash, (word32)sizeof(hash), wc_sig, (word32)sizeof(wc_sig), + WC_HASH_TYPE_SHA256, WC_MGF1SHA256, &rsa_key, &rng); + if (wc_sig_len <= 0) { + printf("FAIL: wc_RsaPSS_Sign (%d)\n", wc_sig_len); + wc_FreeRng(&rng); + wc_FreeRsaKey(&rsa_key); + goto fail; + } + + st = psa_verify_hash(priv_key, PSA_ALG_RSA_PSS(PSA_ALG_SHA_256), + hash, sizeof(hash), wc_sig, (size_t)wc_sig_len); + if (check_status("psa_verify_hash(private)", st) != 0) { + wc_FreeRng(&rng); + wc_FreeRsaKey(&rsa_key); + goto fail; + } + + st = psa_sign_hash(priv_key, PSA_ALG_RSA_PSS(PSA_ALG_SHA_256), + hash, sizeof(hash), psa_sig, sizeof(psa_sig), &psa_sig_len); + if (check_status("psa_sign_hash(private)", st) != 0) { + wc_FreeRng(&rng); + wc_FreeRsaKey(&rsa_key); + goto fail; + } + + ret = wc_RsaPSS_Verify_ex(psa_sig, (word32)psa_sig_len, decoded, + (word32)sizeof(decoded), WC_HASH_TYPE_SHA256, + WC_MGF1SHA256, RSA_PSS_SALT_LEN_DEFAULT, + &rsa_key); + if (ret <= 0 || + check_pss_padding(hash, sizeof(hash), decoded, (size_t)ret, + &rsa_key) != 0) { + printf("FAIL: wc_RsaPSS_Verify_ex mismatch (%d)\n", ret); + wc_FreeRng(&rng); + wc_FreeRsaKey(&rsa_key); + goto fail; + } + + wc_FreeRng(&rng); + wc_FreeRsaKey(&rsa_key); + (void)psa_destroy_key(priv_key); + free(priv_der); + puts("RSA-PSS interop: OK"); + return 0; + +fail: + if (priv_key != 0) { + (void)psa_destroy_key(priv_key); + } + free(priv_der); + return 1; +#endif +} From 8e947b88d7d3ce67193a0adf1dbb43c5975c06aa Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 10 Mar 2026 18:40:48 +0100 Subject: [PATCH 12/25] Use constant-time compare for MAC verify F/004 --- src/psa_mac.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/psa_mac.c b/src/psa_mac.c index af6da24..3f51ea4 100644 --- a/src/psa_mac.c +++ b/src/psa_mac.c @@ -33,6 +33,11 @@ #include #include #include +#include +#ifndef NO_INLINE + #define WOLFSSL_MISC_INCLUDED + #include +#endif typedef enum { WOLFPSA_MAC_NONE = 0, @@ -451,7 +456,7 @@ psa_status_t psa_mac_verify_finish(psa_mac_operation_t *operation, return PSA_ERROR_INVALID_SIGNATURE; } - if (XMEMCMP(computed, mac, mac_length) != 0) { + if (ConstantCompare(computed, mac, (int)mac_length) != 0) { return PSA_ERROR_INVALID_SIGNATURE; } From a826de817fe777023a232eb677e2d96ab0ffbfb8 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 10 Mar 2026 18:41:25 +0100 Subject: [PATCH 13/25] Use constant-time compare for KDF verify F/005 --- src/psa_key_derivation.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/psa_key_derivation.c b/src/psa_key_derivation.c index dfdb5ae..fe8388a 100644 --- a/src/psa_key_derivation.c +++ b/src/psa_key_derivation.c @@ -36,6 +36,11 @@ #include #include #include +#include +#ifndef NO_INLINE + #define WOLFSSL_MISC_INCLUDED + #include +#endif typedef struct wolfpsa_kdf_ctx { psa_algorithm_t alg; @@ -1103,7 +1108,7 @@ psa_status_t psa_key_derivation_verify_bytes(psa_key_derivation_operation_t *ope return status; } - if (XMEMCMP(buffer, expected, expected_length) != 0) { + if (ConstantCompare(buffer, expected, (int)expected_length) != 0) { status = PSA_ERROR_INVALID_SIGNATURE; } else { From d5001731d7206fc416b439f2d3aef8adddb6b818 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 10 Mar 2026 18:42:14 +0100 Subject: [PATCH 14/25] Use constant-time compare for RSA verify F/006 --- src/psa_rsa.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/psa_rsa.c b/src/psa_rsa.c index 5222fef..5b2b09c 100644 --- a/src/psa_rsa.c +++ b/src/psa_rsa.c @@ -35,8 +35,13 @@ #include #include #include +#include #include #include +#ifndef NO_INLINE + #define WOLFSSL_MISC_INCLUDED + #include +#endif int wc_psa_get_rsa_padding(psa_algorithm_t alg); int wc_psa_get_hash_type(psa_algorithm_t alg); @@ -269,7 +274,7 @@ psa_status_t psa_asymmetric_verify_rsa(psa_key_type_t key_type, if (ret > 0) { if ((size_t)ret != hash_length || - XMEMCMP(decoded, hash, hash_length) != 0) { + ConstantCompare(decoded, hash, (int)hash_length) != 0) { ret = SIG_VERIFY_E; } } From b7209998e4032e7a28c790b470bbd0276a027a44 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 10 Mar 2026 18:42:51 +0100 Subject: [PATCH 15/25] Use constant-time compare for hash verify F/010 --- src/psa_hash_engine.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/psa_hash_engine.c b/src/psa_hash_engine.c index 55be777..c7aacd3 100644 --- a/src/psa_hash_engine.c +++ b/src/psa_hash_engine.c @@ -36,6 +36,11 @@ #include #include #include +#include +#ifndef NO_INLINE + #define WOLFSSL_MISC_INCLUDED + #include +#endif #ifdef WOLFSSL_MD5 #include @@ -642,7 +647,7 @@ psa_status_t psa_hash_verify(psa_hash_operation_t *operation, return PSA_ERROR_INVALID_SIGNATURE; } - if (XMEMCMP(computed_hash, hash, computed_hash_length) != 0) { + if (ConstantCompare(computed_hash, hash, (int)computed_hash_length) != 0) { return PSA_ERROR_INVALID_SIGNATURE; } @@ -865,7 +870,7 @@ psa_status_t psa_hash_compare(psa_algorithm_t alg, } /* Compare the computed hash with the reference hash */ - if (XMEMCMP(computed_hash, hash, computed_hash_length) != 0) { + if (ConstantCompare(computed_hash, hash, (int)computed_hash_length) != 0) { return PSA_ERROR_INVALID_SIGNATURE; } From ba7aea990c23c2a33eeb671add9cb16b1a3f2a74 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 10 Mar 2026 18:44:02 +0100 Subject: [PATCH 16/25] Use Dilithium export APIs for ML-DSA keys F/016 --- src/psa_mldsa.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/psa_mldsa.c b/src/psa_mldsa.c index d0a329d..ae2440e 100644 --- a/src/psa_mldsa.c +++ b/src/psa_mldsa.c @@ -65,6 +65,8 @@ psa_status_t psa_ml_dsa_generate_key(psa_ml_dsa_parameter_t parameter, dilithium_key key; int type; WC_RNG rng; + word32 priv_len; + word32 pub_len; /* Convert parameter to wolfCrypt key type */ type = psa_ml_dsa_parameter_to_type(parameter); @@ -93,20 +95,23 @@ psa_status_t psa_ml_dsa_generate_key(psa_ml_dsa_parameter_t parameter, return wc_error_to_psa_status(ret); } - /* Check buffer sizes */ - if (private_key_size < key.priv_key_len || public_key_size < key.pub_key_len) { + priv_len = (word32)private_key_size; + ret = wc_dilithium_export_private(&key, private_key, &priv_len); + if (ret != 0) { wc_FreeRng(&rng); wc_dilithium_free(&key); - return PSA_ERROR_BUFFER_TOO_SMALL; + return wc_error_to_psa_status(ret); } - - /* Export private key */ - XMEMCPY(private_key, key.k, key.priv_key_len); - *private_key_length = key.priv_key_len; - - /* Export public key */ - XMEMCPY(public_key, key.p, key.pub_key_len); - *public_key_length = key.pub_key_len; + *private_key_length = priv_len; + + pub_len = (word32)public_key_size; + ret = wc_dilithium_export_public(&key, public_key, &pub_len); + if (ret != 0) { + wc_FreeRng(&rng); + wc_dilithium_free(&key); + return wc_error_to_psa_status(ret); + } + *public_key_length = pub_len; wc_FreeRng(&rng); wc_dilithium_free(&key); From 4207379e02fabc79d0efefc76bf3631c88500fd8 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 11 Mar 2026 10:07:47 +0100 Subject: [PATCH 17/25] Delay ML-DSA length outputs until both exports succeed F/016 --- src/psa_mldsa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/psa_mldsa.c b/src/psa_mldsa.c index ae2440e..24c843b 100644 --- a/src/psa_mldsa.c +++ b/src/psa_mldsa.c @@ -102,7 +102,6 @@ psa_status_t psa_ml_dsa_generate_key(psa_ml_dsa_parameter_t parameter, wc_dilithium_free(&key); return wc_error_to_psa_status(ret); } - *private_key_length = priv_len; pub_len = (word32)public_key_size; ret = wc_dilithium_export_public(&key, public_key, &pub_len); @@ -111,6 +110,7 @@ psa_status_t psa_ml_dsa_generate_key(psa_ml_dsa_parameter_t parameter, wc_dilithium_free(&key); return wc_error_to_psa_status(ret); } + *private_key_length = priv_len; *public_key_length = pub_len; wc_FreeRng(&rng); From efd18edd31ef81dd7e8865083bd5e4b901592c16 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 11 Mar 2026 10:08:27 +0100 Subject: [PATCH 18/25] Zero old KDF buffers during reallocation F/002 --- src/psa_key_derivation.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/psa_key_derivation.c b/src/psa_key_derivation.c index fe8388a..5334ea1 100644 --- a/src/psa_key_derivation.c +++ b/src/psa_key_derivation.c @@ -120,6 +120,7 @@ static psa_status_t wolfpsa_kdf_append(uint8_t **buf, size_t *len, if (*buf != NULL) { XMEMCPY(new_buf, *buf, *len); + wc_ForceZero(*buf, *len); XFREE(*buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); } From a6e0e5592f6ac41d2c879a7a9d1de6ea42471653 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 11 Mar 2026 10:08:56 +0100 Subject: [PATCH 19/25] Zero old AEAD buffers during reallocation F/001 --- src/psa_aead.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/psa_aead.c b/src/psa_aead.c index fa46c1b..4f9f188 100644 --- a/src/psa_aead.c +++ b/src/psa_aead.c @@ -84,6 +84,7 @@ static psa_status_t wolfpsa_aead_append(uint8_t **buf, size_t *len, if (*buf != NULL) { XMEMCPY(new_buf, *buf, *len); + wc_ForceZero(*buf, *len); XFREE(*buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); } From 54d4600f5207a7dda2515c2355f89148dd895e3e Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 11 Mar 2026 10:10:07 +0100 Subject: [PATCH 20/25] Clarify Ed25519 length regression coverage F/014 --- test/psa_server/psa_api_test.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index 80c5598..fce86f2 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -449,7 +449,14 @@ static int test_ed25519_signature_length(void) 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f }; uint8_t sig[128]; - size_t sig_len = ~(size_t)0; + /* + * The original bug cast `size_t*` to `word32*`. On 64-bit builds that can + * update only the low 32 bits, leaving the upper 32 bits stale. Seed + * `sig_len` with the low half clear and the upper half all ones so a + * truncated store becomes a large non-64 value instead of accidentally + * looking correct. + */ + size_t sig_len = ~((size_t)UINT32_MAX); psa_key_id_t key_id = 0; psa_key_attributes_t attrs = psa_key_attributes_init(); psa_status_t st; From aceefeb62b4c7198fe65f60755fcc71c418692aa Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 11 Mar 2026 10:11:50 +0100 Subject: [PATCH 21/25] Fix ChaCha20/AES regression test intent F/003 --- test/psa_server/psa_api_test.c | 34 ++++++++++------------------------ 1 file changed, 10 insertions(+), 24 deletions(-) diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index fce86f2..13dc63f 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -306,16 +306,6 @@ static int test_chacha20_poly1305_rejects_aes_key(void) 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f }; - static const uint8_t nonce[12] = { - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00 - }; - static const uint8_t plaintext[16] = { - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 - }; - uint8_t out[sizeof(plaintext) + 16]; - size_t out_len = 0; psa_key_id_t key_id = 0; psa_key_attributes_t attrs = psa_key_attributes_init(); psa_status_t st; @@ -325,24 +315,20 @@ static int test_chacha20_poly1305_rejects_aes_key(void) psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_ENCRYPT); psa_set_key_algorithm(&attrs, PSA_ALG_CHACHA20_POLY1305); + /* + * The fixed behavior rejects this invalid key-type/algorithm pairing at + * import time. If import ever succeeds, the test is no longer checking + * the intended regression. + */ st = psa_import_key(&attrs, key, sizeof(key), &key_id); - if (check_status(st, "psa_import_key(AES for ChaCha20)") != TEST_OK) return TEST_FAIL; - - st = psa_aead_encrypt(key_id, PSA_ALG_CHACHA20_POLY1305, - nonce, sizeof(nonce), - NULL, 0, - plaintext, sizeof(plaintext), - out, sizeof(out), &out_len); - if (st != PSA_ERROR_INVALID_ARGUMENT) { - (void)psa_destroy_key(key_id); - printf("FAIL: psa_aead_encrypt accepted AES key for ChaCha20-Poly1305 (status=%d)\n", - (int)st); + if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, + "psa_import_key(AES for ChaCha20) rejected") != TEST_OK) { + if (key_id != 0) { + (void)psa_destroy_key(key_id); + } return TEST_FAIL; } - st = psa_destroy_key(key_id); - if (check_status(st, "psa_destroy_key(AES for ChaCha20)") != TEST_OK) return TEST_FAIL; - return TEST_OK; } From b818ba1816b996678e9c3835025928d34f1fadcd Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 11 Mar 2026 10:16:14 +0100 Subject: [PATCH 22/25] Report skipped PSA API tests and fix ChaCha regression check --- test/psa_server/psa_api_test.c | 72 ++++++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 20 deletions(-) diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index 13dc63f..da19dea 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -27,9 +27,13 @@ #define TEST_OK 0 #define TEST_FAIL 1 +#define TEST_SKIPPED 2 typedef int (*test_fn_t)(void); +static int tests_passed = 0; +static int tests_skipped = 0; + static int check_status(psa_status_t st, const char* what) { if (st != PSA_SUCCESS) { @@ -306,6 +310,16 @@ static int test_chacha20_poly1305_rejects_aes_key(void) 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f }; + static const uint8_t nonce[12] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00 + }; + static const uint8_t plaintext[16] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + uint8_t out[sizeof(plaintext) + 16]; + size_t out_len = 0; psa_key_id_t key_id = 0; psa_key_attributes_t attrs = psa_key_attributes_init(); psa_status_t st; @@ -313,22 +327,29 @@ static int test_chacha20_poly1305_rejects_aes_key(void) psa_set_key_type(&attrs, PSA_KEY_TYPE_AES); psa_set_key_bits(&attrs, 256); psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_ENCRYPT); - psa_set_key_algorithm(&attrs, PSA_ALG_CHACHA20_POLY1305); + psa_set_key_algorithm(&attrs, PSA_ALG_GCM); /* - * The fixed behavior rejects this invalid key-type/algorithm pairing at - * import time. If import ever succeeds, the test is no longer checking - * the intended regression. + * Import a valid AES AEAD key first, then prove that the ChaCha20-Poly1305 + * path rejects it when the operation algorithm does not match the key type. */ st = psa_import_key(&attrs, key, sizeof(key), &key_id); + if (check_status(st, "psa_import_key(AES for GCM)") != TEST_OK) return TEST_FAIL; + + st = psa_aead_encrypt(key_id, PSA_ALG_CHACHA20_POLY1305, + nonce, sizeof(nonce), + NULL, 0, + plaintext, sizeof(plaintext), + out, sizeof(out), &out_len); if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, - "psa_import_key(AES for ChaCha20) rejected") != TEST_OK) { - if (key_id != 0) { - (void)psa_destroy_key(key_id); - } + "psa_aead_encrypt(AES key with ChaCha20) rejected") != TEST_OK) { + (void)psa_destroy_key(key_id); return TEST_FAIL; } + st = psa_destroy_key(key_id); + if (check_status(st, "psa_destroy_key(AES for GCM)") != TEST_OK) return TEST_FAIL; + return TEST_OK; } @@ -455,7 +476,7 @@ static int test_ed25519_signature_length(void) st = psa_generate_key(&attrs, &key_id); if (st == PSA_ERROR_NOT_SUPPORTED) { - return TEST_OK; + return TEST_SKIPPED; } if (check_status(st, "psa_generate_key(ED25519)") != TEST_OK) return TEST_FAIL; @@ -500,8 +521,18 @@ static int test_kdf_null_capacity(void) static int run_named_test(const char* name, test_fn_t fn) { + int ret; + fprintf(stderr, "RUN %s\n", name); - return fn(); + ret = fn(); + if (ret == TEST_OK) { + tests_passed++; + } + else if (ret == TEST_SKIPPED) { + tests_skipped++; + fprintf(stderr, "SKIP %s\n", name); + } + return ret; } int main(int argc, char** argv) @@ -513,38 +544,39 @@ int main(int argc, char** argv) if (check_status(st, "psa_crypto_init") != TEST_OK) return TEST_FAIL; if (only == NULL || strcmp(only, "random") == 0) { - if (run_named_test("random", test_random) != TEST_OK) return TEST_FAIL; + if (run_named_test("random", test_random) == TEST_FAIL) return TEST_FAIL; } if (only == NULL || strcmp(only, "hash") == 0) { - if (run_named_test("hash", test_hash) != TEST_OK) return TEST_FAIL; + if (run_named_test("hash", test_hash) == TEST_FAIL) return TEST_FAIL; } if (only == NULL || strcmp(only, "hmac") == 0) { - if (run_named_test("hmac", test_hmac) != TEST_OK) return TEST_FAIL; + if (run_named_test("hmac", test_hmac) == TEST_FAIL) return TEST_FAIL; } if (only == NULL || strcmp(only, "cipher_cbc") == 0) { - if (run_named_test("cipher_cbc", test_cipher_cbc) != TEST_OK) return TEST_FAIL; + if (run_named_test("cipher_cbc", test_cipher_cbc) == TEST_FAIL) return TEST_FAIL; } if (only == NULL || strcmp(only, "aead_gcm") == 0) { - if (run_named_test("aead_gcm", test_aead_gcm) != TEST_OK) return TEST_FAIL; + if (run_named_test("aead_gcm", test_aead_gcm) == TEST_FAIL) return TEST_FAIL; } if (only == NULL || strcmp(only, "chacha20_aes_reject") == 0) { if (run_named_test("chacha20_aes_reject", - test_chacha20_poly1305_rejects_aes_key) != TEST_OK) { + test_chacha20_poly1305_rejects_aes_key) == TEST_FAIL) { return TEST_FAIL; } } if (only == NULL || strcmp(only, "asym_ecc") == 0) { - if (run_named_test("asym_ecc", test_asym_ecc) != TEST_OK) return TEST_FAIL; + if (run_named_test("asym_ecc", test_asym_ecc) == TEST_FAIL) return TEST_FAIL; } if (only == NULL || strcmp(only, "ed25519_sig_len") == 0) { - if (run_named_test("ed25519_sig_len", test_ed25519_signature_length) != TEST_OK) { + if (run_named_test("ed25519_sig_len", test_ed25519_signature_length) == TEST_FAIL) { return TEST_FAIL; } } if (only == NULL || strcmp(only, "kdf_null_capacity") == 0) { - if (run_named_test("kdf_null_capacity", test_kdf_null_capacity) != TEST_OK) return TEST_FAIL; + if (run_named_test("kdf_null_capacity", test_kdf_null_capacity) == TEST_FAIL) return TEST_FAIL; } - printf("PSA API test: OK\n"); + printf("PSA API test: OK (passed=%d skipped=%d)\n", + tests_passed, tests_skipped); return TEST_OK; } From 32679bfa934ceed17b5c0e59eabac93afc1546a2 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 11 Mar 2026 12:43:25 +0100 Subject: [PATCH 23/25] Provide misc helpers for NO_INLINE builds --- src/misc_impl.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/misc_impl.c diff --git a/src/misc_impl.c b/src/misc_impl.c new file mode 100644 index 0000000..b3c64e7 --- /dev/null +++ b/src/misc_impl.c @@ -0,0 +1,15 @@ +/* misc_impl.c + * + * Provide misc.c helper implementations for NO_INLINE builds. + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include + +#if defined(WOLFSSL_PSA_ENGINE) && defined(NO_INLINE) + #define WOLFSSL_MISC_INCLUDED + #include +#endif From a30c739f4b54ffc2a1a003dadd9ab2f58e37d399 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 11 Mar 2026 12:45:35 +0100 Subject: [PATCH 24/25] Harden KDF verify temp buffer handling --- src/psa_key_derivation.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/psa_key_derivation.c b/src/psa_key_derivation.c index 5334ea1..45ff67b 100644 --- a/src/psa_key_derivation.c +++ b/src/psa_key_derivation.c @@ -23,6 +23,7 @@ #include #endif +#include #include #if defined(WOLFSSL_PSA_ENGINE) @@ -1105,10 +1106,17 @@ psa_status_t psa_key_derivation_verify_bytes(psa_key_derivation_operation_t *ope status = psa_key_derivation_output_bytes(operation, buffer, expected_length); if (status != PSA_SUCCESS) { + wc_ForceZero(buffer, expected_length); XFREE(buffer, NULL, DYNAMIC_TYPE_TMP_BUFFER); return status; } + if (expected_length > INT_MAX) { + wc_ForceZero(buffer, expected_length); + XFREE(buffer, NULL, DYNAMIC_TYPE_TMP_BUFFER); + return PSA_ERROR_INVALID_ARGUMENT; + } + if (ConstantCompare(buffer, expected, (int)expected_length) != 0) { status = PSA_ERROR_INVALID_SIGNATURE; } @@ -1116,6 +1124,7 @@ psa_status_t psa_key_derivation_verify_bytes(psa_key_derivation_operation_t *ope status = PSA_SUCCESS; } + wc_ForceZero(buffer, expected_length); XFREE(buffer, NULL, DYNAMIC_TYPE_TMP_BUFFER); return status; } From 5be1b009c642232909089de83e366d2f2895ccc0 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 11 Mar 2026 12:47:33 +0100 Subject: [PATCH 25/25] Validate append buffer pointers before dereference --- src/psa_aead.c | 3 +++ src/psa_key_derivation.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/psa_aead.c b/src/psa_aead.c index 4f9f188..9ef5222 100644 --- a/src/psa_aead.c +++ b/src/psa_aead.c @@ -66,6 +66,9 @@ static psa_status_t wolfpsa_aead_append(uint8_t **buf, size_t *len, { uint8_t *new_buf; + if (buf == NULL || len == NULL) { + return PSA_ERROR_INVALID_ARGUMENT; + } if (data == NULL && data_length > 0) { return PSA_ERROR_INVALID_ARGUMENT; } diff --git a/src/psa_key_derivation.c b/src/psa_key_derivation.c index 45ff67b..041543e 100644 --- a/src/psa_key_derivation.c +++ b/src/psa_key_derivation.c @@ -102,6 +102,9 @@ static psa_status_t wolfpsa_kdf_append(uint8_t **buf, size_t *len, { uint8_t *new_buf; + if (buf == NULL || len == NULL) { + return PSA_ERROR_INVALID_ARGUMENT; + } if (data == NULL && data_length > 0) { return PSA_ERROR_INVALID_ARGUMENT; }