From 8affc91f849ba8349fd7ea786d4484da1c541857 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 23 Mar 2026 08:17:22 +0100 Subject: [PATCH 01/15] Guard PKCS7 CBC decrypt length underflow F/1188 --- src/psa_cipher.c | 7 +- test/psa_server/psa_api_test.c | 146 +++++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+), 1 deletion(-) diff --git a/src/psa_cipher.c b/src/psa_cipher.c index 534468d..097080a 100644 --- a/src/psa_cipher.c +++ b/src/psa_cipher.c @@ -921,7 +921,12 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, output_offset += block_size; input_offset += needed; ctx->partial_len = 0; - bytes_to_process -= block_size; + if (bytes_to_process >= block_size) { + bytes_to_process -= block_size; + } + else { + bytes_to_process = 0; + } } full_blocks_len = bytes_to_process; diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index 3a60e4d..06c4498 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -241,6 +241,146 @@ static int test_cipher_cbc(void) return TEST_OK; } +static int test_cipher_cbc_pkcs7_multipart_decrypt(void) +{ + static const uint8_t key[16] = { + 0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6, + 0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c + }; + static const uint8_t iv[16] = { + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f + }; + static const uint8_t plaintext[17] = { + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, + 0x38,0x39,0x61,0x62,0x63,0x64,0x65,0x66, + 0x67 + }; + uint8_t ciphertext[sizeof(plaintext) + 16]; + uint8_t decrypted[sizeof(plaintext)]; + size_t ciphertext_len = 0; + size_t part_len = 0; + size_t finish_len = 0; + size_t dec_len = 0; + size_t dec_part_len = 0; + size_t dec_finish_len = 0; + psa_key_id_t key_id = 0; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_cipher_operation_t op = psa_cipher_operation_init(); + psa_status_t st; + + psa_set_key_type(&attrs, PSA_KEY_TYPE_AES); + psa_set_key_bits(&attrs, 128); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT); + psa_set_key_algorithm(&attrs, PSA_ALG_CBC_PKCS7); + + st = psa_import_key(&attrs, key, sizeof(key), &key_id); + if (check_status(st, "psa_import_key(AES PKCS7)") != TEST_OK) return TEST_FAIL; + + st = psa_cipher_encrypt_setup(&op, key_id, PSA_ALG_CBC_PKCS7); + if (st == PSA_ERROR_NOT_SUPPORTED) { + (void)psa_destroy_key(key_id); + return TEST_SKIPPED; + } + if (check_status(st, "psa_cipher_encrypt_setup(PKCS7)") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + st = psa_cipher_set_iv(&op, iv, sizeof(iv)); + if (check_status(st, "psa_cipher_set_iv(PKCS7)") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + st = psa_cipher_update(&op, plaintext, sizeof(plaintext), + ciphertext, sizeof(ciphertext), &part_len); + if (check_status(st, "psa_cipher_update(PKCS7 enc)") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + ciphertext_len += part_len; + st = psa_cipher_finish(&op, ciphertext + ciphertext_len, + sizeof(ciphertext) - ciphertext_len, &finish_len); + if (check_status(st, "psa_cipher_finish(PKCS7 enc)") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + ciphertext_len += finish_len; + + op = psa_cipher_operation_init(); + st = psa_cipher_decrypt_setup(&op, key_id, PSA_ALG_CBC_PKCS7); + if (check_status(st, "psa_cipher_decrypt_setup(PKCS7)") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + st = psa_cipher_set_iv(&op, iv, sizeof(iv)); + if (check_status(st, "psa_cipher_set_iv(PKCS7 dec)") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + + st = psa_cipher_update(&op, ciphertext, 1, + decrypted, sizeof(decrypted), &dec_part_len); + if (check_status(st, "psa_cipher_update(PKCS7 dec 1)") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + if (check_true(dec_part_len == 0, "psa_cipher_update(PKCS7 dec 1) length") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + + st = psa_cipher_update(&op, ciphertext + 1, 16, + decrypted, sizeof(decrypted), &dec_part_len); + if (check_status(st, "psa_cipher_update(PKCS7 dec 16)") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + if (check_true(dec_part_len == 16, "psa_cipher_update(PKCS7 dec 16) length") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + if (check_buf_eq("psa_cipher_update(PKCS7 dec 16)", + decrypted, plaintext, dec_part_len) != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + dec_len += dec_part_len; + + st = psa_cipher_update(&op, ciphertext + 17, ciphertext_len - 17, + decrypted + dec_len, sizeof(decrypted) - dec_len, + &dec_part_len); + if (check_status(st, "psa_cipher_update(PKCS7 dec tail)") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + if (check_true(dec_part_len == 0, "psa_cipher_update(PKCS7 dec tail) length") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + + st = psa_cipher_finish(&op, decrypted + dec_len, sizeof(decrypted) - dec_len, + &dec_finish_len); + if (check_status(st, "psa_cipher_finish(PKCS7 dec)") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + dec_len += dec_finish_len; + + if (check_true(dec_len == sizeof(plaintext), "psa_cipher_decrypt(PKCS7) length") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + if (check_buf_eq("psa_cipher_decrypt(PKCS7)", decrypted, plaintext, sizeof(plaintext)) != 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 PKCS7)") != TEST_OK) return TEST_FAIL; + + return TEST_OK; +} + static int test_aead_gcm(void) { static const uint8_t key[16] = { @@ -623,6 +763,12 @@ int main(int argc, char** argv) if (only == NULL || strcmp(only, "cipher_cbc") == 0) { if (run_named_test("cipher_cbc", test_cipher_cbc) == TEST_FAIL) return TEST_FAIL; } + if (only == NULL || strcmp(only, "cipher_cbc_pkcs7_multipart") == 0) { + if (run_named_test("cipher_cbc_pkcs7_multipart", + test_cipher_cbc_pkcs7_multipart_decrypt) == TEST_FAIL) { + return TEST_FAIL; + } + } if (only == NULL || strcmp(only, "aead_gcm") == 0) { if (run_named_test("aead_gcm", test_aead_gcm) == TEST_FAIL) return TEST_FAIL; } From 6dd7db77176a218e64ab45cd4931fe02ad46c234 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 23 Mar 2026 08:23:49 +0100 Subject: [PATCH 02/15] Fix EdDSA PSA dispatch order F/1189 --- src/psa_asymmetric_api.c | 52 +++++++++++++++++----------------- test/psa_server/psa_api_test.c | 24 +++++++++++++++- 2 files changed, 49 insertions(+), 27 deletions(-) diff --git a/src/psa_asymmetric_api.c b/src/psa_asymmetric_api.c index f22a71a..5a9d49a 100644 --- a/src/psa_asymmetric_api.c +++ b/src/psa_asymmetric_api.c @@ -310,13 +310,6 @@ psa_status_t psa_sign_hash(psa_key_id_t key, signature, signature_size, signature_length); } - else if (PSA_KEY_TYPE_IS_ECC(attributes.type)) { - status = psa_asymmetric_sign_ecc(attributes.type, attributes.bits, - key_data, key_data_length, - alg, hash, hash_length, - signature, signature_size, - signature_length); - } #if defined(HAVE_ED25519) || defined(HAVE_ED448) else if (attributes.type == PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_TWISTED_EDWARDS)) { #ifdef HAVE_ED25519 @@ -344,6 +337,13 @@ psa_status_t psa_sign_hash(psa_key_id_t key, } } #endif + else if (PSA_KEY_TYPE_IS_ECC(attributes.type)) { + status = psa_asymmetric_sign_ecc(attributes.type, attributes.bits, + key_data, key_data_length, + alg, hash, hash_length, + signature, signature_size, + signature_length); + } else { status = PSA_ERROR_NOT_SUPPORTED; } @@ -382,12 +382,6 @@ psa_status_t psa_verify_hash(psa_key_id_t key, alg, hash, hash_length, signature, signature_length); } - else if (PSA_KEY_TYPE_IS_ECC(attributes.type)) { - status = psa_asymmetric_verify_ecc(attributes.type, attributes.bits, - key_data, key_data_length, - alg, hash, hash_length, - signature, signature_length); - } #if defined(HAVE_ED25519) || defined(HAVE_ED448) else if (attributes.type == PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_TWISTED_EDWARDS)) { #ifdef HAVE_ED25519 @@ -413,6 +407,12 @@ psa_status_t psa_verify_hash(psa_key_id_t key, } } #endif + else if (PSA_KEY_TYPE_IS_ECC(attributes.type)) { + status = psa_asymmetric_verify_ecc(attributes.type, attributes.bits, + key_data, key_data_length, + alg, hash, hash_length, + signature, signature_length); + } else { status = PSA_ERROR_NOT_SUPPORTED; } @@ -478,13 +478,6 @@ psa_status_t psa_sign_message(psa_key_id_t key, signature, signature_size, signature_length); } - else if (PSA_KEY_TYPE_IS_ECC(attributes.type)) { - status = psa_asymmetric_sign_ecc(attributes.type, attributes.bits, - key_data, key_data_length, - alg, hash, hash_length, - signature, signature_size, - signature_length); - } #if defined(HAVE_ED25519) || defined(HAVE_ED448) else if (attributes.type == PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_TWISTED_EDWARDS)) { #ifdef HAVE_ED25519 @@ -512,6 +505,13 @@ psa_status_t psa_sign_message(psa_key_id_t key, } } #endif + else if (PSA_KEY_TYPE_IS_ECC(attributes.type)) { + status = psa_asymmetric_sign_ecc(attributes.type, attributes.bits, + key_data, key_data_length, + alg, hash, hash_length, + signature, signature_size, + signature_length); + } else { status = PSA_ERROR_NOT_SUPPORTED; } @@ -575,12 +575,6 @@ psa_status_t psa_verify_message(psa_key_id_t key, alg, hash, hash_length, signature, signature_length); } - else if (PSA_KEY_TYPE_IS_ECC(attributes.type)) { - status = psa_asymmetric_verify_ecc(attributes.type, attributes.bits, - key_data, key_data_length, - alg, hash, hash_length, - signature, signature_length); - } #if defined(HAVE_ED25519) || defined(HAVE_ED448) else if (attributes.type == PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_TWISTED_EDWARDS)) { #ifdef HAVE_ED25519 @@ -606,6 +600,12 @@ psa_status_t psa_verify_message(psa_key_id_t key, } } #endif + else if (PSA_KEY_TYPE_IS_ECC(attributes.type)) { + status = psa_asymmetric_verify_ecc(attributes.type, attributes.bits, + key_data, key_data_length, + alg, hash, hash_length, + signature, signature_length); + } else { status = PSA_ERROR_NOT_SUPPORTED; } diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index 06c4498..09a5a01 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -663,6 +663,7 @@ static int test_ed25519_signature_length(void) 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f }; + static const uint8_t msg[] = "ed25519 message dispatch"; uint8_t sig[128]; /* * The original bug cast `size_t*` to `word32*`. On 64-bit builds that can @@ -679,7 +680,8 @@ static int test_ed25519_signature_length(void) 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_KEY_USAGE_SIGN_HASH | PSA_KEY_USAGE_VERIFY_HASH | + PSA_KEY_USAGE_SIGN_MESSAGE | PSA_KEY_USAGE_VERIFY_MESSAGE); psa_set_key_algorithm(&attrs, PSA_ALG_ED25519PH); st = psa_generate_key(&attrs, &key_id); @@ -707,6 +709,26 @@ static int test_ed25519_signature_length(void) return TEST_FAIL; } + sig_len = sizeof(sig); + st = psa_sign_message(key_id, PSA_ALG_ED25519PH, + msg, sizeof(msg) - 1, + sig, sizeof(sig), &sig_len); + if (check_status(st, "psa_sign_message(ED25519PH)") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + if (check_true(sig_len == 64u, "psa_sign_message(ED25519PH) length") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + + st = psa_verify_message(key_id, PSA_ALG_ED25519PH, + msg, sizeof(msg) - 1, sig, sig_len); + if (check_status(st, "psa_verify_message(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; From 3ba730cebe5a2043d9da0210868e24e1aed5add8 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 23 Mar 2026 08:25:26 +0100 Subject: [PATCH 03/15] zero cipher context before free F/1196 --- src/psa_cipher.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/psa_cipher.c b/src/psa_cipher.c index 097080a..cec251d 100644 --- a/src/psa_cipher.c +++ b/src/psa_cipher.c @@ -1307,6 +1307,7 @@ psa_status_t psa_cipher_abort(psa_cipher_operation_t *operation) else if (!ctx->is_chacha) { wc_AesFree(&ctx->aes); } + wc_ForceZero(ctx, sizeof(*ctx)); XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER); operation->opaque = (uintptr_t)NULL; } From 66a24e68b39acc956a841682bf5ba219a9025aa8 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 23 Mar 2026 08:26:35 +0100 Subject: [PATCH 04/15] zero import buffer before free F/1197 --- src/psa_key_storage.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/psa_key_storage.c b/src/psa_key_storage.c index 3731846..bfa9003 100644 --- a/src/psa_key_storage.c +++ b/src/psa_key_storage.c @@ -867,6 +867,7 @@ psa_status_t psa_import_key( } } + wc_ForceZero(buffer, buffer_size); XFREE(buffer, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (ret < 0 || (size_t)ret != (attr_length + sizeof(size_t) + data_length)) { From 996cc154c03e6da4705d19bb16e9659010bf0674 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 23 Mar 2026 08:27:24 +0100 Subject: [PATCH 05/15] zeroize MAC context on abort F/1198 --- src/psa_mac.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/psa_mac.c b/src/psa_mac.c index 528e42a..d316e66 100644 --- a/src/psa_mac.c +++ b/src/psa_mac.c @@ -478,6 +478,7 @@ psa_status_t psa_mac_abort(psa_mac_operation_t *operation) if (ctx->type == WOLFPSA_MAC_CMAC) { wc_CmacFree(&ctx->ctx.cmac); } + wc_ForceZero(ctx, sizeof(*ctx)); XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER); operation->opaque = (uintptr_t)NULL; } From 24bbd81ada6409df1c800c7145ad83b2b331d09b Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 23 Mar 2026 08:32:41 +0100 Subject: [PATCH 06/15] Dispatch Edwards key generation correctly F/1190 --- src/psa_key_storage.c | 58 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/src/psa_key_storage.c b/src/psa_key_storage.c index bfa9003..1ffe95c 100644 --- a/src/psa_key_storage.c +++ b/src/psa_key_storage.c @@ -79,6 +79,26 @@ psa_status_t psa_asymmetric_generate_key_ecc(psa_key_type_t key_type, uint8_t *public_key, size_t public_key_size, size_t *public_key_length); +#ifdef HAVE_ED25519 +psa_status_t psa_asymmetric_generate_key_ed25519(psa_key_type_t key_type, + size_t key_bits, + uint8_t *private_key, + size_t private_key_size, + size_t *private_key_length, + uint8_t *public_key, + size_t public_key_size, + size_t *public_key_length); +#endif +#ifdef HAVE_ED448 +psa_status_t psa_asymmetric_generate_key_ed448(psa_key_type_t key_type, + size_t key_bits, + uint8_t *private_key, + size_t private_key_size, + size_t *private_key_length, + uint8_t *public_key, + size_t public_key_size, + size_t *public_key_length); +#endif static psa_status_t psa_wc_error_to_psa_status(int ret) { @@ -972,6 +992,7 @@ psa_status_t psa_generate_key( if (PSA_KEY_TYPE_IS_ECC_KEY_PAIR(key_type)) { #ifdef HAVE_ECC + psa_ecc_family_t family = PSA_KEY_TYPE_ECC_GET_FAMILY(key_type); size_t priv_buf_size = PSA_KEY_EXPORT_ECC_KEY_PAIR_MAX_SIZE(key_bits); size_t pub_buf_size = PSA_KEY_EXPORT_ECC_PUBLIC_KEY_MAX_SIZE(key_bits); size_t priv_len = 0; @@ -991,11 +1012,38 @@ psa_status_t psa_generate_key( return PSA_ERROR_INSUFFICIENT_MEMORY; } - status = psa_asymmetric_generate_key_ecc(key_type, key_bits, - key_data, priv_buf_size, - &priv_len, - pub_buf, pub_buf_size, - &pub_len); + if (family == PSA_ECC_FAMILY_TWISTED_EDWARDS) { +#ifdef HAVE_ED25519 + if (key_bits == 255) { + status = psa_asymmetric_generate_key_ed25519(key_type, key_bits, + key_data, priv_buf_size, + &priv_len, + pub_buf, pub_buf_size, + &pub_len); + } + else +#endif +#ifdef HAVE_ED448 + if (key_bits == 448) { + status = psa_asymmetric_generate_key_ed448(key_type, key_bits, + key_data, priv_buf_size, + &priv_len, + pub_buf, pub_buf_size, + &pub_len); + } + else +#endif + { + status = PSA_ERROR_INVALID_ARGUMENT; + } + } + else { + status = psa_asymmetric_generate_key_ecc(key_type, key_bits, + key_data, priv_buf_size, + &priv_len, + pub_buf, pub_buf_size, + &pub_len); + } XFREE(pub_buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (status != PSA_SUCCESS) { wc_ForceZero(key_data, priv_buf_size); From 168dcec8c3690344605df28280df895239399dc5 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 23 Mar 2026 08:36:47 +0100 Subject: [PATCH 07/15] Route Twisted Edwards public key export F/1191 --- src/psa_key_storage.c | 81 +++++++++++++++++++++++++--------- test/psa_server/psa_api_test.c | 68 ++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+), 21 deletions(-) diff --git a/src/psa_key_storage.c b/src/psa_key_storage.c index 1ffe95c..c6be067 100644 --- a/src/psa_key_storage.c +++ b/src/psa_key_storage.c @@ -88,6 +88,13 @@ psa_status_t psa_asymmetric_generate_key_ed25519(psa_key_type_t key_type, uint8_t *public_key, size_t public_key_size, size_t *public_key_length); +psa_status_t psa_asymmetric_export_public_key_ed25519(psa_key_type_t key_type, + size_t key_bits, + const uint8_t *key_buffer, + size_t key_buffer_size, + uint8_t *output, + size_t output_size, + size_t *output_length); #endif #ifdef HAVE_ED448 psa_status_t psa_asymmetric_generate_key_ed448(psa_key_type_t key_type, @@ -98,6 +105,13 @@ psa_status_t psa_asymmetric_generate_key_ed448(psa_key_type_t key_type, uint8_t *public_key, size_t public_key_size, size_t *public_key_length); +psa_status_t psa_asymmetric_export_public_key_ed448(psa_key_type_t key_type, + size_t key_bits, + const uint8_t *key_buffer, + size_t key_buffer_size, + uint8_t *output, + size_t output_size, + size_t *output_length); #endif static psa_status_t psa_wc_error_to_psa_status(int ret) @@ -1395,40 +1409,65 @@ psa_status_t psa_export_public_key( } } else { - ecc_key ecc; - word32 out_len = (word32)data_size; - int curve_id = wc_psa_get_ecc_curve_id(attributes.type, - attributes.bits); - - if (curve_id == ECC_CURVE_INVALID) { - status = PSA_ERROR_NOT_SUPPORTED; - } - else if (wc_ecc_init(&ecc) != 0) { - status = PSA_ERROR_INSUFFICIENT_MEMORY; + psa_ecc_family_t family = PSA_KEY_TYPE_ECC_GET_FAMILY(attributes.type); + + if (family == PSA_ECC_FAMILY_TWISTED_EDWARDS) { + #ifdef HAVE_ED25519 + if (attributes.bits == 255) { + status = psa_asymmetric_export_public_key_ed25519( + attributes.type, attributes.bits, key_data, + key_data_length, data, data_size, data_length); + } + else + #endif + #ifdef HAVE_ED448 + if (attributes.bits == 448) { + status = psa_asymmetric_export_public_key_ed448( + attributes.type, attributes.bits, key_data, + key_data_length, data, data_size, data_length); + } + else + #endif + { + status = PSA_ERROR_NOT_SUPPORTED; + } } else { - ret = wc_ecc_import_private_key_ex(key_data, - (word32)key_data_length, - NULL, 0, &ecc, curve_id); - if (ret != 0) { - wc_ecc_free(&ecc); - status = psa_wc_error_to_psa_status(ret); + ecc_key ecc; + word32 out_len = (word32)data_size; + int curve_id = wc_psa_get_ecc_curve_id(attributes.type, + attributes.bits); + + if (curve_id == ECC_CURVE_INVALID) { + status = PSA_ERROR_NOT_SUPPORTED; + } + else if (wc_ecc_init(&ecc) != 0) { + status = PSA_ERROR_INSUFFICIENT_MEMORY; } else { - ret = wc_ecc_make_pub_ex(&ecc, NULL, NULL); + ret = wc_ecc_import_private_key_ex(key_data, + (word32)key_data_length, + NULL, 0, &ecc, curve_id); if (ret != 0) { wc_ecc_free(&ecc); status = psa_wc_error_to_psa_status(ret); } else { - ret = wc_ecc_export_x963(&ecc, data, &out_len); - wc_ecc_free(&ecc); + ret = wc_ecc_make_pub_ex(&ecc, NULL, NULL); if (ret != 0) { + wc_ecc_free(&ecc); status = psa_wc_error_to_psa_status(ret); } else { - *data_length = (size_t)out_len; - status = PSA_SUCCESS; + ret = wc_ecc_export_x963(&ecc, data, &out_len); + wc_ecc_free(&ecc); + if (ret != 0) { + status = psa_wc_error_to_psa_status(ret); + } + else { + *data_length = (size_t)out_len; + status = PSA_SUCCESS; + } } } } diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index 09a5a01..4f6f427 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -29,6 +29,8 @@ #define TEST_OK 0 #define TEST_FAIL 1 #define TEST_SKIPPED 2 +#define ED25519_PUBLIC_KEY_BYTES 32u +#define ED448_PUBLIC_KEY_BYTES 57u typedef int (*test_fn_t)(void); @@ -735,6 +737,62 @@ static int test_ed25519_signature_length(void) return TEST_OK; } +static int test_twisted_edwards_export_public_key(size_t bits, + psa_algorithm_t alg, + size_t expected_pub_len, + const char* label) +{ + uint8_t pub[ED448_PUBLIC_KEY_BYTES]; + size_t pub_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_ECC_KEY_PAIR(PSA_ECC_FAMILY_TWISTED_EDWARDS)); + psa_set_key_bits(&attrs, bits); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_EXPORT); + psa_set_key_algorithm(&attrs, alg); + + st = psa_generate_key(&attrs, &key_id); + if (st == PSA_ERROR_NOT_SUPPORTED) { + return TEST_SKIPPED; + } + if (check_status(st, label) != TEST_OK) return TEST_FAIL; + + st = psa_export_public_key(key_id, pub, sizeof(pub), &pub_len); + if (check_status(st, "psa_export_public_key(Twisted Edwards)") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + if (check_true(pub_len == expected_pub_len, + "psa_export_public_key(Twisted Edwards) length") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + + st = psa_destroy_key(key_id); + if (check_status(st, "psa_destroy_key(Twisted Edwards)") != TEST_OK) { + return TEST_FAIL; + } + + return TEST_OK; +} + +static int test_ed25519_export_public_key(void) +{ + return test_twisted_edwards_export_public_key(255, PSA_ALG_ED25519PH, + ED25519_PUBLIC_KEY_BYTES, + "psa_generate_key(ED25519 export)"); +} + +static int test_ed448_export_public_key(void) +{ + return test_twisted_edwards_export_public_key(448, PSA_ALG_ED448PH, + ED448_PUBLIC_KEY_BYTES, + "psa_generate_key(ED448 export)"); +} + static int test_kdf_null_capacity(void) { size_t capacity = 0; @@ -814,6 +872,16 @@ int main(int argc, char** argv) return TEST_FAIL; } } + if (only == NULL || strcmp(only, "ed25519_export_pub") == 0) { + if (run_named_test("ed25519_export_pub", test_ed25519_export_public_key) == TEST_FAIL) { + return TEST_FAIL; + } + } + if (only == NULL || strcmp(only, "ed448_export_pub") == 0) { + if (run_named_test("ed448_export_pub", test_ed448_export_public_key) == 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_FAIL) return TEST_FAIL; } From cf32ac02169f651b3dafbf787485be6a0b256594 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 23 Mar 2026 08:39:29 +0100 Subject: [PATCH 08/15] Make CBC PKCS7 padding check constant time F/1199 --- src/psa_cipher.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/psa_cipher.c b/src/psa_cipher.c index cec251d..d6dac6e 100644 --- a/src/psa_cipher.c +++ b/src/psa_cipher.c @@ -37,6 +37,11 @@ #endif #include #include +#include +#ifndef NO_INLINE + #define WOLFSSL_MISC_INCLUDED + #include +#endif #define WOLFPSA_CHACHA20_IV_BYTES 12 @@ -1234,6 +1239,7 @@ psa_status_t psa_cipher_finish(psa_cipher_operation_t *operation, } else { uint8_t block[AES_BLOCK_SIZE]; + byte invalid; size_t pad_len; size_t plain_len; @@ -1258,13 +1264,15 @@ psa_status_t psa_cipher_finish(psa_cipher_operation_t *operation, } pad_len = block[block_size - 1]; - if (pad_len == 0 || pad_len > block_size) { - return PSA_ERROR_INVALID_PADDING; + invalid = ctMaskEq((int)pad_len, 0); + invalid |= ctMaskGT((int)pad_len, (int)block_size); + for (size_t i = 0; i < block_size; i++) { + volatile byte mask = ctMaskLT((int)i, (int)pad_len); + invalid |= mask & (byte)(block[block_size - 1 - i] ^ + (byte)pad_len); } - for (size_t i = 0; i < pad_len; i++) { - if (block[block_size - 1 - i] != pad_len) { - return PSA_ERROR_INVALID_PADDING; - } + if (invalid != 0) { + return PSA_ERROR_INVALID_PADDING; } plain_len = block_size - pad_len; From f0965c7501bb34906aa240adceedd3b1cecbd051 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 23 Mar 2026 08:40:29 +0100 Subject: [PATCH 09/15] Zero AEAD buffers on abort F/1200 --- src/psa_aead.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/psa_aead.c b/src/psa_aead.c index de456d3..d3c6451 100644 --- a/src/psa_aead.c +++ b/src/psa_aead.c @@ -840,9 +840,11 @@ psa_status_t psa_aead_abort(psa_aead_operation_t *operation) if (ctx != NULL) { if (ctx->aad != NULL) { + wc_ForceZero(ctx->aad, ctx->aad_length); XFREE(ctx->aad, NULL, DYNAMIC_TYPE_TMP_BUFFER); } if (ctx->input != NULL) { + wc_ForceZero(ctx->input, ctx->input_length); XFREE(ctx->input, NULL, DYNAMIC_TYPE_TMP_BUFFER); } if (ctx->key != NULL) { From 52b036a9eeffa31ca30be9998a0f9f130ca5f4cb Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 23 Mar 2026 08:41:33 +0100 Subject: [PATCH 10/15] check TLS13 PRF length narrowing F/1201 --- src/psa_tls_prf.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/psa_tls_prf.c b/src/psa_tls_prf.c index 7b7de12..b554ec1 100644 --- a/src/psa_tls_prf.c +++ b/src/psa_tls_prf.c @@ -74,6 +74,15 @@ static int psa_tls13_get_hash_type(psa_algorithm_t alg, enum wc_HashType* hashTy } } +static psa_status_t psa_tls13_check_word32_length(size_t length) +{ + if (length > UINT32_MAX) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + return PSA_SUCCESS; +} + /* TLS 1.3 PRF (HKDF) */ psa_status_t psa_tls13_prf( psa_algorithm_t alg, @@ -105,6 +114,12 @@ psa_status_t psa_tls13_prf( if (output == NULL || output_length == 0) { return PSA_ERROR_INVALID_ARGUMENT; } + if ((psa_tls13_check_word32_length(secret_length) != PSA_SUCCESS) || + (psa_tls13_check_word32_length(label_length) != PSA_SUCCESS) || + (psa_tls13_check_word32_length(context_length) != PSA_SUCCESS) || + (psa_tls13_check_word32_length(output_length) != PSA_SUCCESS)) { + return PSA_ERROR_INVALID_ARGUMENT; + } /* Get hash type */ ret = psa_tls13_get_hash_type(alg, &hashType); @@ -156,6 +171,10 @@ psa_status_t psa_tls13_hkdf_extract( if (output == NULL || output_size == 0 || output_length == NULL) { return PSA_ERROR_INVALID_ARGUMENT; } + if ((psa_tls13_check_word32_length(salt_length) != PSA_SUCCESS) || + (psa_tls13_check_word32_length(ikm_length) != PSA_SUCCESS)) { + return PSA_ERROR_INVALID_ARGUMENT; + } /* Get hash type */ ret = psa_tls13_get_hash_type(alg, &hashType); @@ -218,6 +237,11 @@ psa_status_t psa_tls13_hkdf_expand( if (output == NULL || output_length == 0) { return PSA_ERROR_INVALID_ARGUMENT; } + if ((psa_tls13_check_word32_length(prk_length) != PSA_SUCCESS) || + (psa_tls13_check_word32_length(info_length) != PSA_SUCCESS) || + (psa_tls13_check_word32_length(output_length) != PSA_SUCCESS)) { + return PSA_ERROR_INVALID_ARGUMENT; + } /* Get hash type */ ret = psa_tls13_get_hash_type(alg, &hashType); @@ -271,6 +295,12 @@ psa_status_t psa_tls13_hkdf_expand_label( if (output == NULL || output_length == 0) { return PSA_ERROR_INVALID_ARGUMENT; } + if ((psa_tls13_check_word32_length(secret_length) != PSA_SUCCESS) || + (psa_tls13_check_word32_length(label_length) != PSA_SUCCESS) || + (psa_tls13_check_word32_length(context_length) != PSA_SUCCESS) || + (psa_tls13_check_word32_length(output_length) != PSA_SUCCESS)) { + return PSA_ERROR_INVALID_ARGUMENT; + } /* Get hash type */ ret = psa_tls13_get_hash_type(alg, &hashType); From 9a01ef3287a9cc8998c769f87624fdb819cf942f Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 23 Mar 2026 08:43:02 +0100 Subject: [PATCH 11/15] zero MAC stack buffer on exit F/1202 --- src/psa_mac.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/psa_mac.c b/src/psa_mac.c index d316e66..fe53bea 100644 --- a/src/psa_mac.c +++ b/src/psa_mac.c @@ -352,6 +352,7 @@ static psa_status_t wolfpsa_mac_final(wolfpsa_mac_ctx_t *ctx, uint8_t full_mac[PSA_MAC_MAX_SIZE]; word32 full_len = 0; int ret; + psa_status_t status; if (mac == NULL || mac_length == NULL) { return PSA_ERROR_INVALID_ARGUMENT; @@ -369,7 +370,8 @@ static psa_status_t wolfpsa_mac_final(wolfpsa_mac_ctx_t *ctx, full_len = (word32)sizeof(full_mac); ret = wc_HmacFinal(&ctx->ctx.hmac, full_mac); if (ret != 0) { - return wc_error_to_psa_status(ret); + status = wc_error_to_psa_status(ret); + goto cleanup; } full_len = (word32)ctx->full_length; } @@ -377,20 +379,27 @@ static psa_status_t wolfpsa_mac_final(wolfpsa_mac_ctx_t *ctx, full_len = (word32)ctx->full_length; ret = wc_CmacFinal(&ctx->ctx.cmac, full_mac, &full_len); if (ret != 0) { - return wc_error_to_psa_status(ret); + status = wc_error_to_psa_status(ret); + goto cleanup; } } else { - return PSA_ERROR_BAD_STATE; + status = PSA_ERROR_BAD_STATE; + goto cleanup; } if (ctx->mac_length > (size_t)full_len) { - return PSA_ERROR_INVALID_ARGUMENT; + status = PSA_ERROR_INVALID_ARGUMENT; + goto cleanup; } XMEMCPY(mac, full_mac, ctx->mac_length); *mac_length = ctx->mac_length; - return PSA_SUCCESS; + status = PSA_SUCCESS; + +cleanup: + wc_ForceZero(full_mac, sizeof(full_mac)); + return status; } psa_status_t psa_mac_sign_finish(psa_mac_operation_t *operation, From dc926853de94a820e89702f019962bd716069ee8 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 23 Mar 2026 08:44:13 +0100 Subject: [PATCH 12/15] Zero MAC verify stack buffer F/1203 --- src/psa_mac.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/psa_mac.c b/src/psa_mac.c index fe53bea..9b28d70 100644 --- a/src/psa_mac.c +++ b/src/psa_mac.c @@ -431,7 +431,7 @@ psa_status_t psa_mac_verify_finish(psa_mac_operation_t *operation, uint8_t computed[PSA_MAC_MAX_SIZE]; size_t computed_length = 0; size_t min_length; - psa_status_t status; + psa_status_t status = PSA_ERROR_BAD_STATE; if (ctx == NULL) { return PSA_ERROR_BAD_STATE; @@ -458,18 +458,24 @@ psa_status_t psa_mac_verify_finish(psa_mac_operation_t *operation, &computed_length); psa_mac_abort(operation); if (status != PSA_SUCCESS) { - return status; + goto cleanup; } if (mac_length > computed_length) { - return PSA_ERROR_INVALID_SIGNATURE; + status = PSA_ERROR_INVALID_SIGNATURE; + goto cleanup; } if (ConstantCompare(computed, mac, (int)mac_length) != 0) { - return PSA_ERROR_INVALID_SIGNATURE; + status = PSA_ERROR_INVALID_SIGNATURE; + goto cleanup; } - return PSA_SUCCESS; + status = PSA_SUCCESS; + +cleanup: + wc_ForceZero(computed, sizeof(computed)); + return status; } psa_status_t psa_mac_abort(psa_mac_operation_t *operation) From 9148d237b356090dc42d6c3b4ce0336b98fccbe5 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 23 Mar 2026 11:26:42 +0100 Subject: [PATCH 13/15] Fix Ed25519-Ed448 import paths --- src/psa_asymmetric_api.c | 6 +- src/psa_ed25519_ed448.c | 132 +++++++++++++++++++-------------------- src/psa_key_storage.c | 18 ++++++ user_settings.h | 6 ++ 4 files changed, 94 insertions(+), 68 deletions(-) diff --git a/src/psa_asymmetric_api.c b/src/psa_asymmetric_api.c index 5a9d49a..3000683 100644 --- a/src/psa_asymmetric_api.c +++ b/src/psa_asymmetric_api.c @@ -383,7 +383,8 @@ psa_status_t psa_verify_hash(psa_key_id_t key, signature, signature_length); } #if defined(HAVE_ED25519) || defined(HAVE_ED448) - else if (attributes.type == PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_TWISTED_EDWARDS)) { + else if (attributes.type == PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_TWISTED_EDWARDS) || + attributes.type == PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_TWISTED_EDWARDS)) { #ifdef HAVE_ED25519 if (attributes.bits == 255) { status = psa_asymmetric_verify_ed25519(attributes.type, attributes.bits, @@ -576,7 +577,8 @@ psa_status_t psa_verify_message(psa_key_id_t key, signature, signature_length); } #if defined(HAVE_ED25519) || defined(HAVE_ED448) - else if (attributes.type == PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_TWISTED_EDWARDS)) { + else if (attributes.type == PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_TWISTED_EDWARDS) || + attributes.type == PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_TWISTED_EDWARDS)) { #ifdef HAVE_ED25519 if (attributes.bits == 255) { status = psa_asymmetric_verify_ed25519(attributes.type, attributes.bits, diff --git a/src/psa_ed25519_ed448.c b/src/psa_ed25519_ed448.c index cb85617..161e1a1 100644 --- a/src/psa_ed25519_ed448.c +++ b/src/psa_ed25519_ed448.c @@ -61,7 +61,7 @@ psa_status_t psa_asymmetric_sign_ed25519(psa_key_type_t key_type, ed25519_key ed_key; word32 sig_len32; - if (alg != PSA_ALG_ED25519 && alg != PSA_ALG_ED25519PH) { + if (alg != PSA_ALG_ED25519PH) { return PSA_ERROR_NOT_SUPPORTED; } @@ -77,9 +77,12 @@ psa_status_t psa_asymmetric_sign_ed25519(psa_key_type_t key_type, return wc_error_to_psa_status(ret); } - /* Import private key */ - ret = wc_ed25519_import_private_key(key_buffer, (word32)key_buffer_size, - NULL, 0, &ed_key); + /* Import the stored private seed and derive the public component. */ + ret = wc_ed25519_import_private_only(key_buffer, (word32)key_buffer_size, + &ed_key); + if (ret == 0) { + ret = wc_ed25519_make_public(&ed_key, ed_key.p, ED25519_PUB_KEY_SIZE); + } if (ret != 0) { wc_ed25519_free(&ed_key); return wc_error_to_psa_status(ret); @@ -87,16 +90,8 @@ 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, - &sig_len32, &ed_key, NULL, 0); - } - else if (alg == PSA_ALG_ED25519) { - /* Sign message */ - ret = wc_ed25519_sign_msg(hash, (word32)hash_length, signature, - &sig_len32, &ed_key); - } + ret = wc_ed25519ph_sign_hash(hash, (word32)hash_length, signature, + &sig_len32, &ed_key, NULL, 0); wc_ed25519_free(&ed_key); @@ -124,12 +119,13 @@ psa_status_t psa_asymmetric_verify_ed25519(psa_key_type_t key_type, ed25519_key ed_key; int verify_res = 0; - if (alg != PSA_ALG_ED25519 && alg != PSA_ALG_ED25519PH) { + if (alg != PSA_ALG_ED25519PH) { return PSA_ERROR_NOT_SUPPORTED; } - /* Check if key type is ED25519 public key */ - if (key_type != PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_TWISTED_EDWARDS) || + /* Check if key type is ED25519 */ + if ((key_type != PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_TWISTED_EDWARDS) && + key_type != PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_TWISTED_EDWARDS)) || key_bits != 255) { return PSA_ERROR_INVALID_ARGUMENT; } @@ -141,26 +137,27 @@ psa_status_t psa_asymmetric_verify_ed25519(psa_key_type_t key_type, return wc_error_to_psa_status(ret); } - /* Import public key */ - ret = wc_ed25519_import_public(key_buffer, (word32)key_buffer_size, &ed_key); + /* Import the key material needed for verification. */ + if (key_type == PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_TWISTED_EDWARDS)) { + ret = wc_ed25519_import_private_only(key_buffer, (word32)key_buffer_size, + &ed_key); + if (ret == 0) { + ret = wc_ed25519_make_public(&ed_key, ed_key.p, + ED25519_PUB_KEY_SIZE); + } + } + else { + ret = wc_ed25519_import_public(key_buffer, (word32)key_buffer_size, &ed_key); + } if (ret != 0) { wc_ed25519_free(&ed_key); return wc_error_to_psa_status(ret); } /* Verify signature */ - if (alg == PSA_ALG_ED25519PH) { - /* Verify hash */ - ret = wc_ed25519ph_verify_hash(signature, (word32)signature_length, - hash, (word32)hash_length, - &verify_res, &ed_key); - } - else if (alg == PSA_ALG_ED25519) { - /* Verify message */ - ret = wc_ed25519_verify_msg(signature, (word32)signature_length, - hash, (word32)hash_length, - &verify_res, &ed_key); - } + ret = wc_ed25519ph_verify_hash(signature, (word32)signature_length, + hash, (word32)hash_length, + &verify_res, &ed_key, NULL, 0); wc_ed25519_free(&ed_key); @@ -273,8 +270,12 @@ psa_status_t psa_asymmetric_export_public_key_ed25519(psa_key_type_t key_type, /* Import key */ if (key_type == PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_TWISTED_EDWARDS)) { - ret = wc_ed25519_import_private_key(key_buffer, (word32)key_buffer_size, - NULL, 0, &ed_key); + ret = wc_ed25519_import_private_only(key_buffer, (word32)key_buffer_size, + &ed_key); + if (ret == 0) { + ret = wc_ed25519_make_public(&ed_key, ed_key.p, + ED25519_PUB_KEY_SIZE); + } } else { ret = wc_ed25519_import_public(key_buffer, (word32)key_buffer_size, &ed_key); @@ -318,7 +319,7 @@ psa_status_t psa_asymmetric_sign_ed448(psa_key_type_t key_type, ed448_key ed_key; word32 sig_len32; - if (alg != PSA_ALG_ED448 && alg != PSA_ALG_ED448PH) { + if (alg != PSA_ALG_ED448PH) { return PSA_ERROR_NOT_SUPPORTED; } @@ -334,9 +335,12 @@ psa_status_t psa_asymmetric_sign_ed448(psa_key_type_t key_type, return wc_error_to_psa_status(ret); } - /* Import private key */ - ret = wc_ed448_import_private_key(key_buffer, (word32)key_buffer_size, - NULL, 0, &ed_key); + /* Import the stored private seed and derive the public component. */ + ret = wc_ed448_import_private_only(key_buffer, (word32)key_buffer_size, + &ed_key); + if (ret == 0) { + ret = wc_ed448_make_public(&ed_key, ed_key.p, ED448_PUB_KEY_SIZE); + } if (ret != 0) { wc_ed448_free(&ed_key); return wc_error_to_psa_status(ret); @@ -344,16 +348,8 @@ 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, - &sig_len32, &ed_key, NULL, 0); - } - else if (alg == PSA_ALG_ED448) { - /* Sign message */ - ret = wc_ed448_sign_msg(hash, (word32)hash_length, signature, - &sig_len32, &ed_key, NULL, 0); - } + ret = wc_ed448ph_sign_hash(hash, (word32)hash_length, signature, + &sig_len32, &ed_key, NULL, 0); wc_ed448_free(&ed_key); @@ -381,12 +377,13 @@ psa_status_t psa_asymmetric_verify_ed448(psa_key_type_t key_type, ed448_key ed_key; int verify_res = 0; - if (alg != PSA_ALG_ED448 && alg != PSA_ALG_ED448PH) { + if (alg != PSA_ALG_ED448PH) { return PSA_ERROR_NOT_SUPPORTED; } - /* Check if key type is ED448 public key */ - if (key_type != PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_TWISTED_EDWARDS) || + /* Check if key type is ED448 */ + if ((key_type != PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_TWISTED_EDWARDS) && + key_type != PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_TWISTED_EDWARDS)) || key_bits != 448) { return PSA_ERROR_INVALID_ARGUMENT; } @@ -398,26 +395,26 @@ psa_status_t psa_asymmetric_verify_ed448(psa_key_type_t key_type, return wc_error_to_psa_status(ret); } - /* Import public key */ - ret = wc_ed448_import_public(key_buffer, (word32)key_buffer_size, &ed_key); + /* Import the key material needed for verification. */ + if (key_type == PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_TWISTED_EDWARDS)) { + ret = wc_ed448_import_private_only(key_buffer, (word32)key_buffer_size, + &ed_key); + if (ret == 0) { + ret = wc_ed448_make_public(&ed_key, ed_key.p, ED448_PUB_KEY_SIZE); + } + } + else { + ret = wc_ed448_import_public(key_buffer, (word32)key_buffer_size, &ed_key); + } if (ret != 0) { wc_ed448_free(&ed_key); return wc_error_to_psa_status(ret); } /* Verify signature */ - if (alg == PSA_ALG_ED448PH) { - /* Verify hash */ - ret = wc_ed448ph_verify_hash(signature, (word32)signature_length, - hash, (word32)hash_length, - &verify_res, &ed_key); - } - else if (alg == PSA_ALG_ED448) { - /* Verify message */ - ret = wc_ed448_verify_msg(signature, (word32)signature_length, - hash, (word32)hash_length, - &verify_res, &ed_key, NULL, 0); - } + ret = wc_ed448ph_verify_hash(signature, (word32)signature_length, + hash, (word32)hash_length, + &verify_res, &ed_key, NULL, 0); wc_ed448_free(&ed_key); @@ -530,8 +527,11 @@ psa_status_t psa_asymmetric_export_public_key_ed448(psa_key_type_t key_type, /* Import key */ if (key_type == PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_TWISTED_EDWARDS)) { - ret = wc_ed448_import_private_key(key_buffer, (word32)key_buffer_size, - NULL, 0, &ed_key); + ret = wc_ed448_import_private_only(key_buffer, (word32)key_buffer_size, + &ed_key); + if (ret == 0) { + ret = wc_ed448_make_public(&ed_key, ed_key.p, ED448_PUB_KEY_SIZE); + } } else { ret = wc_ed448_import_public(key_buffer, (word32)key_buffer_size, &ed_key); diff --git a/src/psa_key_storage.c b/src/psa_key_storage.c index c6be067..8c452b4 100644 --- a/src/psa_key_storage.c +++ b/src/psa_key_storage.c @@ -1013,6 +1013,24 @@ psa_status_t psa_generate_key( size_t pub_len = 0; uint8_t *pub_buf = NULL; + if (family == PSA_ECC_FAMILY_TWISTED_EDWARDS) { +#ifdef HAVE_ED25519 + if (key_bits == 255) { + priv_buf_size = PSA_BITS_TO_BYTES(key_bits) + 1U; + } + else +#endif +#ifdef HAVE_ED448 + if (key_bits == 448) { + priv_buf_size = PSA_BITS_TO_BYTES(key_bits) + 1U; + } + else +#endif + { + return PSA_ERROR_INVALID_ARGUMENT; + } + } + key_data = (uint8_t *)XMALLOC(priv_buf_size, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (key_data == NULL) { diff --git a/user_settings.h b/user_settings.h index 5b3f6bd..f1bba73 100644 --- a/user_settings.h +++ b/user_settings.h @@ -65,5 +65,11 @@ #define WOLFSSL_CMAC #define HAVE_CHACHA #define HAVE_POLY1305 +#define HAVE_CURVE25519 +#define HAVE_ED25519 +#define WOLFSSL_ED25519_STREAMING_VERIFY +#define HAVE_CURVE448 +#define HAVE_ED448 +#define WOLFSSL_ED448_STREAMING_VERIFY #endif /* WOLFSSL_USER_SETTINGS_H */ From 8d2435928cc3ffa7d0f9ecf8bfc596cdd9e4e7f7 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 23 Mar 2026 11:47:20 +0100 Subject: [PATCH 14/15] Zeroize final plaintext block in PKCS7 decrypt --- src/psa_cipher.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/psa_cipher.c b/src/psa_cipher.c index d6dac6e..e7851bb 100644 --- a/src/psa_cipher.c +++ b/src/psa_cipher.c @@ -1242,6 +1242,7 @@ psa_status_t psa_cipher_finish(psa_cipher_operation_t *operation, byte invalid; size_t pad_len; size_t plain_len; + psa_status_t status = PSA_SUCCESS; if (ctx->partial_len != block_size) { return PSA_ERROR_INVALID_PADDING; @@ -1260,6 +1261,7 @@ psa_status_t psa_cipher_finish(psa_cipher_operation_t *operation, (word32)block_size); } if (ret != 0) { + wc_ForceZero(block, sizeof(block)); return wc_error_to_psa_status(ret); } @@ -1272,19 +1274,24 @@ psa_status_t psa_cipher_finish(psa_cipher_operation_t *operation, (byte)pad_len); } if (invalid != 0) { - return PSA_ERROR_INVALID_PADDING; + status = PSA_ERROR_INVALID_PADDING; + goto cbc_pkcs7_decrypt_done; } plain_len = block_size - pad_len; if (output_size < plain_len) { - return PSA_ERROR_BUFFER_TOO_SMALL; + status = PSA_ERROR_BUFFER_TOO_SMALL; + goto cbc_pkcs7_decrypt_done; } if (plain_len > 0) { XMEMCPY(output, block, plain_len); } ctx->partial_len = 0; *output_length = plain_len; - return PSA_SUCCESS; + +cbc_pkcs7_decrypt_done: + wc_ForceZero(block, sizeof(block)); + return status; } } From 7ed7e350e16f15404ce6b98b3da820d4b9750ac7 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 23 Mar 2026 16:23:44 +0100 Subject: [PATCH 15/15] Addressed copilot's comments --- src/psa_cipher.c | 28 ++---- src/psa_key_storage.c | 32 ++++--- test/psa_server/psa_api_test.c | 168 ++++++++++++++++++++++++++------- 3 files changed, 161 insertions(+), 67 deletions(-) diff --git a/src/psa_cipher.c b/src/psa_cipher.c index e7851bb..e198865 100644 --- a/src/psa_cipher.c +++ b/src/psa_cipher.c @@ -872,7 +872,7 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, return PSA_SUCCESS; } else { - size_t bytes_to_process; + size_t process_len; size_t full_blocks_len; if (total < block_size) { @@ -883,21 +883,15 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, return PSA_SUCCESS; } - bytes_to_process = total - block_size; - if (bytes_to_process == 0) { - if (ctx->partial_len > 0) { - size_t needed = block_size - ctx->partial_len; - XMEMCPY(ctx->partial + ctx->partial_len, input, needed); - ctx->partial_len = block_size; - } - else { - XMEMCPY(ctx->partial, input, block_size); - ctx->partial_len = block_size; - } + /* Decrypt whole blocks while retaining the final ciphertext block. */ + process_len = ((total - 1U) / block_size) * block_size; + if (process_len == 0) { + XMEMCPY(ctx->partial + ctx->partial_len, input, input_length); + ctx->partial_len += input_length; return PSA_SUCCESS; } - if (output_size < bytes_to_process) { + if (output_size < process_len) { return PSA_ERROR_BUFFER_TOO_SMALL; } @@ -926,15 +920,9 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, output_offset += block_size; input_offset += needed; ctx->partial_len = 0; - if (bytes_to_process >= block_size) { - bytes_to_process -= block_size; - } - else { - bytes_to_process = 0; - } } - full_blocks_len = bytes_to_process; + full_blocks_len = process_len - output_offset; if (full_blocks_len > 0) { if (ctx->is_des3) { #ifndef NO_DES3 diff --git a/src/psa_key_storage.c b/src/psa_key_storage.c index 8c452b4..358885a 100644 --- a/src/psa_key_storage.c +++ b/src/psa_key_storage.c @@ -1014,19 +1014,21 @@ psa_status_t psa_generate_key( uint8_t *pub_buf = NULL; if (family == PSA_ECC_FAMILY_TWISTED_EDWARDS) { -#ifdef HAVE_ED25519 if (key_bits == 255) { +#ifdef HAVE_ED25519 priv_buf_size = PSA_BITS_TO_BYTES(key_bits) + 1U; - } - else +#else + return PSA_ERROR_NOT_SUPPORTED; #endif + } + else if (key_bits == 448) { #ifdef HAVE_ED448 - if (key_bits == 448) { priv_buf_size = PSA_BITS_TO_BYTES(key_bits) + 1U; - } - else +#else + return PSA_ERROR_NOT_SUPPORTED; #endif - { + } + else { return PSA_ERROR_INVALID_ARGUMENT; } } @@ -1045,27 +1047,29 @@ psa_status_t psa_generate_key( } if (family == PSA_ECC_FAMILY_TWISTED_EDWARDS) { -#ifdef HAVE_ED25519 if (key_bits == 255) { +#ifdef HAVE_ED25519 status = psa_asymmetric_generate_key_ed25519(key_type, key_bits, key_data, priv_buf_size, &priv_len, pub_buf, pub_buf_size, &pub_len); - } - else +#else + status = PSA_ERROR_NOT_SUPPORTED; #endif + } + else if (key_bits == 448) { #ifdef HAVE_ED448 - if (key_bits == 448) { status = psa_asymmetric_generate_key_ed448(key_type, key_bits, key_data, priv_buf_size, &priv_len, pub_buf, pub_buf_size, &pub_len); - } - else +#else + status = PSA_ERROR_NOT_SUPPORTED; #endif - { + } + else { status = PSA_ERROR_INVALID_ARGUMENT; } } diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index 4f6f427..fb71317 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -270,6 +270,7 @@ static int test_cipher_cbc_pkcs7_multipart_decrypt(void) psa_key_attributes_t attrs = psa_key_attributes_init(); psa_cipher_operation_t op = psa_cipher_operation_init(); psa_status_t st; + int result = TEST_FAIL; psa_set_key_type(&attrs, PSA_KEY_TYPE_AES); psa_set_key_bits(&attrs, 128); @@ -277,74 +278,64 @@ static int test_cipher_cbc_pkcs7_multipart_decrypt(void) psa_set_key_algorithm(&attrs, PSA_ALG_CBC_PKCS7); st = psa_import_key(&attrs, key, sizeof(key), &key_id); - if (check_status(st, "psa_import_key(AES PKCS7)") != TEST_OK) return TEST_FAIL; + if (check_status(st, "psa_import_key(AES PKCS7)") != TEST_OK) goto cleanup; st = psa_cipher_encrypt_setup(&op, key_id, PSA_ALG_CBC_PKCS7); if (st == PSA_ERROR_NOT_SUPPORTED) { - (void)psa_destroy_key(key_id); - return TEST_SKIPPED; + result = TEST_SKIPPED; + goto cleanup; } if (check_status(st, "psa_cipher_encrypt_setup(PKCS7)") != TEST_OK) { - (void)psa_destroy_key(key_id); - return TEST_FAIL; + goto cleanup; } st = psa_cipher_set_iv(&op, iv, sizeof(iv)); if (check_status(st, "psa_cipher_set_iv(PKCS7)") != TEST_OK) { - (void)psa_destroy_key(key_id); - return TEST_FAIL; + goto cleanup; } st = psa_cipher_update(&op, plaintext, sizeof(plaintext), ciphertext, sizeof(ciphertext), &part_len); if (check_status(st, "psa_cipher_update(PKCS7 enc)") != TEST_OK) { - (void)psa_destroy_key(key_id); - return TEST_FAIL; + goto cleanup; } ciphertext_len += part_len; st = psa_cipher_finish(&op, ciphertext + ciphertext_len, sizeof(ciphertext) - ciphertext_len, &finish_len); if (check_status(st, "psa_cipher_finish(PKCS7 enc)") != TEST_OK) { - (void)psa_destroy_key(key_id); - return TEST_FAIL; + goto cleanup; } ciphertext_len += finish_len; + (void)psa_cipher_abort(&op); op = psa_cipher_operation_init(); st = psa_cipher_decrypt_setup(&op, key_id, PSA_ALG_CBC_PKCS7); if (check_status(st, "psa_cipher_decrypt_setup(PKCS7)") != TEST_OK) { - (void)psa_destroy_key(key_id); - return TEST_FAIL; + goto cleanup; } st = psa_cipher_set_iv(&op, iv, sizeof(iv)); if (check_status(st, "psa_cipher_set_iv(PKCS7 dec)") != TEST_OK) { - (void)psa_destroy_key(key_id); - return TEST_FAIL; + goto cleanup; } st = psa_cipher_update(&op, ciphertext, 1, decrypted, sizeof(decrypted), &dec_part_len); if (check_status(st, "psa_cipher_update(PKCS7 dec 1)") != TEST_OK) { - (void)psa_destroy_key(key_id); - return TEST_FAIL; + goto cleanup; } if (check_true(dec_part_len == 0, "psa_cipher_update(PKCS7 dec 1) length") != TEST_OK) { - (void)psa_destroy_key(key_id); - return TEST_FAIL; + goto cleanup; } st = psa_cipher_update(&op, ciphertext + 1, 16, decrypted, sizeof(decrypted), &dec_part_len); if (check_status(st, "psa_cipher_update(PKCS7 dec 16)") != TEST_OK) { - (void)psa_destroy_key(key_id); - return TEST_FAIL; + goto cleanup; } if (check_true(dec_part_len == 16, "psa_cipher_update(PKCS7 dec 16) length") != TEST_OK) { - (void)psa_destroy_key(key_id); - return TEST_FAIL; + goto cleanup; } if (check_buf_eq("psa_cipher_update(PKCS7 dec 16)", decrypted, plaintext, dec_part_len) != TEST_OK) { - (void)psa_destroy_key(key_id); - return TEST_FAIL; + goto cleanup; } dec_len += dec_part_len; @@ -352,33 +343,138 @@ static int test_cipher_cbc_pkcs7_multipart_decrypt(void) decrypted + dec_len, sizeof(decrypted) - dec_len, &dec_part_len); if (check_status(st, "psa_cipher_update(PKCS7 dec tail)") != TEST_OK) { - (void)psa_destroy_key(key_id); - return TEST_FAIL; + goto cleanup; } if (check_true(dec_part_len == 0, "psa_cipher_update(PKCS7 dec tail) length") != TEST_OK) { - (void)psa_destroy_key(key_id); - return TEST_FAIL; + goto cleanup; } st = psa_cipher_finish(&op, decrypted + dec_len, sizeof(decrypted) - dec_len, &dec_finish_len); if (check_status(st, "psa_cipher_finish(PKCS7 dec)") != TEST_OK) { - (void)psa_destroy_key(key_id); - return TEST_FAIL; + goto cleanup; } dec_len += dec_finish_len; + (void)psa_cipher_abort(&op); if (check_true(dec_len == sizeof(plaintext), "psa_cipher_decrypt(PKCS7) length") != TEST_OK) { + goto cleanup; + } + if (check_buf_eq("psa_cipher_decrypt(PKCS7)", decrypted, plaintext, sizeof(plaintext)) != TEST_OK) { + goto cleanup; + } + + st = psa_destroy_key(key_id); + if (check_status(st, "psa_destroy_key(AES PKCS7)") != TEST_OK) return TEST_FAIL; + + key_id = 0; + result = TEST_OK; + +cleanup: + (void)psa_cipher_abort(&op); + if (key_id != 0) { (void)psa_destroy_key(key_id); + } + return result; +} + +static int test_cipher_cbc_pkcs7_decrypt_update_small_output(void) +{ + static const uint8_t key[16] = { + 0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6, + 0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c + }; + static const uint8_t iv[16] = { + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f + }; + static const uint8_t plaintext[17] = { + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, + 0x38,0x39,0x61,0x62,0x63,0x64,0x65,0x66, + 0x67 + }; + uint8_t ciphertext[sizeof(plaintext) + 16]; + uint8_t too_small[1]; + size_t ciphertext_len = 0; + size_t part_len = 0; + size_t finish_len = 0; + size_t out_len = 0; + psa_key_id_t key_id = 0; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_cipher_operation_t op = psa_cipher_operation_init(); + psa_status_t st; + + psa_set_key_type(&attrs, PSA_KEY_TYPE_AES); + psa_set_key_bits(&attrs, 128); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT); + psa_set_key_algorithm(&attrs, PSA_ALG_CBC_PKCS7); + + st = psa_import_key(&attrs, key, sizeof(key), &key_id); + if (check_status(st, "psa_import_key(AES PKCS7 small out)") != TEST_OK) { return TEST_FAIL; } - if (check_buf_eq("psa_cipher_decrypt(PKCS7)", decrypted, plaintext, sizeof(plaintext)) != TEST_OK) { + + st = psa_cipher_encrypt_setup(&op, key_id, PSA_ALG_CBC_PKCS7); + if (check_status(st, "psa_cipher_encrypt_setup(PKCS7 small out)") != TEST_OK) { (void)psa_destroy_key(key_id); return TEST_FAIL; } + st = psa_cipher_set_iv(&op, iv, sizeof(iv)); + if (check_status(st, "psa_cipher_set_iv(PKCS7 small out enc)") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + st = psa_cipher_update(&op, plaintext, sizeof(plaintext), + ciphertext, sizeof(ciphertext), &part_len); + if (check_status(st, "psa_cipher_update(PKCS7 small out enc)") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + ciphertext_len += part_len; + st = psa_cipher_finish(&op, ciphertext + ciphertext_len, + sizeof(ciphertext) - ciphertext_len, &finish_len); + if (check_status(st, "psa_cipher_finish(PKCS7 small out enc)") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + ciphertext_len += finish_len; + op = psa_cipher_operation_init(); + st = psa_cipher_decrypt_setup(&op, key_id, PSA_ALG_CBC_PKCS7); + if (check_status(st, "psa_cipher_decrypt_setup(PKCS7 small out)") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + st = psa_cipher_set_iv(&op, iv, sizeof(iv)); + if (check_status(st, "psa_cipher_set_iv(PKCS7 small out dec)") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + + st = psa_cipher_update(&op, ciphertext, 1, too_small, sizeof(too_small), &out_len); + if (check_status(st, "psa_cipher_update(PKCS7 small out seed)") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + if (check_true(out_len == 0, + "psa_cipher_update(PKCS7 small out seed) length") != TEST_OK) { + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + + st = psa_cipher_update(&op, ciphertext + 1, 16, too_small, sizeof(too_small), &out_len); + if (check_true(st == PSA_ERROR_BUFFER_TOO_SMALL, + "psa_cipher_update(PKCS7 small out status)") != TEST_OK) { + (void)psa_cipher_abort(&op); + (void)psa_destroy_key(key_id); + return TEST_FAIL; + } + + (void)psa_cipher_abort(&op); st = psa_destroy_key(key_id); - if (check_status(st, "psa_destroy_key(AES PKCS7)") != TEST_OK) return TEST_FAIL; + if (check_status(st, "psa_destroy_key(AES PKCS7 small out)") != TEST_OK) { + return TEST_FAIL; + } return TEST_OK; } @@ -849,6 +945,12 @@ int main(int argc, char** argv) return TEST_FAIL; } } + if (only == NULL || strcmp(only, "cipher_cbc_pkcs7_small_output") == 0) { + if (run_named_test("cipher_cbc_pkcs7_small_output", + test_cipher_cbc_pkcs7_decrypt_update_small_output) == TEST_FAIL) { + return TEST_FAIL; + } + } if (only == NULL || strcmp(only, "aead_gcm") == 0) { if (run_named_test("aead_gcm", test_aead_gcm) == TEST_FAIL) return TEST_FAIL; }