From 712dff3c6a6fa3d603f39cac75fb3eed72259605 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 10 Apr 2026 07:04:32 +0200 Subject: [PATCH 01/38] Add psa_export_key usage test F/2404 --- test/psa_server/psa_api_test.c | 39 ++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index 47a5ffb..e6fca5b 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -244,6 +244,39 @@ static int test_cipher_cbc(void) return TEST_OK; } +static int test_export_key_requires_usage_flag(void) +{ + static const uint8_t key[16] = { + 0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6, + 0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c + }; + uint8_t exported[sizeof(key)]; + size_t exported_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, 128); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_ENCRYPT); + psa_set_key_algorithm(&attrs, PSA_ALG_CBC_NO_PADDING); + + st = psa_import_key(&attrs, key, sizeof(key), &key_id); + if (check_status(st, "psa_import_key(AES no export)") != TEST_OK) return TEST_FAIL; + + st = psa_export_key(key_id, exported, sizeof(exported), &exported_len); + if (check_true(st == PSA_ERROR_NOT_PERMITTED, + "psa_export_key requires PSA_KEY_USAGE_EXPORT") != 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 no export)") != TEST_OK) return TEST_FAIL; + + return TEST_OK; +} + static int test_cipher_cbc_pkcs7_multipart_decrypt(void) { static const uint8_t key[16] = { @@ -1333,6 +1366,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, "export_requires_usage") == 0) { + if (run_named_test("export_requires_usage", + test_export_key_requires_usage_flag) == 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) { From 872ab2e2e5173c23bb9ba824b332c421b993d732 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 10 Apr 2026 07:08:06 +0200 Subject: [PATCH 02/38] Add psa_copy_key API coverage F/2405 --- test/psa_server/psa_api_test.c | 300 +++++++++++++++++++++++++++++++++ 1 file changed, 300 insertions(+) diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index e6fca5b..68c5d0e 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -277,6 +277,282 @@ static int test_export_key_requires_usage_flag(void) return TEST_OK; } +static void setup_aes_key_attrs(psa_key_attributes_t* attrs, psa_key_usage_t usage, + psa_algorithm_t alg, psa_key_lifetime_t lifetime) +{ + *attrs = psa_key_attributes_init(); + psa_set_key_type(attrs, PSA_KEY_TYPE_AES); + psa_set_key_bits(attrs, 128); + psa_set_key_usage_flags(attrs, usage); + psa_set_key_algorithm(attrs, alg); + psa_set_key_lifetime(attrs, lifetime); +} + +static int test_copy_key_copies_material_and_attributes(void) +{ + static const uint8_t key[16] = { + 0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6, + 0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c + }; + uint8_t exported[sizeof(key)]; + size_t exported_len = 0; + psa_key_id_t src_key = 0; + psa_key_id_t copy_key = 0; + psa_key_attributes_t src_attrs = psa_key_attributes_init(); + psa_key_attributes_t dst_attrs = psa_key_attributes_init(); + psa_key_attributes_t got_attrs = psa_key_attributes_init(); + psa_status_t st; + int ret = TEST_FAIL; + + setup_aes_key_attrs(&src_attrs, + PSA_KEY_USAGE_COPY | PSA_KEY_USAGE_EXPORT | + PSA_KEY_USAGE_ENCRYPT, + PSA_ALG_CBC_NO_PADDING, + PSA_KEY_LIFETIME_VOLATILE); + st = psa_import_key(&src_attrs, key, sizeof(key), &src_key); + if (check_status(st, "psa_import_key(AES copy source)") != TEST_OK) { + goto cleanup; + } + + setup_aes_key_attrs(&dst_attrs, + PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_ENCRYPT, + PSA_ALG_CBC_NO_PADDING, + PSA_KEY_LIFETIME_VOLATILE); + st = psa_copy_key(src_key, &dst_attrs, ©_key); + if (check_status(st, "psa_copy_key(success)") != TEST_OK) { + goto cleanup; + } + if (check_true(copy_key != src_key, "psa_copy_key returns a new key id") != TEST_OK) { + goto cleanup; + } + + st = psa_export_key(copy_key, exported, sizeof(exported), &exported_len); + if (check_status(st, "psa_export_key(copied AES)") != TEST_OK) { + goto cleanup; + } + if (check_true(exported_len == sizeof(key), "psa_export_key(copied AES) length") != TEST_OK) { + goto cleanup; + } + if (check_buf_eq("psa_export_key(copied AES)", exported, key, sizeof(key)) != TEST_OK) { + goto cleanup; + } + + st = psa_get_key_attributes(copy_key, &got_attrs); + if (check_status(st, "psa_get_key_attributes(copied AES)") != TEST_OK) { + goto cleanup; + } + if (check_true(psa_get_key_type(&got_attrs) == PSA_KEY_TYPE_AES, + "psa_copy_key preserves type") != TEST_OK) { + goto cleanup; + } + if (check_true(psa_get_key_bits(&got_attrs) == 128, + "psa_copy_key preserves bits") != TEST_OK) { + goto cleanup; + } + if (check_true(psa_get_key_algorithm(&got_attrs) == PSA_ALG_CBC_NO_PADDING, + "psa_copy_key preserves algorithm") != TEST_OK) { + goto cleanup; + } + if (check_true(psa_get_key_lifetime(&got_attrs) == PSA_KEY_LIFETIME_VOLATILE, + "psa_copy_key preserves lifetime") != TEST_OK) { + goto cleanup; + } + if (check_true(psa_get_key_usage_flags(&got_attrs) == + (PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_ENCRYPT), + "psa_copy_key intersects usage flags") != TEST_OK) { + goto cleanup; + } + + ret = TEST_OK; + +cleanup: + psa_reset_key_attributes(&got_attrs); + psa_reset_key_attributes(&dst_attrs); + psa_reset_key_attributes(&src_attrs); + if (copy_key != 0) { + (void)psa_destroy_key(copy_key); + } + if (src_key != 0) { + (void)psa_destroy_key(src_key); + } + return ret; +} + +static int test_copy_key_requires_copy_usage_flag(void) +{ + static const uint8_t key[16] = { + 0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6, + 0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c + }; + psa_key_id_t src_key = 0; + psa_key_id_t copy_key = PSA_KEY_ID_NULL; + psa_key_attributes_t src_attrs = psa_key_attributes_init(); + psa_key_attributes_t dst_attrs = psa_key_attributes_init(); + psa_status_t st; + int ret = TEST_FAIL; + + setup_aes_key_attrs(&src_attrs, + PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_ENCRYPT, + PSA_ALG_CBC_NO_PADDING, + PSA_KEY_LIFETIME_VOLATILE); + st = psa_import_key(&src_attrs, key, sizeof(key), &src_key); + if (check_status(st, "psa_import_key(AES no copy)") != TEST_OK) { + goto cleanup; + } + + setup_aes_key_attrs(&dst_attrs, + PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_ENCRYPT, + PSA_ALG_CBC_NO_PADDING, + PSA_KEY_LIFETIME_VOLATILE); + st = psa_copy_key(src_key, &dst_attrs, ©_key); + if (check_true(st == PSA_ERROR_NOT_PERMITTED, + "psa_copy_key requires PSA_KEY_USAGE_COPY for volatile keys") != TEST_OK) { + goto cleanup; + } + + ret = TEST_OK; + +cleanup: + psa_reset_key_attributes(&dst_attrs); + psa_reset_key_attributes(&src_attrs); + if (copy_key != PSA_KEY_ID_NULL) { + (void)psa_destroy_key(copy_key); + } + if (src_key != 0) { + (void)psa_destroy_key(src_key); + } + return ret; +} + +static int test_copy_key_rejects_attribute_mismatch(void) +{ + static const uint8_t key[16] = { + 0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6, + 0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c + }; + psa_key_id_t src_key = 0; + psa_key_id_t copy_key = PSA_KEY_ID_NULL; + psa_key_attributes_t src_attrs = psa_key_attributes_init(); + psa_key_attributes_t dst_attrs = psa_key_attributes_init(); + psa_status_t st; + int ret = TEST_FAIL; + + setup_aes_key_attrs(&src_attrs, + PSA_KEY_USAGE_COPY | PSA_KEY_USAGE_EXPORT | + PSA_KEY_USAGE_ENCRYPT, + PSA_ALG_CBC_NO_PADDING, + PSA_KEY_LIFETIME_VOLATILE); + st = psa_import_key(&src_attrs, key, sizeof(key), &src_key); + if (check_status(st, "psa_import_key(AES copy mismatch source)") != TEST_OK) { + goto cleanup; + } + + setup_aes_key_attrs(&dst_attrs, + PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_ENCRYPT, + PSA_ALG_CBC_NO_PADDING, + PSA_KEY_LIFETIME_VOLATILE); + psa_set_key_type(&dst_attrs, PSA_KEY_TYPE_DES); + st = psa_copy_key(src_key, &dst_attrs, ©_key); + if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, + "psa_copy_key rejects type mismatch") != TEST_OK) { + goto cleanup; + } + + setup_aes_key_attrs(&dst_attrs, + PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_ENCRYPT, + PSA_ALG_CBC_NO_PADDING, + PSA_KEY_LIFETIME_VOLATILE); + psa_set_key_bits(&dst_attrs, 192); + st = psa_copy_key(src_key, &dst_attrs, ©_key); + if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, + "psa_copy_key rejects bit-size mismatch") != TEST_OK) { + goto cleanup; + } + + setup_aes_key_attrs(&dst_attrs, + PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_ENCRYPT, + PSA_ALG_ECB_NO_PADDING, + PSA_KEY_LIFETIME_VOLATILE); + st = psa_copy_key(src_key, &dst_attrs, ©_key); + if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, + "psa_copy_key rejects algorithm mismatch") != TEST_OK) { + goto cleanup; + } + + setup_aes_key_attrs(&dst_attrs, + PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_ENCRYPT, + PSA_ALG_CBC_NO_PADDING, + PSA_KEY_LIFETIME_PERSISTENT); + st = psa_copy_key(src_key, &dst_attrs, ©_key); + if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, + "psa_copy_key rejects lifetime mismatch") != TEST_OK) { + goto cleanup; + } + + ret = TEST_OK; + +cleanup: + psa_reset_key_attributes(&dst_attrs); + psa_reset_key_attributes(&src_attrs); + if (copy_key != PSA_KEY_ID_NULL) { + (void)psa_destroy_key(copy_key); + } + if (src_key != 0) { + (void)psa_destroy_key(src_key); + } + return ret; +} + +static int test_copy_key_requires_copy_usage_flag_persistent(void) +{ + static const uint8_t key[16] = { + 0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6, + 0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c + }; + psa_key_id_t src_key = 0; + psa_key_id_t copy_key = PSA_KEY_ID_NULL; + psa_key_id_t persistent_id = PSA_KEY_ID_USER_MIN + 2405u; + psa_key_attributes_t src_attrs = psa_key_attributes_init(); + psa_key_attributes_t dst_attrs = psa_key_attributes_init(); + psa_status_t st; + int ret = TEST_FAIL; + + (void)psa_destroy_key(persistent_id); + + setup_aes_key_attrs(&src_attrs, + PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_ENCRYPT, + PSA_ALG_CBC_NO_PADDING, + PSA_KEY_LIFETIME_PERSISTENT); + psa_set_key_id(&src_attrs, persistent_id); + st = psa_import_key(&src_attrs, key, sizeof(key), &src_key); + if (check_status(st, "psa_import_key(AES persistent no copy)") != TEST_OK) { + goto cleanup; + } + + setup_aes_key_attrs(&dst_attrs, + PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_ENCRYPT, + PSA_ALG_CBC_NO_PADDING, + PSA_KEY_LIFETIME_PERSISTENT); + st = psa_copy_key(src_key, &dst_attrs, ©_key); + if (check_true(st == PSA_ERROR_NOT_PERMITTED, + "psa_copy_key requires PSA_KEY_USAGE_COPY for persistent keys") != TEST_OK) { + goto cleanup; + } + + ret = TEST_OK; + +cleanup: + psa_reset_key_attributes(&dst_attrs); + psa_reset_key_attributes(&src_attrs); + if (copy_key != PSA_KEY_ID_NULL) { + (void)psa_destroy_key(copy_key); + } + if (src_key != 0) { + (void)psa_destroy_key(src_key); + } + return ret; +} + static int test_cipher_cbc_pkcs7_multipart_decrypt(void) { static const uint8_t key[16] = { @@ -1372,6 +1648,30 @@ int main(int argc, char** argv) return TEST_FAIL; } } + if (only == NULL || strcmp(only, "copy_key_success") == 0) { + if (run_named_test("copy_key_success", + test_copy_key_copies_material_and_attributes) == TEST_FAIL) { + return TEST_FAIL; + } + } + if (only == NULL || strcmp(only, "copy_key_requires_usage") == 0) { + if (run_named_test("copy_key_requires_usage", + test_copy_key_requires_copy_usage_flag) == TEST_FAIL) { + return TEST_FAIL; + } + } + if (only == NULL || strcmp(only, "copy_key_attribute_mismatch") == 0) { + if (run_named_test("copy_key_attribute_mismatch", + test_copy_key_rejects_attribute_mismatch) == TEST_FAIL) { + return TEST_FAIL; + } + } + if (only == NULL || strcmp(only, "copy_key_persistent_requires_usage") == 0) { + if (run_named_test("copy_key_persistent_requires_usage", + test_copy_key_requires_copy_usage_flag_persistent) == 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) { From bd0368b2a27eff3f35ad9488f70fcf9ed205fe14 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 10 Apr 2026 07:10:33 +0200 Subject: [PATCH 03/38] Fix sequential KDF output slices F/2421 --- src/psa_key_derivation.c | 68 +++++++++++++++++++++++------- test/psa_server/psa_api_test.c | 76 ++++++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+), 16 deletions(-) diff --git a/src/psa_key_derivation.c b/src/psa_key_derivation.c index 0f8e57c..cd630b6 100644 --- a/src/psa_key_derivation.c +++ b/src/psa_key_derivation.c @@ -48,6 +48,7 @@ typedef struct wolfpsa_kdf_ctx { psa_algorithm_t alg; psa_algorithm_t ka_alg; size_t capacity; + size_t output_offset; uint32_t steps_set; uint8_t *secret; size_t secret_length; @@ -1037,11 +1038,37 @@ static psa_status_t wolfpsa_kdf_pbkdf2(wolfpsa_kdf_ctx_t *ctx, return PSA_ERROR_NOT_SUPPORTED; } +static psa_status_t wolfpsa_kdf_compute_output(wolfpsa_kdf_ctx_t *ctx, + uint8_t *output, + size_t output_length) +{ + if (ctx->is_raw_kdf) { + XMEMCPY(output, ctx->secret, output_length); + return PSA_SUCCESS; + } + + if (PSA_ALG_IS_ANY_HKDF(ctx->alg)) { + return wolfpsa_kdf_hkdf(ctx, output, output_length); + } + if (PSA_ALG_IS_TLS12_PRF(ctx->alg)) { + return wolfpsa_kdf_tls12_prf(ctx, output, output_length); + } + if (PSA_ALG_IS_TLS12_PSK_TO_MS(ctx->alg)) { + return wolfpsa_kdf_tls12_psk_to_ms(ctx, output, output_length); + } + if (PSA_ALG_IS_PBKDF2(ctx->alg)) { + return wolfpsa_kdf_pbkdf2(ctx, output, output_length); + } + + return PSA_ERROR_NOT_SUPPORTED; +} + psa_status_t psa_key_derivation_output_bytes(psa_key_derivation_operation_t *operation, uint8_t *output, size_t output_length) { wolfpsa_kdf_ctx_t *ctx = wolfpsa_kdf_get_ctx(operation); + size_t total_output_length; psa_status_t status; if (ctx == NULL || output == NULL) { @@ -1050,7 +1077,8 @@ psa_status_t psa_key_derivation_output_bytes(psa_key_derivation_operation_t *ope if (ctx->is_raw_kdf) { if ((ctx->steps_set & WOLFPSA_KDF_STEP_SECRET) == 0 || - output_length > ctx->secret_length) { + ctx->output_offset > ctx->secret_length || + output_length > ctx->secret_length - ctx->output_offset) { return PSA_ERROR_INSUFFICIENT_DATA; } } @@ -1101,26 +1129,34 @@ psa_status_t psa_key_derivation_output_bytes(psa_key_derivation_operation_t *ope } ctx->output_started = 1; - - if (ctx->is_raw_kdf) { - XMEMCPY(output, ctx->secret, output_length); - return PSA_SUCCESS; + total_output_length = ctx->output_offset + output_length; + if (total_output_length < ctx->output_offset) { + return PSA_ERROR_INVALID_ARGUMENT; } - if (PSA_ALG_IS_ANY_HKDF(ctx->alg)) { - return wolfpsa_kdf_hkdf(ctx, output, output_length); + if (ctx->output_offset == 0) { + status = wolfpsa_kdf_compute_output(ctx, output, output_length); } - if (PSA_ALG_IS_TLS12_PRF(ctx->alg)) { - return wolfpsa_kdf_tls12_prf(ctx, output, output_length); - } - if (PSA_ALG_IS_TLS12_PSK_TO_MS(ctx->alg)) { - return wolfpsa_kdf_tls12_psk_to_ms(ctx, output, output_length); + else { + uint8_t *full_output; + + full_output = (uint8_t *)XMALLOC(total_output_length, NULL, + DYNAMIC_TYPE_TMP_BUFFER); + if (full_output == NULL) { + return PSA_ERROR_INSUFFICIENT_MEMORY; + } + + status = wolfpsa_kdf_compute_output(ctx, full_output, total_output_length); + if (status == PSA_SUCCESS) { + XMEMCPY(output, full_output + ctx->output_offset, output_length); + } + wc_ForceZero(full_output, total_output_length); + XFREE(full_output, NULL, DYNAMIC_TYPE_TMP_BUFFER); } - if (PSA_ALG_IS_PBKDF2(ctx->alg)) { - return wolfpsa_kdf_pbkdf2(ctx, output, output_length); + if (status == PSA_SUCCESS) { + ctx->output_offset += output_length; } - - return PSA_ERROR_NOT_SUPPORTED; + return status; } psa_status_t psa_key_derivation_output_key(const psa_key_attributes_t *attributes, diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index 68c5d0e..1416fa5 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -1606,6 +1606,76 @@ static int test_kdf_hkdf_optional_info(void) return TEST_OK; } +static int test_kdf_output_bytes_are_sequential(void) +{ + static const uint8_t secret[] = "hkdf sequential output secret"; + static const uint8_t info[] = "hkdf sequential output info"; + uint8_t expected[32]; + uint8_t output_part1[16]; + uint8_t output_part2[16]; + psa_key_derivation_operation_t op = psa_key_derivation_operation_init(); + psa_status_t st; + int ret; + + ret = wc_HKDF(WC_HASH_TYPE_SHA256, + secret, (word32)(sizeof(secret) - 1u), + NULL, 0, + info, (word32)(sizeof(info) - 1u), + expected, (word32)sizeof(expected)); + if (ret != 0) { + printf("FAIL: wc_HKDF(sequential output reference) (%d)\n", ret); + return TEST_FAIL; + } + + st = psa_key_derivation_setup(&op, PSA_ALG_HKDF(PSA_ALG_SHA_256)); + if (check_status(st, "psa_key_derivation_setup(HKDF sequential output)") != TEST_OK) { + return TEST_FAIL; + } + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_SECRET, + secret, sizeof(secret) - 1u); + if (check_status(st, "psa_key_derivation_input_bytes(SECRET HKDF sequential output)") != TEST_OK) { + (void)psa_key_derivation_abort(&op); + return TEST_FAIL; + } + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_INFO, + info, sizeof(info) - 1u); + if (check_status(st, "psa_key_derivation_input_bytes(INFO HKDF sequential output)") != TEST_OK) { + (void)psa_key_derivation_abort(&op); + return TEST_FAIL; + } + st = psa_key_derivation_set_capacity(&op, sizeof(expected)); + if (check_status(st, "psa_key_derivation_set_capacity(HKDF sequential output)") != TEST_OK) { + (void)psa_key_derivation_abort(&op); + return TEST_FAIL; + } + st = psa_key_derivation_output_bytes(&op, output_part1, sizeof(output_part1)); + if (check_status(st, "psa_key_derivation_output_bytes(HKDF sequential output first)") != TEST_OK) { + (void)psa_key_derivation_abort(&op); + return TEST_FAIL; + } + st = psa_key_derivation_output_bytes(&op, output_part2, sizeof(output_part2)); + if (check_status(st, "psa_key_derivation_output_bytes(HKDF sequential output second)") != TEST_OK) { + (void)psa_key_derivation_abort(&op); + return TEST_FAIL; + } + st = psa_key_derivation_abort(&op); + if (check_status(st, "psa_key_derivation_abort(HKDF sequential output)") != TEST_OK) { + return TEST_FAIL; + } + + if (check_buf_eq("psa_key_derivation_output_bytes(HKDF sequential output first)", + output_part1, expected, sizeof(output_part1)) != TEST_OK) { + return TEST_FAIL; + } + if (check_buf_eq("psa_key_derivation_output_bytes(HKDF sequential output second)", + output_part2, expected + sizeof(output_part1), + sizeof(output_part2)) != TEST_OK) { + return TEST_FAIL; + } + + return TEST_OK; +} + static int run_named_test(const char* name, test_fn_t fn) { int ret; @@ -1754,6 +1824,12 @@ int main(int argc, char** argv) return TEST_FAIL; } } + if (only == NULL || strcmp(only, "kdf_output_bytes_are_sequential") == 0) { + if (run_named_test("kdf_output_bytes_are_sequential", + test_kdf_output_bytes_are_sequential) == TEST_FAIL) { + return TEST_FAIL; + } + } printf("PSA API test: OK (passed=%d skipped=%d)\n", tests_passed, tests_skipped); From f76ded24a8b06f74d825180dfbcf46959657cbe2 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 10 Apr 2026 07:15:11 +0200 Subject: [PATCH 04/38] Reject PSA_ALG_NONE key operations F/2396 --- src/psa_aead.c | 9 +- src/psa_asymmetric_api.c | 27 +++--- src/psa_cipher.c | 2 +- src/psa_key_derivation.c | 21 +++-- src/psa_mac.c | 59 ++++++++------ test/psa_server/psa_api_test.c | 145 +++++++++++++++++++++++++++++++++ 6 files changed, 214 insertions(+), 49 deletions(-) diff --git a/src/psa_aead.c b/src/psa_aead.c index 0ba7e74..58fa794 100644 --- a/src/psa_aead.c +++ b/src/psa_aead.c @@ -134,7 +134,14 @@ static psa_status_t wolfpsa_aead_check_key(psa_key_id_t key, } key_alg = psa_get_key_algorithm(attributes); - if (key_alg != PSA_ALG_NONE) { + if (key_alg == PSA_ALG_NONE) { + wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); + *key_data = NULL; + *key_data_length = 0; + return PSA_ERROR_NOT_PERMITTED; + } + + { psa_algorithm_t key_base = PSA_ALG_AEAD_WITH_DEFAULT_LENGTH_TAG(key_alg); psa_algorithm_t req_base = PSA_ALG_AEAD_WITH_DEFAULT_LENGTH_TAG(alg); key_tag_len = wolfpsa_aead_tag_length(key_alg); diff --git a/src/psa_asymmetric_api.c b/src/psa_asymmetric_api.c index c61f69d..621f731 100644 --- a/src/psa_asymmetric_api.c +++ b/src/psa_asymmetric_api.c @@ -167,23 +167,28 @@ static psa_status_t wolfpsa_asymmetric_check_key(psa_key_id_t key, } key_alg = psa_get_key_algorithm(attributes); - if (key_alg != PSA_ALG_NONE) { - if (PSA_ALG_IS_KEY_AGREEMENT(alg) && PSA_ALG_IS_KEY_AGREEMENT(key_alg)) { - if (PSA_ALG_KEY_AGREEMENT_GET_BASE(key_alg) != - PSA_ALG_KEY_AGREEMENT_GET_BASE(alg)) { - wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); - *key_data = NULL; - *key_data_length = 0; - return PSA_ERROR_NOT_PERMITTED; - } - } - else if (key_alg != alg) { + if (key_alg == PSA_ALG_NONE) { + wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); + *key_data = NULL; + *key_data_length = 0; + return PSA_ERROR_NOT_PERMITTED; + } + + if (PSA_ALG_IS_KEY_AGREEMENT(alg) && PSA_ALG_IS_KEY_AGREEMENT(key_alg)) { + if (PSA_ALG_KEY_AGREEMENT_GET_BASE(key_alg) != + PSA_ALG_KEY_AGREEMENT_GET_BASE(alg)) { wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); *key_data = NULL; *key_data_length = 0; return PSA_ERROR_NOT_PERMITTED; } } + else if (key_alg != alg) { + wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); + *key_data = NULL; + *key_data_length = 0; + return PSA_ERROR_NOT_PERMITTED; + } return PSA_SUCCESS; } diff --git a/src/psa_cipher.c b/src/psa_cipher.c index 69fc374..de990f0 100644 --- a/src/psa_cipher.c +++ b/src/psa_cipher.c @@ -176,7 +176,7 @@ static psa_status_t wolfpsa_cipher_check_key( } key_alg = psa_get_key_algorithm(attributes); - if (key_alg != PSA_ALG_NONE && key_alg != alg) { + if (key_alg == PSA_ALG_NONE || key_alg != alg) { wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); *key_data = NULL; *key_data_length = 0; diff --git a/src/psa_key_derivation.c b/src/psa_key_derivation.c index cd630b6..8a3e6fb 100644 --- a/src/psa_key_derivation.c +++ b/src/psa_key_derivation.c @@ -615,19 +615,22 @@ psa_status_t psa_key_derivation_input_key(psa_key_derivation_operation_t *operat } key_alg = psa_get_key_algorithm(&attributes); - if (key_alg != PSA_ALG_NONE) { - if (ctx->is_key_agreement) { - if (!PSA_ALG_IS_KEY_AGREEMENT(key_alg) || - PSA_ALG_KEY_AGREEMENT_GET_KDF(key_alg) != ctx->alg) { - wolfpsa_forcezero_free_key_data(key_data, key_data_length); - return PSA_ERROR_NOT_PERMITTED; - } - } - else if (key_alg != ctx->alg) { + if (key_alg == PSA_ALG_NONE) { + wolfpsa_forcezero_free_key_data(key_data, key_data_length); + return PSA_ERROR_NOT_PERMITTED; + } + + if (ctx->is_key_agreement) { + if (!PSA_ALG_IS_KEY_AGREEMENT(key_alg) || + PSA_ALG_KEY_AGREEMENT_GET_KDF(key_alg) != ctx->alg) { wolfpsa_forcezero_free_key_data(key_data, key_data_length); return PSA_ERROR_NOT_PERMITTED; } } + else if (key_alg != ctx->alg) { + wolfpsa_forcezero_free_key_data(key_data, key_data_length); + return PSA_ERROR_NOT_PERMITTED; + } status = psa_key_derivation_input_bytes(operation, step, key_data, key_data_length); diff --git a/src/psa_mac.c b/src/psa_mac.c index f59f358..5480295 100644 --- a/src/psa_mac.c +++ b/src/psa_mac.c @@ -155,44 +155,49 @@ static psa_status_t wolfpsa_mac_check_key(psa_key_id_t key, key_alg = psa_get_key_algorithm(attributes); key_alg_full = PSA_ALG_FULL_LENGTH_MAC(key_alg); req_alg_full = PSA_ALG_FULL_LENGTH_MAC(alg); - if (key_alg != PSA_ALG_NONE) { - if (key_alg_full != req_alg_full) { + if (key_alg == PSA_ALG_NONE) { + wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); + *key_data = NULL; + *key_data_length = 0; + return PSA_ERROR_NOT_PERMITTED; + } + + if (key_alg_full != req_alg_full) { + wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); + *key_data = NULL; + *key_data_length = 0; + return PSA_ERROR_NOT_PERMITTED; + } + + req_mac_length = PSA_MAC_LENGTH(attributes->type, attributes->bits, alg); + key_full_length = PSA_MAC_LENGTH(attributes->type, attributes->bits, key_alg_full); + key_min_length = PSA_MAC_TRUNCATED_LENGTH(key_alg); + if (key_min_length == 0) { + key_min_length = key_full_length; + } + + if ((key_alg & PSA_ALG_MAC_AT_LEAST_THIS_LENGTH_FLAG) != 0) { + if (req_mac_length < key_min_length) { wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); *key_data = NULL; *key_data_length = 0; return PSA_ERROR_NOT_PERMITTED; } - - req_mac_length = PSA_MAC_LENGTH(attributes->type, attributes->bits, alg); - key_full_length = PSA_MAC_LENGTH(attributes->type, attributes->bits, key_alg_full); - key_min_length = PSA_MAC_TRUNCATED_LENGTH(key_alg); - if (key_min_length == 0) { - key_min_length = key_full_length; - } - - if ((key_alg & PSA_ALG_MAC_AT_LEAST_THIS_LENGTH_FLAG) != 0) { - if (req_mac_length < key_min_length) { + } + else { + if ((key_alg & PSA_ALG_MAC_TRUNCATION_MASK) != 0) { + if (req_mac_length != key_min_length) { wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); *key_data = NULL; *key_data_length = 0; return PSA_ERROR_NOT_PERMITTED; } } - else { - if ((key_alg & PSA_ALG_MAC_TRUNCATION_MASK) != 0) { - if (req_mac_length != key_min_length) { - wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); - *key_data = NULL; - *key_data_length = 0; - return PSA_ERROR_NOT_PERMITTED; - } - } - else if (req_mac_length != key_full_length) { - wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); - *key_data = NULL; - *key_data_length = 0; - return PSA_ERROR_NOT_PERMITTED; - } + else if (req_mac_length != key_full_length) { + wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); + *key_data = NULL; + *key_data_length = 0; + return PSA_ERROR_NOT_PERMITTED; } } diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index 1416fa5..fb69afd 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -170,6 +170,145 @@ static int test_hmac(void) return TEST_OK; } +static int test_algorithm_none_rejects_key_usage(void) +{ + static const uint8_t aes_key[16] = { + 0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6, + 0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c + }; + static const uint8_t hmac_key[16] = { + 0x60,0x3d,0xeb,0x10,0x15,0xca,0x71,0xbe, + 0x2b,0x73,0xae,0xf0,0x85,0x7d,0x77,0x81 + }; + static const uint8_t hash[WC_SHA256_DIGEST_SIZE] = { + 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, + 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23, + 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, + 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad + }; + uint8_t sig[80]; + size_t sig_len = 0; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_cipher_operation_t cipher_op = psa_cipher_operation_init(); + psa_aead_operation_t aead_op = psa_aead_operation_init(); + psa_mac_operation_t mac_op = psa_mac_operation_init(); + psa_key_derivation_operation_t kdf_op = psa_key_derivation_operation_init(); + psa_key_id_t key_id = 0; + psa_status_t st; + int ret = TEST_FAIL; + + 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); + st = psa_import_key(&attrs, aes_key, sizeof(aes_key), &key_id); + if (check_status(st, "psa_import_key(AES alg none)") != TEST_OK) { + return TEST_FAIL; + } + + st = psa_cipher_encrypt_setup(&cipher_op, key_id, PSA_ALG_CBC_NO_PADDING); + if (check_true(st == PSA_ERROR_NOT_PERMITTED, + "psa_cipher_encrypt_setup rejects PSA_ALG_NONE policy") != TEST_OK) { + goto cleanup; + } + st = psa_destroy_key(key_id); + if (check_status(st, "psa_destroy_key(AES cipher alg none)") != TEST_OK) { + return TEST_FAIL; + } + key_id = 0; + + psa_reset_key_attributes(&attrs); + 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); + st = psa_import_key(&attrs, aes_key, sizeof(aes_key), &key_id); + if (check_status(st, "psa_import_key(AES AEAD alg none)") != TEST_OK) { + return TEST_FAIL; + } + + st = psa_aead_encrypt_setup(&aead_op, key_id, PSA_ALG_GCM); + if (check_true(st == PSA_ERROR_NOT_PERMITTED, + "psa_aead_encrypt_setup rejects PSA_ALG_NONE policy") != TEST_OK) { + goto cleanup; + } + st = psa_destroy_key(key_id); + if (check_status(st, "psa_destroy_key(AES AEAD alg none)") != TEST_OK) { + return TEST_FAIL; + } + key_id = 0; + + psa_reset_key_attributes(&attrs); + psa_set_key_type(&attrs, PSA_KEY_TYPE_HMAC); + psa_set_key_bits(&attrs, sizeof(hmac_key) * 8u); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_SIGN_MESSAGE); + st = psa_import_key(&attrs, hmac_key, sizeof(hmac_key), &key_id); + if (check_status(st, "psa_import_key(HMAC alg none)") != TEST_OK) { + return TEST_FAIL; + } + + st = psa_mac_sign_setup(&mac_op, key_id, PSA_ALG_HMAC(PSA_ALG_SHA_256)); + if (check_true(st == PSA_ERROR_NOT_PERMITTED, + "psa_mac_sign_setup rejects PSA_ALG_NONE policy") != TEST_OK) { + goto cleanup; + } + st = psa_destroy_key(key_id); + if (check_status(st, "psa_destroy_key(HMAC alg none)") != TEST_OK) { + return TEST_FAIL; + } + key_id = 0; + + psa_reset_key_attributes(&attrs); + psa_set_key_type(&attrs, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); + psa_set_key_bits(&attrs, 256); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_SIGN_HASH); + st = psa_generate_key(&attrs, &key_id); + if (check_status(st, "psa_generate_key(ECDSA alg none)") != TEST_OK) { + return TEST_FAIL; + } + + st = psa_sign_hash(key_id, PSA_ALG_ECDSA(PSA_ALG_SHA_256), + hash, sizeof(hash), sig, sizeof(sig), &sig_len); + if (check_true(st == PSA_ERROR_NOT_PERMITTED, + "psa_sign_hash rejects PSA_ALG_NONE policy") != TEST_OK) { + goto cleanup; + } + st = psa_destroy_key(key_id); + if (check_status(st, "psa_destroy_key(ECDSA alg none)") != TEST_OK) { + return TEST_FAIL; + } + key_id = 0; + + psa_reset_key_attributes(&attrs); + psa_set_key_type(&attrs, PSA_KEY_TYPE_DERIVE); + psa_set_key_bits(&attrs, sizeof(hmac_key) * 8u); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_DERIVE); + st = psa_import_key(&attrs, hmac_key, sizeof(hmac_key), &key_id); + if (check_status(st, "psa_import_key(DERIVE alg none)") != TEST_OK) { + return TEST_FAIL; + } + + st = psa_key_derivation_setup(&kdf_op, PSA_ALG_HKDF(PSA_ALG_SHA_256)); + if (check_status(st, "psa_key_derivation_setup(HKDF alg none)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_key(&kdf_op, PSA_KEY_DERIVATION_INPUT_SECRET, key_id); + if (check_true(st == PSA_ERROR_NOT_PERMITTED, + "psa_key_derivation_input_key rejects PSA_ALG_NONE policy") != TEST_OK) { + goto cleanup; + } + + ret = TEST_OK; + +cleanup: + if (key_id != 0) { + (void)psa_destroy_key(key_id); + } + (void)psa_cipher_abort(&cipher_op); + (void)psa_aead_abort(&aead_op); + (void)psa_mac_abort(&mac_op); + (void)psa_key_derivation_abort(&kdf_op); + return ret; +} + static int test_cipher_cbc(void) { static const uint8_t key[16] = { @@ -1709,6 +1848,12 @@ int main(int argc, char** argv) if (only == NULL || strcmp(only, "hmac") == 0) { if (run_named_test("hmac", test_hmac) == TEST_FAIL) return TEST_FAIL; } + if (only == NULL || strcmp(only, "alg_none_policy") == 0) { + if (run_named_test("alg_none_policy", + test_algorithm_none_rejects_key_usage) == TEST_FAIL) { + return TEST_FAIL; + } + } if (only == NULL || strcmp(only, "cipher_cbc") == 0) { if (run_named_test("cipher_cbc", test_cipher_cbc) == TEST_FAIL) return TEST_FAIL; } From dff642e7fb479db2acbb91857e38aa222121d6e4 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 10 Apr 2026 07:16:58 +0200 Subject: [PATCH 05/38] Add cipher algorithm mismatch policy test F/2406 --- test/psa_server/psa_api_test.c | 44 ++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index fb69afd..97b1554 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -383,6 +383,44 @@ static int test_cipher_cbc(void) return TEST_OK; } +static int test_cipher_rejects_algorithm_mismatch(void) +{ + static const uint8_t key[16] = { + 0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6, + 0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c + }; + 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; + int ret = TEST_FAIL; + + 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_set_key_algorithm(&attrs, PSA_ALG_CBC_NO_PADDING); + + st = psa_import_key(&attrs, key, sizeof(key), &key_id); + if (check_status(st, "psa_import_key(AES CBC policy)") != TEST_OK) { + goto cleanup; + } + + st = psa_cipher_encrypt_setup(&op, key_id, PSA_ALG_CTR); + if (check_true(st == PSA_ERROR_NOT_PERMITTED, + "psa_cipher_encrypt_setup rejects mismatched algorithm policy") != TEST_OK) { + goto cleanup; + } + + ret = TEST_OK; + +cleanup: + if (key_id != 0) { + (void)psa_destroy_key(key_id); + } + (void)psa_cipher_abort(&op); + return ret; +} + static int test_export_key_requires_usage_flag(void) { static const uint8_t key[16] = { @@ -1857,6 +1895,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_algorithm_mismatch") == 0) { + if (run_named_test("cipher_algorithm_mismatch", + test_cipher_rejects_algorithm_mismatch) == TEST_FAIL) { + return TEST_FAIL; + } + } if (only == NULL || strcmp(only, "export_requires_usage") == 0) { if (run_named_test("export_requires_usage", test_export_key_requires_usage_flag) == TEST_FAIL) { From 1fa2a9487434ae7f32e190dde1b068982800a5dd Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 10 Apr 2026 07:22:37 +0200 Subject: [PATCH 06/38] Add KDF input key policy coverage F/2408 --- test/psa_server/psa_api_test.c | 174 +++++++++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index 97b1554..9852689 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -1490,6 +1490,175 @@ static int test_kdf_null_capacity(void) return TEST_OK; } +static int test_kdf_input_key_policy(void) +{ + static const uint8_t secret[] = { + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f + }; + static const uint8_t info[] = "kdf-input-key"; + uint8_t output[16]; + size_t output_len = 0; + psa_key_derivation_operation_t op = psa_key_derivation_operation_init(); + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_key_id_t derive_key = 0; + psa_key_id_t no_usage_key = 0; + psa_key_id_t raw_key = 0; + psa_key_id_t password_key = 0; + psa_key_id_t wrong_pbkdf2_key = 0; + psa_status_t st; + int ret = TEST_FAIL; + + psa_set_key_type(&attrs, PSA_KEY_TYPE_DERIVE); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_DERIVE); + psa_set_key_algorithm(&attrs, PSA_ALG_HKDF(PSA_ALG_SHA_256)); + st = psa_import_key(&attrs, secret, sizeof(secret), &derive_key); + if (check_status(st, "psa_import_key(KDF derive key)") != TEST_OK) { + goto cleanup; + } + + st = psa_key_derivation_setup(&op, PSA_ALG_HKDF(PSA_ALG_SHA_256)); + if (check_status(st, "psa_key_derivation_setup(HKDF input key)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_key(&op, PSA_KEY_DERIVATION_INPUT_SECRET, derive_key); + if (check_status(st, "psa_key_derivation_input_key(HKDF derive key)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_INFO, + info, sizeof(info) - 1u); + if (check_status(st, "psa_key_derivation_input_bytes(INFO input key)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_output_bytes(&op, output, sizeof(output)); + if (check_status(st, "psa_key_derivation_output_bytes(input key)") != TEST_OK) { + goto cleanup; + } + output_len = sizeof(output); + if (check_true(output_len == sizeof(output), + "psa_key_derivation_output_bytes(input key) length") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_abort(&op); + if (check_status(st, "psa_key_derivation_abort(HKDF input key)") != TEST_OK) { + goto cleanup; + } + op = psa_key_derivation_operation_init(); + + psa_reset_key_attributes(&attrs); + psa_set_key_type(&attrs, PSA_KEY_TYPE_DERIVE); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_EXPORT); + psa_set_key_algorithm(&attrs, PSA_ALG_HKDF(PSA_ALG_SHA_256)); + st = psa_import_key(&attrs, secret, sizeof(secret), &no_usage_key); + if (check_status(st, "psa_import_key(KDF no usage key)") != TEST_OK) { + goto cleanup; + } + + st = psa_key_derivation_setup(&op, PSA_ALG_HKDF(PSA_ALG_SHA_256)); + if (check_status(st, "psa_key_derivation_setup(HKDF no usage)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_key(&op, PSA_KEY_DERIVATION_INPUT_SECRET, no_usage_key); + if (check_true(st == PSA_ERROR_NOT_PERMITTED, + "psa_key_derivation_input_key rejects missing DERIVE usage") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_abort(&op); + if (check_status(st, "psa_key_derivation_abort(HKDF no usage)") != TEST_OK) { + goto cleanup; + } + op = psa_key_derivation_operation_init(); + + psa_reset_key_attributes(&attrs); + psa_set_key_type(&attrs, PSA_KEY_TYPE_RAW_DATA); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_DERIVE); + psa_set_key_algorithm(&attrs, PSA_ALG_HKDF(PSA_ALG_SHA_256)); + st = psa_import_key(&attrs, secret, sizeof(secret), &raw_key); + if (check_status(st, "psa_import_key(KDF raw key)") != TEST_OK) { + goto cleanup; + } + + st = psa_key_derivation_setup(&op, PSA_ALG_HKDF(PSA_ALG_SHA_256)); + if (check_status(st, "psa_key_derivation_setup(HKDF raw key)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_key(&op, PSA_KEY_DERIVATION_INPUT_SECRET, raw_key); + if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, + "psa_key_derivation_input_key rejects non-DERIVE secret key type") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_abort(&op); + if (check_status(st, "psa_key_derivation_abort(HKDF raw key)") != TEST_OK) { + goto cleanup; + } + op = psa_key_derivation_operation_init(); + + psa_reset_key_attributes(&attrs); + psa_set_key_type(&attrs, PSA_KEY_TYPE_PASSWORD); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_DERIVE); + psa_set_key_algorithm(&attrs, PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_256)); + st = psa_import_key(&attrs, secret, sizeof(secret), &password_key); + if (check_status(st, "psa_import_key(PBKDF2 password key)") != TEST_OK) { + goto cleanup; + } + + st = psa_key_derivation_setup(&op, PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_256)); + if (check_status(st, "psa_key_derivation_setup(PBKDF2 password)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_key(&op, PSA_KEY_DERIVATION_INPUT_PASSWORD, password_key); + if (check_status(st, "psa_key_derivation_input_key(PBKDF2 password)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_abort(&op); + if (check_status(st, "psa_key_derivation_abort(PBKDF2 password)") != TEST_OK) { + goto cleanup; + } + op = psa_key_derivation_operation_init(); + + psa_reset_key_attributes(&attrs); + psa_set_key_type(&attrs, PSA_KEY_TYPE_DERIVE); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_DERIVE); + psa_set_key_algorithm(&attrs, PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_256)); + st = psa_import_key(&attrs, secret, sizeof(secret), &wrong_pbkdf2_key); + if (check_status(st, "psa_import_key(PBKDF2 wrong type key)") != TEST_OK) { + goto cleanup; + } + + st = psa_key_derivation_setup(&op, PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_256)); + if (check_status(st, "psa_key_derivation_setup(PBKDF2 wrong type)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_key(&op, PSA_KEY_DERIVATION_INPUT_PASSWORD, + wrong_pbkdf2_key); + if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, + "psa_key_derivation_input_key rejects non-PASSWORD PBKDF2 key type") != TEST_OK) { + goto cleanup; + } + + ret = TEST_OK; + +cleanup: + (void)psa_key_derivation_abort(&op); + if (wrong_pbkdf2_key != 0) { + (void)psa_destroy_key(wrong_pbkdf2_key); + } + if (password_key != 0) { + (void)psa_destroy_key(password_key); + } + if (raw_key != 0) { + (void)psa_destroy_key(raw_key); + } + if (no_usage_key != 0) { + (void)psa_destroy_key(no_usage_key); + } + if (derive_key != 0) { + (void)psa_destroy_key(derive_key); + } + psa_reset_key_attributes(&attrs); + return ret; +} + static int test_kdf_tls12_psk_to_ms_rfc4279_order(void) { static const uint8_t psk[] = { 0xa1, 0xb2, 0xc3, 0xd4, 0xe5 }; @@ -1985,6 +2154,11 @@ int main(int argc, char** argv) 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; } + if (only == NULL || strcmp(only, "kdf_input_key_policy") == 0) { + if (run_named_test("kdf_input_key_policy", test_kdf_input_key_policy) == TEST_FAIL) { + return TEST_FAIL; + } + } if (only == NULL || strcmp(only, "kdf_tls12_psk_to_ms") == 0) { if (run_named_test("kdf_tls12_psk_to_ms", test_kdf_tls12_psk_to_ms_rfc4279_order) == TEST_FAIL) { From 57e1105585b36ee18a9027f88a5eac13c8e5eb8e Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 10 Apr 2026 07:25:33 +0200 Subject: [PATCH 07/38] Add key agreement KDF policy coverage F/2409 --- test/psa_server/psa_api_test.c | 146 +++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index 9852689..93559e3 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -1659,6 +1659,146 @@ static int test_kdf_input_key_policy(void) return ret; } +static int test_kdf_key_agreement_policy(void) +{ + uint8_t peer_pub[128]; + size_t peer_pub_len = 0; + uint8_t good_pub[128]; + size_t good_pub_len = 0; + uint8_t expected_secret[128]; + size_t expected_secret_len = 0; + uint8_t derived_secret[128]; + psa_key_derivation_operation_t op = psa_key_derivation_operation_init(); + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_key_id_t good_key = 0; + psa_key_id_t peer_key = 0; + psa_key_id_t no_usage_key = 0; + psa_key_id_t public_key = 0; + psa_status_t st; + int ret = TEST_FAIL; + + psa_set_key_type(&attrs, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); + psa_set_key_bits(&attrs, 256); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_DERIVE | PSA_KEY_USAGE_EXPORT); + psa_set_key_algorithm(&attrs, PSA_ALG_ECDH); + + st = psa_generate_key(&attrs, &good_key); + if (check_status(st, "psa_generate_key(KDF ECDH derive key)") != TEST_OK) { + goto cleanup; + } + st = psa_generate_key(&attrs, &peer_key); + if (check_status(st, "psa_generate_key(KDF ECDH peer key)") != TEST_OK) { + goto cleanup; + } + + st = psa_export_public_key(peer_key, peer_pub, sizeof(peer_pub), &peer_pub_len); + if (check_status(st, "psa_export_public_key(KDF ECDH peer key)") != TEST_OK) { + goto cleanup; + } + st = psa_export_public_key(good_key, good_pub, sizeof(good_pub), &good_pub_len); + if (check_status(st, "psa_export_public_key(KDF ECDH derive key)") != TEST_OK) { + goto cleanup; + } + + st = psa_raw_key_agreement(PSA_ALG_ECDH, good_key, peer_pub, peer_pub_len, + expected_secret, sizeof(expected_secret), + &expected_secret_len); + if (check_status(st, "psa_raw_key_agreement(KDF ECDH expected secret)") != TEST_OK) { + goto cleanup; + } + + st = psa_key_derivation_setup(&op, PSA_ALG_ECDH); + if (check_status(st, "psa_key_derivation_setup(raw ECDH)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_key_agreement(&op, PSA_KEY_DERIVATION_INPUT_SECRET, + good_key, peer_pub, peer_pub_len); + if (check_status(st, "psa_key_derivation_key_agreement(derive key)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_output_bytes(&op, derived_secret, expected_secret_len); + if (check_status(st, "psa_key_derivation_output_bytes(raw ECDH)") != TEST_OK) { + goto cleanup; + } + if (check_buf_eq("psa_key_derivation_key_agreement matches raw ECDH", + derived_secret, expected_secret, expected_secret_len) != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_abort(&op); + if (check_status(st, "psa_key_derivation_abort(raw ECDH success)") != TEST_OK) { + goto cleanup; + } + op = psa_key_derivation_operation_init(); + + psa_reset_key_attributes(&attrs); + psa_set_key_type(&attrs, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); + psa_set_key_bits(&attrs, 256); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_EXPORT); + psa_set_key_algorithm(&attrs, PSA_ALG_ECDH); + + st = psa_generate_key(&attrs, &no_usage_key); + if (check_status(st, "psa_generate_key(KDF ECDH no usage key)") != TEST_OK) { + goto cleanup; + } + + st = psa_key_derivation_setup(&op, PSA_ALG_ECDH); + if (check_status(st, "psa_key_derivation_setup(raw ECDH no usage)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_key_agreement(&op, PSA_KEY_DERIVATION_INPUT_SECRET, + no_usage_key, peer_pub, peer_pub_len); + if (check_true(st == PSA_ERROR_NOT_PERMITTED, + "psa_key_derivation_key_agreement rejects missing DERIVE usage") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_abort(&op); + if (check_status(st, "psa_key_derivation_abort(raw ECDH no usage)") != TEST_OK) { + goto cleanup; + } + op = psa_key_derivation_operation_init(); + + psa_reset_key_attributes(&attrs); + psa_set_key_type(&attrs, PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1)); + psa_set_key_bits(&attrs, 256); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_DERIVE); + psa_set_key_algorithm(&attrs, PSA_ALG_ECDH); + + st = psa_import_key(&attrs, good_pub, good_pub_len, &public_key); + if (check_status(st, "psa_import_key(KDF ECDH public key)") != TEST_OK) { + goto cleanup; + } + + st = psa_key_derivation_setup(&op, PSA_ALG_ECDH); + if (check_status(st, "psa_key_derivation_setup(raw ECDH public key)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_key_agreement(&op, PSA_KEY_DERIVATION_INPUT_SECRET, + public_key, peer_pub, peer_pub_len); + if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, + "psa_key_derivation_key_agreement rejects public key input") != TEST_OK) { + goto cleanup; + } + + ret = TEST_OK; + +cleanup: + (void)psa_key_derivation_abort(&op); + if (public_key != 0) { + (void)psa_destroy_key(public_key); + } + if (no_usage_key != 0) { + (void)psa_destroy_key(no_usage_key); + } + if (peer_key != 0) { + (void)psa_destroy_key(peer_key); + } + if (good_key != 0) { + (void)psa_destroy_key(good_key); + } + psa_reset_key_attributes(&attrs); + return ret; +} + static int test_kdf_tls12_psk_to_ms_rfc4279_order(void) { static const uint8_t psk[] = { 0xa1, 0xb2, 0xc3, 0xd4, 0xe5 }; @@ -2159,6 +2299,12 @@ int main(int argc, char** argv) return TEST_FAIL; } } + if (only == NULL || strcmp(only, "kdf_key_agreement_policy") == 0) { + if (run_named_test("kdf_key_agreement_policy", + test_kdf_key_agreement_policy) == TEST_FAIL) { + return TEST_FAIL; + } + } if (only == NULL || strcmp(only, "kdf_tls12_psk_to_ms") == 0) { if (run_named_test("kdf_tls12_psk_to_ms", test_kdf_tls12_psk_to_ms_rfc4279_order) == TEST_FAIL) { From bab54f3a60e7a65c66bb64470400a7bdac2806b8 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 10 Apr 2026 07:27:19 +0200 Subject: [PATCH 08/38] Add AEAD policy mismatch coverage F/2410 --- test/psa_server/psa_api_test.c | 83 ++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index 93559e3..0f3bd76 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -1195,6 +1195,83 @@ static int test_aead_finish_verify_word32_overflow_rejected(void) return ret; } +static int test_aead_policy_mismatch_rejected(void) +{ + static const uint8_t key[16] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + psa_key_id_t key_id = 0; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_aead_operation_t op = psa_aead_operation_init(); + psa_status_t st; + int ret = TEST_OK; + + 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_set_key_algorithm(&attrs, PSA_ALG_GCM); + st = psa_import_key(&attrs, key, sizeof(key), &key_id); + if (check_status(st, "psa_import_key(GCM policy)") != TEST_OK) { + return TEST_FAIL; + } + + st = psa_aead_encrypt_setup(&op, key_id, PSA_ALG_CCM); + if (check_true(st == PSA_ERROR_NOT_PERMITTED, + "psa_aead_encrypt_setup rejects AEAD base mismatch") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + + st = psa_aead_encrypt_setup(&op, key_id, + PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, 8)); + if (check_true(st == PSA_ERROR_NOT_PERMITTED, + "psa_aead_encrypt_setup rejects exact tag mismatch") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + +cleanup: + psa_aead_abort(&op); + st = psa_destroy_key(key_id); + if (check_status(st, "psa_destroy_key(GCM policy)") != TEST_OK) { + return TEST_FAIL; + } + + psa_set_key_algorithm(&attrs, + PSA_ALG_AEAD_WITH_AT_LEAST_THIS_LENGTH_TAG(PSA_ALG_GCM, 8)); + st = psa_import_key(&attrs, key, sizeof(key), &key_id); + if (check_status(st, "psa_import_key(GCM at least tag)") != TEST_OK) { + return TEST_FAIL; + } + + st = psa_aead_encrypt_setup(&op, key_id, + PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, 12)); + if (check_status(st, "psa_aead_encrypt_setup(allows longer AEAD tag)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup_at_least; + } + psa_aead_abort(&op); + + st = psa_aead_encrypt_setup(&op, key_id, + PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, 4)); + if (check_true(st == PSA_ERROR_NOT_PERMITTED, + "psa_aead_encrypt_setup rejects shorter AEAD tag than policy") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup_at_least; + } + +cleanup_at_least: + psa_aead_abort(&op); + st = psa_destroy_key(key_id); + if (check_status(st, "psa_destroy_key(GCM at least tag)") != TEST_OK) { + return TEST_FAIL; + } + + return ret; +} + static int test_chacha20_poly1305_rejects_aes_key(void) { static const uint8_t key[32] = { @@ -2267,6 +2344,12 @@ int main(int argc, char** argv) return TEST_FAIL; } } + if (only == NULL || strcmp(only, "aead_policy_mismatch") == 0) { + if (run_named_test("aead_policy_mismatch", + test_aead_policy_mismatch_rejected) == 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_FAIL) { From 8227f5cddaf472fac41c0600a8f1da5aead47610 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 10 Apr 2026 07:29:06 +0200 Subject: [PATCH 09/38] Add MAC policy regression coverage F/2414 --- test/psa_server/psa_api_test.c | 69 ++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index 0f3bd76..94ba308 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -421,6 +421,69 @@ static int test_cipher_rejects_algorithm_mismatch(void) return ret; } +static int test_mac_rejects_algorithm_mismatch(void) +{ + static const uint8_t hmac_key[16] = { + 0x60,0x3d,0xeb,0x10,0x15,0xca,0x71,0xbe, + 0x2b,0x73,0xae,0xf0,0x85,0x7d,0x77,0x81 + }; + psa_key_id_t key_id = 0; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_mac_operation_t op = psa_mac_operation_init(); + psa_status_t st; + int ret = TEST_FAIL; + + psa_set_key_type(&attrs, PSA_KEY_TYPE_HMAC); + psa_set_key_bits(&attrs, sizeof(hmac_key) * 8u); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_SIGN_MESSAGE); + psa_set_key_algorithm(&attrs, PSA_ALG_HMAC(PSA_ALG_SHA_256)); + + st = psa_import_key(&attrs, hmac_key, sizeof(hmac_key), &key_id); + if (check_status(st, "psa_import_key(HMAC SHA-256 policy)") != TEST_OK) { + goto cleanup; + } + + st = psa_mac_sign_setup(&op, key_id, PSA_ALG_HMAC(PSA_ALG_SHA_512)); + if (check_true(st == PSA_ERROR_NOT_PERMITTED, + "psa_mac_sign_setup rejects mismatched HMAC algorithm policy") != TEST_OK) { + goto cleanup; + } + + st = psa_destroy_key(key_id); + if (check_status(st, "psa_destroy_key(HMAC SHA-256 policy)") != TEST_OK) { + return TEST_FAIL; + } + key_id = 0; + + psa_reset_key_attributes(&attrs); + psa_set_key_type(&attrs, PSA_KEY_TYPE_HMAC); + psa_set_key_bits(&attrs, sizeof(hmac_key) * 8u); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_SIGN_MESSAGE); + psa_set_key_algorithm(&attrs, + PSA_ALG_AT_LEAST_THIS_LENGTH_MAC(PSA_ALG_HMAC(PSA_ALG_SHA_256), 16)); + + st = psa_import_key(&attrs, hmac_key, sizeof(hmac_key), &key_id); + if (check_status(st, "psa_import_key(HMAC minimum-length policy)") != TEST_OK) { + goto cleanup; + } + + st = psa_mac_sign_setup(&op, key_id, + PSA_ALG_TRUNCATED_MAC(PSA_ALG_HMAC(PSA_ALG_SHA_256), 8)); + if (check_true(st == PSA_ERROR_NOT_PERMITTED, + "psa_mac_sign_setup rejects MAC shorter than minimum policy") != TEST_OK) { + goto cleanup; + } + + ret = TEST_OK; + +cleanup: + if (key_id != 0) { + (void)psa_destroy_key(key_id); + } + (void)psa_mac_abort(&op); + return ret; +} + static int test_export_key_requires_usage_flag(void) { static const uint8_t key[16] = { @@ -2287,6 +2350,12 @@ int main(int argc, char** argv) return TEST_FAIL; } } + if (only == NULL || strcmp(only, "mac_algorithm_mismatch") == 0) { + if (run_named_test("mac_algorithm_mismatch", + test_mac_rejects_algorithm_mismatch) == TEST_FAIL) { + return TEST_FAIL; + } + } if (only == NULL || strcmp(only, "export_requires_usage") == 0) { if (run_named_test("export_requires_usage", test_export_key_requires_usage_flag) == TEST_FAIL) { From d321c58a9558fe3b6d61aec0f9d3b594b45cea81 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 10 Apr 2026 07:31:28 +0200 Subject: [PATCH 10/38] Allow multipart CCM_STAR_NO_TAG updates F/2422 --- src/psa_cipher.c | 4 - test/psa_server/psa_api_test.c | 156 +++++++++++++++++++++++++++++++++ 2 files changed, 156 insertions(+), 4 deletions(-) diff --git a/src/psa_cipher.c b/src/psa_cipher.c index de990f0..9f177bf 100644 --- a/src/psa_cipher.c +++ b/src/psa_cipher.c @@ -980,9 +980,6 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, if (!ctx->iv_set) { return PSA_ERROR_BAD_STATE; } - if (ctx->partial_len != 0) { - return PSA_ERROR_BAD_STATE; - } if (output_size < input_length) { return PSA_ERROR_BUFFER_TOO_SMALL; } @@ -994,7 +991,6 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, if (ret != 0) { return wc_error_to_psa_status(ret); } - ctx->partial_len = 1; *output_length = input_length; return PSA_SUCCESS; } diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index 94ba308..b39c077 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -1029,6 +1029,156 @@ static int test_cipher_cbc_pkcs7_decrypt_update_small_output(void) return TEST_OK; } +static int test_cipher_ccm_star_no_tag_multipart(void) +{ + static const uint8_t key[16] = { + 0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7, + 0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf + }; + static const uint8_t nonce[13] = { + 0x00,0x00,0x00,0x03,0x02,0x01,0x00,0xa0, + 0xa1,0xa2,0xa3,0xa4,0xa5 + }; + static const uint8_t plaintext[32] = { + 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 + }; + uint8_t single[sizeof(plaintext)]; + uint8_t multi[sizeof(plaintext)]; + uint8_t decrypted[sizeof(plaintext)]; + size_t single_len = 0; + size_t multi_len = 0; + size_t part_len = 0; + size_t finish_len = 0; + size_t decrypted_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; + int result = TEST_FAIL; + + 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_CCM_STAR_NO_TAG); + + st = psa_import_key(&attrs, key, sizeof(key), &key_id); + if (check_status(st, "psa_import_key(AES CCM* no tag)") != TEST_OK) { + goto cleanup; + } + + st = psa_cipher_encrypt_setup(&op, key_id, PSA_ALG_CCM_STAR_NO_TAG); + if (st == PSA_ERROR_NOT_SUPPORTED) { + result = TEST_SKIPPED; + goto cleanup; + } + if (check_status(st, "psa_cipher_encrypt_setup(CCM* no tag single)") != TEST_OK) { + goto cleanup; + } + st = psa_cipher_set_iv(&op, nonce, sizeof(nonce)); + if (check_status(st, "psa_cipher_set_iv(CCM* no tag single)") != TEST_OK) { + goto cleanup; + } + st = psa_cipher_update(&op, plaintext, sizeof(plaintext), + single, sizeof(single), &single_len); + if (check_status(st, "psa_cipher_update(CCM* no tag single)") != TEST_OK) { + goto cleanup; + } + st = psa_cipher_finish(&op, single + single_len, sizeof(single) - single_len, + &finish_len); + if (check_status(st, "psa_cipher_finish(CCM* no tag single)") != TEST_OK) { + goto cleanup; + } + single_len += finish_len; + (void)psa_cipher_abort(&op); + + op = psa_cipher_operation_init(); + st = psa_cipher_encrypt_setup(&op, key_id, PSA_ALG_CCM_STAR_NO_TAG); + if (check_status(st, "psa_cipher_encrypt_setup(CCM* no tag multi)") != TEST_OK) { + goto cleanup; + } + st = psa_cipher_set_iv(&op, nonce, sizeof(nonce)); + if (check_status(st, "psa_cipher_set_iv(CCM* no tag multi)") != TEST_OK) { + goto cleanup; + } + st = psa_cipher_update(&op, plaintext, 16, multi, sizeof(multi), &part_len); + if (check_status(st, "psa_cipher_update(CCM* no tag first chunk)") != TEST_OK) { + goto cleanup; + } + multi_len += part_len; + st = psa_cipher_update(&op, plaintext + 16, sizeof(plaintext) - 16, + multi + multi_len, sizeof(multi) - multi_len, &part_len); + if (check_status(st, "psa_cipher_update(CCM* no tag second chunk)") != TEST_OK) { + goto cleanup; + } + multi_len += part_len; + st = psa_cipher_finish(&op, multi + multi_len, sizeof(multi) - multi_len, + &finish_len); + if (check_status(st, "psa_cipher_finish(CCM* no tag multi)") != TEST_OK) { + goto cleanup; + } + multi_len += finish_len; + (void)psa_cipher_abort(&op); + + if (check_true(single_len == sizeof(plaintext), "psa_cipher_update(CCM* no tag single) length") != TEST_OK) { + goto cleanup; + } + if (check_true(multi_len == single_len, "psa_cipher_update(CCM* no tag multi) length") != TEST_OK) { + goto cleanup; + } + if (check_buf_eq("psa_cipher_update(CCM* no tag split matches single)", + multi, single, single_len) != TEST_OK) { + goto cleanup; + } + + op = psa_cipher_operation_init(); + st = psa_cipher_decrypt_setup(&op, key_id, PSA_ALG_CCM_STAR_NO_TAG); + if (check_status(st, "psa_cipher_decrypt_setup(CCM* no tag multi)") != TEST_OK) { + goto cleanup; + } + st = psa_cipher_set_iv(&op, nonce, sizeof(nonce)); + if (check_status(st, "psa_cipher_set_iv(CCM* no tag dec)") != TEST_OK) { + goto cleanup; + } + st = psa_cipher_update(&op, multi, 16, decrypted, sizeof(decrypted), &part_len); + if (check_status(st, "psa_cipher_update(CCM* no tag dec first chunk)") != TEST_OK) { + goto cleanup; + } + decrypted_len += part_len; + st = psa_cipher_update(&op, multi + 16, sizeof(multi) - 16, + decrypted + decrypted_len, sizeof(decrypted) - decrypted_len, + &part_len); + if (check_status(st, "psa_cipher_update(CCM* no tag dec second chunk)") != TEST_OK) { + goto cleanup; + } + decrypted_len += part_len; + st = psa_cipher_finish(&op, decrypted + decrypted_len, + sizeof(decrypted) - decrypted_len, &finish_len); + if (check_status(st, "psa_cipher_finish(CCM* no tag dec)") != TEST_OK) { + goto cleanup; + } + decrypted_len += finish_len; + + if (check_true(decrypted_len == sizeof(plaintext), "psa_cipher_decrypt(CCM* no tag multi) length") != TEST_OK) { + goto cleanup; + } + if (check_buf_eq("psa_cipher_decrypt(CCM* no tag multi)", + decrypted, plaintext, sizeof(plaintext)) != TEST_OK) { + goto cleanup; + } + + result = TEST_OK; + +cleanup: + (void)psa_cipher_abort(&op); + if (key_id != 0) { + (void)psa_destroy_key(key_id); + } + return result; +} + static int test_aead_gcm(void) { static const uint8_t key[16] = { @@ -2398,6 +2548,12 @@ int main(int argc, char** argv) return TEST_FAIL; } } + if (only == NULL || strcmp(only, "cipher_ccm_star_no_tag_multipart") == 0) { + if (run_named_test("cipher_ccm_star_no_tag_multipart", + test_cipher_ccm_star_no_tag_multipart) == 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 e1d65f822d0ad055200a9b678ac6765f27197338 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 10 Apr 2026 07:33:46 +0200 Subject: [PATCH 11/38] Allow partial HKDF-Extract output F/2423 --- src/psa_key_derivation.c | 18 +++++++--- test/psa_server/psa_api_test.c | 62 ++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 5 deletions(-) diff --git a/src/psa_key_derivation.c b/src/psa_key_derivation.c index 8a3e6fb..6c9551f 100644 --- a/src/psa_key_derivation.c +++ b/src/psa_key_derivation.c @@ -719,21 +719,29 @@ static psa_status_t wolfpsa_kdf_hkdf(wolfpsa_kdf_ctx_t *ctx, if (PSA_ALG_IS_HKDF_EXTRACT(ctx->alg)) { int hash_len = wc_HashGetDigestSize(hash_type); + uint8_t prk[WC_MAX_DIGEST_SIZE]; if (hash_len <= 0) { return PSA_ERROR_NOT_SUPPORTED; } + if ((size_t)hash_len > sizeof(prk)) { + return PSA_ERROR_NOT_SUPPORTED; + } if ((wolfpsa_check_word32_length(ctx->salt_length) != PSA_SUCCESS) || (wolfpsa_check_word32_length(ctx->secret_length) != PSA_SUCCESS)) { return PSA_ERROR_INVALID_ARGUMENT; } - if (output_length != (size_t)hash_len) { - return PSA_ERROR_INVALID_ARGUMENT; - } ret = wc_HKDF_Extract(hash_type, ctx->salt, (word32)ctx->salt_length, ctx->secret, (word32)ctx->secret_length, - output); - return ret == 0 ? PSA_SUCCESS : wc_error_to_psa_status(ret); + prk); + if (ret != 0) { + wc_ForceZero(prk, (size_t)hash_len); + return wc_error_to_psa_status(ret); + } + + XMEMCPY(output, prk, output_length); + wc_ForceZero(prk, (size_t)hash_len); + return PSA_SUCCESS; } if (PSA_ALG_IS_HKDF_EXPAND(ctx->alg)) { diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index b39c077..aa52915 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -2274,6 +2274,62 @@ static int test_kdf_hkdf_extract_optional_salt(void) return TEST_OK; } +static int test_kdf_hkdf_extract_prefix_output(void) +{ + static const uint8_t secret[] = "hkdf extract secret"; + uint8_t expected[WC_SHA256_DIGEST_SIZE]; + uint8_t output[16]; + psa_key_derivation_operation_t op = psa_key_derivation_operation_init(); + psa_status_t st; + int ret; + + ret = wc_HKDF_Extract(WC_HASH_TYPE_SHA256, + NULL, 0, + secret, (word32)(sizeof(secret) - 1u), + expected); + if (ret != 0) { + printf("FAIL: wc_HKDF_Extract(HKDF_EXTRACT prefix output reference) (%d)\n", ret); + return TEST_FAIL; + } + + st = psa_key_derivation_setup(&op, PSA_ALG_HKDF_EXTRACT(PSA_ALG_SHA_256)); + if (check_status(st, "psa_key_derivation_setup(HKDF_EXTRACT prefix output)") != TEST_OK) { + return TEST_FAIL; + } + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_SALT, NULL, 0); + if (check_status(st, "psa_key_derivation_input_bytes(SALT HKDF_EXTRACT prefix output)") != TEST_OK) { + (void)psa_key_derivation_abort(&op); + return TEST_FAIL; + } + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_SECRET, + secret, sizeof(secret) - 1u); + if (check_status(st, "psa_key_derivation_input_bytes(SECRET HKDF_EXTRACT prefix output)") != TEST_OK) { + (void)psa_key_derivation_abort(&op); + return TEST_FAIL; + } + st = psa_key_derivation_set_capacity(&op, sizeof(expected)); + if (check_status(st, "psa_key_derivation_set_capacity(HKDF_EXTRACT prefix output)") != TEST_OK) { + (void)psa_key_derivation_abort(&op); + return TEST_FAIL; + } + st = psa_key_derivation_output_bytes(&op, output, sizeof(output)); + if (check_status(st, "psa_key_derivation_output_bytes(HKDF_EXTRACT prefix output)") != TEST_OK) { + (void)psa_key_derivation_abort(&op); + return TEST_FAIL; + } + st = psa_key_derivation_abort(&op); + if (check_status(st, "psa_key_derivation_abort(HKDF_EXTRACT prefix output)") != TEST_OK) { + return TEST_FAIL; + } + + if (check_buf_eq("psa_key_derivation_output_bytes(HKDF_EXTRACT prefix output)", + output, expected, sizeof(output)) != TEST_OK) { + return TEST_FAIL; + } + + return TEST_OK; +} + static int test_kdf_hkdf_expand_optional_info(void) { static const uint8_t prk[WC_SHA256_DIGEST_SIZE] = { @@ -2629,6 +2685,12 @@ int main(int argc, char** argv) return TEST_FAIL; } } + if (only == NULL || strcmp(only, "kdf_hkdf_extract_prefix_output") == 0) { + if (run_named_test("kdf_hkdf_extract_prefix_output", + test_kdf_hkdf_extract_prefix_output) == TEST_FAIL) { + return TEST_FAIL; + } + } if (only == NULL || strcmp(only, "kdf_hkdf_expand_optional_info") == 0) { if (run_named_test("kdf_hkdf_expand_optional_info", test_kdf_hkdf_expand_optional_info) == TEST_FAIL) { From 1f1c24041855c388e86240316ddebaf7105d46ed Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 10 Apr 2026 07:34:40 +0200 Subject: [PATCH 12/38] scrub cipher ctx on setup failure F/2416 --- src/psa_cipher.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/psa_cipher.c b/src/psa_cipher.c index 9f177bf..fa5fbca 100644 --- a/src/psa_cipher.c +++ b/src/psa_cipher.c @@ -369,6 +369,7 @@ psa_status_t psa_cipher_encrypt_setup(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); return wc_error_to_psa_status(ret); } @@ -510,6 +511,7 @@ psa_status_t psa_cipher_decrypt_setup(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); return wc_error_to_psa_status(ret); } From 4ef79ee0675d8c6cd2c3ff5b0685cd5a08aa653b Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 10 Apr 2026 07:36:25 +0200 Subject: [PATCH 13/38] Scrub MAC setup failure state F/2417 --- src/psa_mac.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/psa_mac.c b/src/psa_mac.c index 5480295..bbac34b 100644 --- a/src/psa_mac.c +++ b/src/psa_mac.c @@ -277,6 +277,7 @@ static psa_status_t wolfpsa_mac_setup(psa_mac_operation_t *operation, if (ctx->mac_length == 0 || ctx->full_length == 0 || ctx->mac_length > ctx->full_length) { wolfpsa_forcezero_free_key_data(key_data, key_data_length); + wc_ForceZero(ctx, sizeof(*ctx)); XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER); return PSA_ERROR_INVALID_ARGUMENT; } @@ -285,11 +286,13 @@ static psa_status_t wolfpsa_mac_setup(psa_mac_operation_t *operation, size_t trunc_len = PSA_MAC_TRUNCATED_LENGTH(alg); if (trunc_len > ctx->full_length) { wolfpsa_forcezero_free_key_data(key_data, key_data_length); + wc_ForceZero(ctx, sizeof(*ctx)); XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER); return PSA_ERROR_INVALID_ARGUMENT; } if (trunc_len < 4u) { wolfpsa_forcezero_free_key_data(key_data, key_data_length); + wc_ForceZero(ctx, sizeof(*ctx)); XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER); return PSA_ERROR_NOT_SUPPORTED; } @@ -298,6 +301,7 @@ static psa_status_t wolfpsa_mac_setup(psa_mac_operation_t *operation, wolfpsa_forcezero_free_key_data(key_data, key_data_length); if (ret != 0) { + wc_ForceZero(ctx, sizeof(*ctx)); XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER); return wc_error_to_psa_status(ret); } From ce6ceb0d0842bdfe730856a96d43766a6b0660c2 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 10 Apr 2026 07:39:11 +0200 Subject: [PATCH 14/38] Fix ECC public bit inference F/2051 --- src/psa_key_storage.c | 15 +++-- test/psa_server/psa_ecc_bit_inference_test.c | 64 ++++++++++++++++---- 2 files changed, 63 insertions(+), 16 deletions(-) diff --git a/src/psa_key_storage.c b/src/psa_key_storage.c index 358885a..a3d2599 100644 --- a/src/psa_key_storage.c +++ b/src/psa_key_storage.c @@ -374,11 +374,18 @@ static psa_status_t wolfpsa_infer_key_bits(psa_key_attributes_t* attr, psa_key_bits_t inferred_bits; if (PSA_KEY_TYPE_IS_ECC_PUBLIC_KEY(attr->type)) { - if (data_length < 2u || ((data_length - 1u) & 1u) != 0u) { - return PSA_ERROR_INVALID_ARGUMENT; + if (family == PSA_ECC_FAMILY_MONTGOMERY || + family == PSA_ECC_FAMILY_TWISTED_EDWARDS) { + inferred_bits = wolfpsa_ecc_bits_from_length(family, + data_length); + } + else { + if (data_length < 2u || ((data_length - 1u) & 1u) != 0u) { + return PSA_ERROR_INVALID_ARGUMENT; + } + inferred_bits = wolfpsa_ecc_bits_from_length(family, + (data_length - 1u) / 2u); } - inferred_bits = wolfpsa_ecc_bits_from_length(family, - (data_length - 1u) / 2u); } else { inferred_bits = wolfpsa_ecc_bits_from_length(family, data_length); diff --git a/test/psa_server/psa_ecc_bit_inference_test.c b/test/psa_server/psa_ecc_bit_inference_test.c index 488f438..bc76fab 100644 --- a/test/psa_server/psa_ecc_bit_inference_test.c +++ b/test/psa_server/psa_ecc_bit_inference_test.c @@ -11,36 +11,34 @@ #include -static int test_public_key_bits(void) +static int check_inferred_bits(psa_key_type_t key_type, const uint8_t* key_data, + size_t key_data_len, psa_key_bits_t expected_bits, + const char* label) { - uint8_t pub[133]; psa_key_attributes_t attrs = psa_key_attributes_init(); psa_key_attributes_t got = psa_key_attributes_init(); psa_key_id_t key_id = PSA_KEY_ID_NULL; psa_status_t st; - memset(pub, 0, sizeof(pub)); - pub[0] = 0x04; - - psa_set_key_type(&attrs, PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1)); + psa_set_key_type(&attrs, key_type); psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_EXPORT); - st = psa_import_key(&attrs, pub, sizeof(pub), &key_id); + st = psa_import_key(&attrs, key_data, key_data_len, &key_id); if (st != PSA_SUCCESS) { - printf("FAIL public import status=%d\n", (int)st); + printf("FAIL %s import status=%d\n", label, (int)st); return 1; } st = psa_get_key_attributes(key_id, &got); if (st != PSA_SUCCESS) { - printf("FAIL public attrs status=%d\n", (int)st); + printf("FAIL %s attrs status=%d\n", label, (int)st); (void)psa_destroy_key(key_id); return 1; } - if (psa_get_key_bits(&got) != 521) { - printf("FAIL public bits=%u expected=521\n", - (unsigned)psa_get_key_bits(&got)); + if (psa_get_key_bits(&got) != expected_bits) { + printf("FAIL %s bits=%u expected=%u\n", label, + (unsigned)psa_get_key_bits(&got), (unsigned)expected_bits); (void)psa_destroy_key(key_id); return 1; } @@ -49,6 +47,18 @@ static int test_public_key_bits(void) return 0; } +static int test_public_key_bits(void) +{ + uint8_t pub[133]; + + memset(pub, 0, sizeof(pub)); + pub[0] = 0x04; + + return check_inferred_bits( + PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1), + pub, sizeof(pub), 521, "secp521r1 public"); +} + static int test_private_key_bits(void) { uint8_t priv[66]; @@ -87,6 +97,33 @@ static int test_private_key_bits(void) return 0; } +static int test_raw_public_key_bits(void) +{ + uint8_t x25519_pub[32]; + uint8_t ed25519_pub[32]; + uint8_t ed448_pub[57]; + + memset(x25519_pub, 0xA5, sizeof(x25519_pub)); + memset(ed25519_pub, 0x5A, sizeof(ed25519_pub)); + memset(ed448_pub, 0x3C, sizeof(ed448_pub)); + + if (check_inferred_bits( + PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_MONTGOMERY), + x25519_pub, sizeof(x25519_pub), 255, "x25519 public") != 0) { + return 1; + } + + if (check_inferred_bits( + PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_TWISTED_EDWARDS), + ed25519_pub, sizeof(ed25519_pub), 255, "ed25519 public") != 0) { + return 1; + } + + return check_inferred_bits( + PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_TWISTED_EDWARDS), + ed448_pub, sizeof(ed448_pub), 448, "ed448 public"); +} + int main(void) { psa_status_t st = psa_crypto_init(); @@ -101,6 +138,9 @@ int main(void) if (test_private_key_bits() != 0) { return 1; } + if (test_raw_public_key_bits() != 0) { + return 1; + } printf("PSA ECC bit inference test: OK\n"); return 0; From f69741b339ce19ffdc7a9c01a0142e1c877c00e1 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 10 Apr 2026 07:40:43 +0200 Subject: [PATCH 15/38] Reject zero PBKDF2 cost F/2052 --- src/psa_key_derivation.c | 4 ++++ test/psa_server/psa_api_test.c | 44 ++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/src/psa_key_derivation.c b/src/psa_key_derivation.c index 6c9551f..1b72ee0 100644 --- a/src/psa_key_derivation.c +++ b/src/psa_key_derivation.c @@ -554,6 +554,10 @@ psa_status_t psa_key_derivation_input_integer(psa_key_derivation_operation_t *op return PSA_ERROR_INVALID_ARGUMENT; } + if (value == 0) { + return PSA_ERROR_INVALID_ARGUMENT; + } + if (value > 0xFFFFFFFFu) { return PSA_ERROR_NOT_SUPPORTED; } diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index aa52915..8d9a800 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -1780,6 +1780,44 @@ static int test_kdf_null_capacity(void) return TEST_OK; } +static int test_kdf_pbkdf2_zero_cost_rejected(void) +{ + static const uint8_t password[] = "pbkdf2-password"; + static const uint8_t salt[] = "pbkdf2-salt"; + psa_key_derivation_operation_t op = psa_key_derivation_operation_init(); + psa_status_t st; + int ret = TEST_FAIL; + + st = psa_key_derivation_setup(&op, PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_256)); + if (check_status(st, "psa_key_derivation_setup(PBKDF2 zero cost)") != TEST_OK) { + goto cleanup; + } + + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_PASSWORD, + password, sizeof(password) - 1u); + if (check_status(st, "psa_key_derivation_input_bytes(PBKDF2 password)") != TEST_OK) { + goto cleanup; + } + + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_SALT, + salt, sizeof(salt) - 1u); + if (check_status(st, "psa_key_derivation_input_bytes(PBKDF2 salt)") != TEST_OK) { + goto cleanup; + } + + st = psa_key_derivation_input_integer(&op, PSA_KEY_DERIVATION_INPUT_COST, 0); + if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, + "psa_key_derivation_input_integer rejects zero PBKDF2 cost") != TEST_OK) { + goto cleanup; + } + + ret = TEST_OK; + +cleanup: + (void)psa_key_derivation_abort(&op); + return ret; +} + static int test_kdf_input_key_policy(void) { static const uint8_t secret[] = { @@ -2658,6 +2696,12 @@ int main(int argc, char** argv) 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; } + if (only == NULL || strcmp(only, "kdf_pbkdf2_zero_cost") == 0) { + if (run_named_test("kdf_pbkdf2_zero_cost", + test_kdf_pbkdf2_zero_cost_rejected) == TEST_FAIL) { + return TEST_FAIL; + } + } if (only == NULL || strcmp(only, "kdf_input_key_policy") == 0) { if (run_named_test("kdf_input_key_policy", test_kdf_input_key_policy) == TEST_FAIL) { return TEST_FAIL; From e82a02a9666cc0fcd55a5404a0b46da6910df179 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 10 Apr 2026 07:44:24 +0200 Subject: [PATCH 16/38] Reject invalid ChaCha20 key sizes F/2053 --- src/psa_engine.c | 7 ++++++- src/psa_key_storage.c | 10 ++++++++++ test/psa_server/psa_api_test.c | 35 ++++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/psa_engine.c b/src/psa_engine.c index 684f794..47c57ec 100644 --- a/src/psa_engine.c +++ b/src/psa_engine.c @@ -271,12 +271,17 @@ psa_status_t psa_check_key_size_valid(psa_key_type_t type, size_t bits) case PSA_KEY_TYPE_HMAC: case PSA_KEY_TYPE_RAW_DATA: - case PSA_KEY_TYPE_CHACHA20: if (bits > 0) { return PSA_SUCCESS; } return PSA_ERROR_INVALID_ARGUMENT; + case PSA_KEY_TYPE_CHACHA20: + if (bits == 256) { + return PSA_SUCCESS; + } + return PSA_ERROR_INVALID_ARGUMENT; + default: break; } diff --git a/src/psa_key_storage.c b/src/psa_key_storage.c index a3d2599..d1ec6b6 100644 --- a/src/psa_key_storage.c +++ b/src/psa_key_storage.c @@ -824,6 +824,16 @@ psa_status_t psa_import_key( return status; } } + if (attr.type == PSA_KEY_TYPE_CHACHA20) { + if (attr.bits != 256) { + wolfpsa_debug_import_reason("invalid ChaCha20 key bits", &attr, data_length); + return PSA_ERROR_INVALID_ARGUMENT; + } + if (attr.bits != (psa_key_bits_t)(data_length * 8U)) { + wolfpsa_debug_import_reason("ChaCha20 bits/length mismatch", &attr, data_length); + return PSA_ERROR_INVALID_ARGUMENT; + } + } if (attr.type == PSA_KEY_TYPE_AES) { if (attr.bits != 128 && attr.bits != 192 && diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index 8d9a800..d4e409e 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -1536,6 +1536,35 @@ static int test_chacha20_poly1305_rejects_aes_key(void) return TEST_OK; } +static int test_chacha20_import_rejects_invalid_key_size(void) +{ + static const uint8_t key[16] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f + }; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_key_id_t key_id = 0; + psa_status_t st; + + psa_set_key_type(&attrs, PSA_KEY_TYPE_CHACHA20); + psa_set_key_bits(&attrs, 128); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_ENCRYPT); + psa_set_key_algorithm(&attrs, PSA_ALG_STREAM_CIPHER); + + st = psa_import_key(&attrs, key, sizeof(key), &key_id); + psa_reset_key_attributes(&attrs); + + if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, + "psa_import_key rejects invalid ChaCha20 key size") != TEST_OK) { + if (key_id != 0) { + (void)psa_destroy_key(key_id); + } + return TEST_FAIL; + } + + return TEST_OK; +} + static int test_asym_ecc(void) { static const uint8_t msg[] = "psa ecc sign"; @@ -2675,6 +2704,12 @@ int main(int argc, char** argv) return TEST_FAIL; } } + if (only == NULL || strcmp(only, "chacha20_import_key_size") == 0) { + if (run_named_test("chacha20_import_key_size", + test_chacha20_import_rejects_invalid_key_size) == TEST_FAIL) { + return TEST_FAIL; + } + } if (only == NULL || strcmp(only, "asym_ecc") == 0) { if (run_named_test("asym_ecc", test_asym_ecc) == TEST_FAIL) return TEST_FAIL; } From 08c2c13fc4be7368b8dc63b02a3ce02d867f5e2c Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 10 Apr 2026 07:50:46 +0200 Subject: [PATCH 17/38] Abort PSA multipart ops on error F/2397 --- src/psa_cipher.c | 134 +++++++++++++--------- src/psa_hash_engine.c | 35 +++--- src/psa_mac.c | 23 ++-- test/psa_server/psa_api_test.c | 204 +++++++++++++++++++++++++++++++++ 4 files changed, 323 insertions(+), 73 deletions(-) diff --git a/src/psa_cipher.c b/src/psa_cipher.c index fa5fbca..c47da34 100644 --- a/src/psa_cipher.c +++ b/src/psa_cipher.c @@ -75,6 +75,15 @@ static wolfpsa_cipher_ctx_t *wolfpsa_cipher_get_ctx( return (wolfpsa_cipher_ctx_t *)(uintptr_t)operation->opaque; } +psa_status_t psa_cipher_abort(psa_cipher_operation_t *operation); + +static psa_status_t wolfpsa_cipher_fail(psa_cipher_operation_t *operation, + psa_status_t status) +{ + (void)psa_cipher_abort(operation); + return status; +} + static psa_status_t wolfpsa_cipher_check_alg(psa_algorithm_t alg) { if (!PSA_ALG_IS_CIPHER(alg)) { @@ -659,20 +668,20 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, size_t output_len = 0; if (output_length == NULL) { - return PSA_ERROR_INVALID_ARGUMENT; + return wolfpsa_cipher_fail(operation, PSA_ERROR_INVALID_ARGUMENT); } *output_length = 0; if (ctx == NULL) { - return PSA_ERROR_BAD_STATE; + return wolfpsa_cipher_fail(operation, PSA_ERROR_BAD_STATE); } if (input == NULL && input_length > 0) { - return PSA_ERROR_INVALID_ARGUMENT; + return wolfpsa_cipher_fail(operation, PSA_ERROR_INVALID_ARGUMENT); } if (output == NULL && output_size > 0) { - return PSA_ERROR_INVALID_ARGUMENT; + return wolfpsa_cipher_fail(operation, PSA_ERROR_INVALID_ARGUMENT); } if (wolfpsa_check_word32_length(input_length) != PSA_SUCCESS) { - return PSA_ERROR_INVALID_ARGUMENT; + return wolfpsa_cipher_fail(operation, PSA_ERROR_INVALID_ARGUMENT); } if (input_length == 0) { @@ -681,7 +690,7 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, if (ctx->alg == PSA_ALG_CBC_NO_PADDING) { if (!ctx->iv_set) { - return PSA_ERROR_BAD_STATE; + return wolfpsa_cipher_fail(operation, PSA_ERROR_BAD_STATE); } { size_t block_size = ctx->block_size; @@ -693,7 +702,7 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, output_len = full_len; if (output_size < output_len) { - return PSA_ERROR_BUFFER_TOO_SMALL; + return wolfpsa_cipher_fail(operation, PSA_ERROR_BUFFER_TOO_SMALL); } if (blocks == 0) { @@ -722,7 +731,7 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, (word32)block_size); } #else - return PSA_ERROR_NOT_SUPPORTED; + return wolfpsa_cipher_fail(operation, PSA_ERROR_NOT_SUPPORTED); #endif } else { @@ -736,7 +745,8 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, } } if (ret != 0) { - return wc_error_to_psa_status(ret); + return wolfpsa_cipher_fail(operation, + wc_error_to_psa_status(ret)); } output_offset += block_size; input_offset += needed; @@ -763,7 +773,8 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, (word32)full_blocks); } #else - return PSA_ERROR_NOT_SUPPORTED; + return wolfpsa_cipher_fail(operation, + PSA_ERROR_NOT_SUPPORTED); #endif } else { @@ -781,7 +792,8 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, } } if (ret != 0) { - return wc_error_to_psa_status(ret); + return wolfpsa_cipher_fail(operation, + wc_error_to_psa_status(ret)); } output_offset += full_blocks; input_offset += full_blocks; @@ -799,7 +811,7 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, } else if (ctx->alg == PSA_ALG_CBC_PKCS7) { if (!ctx->iv_set) { - return PSA_ERROR_BAD_STATE; + return wolfpsa_cipher_fail(operation, PSA_ERROR_BAD_STATE); } { size_t block_size = ctx->block_size; @@ -812,7 +824,8 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, size_t full_len = full_blocks * block_size; if (output_size < full_len) { - return PSA_ERROR_BUFFER_TOO_SMALL; + return wolfpsa_cipher_fail(operation, + PSA_ERROR_BUFFER_TOO_SMALL); } if (full_blocks == 0) { @@ -835,7 +848,8 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, ret = wc_Des3_CbcEncrypt(&ctx->des3, output, block, (word32)block_size); #else - return PSA_ERROR_NOT_SUPPORTED; + return wolfpsa_cipher_fail(operation, + PSA_ERROR_NOT_SUPPORTED); #endif } else { @@ -843,7 +857,8 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, (word32)block_size); } if (ret != 0) { - return wc_error_to_psa_status(ret); + return wolfpsa_cipher_fail(operation, + wc_error_to_psa_status(ret)); } output_offset += block_size; input_offset += needed; @@ -862,7 +877,8 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, input + input_offset, (word32)full_blocks_len); #else - return PSA_ERROR_NOT_SUPPORTED; + return wolfpsa_cipher_fail(operation, + PSA_ERROR_NOT_SUPPORTED); #endif } else { @@ -872,7 +888,8 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, (word32)full_blocks_len); } if (ret != 0) { - return wc_error_to_psa_status(ret); + return wolfpsa_cipher_fail(operation, + wc_error_to_psa_status(ret)); } output_offset += full_blocks_len; input_offset += full_blocks_len; @@ -908,7 +925,8 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, } if (output_size < process_len) { - return PSA_ERROR_BUFFER_TOO_SMALL; + return wolfpsa_cipher_fail(operation, + PSA_ERROR_BUFFER_TOO_SMALL); } if (ctx->partial_len > 0) { @@ -923,7 +941,8 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, ret = wc_Des3_CbcDecrypt(&ctx->des3, output, block, (word32)block_size); #else - return PSA_ERROR_NOT_SUPPORTED; + return wolfpsa_cipher_fail(operation, + PSA_ERROR_NOT_SUPPORTED); #endif } else { @@ -931,7 +950,8 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, (word32)block_size); } if (ret != 0) { - return wc_error_to_psa_status(ret); + return wolfpsa_cipher_fail(operation, + wc_error_to_psa_status(ret)); } output_offset += block_size; input_offset += needed; @@ -947,7 +967,8 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, input + input_offset, (word32)full_blocks_len); #else - return PSA_ERROR_NOT_SUPPORTED; + return wolfpsa_cipher_fail(operation, + PSA_ERROR_NOT_SUPPORTED); #endif } else { @@ -957,7 +978,8 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, (word32)full_blocks_len); } if (ret != 0) { - return wc_error_to_psa_status(ret); + return wolfpsa_cipher_fail(operation, + wc_error_to_psa_status(ret)); } output_offset += full_blocks_len; input_offset += full_blocks_len; @@ -980,10 +1002,10 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, } else if (ctx->alg == PSA_ALG_CCM_STAR_NO_TAG) { if (!ctx->iv_set) { - return PSA_ERROR_BAD_STATE; + return wolfpsa_cipher_fail(operation, PSA_ERROR_BAD_STATE); } if (output_size < input_length) { - return PSA_ERROR_BUFFER_TOO_SMALL; + return wolfpsa_cipher_fail(operation, PSA_ERROR_BUFFER_TOO_SMALL); } if (input_length == 0) { return PSA_SUCCESS; @@ -991,7 +1013,7 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, ret = wc_AesCtrEncrypt(&ctx->aes, output, input, (word32)input_length); if (ret != 0) { - return wc_error_to_psa_status(ret); + return wolfpsa_cipher_fail(operation, wc_error_to_psa_status(ret)); } *output_length = input_length; return PSA_SUCCESS; @@ -1007,7 +1029,7 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, output_len = full_len; if (output_size < output_len) { - return PSA_ERROR_BUFFER_TOO_SMALL; + return wolfpsa_cipher_fail(operation, PSA_ERROR_BUFFER_TOO_SMALL); } if (blocks == 0) { @@ -1036,7 +1058,7 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, (word32)block_size); } #else - return PSA_ERROR_NOT_SUPPORTED; + return wolfpsa_cipher_fail(operation, PSA_ERROR_NOT_SUPPORTED); #endif } else { @@ -1050,7 +1072,8 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, } } if (ret != 0) { - return wc_error_to_psa_status(ret); + return wolfpsa_cipher_fail(operation, + wc_error_to_psa_status(ret)); } output_offset += block_size; input_offset += needed; @@ -1077,7 +1100,8 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, (word32)full_blocks); } #else - return PSA_ERROR_NOT_SUPPORTED; + return wolfpsa_cipher_fail(operation, + PSA_ERROR_NOT_SUPPORTED); #endif } else { @@ -1095,7 +1119,8 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, } } if (ret != 0) { - return wc_error_to_psa_status(ret); + return wolfpsa_cipher_fail(operation, + wc_error_to_psa_status(ret)); } output_offset += full_blocks; input_offset += full_blocks; @@ -1113,23 +1138,23 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, } else if (ctx->alg == PSA_ALG_CTR) { if (!ctx->iv_set) { - return PSA_ERROR_BAD_STATE; + return wolfpsa_cipher_fail(operation, PSA_ERROR_BAD_STATE); } if (output_size < input_length) { - return PSA_ERROR_BUFFER_TOO_SMALL; + return wolfpsa_cipher_fail(operation, PSA_ERROR_BUFFER_TOO_SMALL); } ret = wc_AesCtrEncrypt(&ctx->aes, output, input, (word32)input_length); if (ret != 0) { - return wc_error_to_psa_status(ret); + return wolfpsa_cipher_fail(operation, wc_error_to_psa_status(ret)); } } else if (ctx->alg == PSA_ALG_CFB) { if (!ctx->iv_set) { - return PSA_ERROR_BAD_STATE; + return wolfpsa_cipher_fail(operation, PSA_ERROR_BAD_STATE); } if (output_size < input_length) { - return PSA_ERROR_BUFFER_TOO_SMALL; + return wolfpsa_cipher_fail(operation, PSA_ERROR_BUFFER_TOO_SMALL); } if (ctx->direction == AES_ENCRYPTION) { ret = wc_AesCfbEncrypt(&ctx->aes, output, input, @@ -1140,15 +1165,15 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, (word32)input_length); } if (ret != 0) { - return wc_error_to_psa_status(ret); + return wolfpsa_cipher_fail(operation, wc_error_to_psa_status(ret)); } } else if (ctx->alg == PSA_ALG_OFB) { if (!ctx->iv_set) { - return PSA_ERROR_BAD_STATE; + return wolfpsa_cipher_fail(operation, PSA_ERROR_BAD_STATE); } if (output_size < input_length) { - return PSA_ERROR_BUFFER_TOO_SMALL; + return wolfpsa_cipher_fail(operation, PSA_ERROR_BUFFER_TOO_SMALL); } if (ctx->direction == AES_ENCRYPTION) { ret = wc_AesOfbEncrypt(&ctx->aes, output, input, @@ -1159,24 +1184,24 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, (word32)input_length); } if (ret != 0) { - return wc_error_to_psa_status(ret); + return wolfpsa_cipher_fail(operation, wc_error_to_psa_status(ret)); } } else if (ctx->alg == PSA_ALG_STREAM_CIPHER) { if (!ctx->iv_set) { - return PSA_ERROR_BAD_STATE; + return wolfpsa_cipher_fail(operation, PSA_ERROR_BAD_STATE); } if (output_size < input_length) { - return PSA_ERROR_BUFFER_TOO_SMALL; + return wolfpsa_cipher_fail(operation, PSA_ERROR_BUFFER_TOO_SMALL); } ret = wc_Chacha_Process(&ctx->chacha, output, input, (word32)input_length); if (ret != 0) { - return wc_error_to_psa_status(ret); + return wolfpsa_cipher_fail(operation, wc_error_to_psa_status(ret)); } } else { - return PSA_ERROR_NOT_SUPPORTED; + return wolfpsa_cipher_fail(operation, PSA_ERROR_NOT_SUPPORTED); } *output_length = input_length; @@ -1192,16 +1217,16 @@ psa_status_t psa_cipher_finish(psa_cipher_operation_t *operation, int ret = 0; if (operation == NULL || output_length == NULL) { - return PSA_ERROR_INVALID_ARGUMENT; + return wolfpsa_cipher_fail(operation, PSA_ERROR_INVALID_ARGUMENT); } if (ctx == NULL) { - return PSA_ERROR_BAD_STATE; + return wolfpsa_cipher_fail(operation, PSA_ERROR_BAD_STATE); } if (ctx->alg == PSA_ALG_CBC_PKCS7) { size_t block_size = ctx->block_size; if (!ctx->iv_set) { - return PSA_ERROR_BAD_STATE; + return wolfpsa_cipher_fail(operation, PSA_ERROR_BAD_STATE); } if (ctx->direction == AES_ENCRYPTION) { uint8_t block[AES_BLOCK_SIZE]; @@ -1211,7 +1236,7 @@ psa_status_t psa_cipher_finish(psa_cipher_operation_t *operation, pad_len = block_size; } if (output_size < block_size) { - return PSA_ERROR_BUFFER_TOO_SMALL; + return wolfpsa_cipher_fail(operation, PSA_ERROR_BUFFER_TOO_SMALL); } if (ctx->partial_len > 0) { XMEMCPY(block, ctx->partial, ctx->partial_len); @@ -1223,7 +1248,7 @@ psa_status_t psa_cipher_finish(psa_cipher_operation_t *operation, ret = wc_Des3_CbcEncrypt(&ctx->des3, output, block, (word32)block_size); #else - return PSA_ERROR_NOT_SUPPORTED; + return wolfpsa_cipher_fail(operation, PSA_ERROR_NOT_SUPPORTED); #endif } else { @@ -1231,7 +1256,7 @@ psa_status_t psa_cipher_finish(psa_cipher_operation_t *operation, (word32)block_size); } if (ret != 0) { - return wc_error_to_psa_status(ret); + return wolfpsa_cipher_fail(operation, wc_error_to_psa_status(ret)); } ctx->partial_len = 0; *output_length = block_size; @@ -1245,7 +1270,7 @@ psa_status_t psa_cipher_finish(psa_cipher_operation_t *operation, psa_status_t status = PSA_SUCCESS; if (ctx->partial_len != block_size) { - return PSA_ERROR_INVALID_PADDING; + return wolfpsa_cipher_fail(operation, PSA_ERROR_INVALID_PADDING); } if (ctx->is_des3) { @@ -1253,7 +1278,7 @@ psa_status_t psa_cipher_finish(psa_cipher_operation_t *operation, ret = wc_Des3_CbcDecrypt(&ctx->des3, block, ctx->partial, (word32)block_size); #else - return PSA_ERROR_NOT_SUPPORTED; + return wolfpsa_cipher_fail(operation, PSA_ERROR_NOT_SUPPORTED); #endif } else { @@ -1262,7 +1287,7 @@ psa_status_t psa_cipher_finish(psa_cipher_operation_t *operation, } if (ret != 0) { wc_ForceZero(block, sizeof(block)); - return wc_error_to_psa_status(ret); + return wolfpsa_cipher_fail(operation, wc_error_to_psa_status(ret)); } pad_len = block[block_size - 1]; @@ -1291,6 +1316,9 @@ psa_status_t psa_cipher_finish(psa_cipher_operation_t *operation, cbc_pkcs7_decrypt_done: wc_ForceZero(block, sizeof(block)); + if (status != PSA_SUCCESS) { + return wolfpsa_cipher_fail(operation, status); + } return status; } } @@ -1298,7 +1326,7 @@ psa_status_t psa_cipher_finish(psa_cipher_operation_t *operation, if ((ctx->alg == PSA_ALG_CBC_NO_PADDING || ctx->alg == PSA_ALG_ECB_NO_PADDING) && ctx->partial_len != 0) { - return PSA_ERROR_INVALID_ARGUMENT; + return wolfpsa_cipher_fail(operation, PSA_ERROR_INVALID_ARGUMENT); } *output_length = 0; diff --git a/src/psa_hash_engine.c b/src/psa_hash_engine.c index 5db9ecd..4477469 100644 --- a/src/psa_hash_engine.c +++ b/src/psa_hash_engine.c @@ -115,6 +115,15 @@ static psa_hash_operation_ctx_t *psa_hash_get_ctx(psa_hash_operation_t *operatio return (psa_hash_operation_ctx_t *)(uintptr_t)operation->opaque; } +psa_status_t psa_hash_abort(psa_hash_operation_t *operation); + +static psa_status_t wolfpsa_hash_fail(psa_hash_operation_t *operation, + psa_status_t status) +{ + (void)psa_hash_abort(operation); + return status; +} + static const psa_hash_operation_ctx_t *psa_hash_get_ctx_const( const psa_hash_operation_t *operation) { @@ -424,18 +433,18 @@ psa_status_t psa_hash_update(psa_hash_operation_t *operation, psa_hash_operation_ctx_t *ctx = psa_hash_get_ctx(operation); if (operation == NULL || (input == NULL && input_length > 0)) { - return PSA_ERROR_INVALID_ARGUMENT; + return wolfpsa_hash_fail(operation, PSA_ERROR_INVALID_ARGUMENT); } if (ctx == NULL || !ctx->initialized || !ctx->started) { - return PSA_ERROR_BAD_STATE; + return wolfpsa_hash_fail(operation, PSA_ERROR_BAD_STATE); } if (ctx->finalized) { - return PSA_ERROR_BAD_STATE; + return wolfpsa_hash_fail(operation, PSA_ERROR_BAD_STATE); } if (wolfpsa_check_word32_length(input_length) != PSA_SUCCESS) { - return PSA_ERROR_INVALID_ARGUMENT; + return wolfpsa_hash_fail(operation, PSA_ERROR_INVALID_ARGUMENT); } /* Update the hash context based on algorithm */ @@ -507,11 +516,11 @@ psa_status_t psa_hash_update(psa_hash_operation_t *operation, break; #endif default: - return PSA_ERROR_NOT_SUPPORTED; + return wolfpsa_hash_fail(operation, PSA_ERROR_NOT_SUPPORTED); } if (ret != 0) { - return wc_error_to_psa_status(ret); + return wolfpsa_hash_fail(operation, wc_error_to_psa_status(ret)); } return PSA_SUCCESS; @@ -528,26 +537,26 @@ psa_status_t psa_hash_finish(psa_hash_operation_t *operation, psa_hash_operation_ctx_t *ctx = psa_hash_get_ctx(operation); if (operation == NULL || hash == NULL || hash_length == NULL) { - return PSA_ERROR_INVALID_ARGUMENT; + return wolfpsa_hash_fail(operation, PSA_ERROR_INVALID_ARGUMENT); } if (ctx == NULL || !ctx->initialized || !ctx->started) { - return PSA_ERROR_BAD_STATE; + return wolfpsa_hash_fail(operation, PSA_ERROR_BAD_STATE); } if (ctx->finalized) { - return PSA_ERROR_BAD_STATE; + return wolfpsa_hash_fail(operation, PSA_ERROR_BAD_STATE); } /* Get the expected hash size */ expected_hash_size = psa_hash_get_size(ctx->alg); if (expected_hash_size == 0) { - return PSA_ERROR_NOT_SUPPORTED; + return wolfpsa_hash_fail(operation, PSA_ERROR_NOT_SUPPORTED); } /* Check if the output buffer is large enough */ if (hash_size < expected_hash_size) { - return PSA_ERROR_BUFFER_TOO_SMALL; + return wolfpsa_hash_fail(operation, PSA_ERROR_BUFFER_TOO_SMALL); } /* Finalize the hash based on algorithm */ @@ -612,11 +621,11 @@ psa_status_t psa_hash_finish(psa_hash_operation_t *operation, break; #endif default: - return PSA_ERROR_NOT_SUPPORTED; + return wolfpsa_hash_fail(operation, PSA_ERROR_NOT_SUPPORTED); } if (ret != 0) { - return wc_error_to_psa_status(ret); + return wolfpsa_hash_fail(operation, wc_error_to_psa_status(ret)); } *hash_length = expected_hash_size; diff --git a/src/psa_mac.c b/src/psa_mac.c index bbac34b..f1f5f29 100644 --- a/src/psa_mac.c +++ b/src/psa_mac.c @@ -67,6 +67,15 @@ static wolfpsa_mac_ctx_t* wolfpsa_mac_get_ctx(psa_mac_operation_t *operation) return (wolfpsa_mac_ctx_t *)(uintptr_t)operation->opaque; } +psa_status_t psa_mac_abort(psa_mac_operation_t *operation); + +static psa_status_t wolfpsa_mac_fail(psa_mac_operation_t *operation, + psa_status_t status) +{ + (void)psa_mac_abort(operation); + return status; +} + static int wolfpsa_hash_type_from_alg(psa_algorithm_t alg) { psa_algorithm_t hash_alg = 0; @@ -332,13 +341,13 @@ psa_status_t psa_mac_update(psa_mac_operation_t *operation, int ret; if (ctx == NULL) { - return PSA_ERROR_BAD_STATE; + return wolfpsa_mac_fail(operation, PSA_ERROR_BAD_STATE); } if (input == NULL && input_length > 0) { - return PSA_ERROR_INVALID_ARGUMENT; + return wolfpsa_mac_fail(operation, PSA_ERROR_INVALID_ARGUMENT); } if (wolfpsa_check_word32_length(input_length) != PSA_SUCCESS) { - return PSA_ERROR_INVALID_ARGUMENT; + return wolfpsa_mac_fail(operation, PSA_ERROR_INVALID_ARGUMENT); } if (input_length == 0) { @@ -352,11 +361,11 @@ psa_status_t psa_mac_update(psa_mac_operation_t *operation, ret = wc_CmacUpdate(&ctx->ctx.cmac, input, (word32)input_length); } else { - return PSA_ERROR_BAD_STATE; + return wolfpsa_mac_fail(operation, PSA_ERROR_BAD_STATE); } if (ret != 0) { - return wc_error_to_psa_status(ret); + return wolfpsa_mac_fail(operation, wc_error_to_psa_status(ret)); } return PSA_SUCCESS; @@ -429,12 +438,12 @@ psa_status_t psa_mac_sign_finish(psa_mac_operation_t *operation, psa_status_t status; if (ctx == NULL) { - return PSA_ERROR_BAD_STATE; + return wolfpsa_mac_fail(operation, PSA_ERROR_BAD_STATE); } status = wolfpsa_mac_final(ctx, mac, mac_size, mac_length); if (status != PSA_SUCCESS) { - return status; + return wolfpsa_mac_fail(operation, status); } psa_mac_abort(operation); diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index d4e409e..02b69fa 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -170,6 +170,40 @@ static int test_hmac(void) return TEST_OK; } +static int test_hash_error_aborts_operation(void) +{ + static const uint8_t msg[] = "hash error state"; + uint8_t out[WC_SHA256_DIGEST_SIZE]; + size_t out_len = 0; + psa_hash_operation_t op = psa_hash_operation_init(); + psa_status_t st; + + st = psa_hash_setup(&op, PSA_ALG_SHA_256); + if (check_status(st, "psa_hash_setup(error state)") != TEST_OK) return TEST_FAIL; + + st = psa_hash_update(&op, msg, sizeof(msg) - 1); + if (check_status(st, "psa_hash_update(error state seed)") != TEST_OK) { + (void)psa_hash_abort(&op); + return TEST_FAIL; + } + + st = psa_hash_finish(&op, out, 1, &out_len); + if (check_true(st == PSA_ERROR_BUFFER_TOO_SMALL, + "psa_hash_finish(error state status)") != TEST_OK) { + (void)psa_hash_abort(&op); + return TEST_FAIL; + } + + st = psa_hash_update(&op, msg, 1); + if (check_true(st == PSA_ERROR_BAD_STATE, + "psa_hash_update(error state aborted)") != TEST_OK) { + (void)psa_hash_abort(&op); + return TEST_FAIL; + } + + return TEST_OK; +} + static int test_algorithm_none_rejects_key_usage(void) { static const uint8_t aes_key[16] = { @@ -309,6 +343,63 @@ static int test_algorithm_none_rejects_key_usage(void) return ret; } +static int test_mac_error_aborts_operation(void) +{ + static const uint8_t key[] = { + 0x60,0x3d,0xeb,0x10,0x15,0xca,0x71,0xbe, + 0x2b,0x73,0xae,0xf0,0x85,0x7d,0x77,0x81 + }; + static const uint8_t msg[] = "mac error state"; + uint8_t mac[WC_SHA256_DIGEST_SIZE]; + size_t mac_len = 0; + psa_key_id_t key_id = 0; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_mac_operation_t op = psa_mac_operation_init(); + psa_status_t st; + int ret = TEST_FAIL; + + psa_set_key_type(&attrs, PSA_KEY_TYPE_HMAC); + psa_set_key_bits(&attrs, (size_t)sizeof(key) * 8u); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_SIGN_MESSAGE); + psa_set_key_algorithm(&attrs, PSA_ALG_HMAC(PSA_ALG_SHA_256)); + + st = psa_import_key(&attrs, key, sizeof(key), &key_id); + if (check_status(st, "psa_import_key(HMAC error state)") != TEST_OK) { + return TEST_FAIL; + } + + st = psa_mac_sign_setup(&op, key_id, PSA_ALG_HMAC(PSA_ALG_SHA_256)); + if (check_status(st, "psa_mac_sign_setup(error state)") != TEST_OK) { + goto cleanup; + } + + st = psa_mac_update(&op, msg, sizeof(msg) - 1); + if (check_status(st, "psa_mac_update(error state seed)") != TEST_OK) { + goto cleanup; + } + + st = psa_mac_sign_finish(&op, mac, 1, &mac_len); + if (check_true(st == PSA_ERROR_BUFFER_TOO_SMALL, + "psa_mac_sign_finish(error state status)") != TEST_OK) { + goto cleanup; + } + + st = psa_mac_update(&op, msg, 1); + if (check_true(st == PSA_ERROR_BAD_STATE, + "psa_mac_update(error state aborted)") != TEST_OK) { + goto cleanup; + } + + ret = TEST_OK; + +cleanup: + (void)psa_mac_abort(&op); + if (key_id != 0) { + (void)psa_destroy_key(key_id); + } + return ret; +} + static int test_cipher_cbc(void) { static const uint8_t key[16] = { @@ -1029,6 +1120,101 @@ static int test_cipher_cbc_pkcs7_decrypt_update_small_output(void) return TEST_OK; } +static int test_cipher_error_aborts_operation(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 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; + int ret = TEST_FAIL; + + 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 error state)") != TEST_OK) { + return TEST_FAIL; + } + + st = psa_cipher_encrypt_setup(&op, key_id, PSA_ALG_CBC_PKCS7); + if (check_status(st, "psa_cipher_encrypt_setup(error state enc)") != TEST_OK) { + goto cleanup; + } + st = psa_cipher_set_iv(&op, iv, sizeof(iv)); + if (check_status(st, "psa_cipher_set_iv(error state enc)") != TEST_OK) { + goto cleanup; + } + st = psa_cipher_update(&op, plaintext, sizeof(plaintext), + ciphertext, sizeof(ciphertext), &part_len); + if (check_status(st, "psa_cipher_update(error state enc)") != TEST_OK) { + 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(error state enc)") != TEST_OK) { + 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(error state)") != TEST_OK) { + goto cleanup; + } + st = psa_cipher_set_iv(&op, iv, sizeof(iv)); + if (check_status(st, "psa_cipher_set_iv(error state dec)") != TEST_OK) { + goto cleanup; + } + st = psa_cipher_update(&op, ciphertext, 1, small, sizeof(small), &out_len); + if (check_status(st, "psa_cipher_update(error state seed)") != TEST_OK) { + goto cleanup; + } + + st = psa_cipher_update(&op, ciphertext + 1, 16, small, sizeof(small), &out_len); + if (check_true(st == PSA_ERROR_BUFFER_TOO_SMALL, + "psa_cipher_update(error state status)") != TEST_OK) { + goto cleanup; + } + + st = psa_cipher_finish(&op, ciphertext, sizeof(ciphertext), &out_len); + if (check_true(st == PSA_ERROR_BAD_STATE, + "psa_cipher_finish(error state aborted)") != TEST_OK) { + goto cleanup; + } + + ret = TEST_OK; + +cleanup: + (void)psa_cipher_abort(&op); + if (key_id != 0) { + (void)psa_destroy_key(key_id); + } + return ret; +} + static int test_cipher_ccm_star_no_tag_multipart(void) { static const uint8_t key[16] = { @@ -2605,9 +2791,21 @@ int main(int argc, char** argv) if (only == NULL || strcmp(only, "hash") == 0) { if (run_named_test("hash", test_hash) == TEST_FAIL) return TEST_FAIL; } + if (only == NULL || strcmp(only, "hash_error_state") == 0) { + if (run_named_test("hash_error_state", + test_hash_error_aborts_operation) == TEST_FAIL) { + return TEST_FAIL; + } + } if (only == NULL || strcmp(only, "hmac") == 0) { if (run_named_test("hmac", test_hmac) == TEST_FAIL) return TEST_FAIL; } + if (only == NULL || strcmp(only, "mac_error_state") == 0) { + if (run_named_test("mac_error_state", + test_mac_error_aborts_operation) == TEST_FAIL) { + return TEST_FAIL; + } + } if (only == NULL || strcmp(only, "alg_none_policy") == 0) { if (run_named_test("alg_none_policy", test_algorithm_none_rejects_key_usage) == TEST_FAIL) { @@ -2671,6 +2869,12 @@ int main(int argc, char** argv) return TEST_FAIL; } } + if (only == NULL || strcmp(only, "cipher_error_state") == 0) { + if (run_named_test("cipher_error_state", + test_cipher_error_aborts_operation) == TEST_FAIL) { + return TEST_FAIL; + } + } if (only == NULL || strcmp(only, "cipher_ccm_star_no_tag_multipart") == 0) { if (run_named_test("cipher_ccm_star_no_tag_multipart", test_cipher_ccm_star_no_tag_multipart) == TEST_FAIL) { From 0eaa83cb0fdd61835e6d19ab03bcf8a665b78596 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 10 Apr 2026 07:57:29 +0200 Subject: [PATCH 18/38] Add RSA OAEP asymmetric API coverage F/2411 --- test/psa_server/psa_api_test.c | 121 +++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index 02b69fa..7455ebf 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -1841,6 +1841,121 @@ static int test_asym_ecc(void) return TEST_OK; } +static int test_asym_rsa_oaep_usage_policy(void) +{ + static const uint8_t plaintext[] = "psa rsa oaep"; + uint8_t exported_pub[256]; + uint8_t ciphertext[256]; + uint8_t decrypted[sizeof(plaintext)]; + size_t exported_pub_len = 0; + size_t ciphertext_len = 0; + size_t decrypted_len = 0; + psa_key_id_t decrypt_key = 0; + psa_key_id_t encrypt_pub_key = 0; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_status_t st; + int result = TEST_FAIL; + + psa_set_key_type(&attrs, PSA_KEY_TYPE_RSA_KEY_PAIR); + psa_set_key_bits(&attrs, 1024); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_DECRYPT | PSA_KEY_USAGE_EXPORT); + psa_set_key_algorithm(&attrs, PSA_ALG_RSA_OAEP(PSA_ALG_SHA_256)); + + st = psa_generate_key(&attrs, &decrypt_key); + if (st == PSA_ERROR_NOT_SUPPORTED) { + return TEST_SKIPPED; + } + if (check_status(st, "psa_generate_key(RSA OAEP decrypt)") != TEST_OK) { + return TEST_FAIL; + } + + st = psa_export_public_key(decrypt_key, exported_pub, sizeof(exported_pub), + &exported_pub_len); + if (check_status(st, "psa_export_public_key(RSA OAEP)") != TEST_OK) { + goto cleanup; + } + + psa_reset_key_attributes(&attrs); + psa_set_key_type(&attrs, PSA_KEY_TYPE_RSA_PUBLIC_KEY); + psa_set_key_bits(&attrs, 1024); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_ENCRYPT); + psa_set_key_algorithm(&attrs, PSA_ALG_RSA_OAEP(PSA_ALG_SHA_256)); + + st = psa_import_key(&attrs, exported_pub, exported_pub_len, &encrypt_pub_key); + if (check_status(st, "psa_import_key(RSA OAEP public)") != TEST_OK) { + goto cleanup; + } + + st = psa_asymmetric_encrypt(encrypt_pub_key, PSA_ALG_RSA_OAEP(PSA_ALG_SHA_256), + plaintext, sizeof(plaintext) - 1, + NULL, 0, ciphertext, sizeof(ciphertext), NULL); + if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, + "psa_asymmetric_encrypt rejects NULL output_length") != TEST_OK) { + goto cleanup; + } + + st = psa_asymmetric_encrypt(decrypt_key, PSA_ALG_RSA_OAEP(PSA_ALG_SHA_256), + plaintext, sizeof(plaintext) - 1, + NULL, 0, ciphertext, sizeof(ciphertext), + &ciphertext_len); + if (check_true(st == PSA_ERROR_NOT_PERMITTED, + "psa_asymmetric_encrypt enforces ENCRYPT usage") != TEST_OK) { + goto cleanup; + } + + st = psa_asymmetric_encrypt(encrypt_pub_key, PSA_ALG_RSA_OAEP(PSA_ALG_SHA_256), + plaintext, sizeof(plaintext) - 1, + NULL, 0, ciphertext, sizeof(ciphertext), + &ciphertext_len); + if (check_status(st, "psa_asymmetric_encrypt(RSA OAEP)") != TEST_OK) { + goto cleanup; + } + + st = psa_asymmetric_decrypt(decrypt_key, PSA_ALG_RSA_OAEP(PSA_ALG_SHA_256), + ciphertext, ciphertext_len, + NULL, 0, decrypted, sizeof(decrypted), NULL); + if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, + "psa_asymmetric_decrypt rejects NULL output_length") != TEST_OK) { + goto cleanup; + } + + st = psa_asymmetric_decrypt(decrypt_key, PSA_ALG_RSA_OAEP(PSA_ALG_SHA_256), + ciphertext, ciphertext_len, + NULL, 0, decrypted, sizeof(decrypted), + &decrypted_len); + if (check_status(st, "psa_asymmetric_decrypt(RSA OAEP)") != TEST_OK) { + goto cleanup; + } + if (check_true(decrypted_len == sizeof(plaintext) - 1, + "psa_asymmetric_decrypt(RSA OAEP) length") != TEST_OK) { + goto cleanup; + } + if (check_buf_eq("psa_asymmetric_encrypt/decrypt(RSA OAEP)", + decrypted, plaintext, sizeof(plaintext) - 1) != TEST_OK) { + goto cleanup; + } + + result = TEST_OK; + +cleanup: + if (encrypt_pub_key != 0) { + psa_status_t destroy_st = psa_destroy_key(encrypt_pub_key); + if (result == TEST_OK && + check_status(destroy_st, "psa_destroy_key(RSA OAEP public)") != TEST_OK) { + result = TEST_FAIL; + } + } + if (decrypt_key != 0) { + psa_status_t destroy_st = psa_destroy_key(decrypt_key); + if (result == TEST_OK && + check_status(destroy_st, "psa_destroy_key(RSA OAEP decrypt)") != TEST_OK) { + result = TEST_FAIL; + } + } + + return result; +} + static int test_ed25519_signature_length(void) { static const uint8_t hash[64] = { @@ -2917,6 +3032,12 @@ int main(int argc, char** argv) if (only == NULL || strcmp(only, "asym_ecc") == 0) { if (run_named_test("asym_ecc", test_asym_ecc) == TEST_FAIL) return TEST_FAIL; } + if (only == NULL || strcmp(only, "asym_rsa_oaep_policy") == 0) { + if (run_named_test("asym_rsa_oaep_policy", + test_asym_rsa_oaep_usage_policy) == 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_FAIL) { return TEST_FAIL; From e217aff0ea322a7441af5b4654ac340be084d349 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 10 Apr 2026 08:00:10 +0200 Subject: [PATCH 19/38] Add KDF verify_key policy coverage F/2412 --- test/psa_server/psa_api_test.c | 230 +++++++++++++++++++++++++++++++++ 1 file changed, 230 insertions(+) diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index 7455ebf..d5811d8 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -2317,6 +2317,231 @@ static int test_kdf_input_key_policy(void) return ret; } +static int test_kdf_verify_key_policy(void) +{ + static const uint8_t hkdf_secret[] = { + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f + }; + static const uint8_t hkdf_info[] = "kdf-verify-key"; + static const uint8_t pbkdf2_password[] = "password"; + static const uint8_t pbkdf2_salt[] = "salt"; + uint8_t hkdf_expected[16]; + uint8_t pbkdf2_expected[16]; + psa_key_derivation_operation_t op = psa_key_derivation_operation_init(); + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_key_id_t hkdf_verify_key = 0; + psa_key_id_t hkdf_no_usage_key = 0; + psa_key_id_t pbkdf2_verify_key = 0; + psa_key_id_t pbkdf2_wrong_type_key = 0; + psa_status_t st; + int ret = TEST_FAIL; + + st = psa_key_derivation_setup(&op, PSA_ALG_HKDF(PSA_ALG_SHA_256)); + if (check_status(st, "psa_key_derivation_setup(HKDF verify expected)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_SECRET, + hkdf_secret, sizeof(hkdf_secret)); + if (check_status(st, "psa_key_derivation_input_bytes(SECRET verify expected)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_INFO, + hkdf_info, sizeof(hkdf_info) - 1u); + if (check_status(st, "psa_key_derivation_input_bytes(INFO verify expected)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_output_bytes(&op, hkdf_expected, sizeof(hkdf_expected)); + if (check_status(st, "psa_key_derivation_output_bytes(HKDF verify expected)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_abort(&op); + if (check_status(st, "psa_key_derivation_abort(HKDF verify expected)") != TEST_OK) { + goto cleanup; + } + op = psa_key_derivation_operation_init(); + + psa_set_key_type(&attrs, PSA_KEY_TYPE_RAW_DATA); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_VERIFY_DERIVATION); + st = psa_import_key(&attrs, hkdf_expected, sizeof(hkdf_expected), &hkdf_verify_key); + if (check_status(st, "psa_import_key(HKDF verify key)") != TEST_OK) { + goto cleanup; + } + + st = psa_key_derivation_setup(&op, PSA_ALG_HKDF(PSA_ALG_SHA_256)); + if (check_status(st, "psa_key_derivation_setup(HKDF verify key)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_SECRET, + hkdf_secret, sizeof(hkdf_secret)); + if (check_status(st, "psa_key_derivation_input_bytes(SECRET verify key)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_INFO, + hkdf_info, sizeof(hkdf_info) - 1u); + if (check_status(st, "psa_key_derivation_input_bytes(INFO verify key)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_verify_key(&op, hkdf_verify_key); + if (check_status(st, "psa_key_derivation_verify_key(HKDF verify usage)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_abort(&op); + if (check_status(st, "psa_key_derivation_abort(HKDF verify key)") != TEST_OK) { + goto cleanup; + } + op = psa_key_derivation_operation_init(); + + psa_reset_key_attributes(&attrs); + psa_set_key_type(&attrs, PSA_KEY_TYPE_RAW_DATA); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_EXPORT); + st = psa_import_key(&attrs, hkdf_expected, sizeof(hkdf_expected), &hkdf_no_usage_key); + if (check_status(st, "psa_import_key(HKDF no verify usage key)") != TEST_OK) { + goto cleanup; + } + + st = psa_key_derivation_setup(&op, PSA_ALG_HKDF(PSA_ALG_SHA_256)); + if (check_status(st, "psa_key_derivation_setup(HKDF no verify usage)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_SECRET, + hkdf_secret, sizeof(hkdf_secret)); + if (check_status(st, "psa_key_derivation_input_bytes(SECRET no verify usage)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_INFO, + hkdf_info, sizeof(hkdf_info) - 1u); + if (check_status(st, "psa_key_derivation_input_bytes(INFO no verify usage)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_verify_key(&op, hkdf_no_usage_key); + if (check_true(st == PSA_ERROR_NOT_PERMITTED, + "psa_key_derivation_verify_key rejects missing VERIFY_DERIVATION usage") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_abort(&op); + if (check_status(st, "psa_key_derivation_abort(HKDF no verify usage)") != TEST_OK) { + goto cleanup; + } + op = psa_key_derivation_operation_init(); + + st = psa_key_derivation_setup(&op, PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_256)); + if (check_status(st, "psa_key_derivation_setup(PBKDF2 verify expected)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_PASSWORD, + pbkdf2_password, sizeof(pbkdf2_password) - 1u); + if (check_status(st, "psa_key_derivation_input_bytes(PASSWORD verify expected)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_SALT, + pbkdf2_salt, sizeof(pbkdf2_salt) - 1u); + if (check_status(st, "psa_key_derivation_input_bytes(SALT verify expected)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_integer(&op, PSA_KEY_DERIVATION_INPUT_COST, 2); + if (check_status(st, "psa_key_derivation_input_integer(COST verify expected)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_output_bytes(&op, pbkdf2_expected, sizeof(pbkdf2_expected)); + if (check_status(st, "psa_key_derivation_output_bytes(PBKDF2 verify expected)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_abort(&op); + if (check_status(st, "psa_key_derivation_abort(PBKDF2 verify expected)") != TEST_OK) { + goto cleanup; + } + op = psa_key_derivation_operation_init(); + + psa_reset_key_attributes(&attrs); + psa_set_key_type(&attrs, PSA_KEY_TYPE_PASSWORD_HASH); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_VERIFY_DERIVATION); + st = psa_import_key(&attrs, pbkdf2_expected, sizeof(pbkdf2_expected), &pbkdf2_verify_key); + if (check_status(st, "psa_import_key(PBKDF2 verify key)") != TEST_OK) { + goto cleanup; + } + + st = psa_key_derivation_setup(&op, PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_256)); + if (check_status(st, "psa_key_derivation_setup(PBKDF2 verify key)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_PASSWORD, + pbkdf2_password, sizeof(pbkdf2_password) - 1u); + if (check_status(st, "psa_key_derivation_input_bytes(PASSWORD verify key)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_SALT, + pbkdf2_salt, sizeof(pbkdf2_salt) - 1u); + if (check_status(st, "psa_key_derivation_input_bytes(SALT verify key)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_integer(&op, PSA_KEY_DERIVATION_INPUT_COST, 2); + if (check_status(st, "psa_key_derivation_input_integer(COST verify key)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_verify_key(&op, pbkdf2_verify_key); + if (check_status(st, "psa_key_derivation_verify_key(PBKDF2 password hash)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_abort(&op); + if (check_status(st, "psa_key_derivation_abort(PBKDF2 verify key)") != TEST_OK) { + goto cleanup; + } + op = psa_key_derivation_operation_init(); + + psa_reset_key_attributes(&attrs); + psa_set_key_type(&attrs, PSA_KEY_TYPE_RAW_DATA); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_VERIFY_DERIVATION); + st = psa_import_key(&attrs, pbkdf2_expected, sizeof(pbkdf2_expected), + &pbkdf2_wrong_type_key); + if (check_status(st, "psa_import_key(PBKDF2 wrong expected key type)") != TEST_OK) { + goto cleanup; + } + + st = psa_key_derivation_setup(&op, PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_256)); + if (check_status(st, "psa_key_derivation_setup(PBKDF2 wrong expected key type)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_PASSWORD, + pbkdf2_password, sizeof(pbkdf2_password) - 1u); + if (check_status(st, "psa_key_derivation_input_bytes(PASSWORD wrong expected key type)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_SALT, + pbkdf2_salt, sizeof(pbkdf2_salt) - 1u); + if (check_status(st, "psa_key_derivation_input_bytes(SALT wrong expected key type)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_input_integer(&op, PSA_KEY_DERIVATION_INPUT_COST, 2); + if (check_status(st, "psa_key_derivation_input_integer(COST wrong expected key type)") != TEST_OK) { + goto cleanup; + } + st = psa_key_derivation_verify_key(&op, pbkdf2_wrong_type_key); + if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, + "psa_key_derivation_verify_key rejects non-PASSWORD_HASH PBKDF2 expected key") != TEST_OK) { + goto cleanup; + } + + ret = TEST_OK; + +cleanup: + (void)psa_key_derivation_abort(&op); + if (pbkdf2_wrong_type_key != 0) { + (void)psa_destroy_key(pbkdf2_wrong_type_key); + } + if (pbkdf2_verify_key != 0) { + (void)psa_destroy_key(pbkdf2_verify_key); + } + if (hkdf_no_usage_key != 0) { + (void)psa_destroy_key(hkdf_no_usage_key); + } + if (hkdf_verify_key != 0) { + (void)psa_destroy_key(hkdf_verify_key); + } + psa_reset_key_attributes(&attrs); + return ret; +} + static int test_kdf_key_agreement_policy(void) { uint8_t peer_pub[128]; @@ -3067,6 +3292,11 @@ int main(int argc, char** argv) return TEST_FAIL; } } + if (only == NULL || strcmp(only, "kdf_verify_key_policy") == 0) { + if (run_named_test("kdf_verify_key_policy", test_kdf_verify_key_policy) == TEST_FAIL) { + return TEST_FAIL; + } + } if (only == NULL || strcmp(only, "kdf_key_agreement_policy") == 0) { if (run_named_test("kdf_key_agreement_policy", test_kdf_key_agreement_policy) == TEST_FAIL) { From 3127df7307342a6585f5b0f17b16e52850e6a42d Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 10 Apr 2026 08:02:05 +0200 Subject: [PATCH 20/38] Add PSA generate key public type test F/2413 --- test/psa_server/psa_api_test.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index d5811d8..cb4e31b 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -1841,6 +1841,28 @@ static int test_asym_ecc(void) return TEST_OK; } +static int test_generate_key_rejects_public_key_type(void) +{ + psa_key_id_t key_id = PSA_KEY_ID_NULL; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_status_t st; + + psa_set_key_type(&attrs, PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1)); + psa_set_key_bits(&attrs, 256); + + st = psa_generate_key(&attrs, &key_id); + if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, + "psa_generate_key rejects ECC public key type") != TEST_OK) { + return TEST_FAIL; + } + if (check_true(key_id == PSA_KEY_ID_NULL, + "psa_generate_key leaves key id null on public key type") != TEST_OK) { + return TEST_FAIL; + } + + return TEST_OK; +} + static int test_asym_rsa_oaep_usage_policy(void) { static const uint8_t plaintext[] = "psa rsa oaep"; @@ -3257,6 +3279,12 @@ int main(int argc, char** argv) if (only == NULL || strcmp(only, "asym_ecc") == 0) { if (run_named_test("asym_ecc", test_asym_ecc) == TEST_FAIL) return TEST_FAIL; } + if (only == NULL || strcmp(only, "generate_key_rejects_public_key_type") == 0) { + if (run_named_test("generate_key_rejects_public_key_type", + test_generate_key_rejects_public_key_type) == TEST_FAIL) { + return TEST_FAIL; + } + } if (only == NULL || strcmp(only, "asym_rsa_oaep_policy") == 0) { if (run_named_test("asym_rsa_oaep_policy", test_asym_rsa_oaep_usage_policy) == TEST_FAIL) { From 50f6c0361dc27a9d23a3acee2799c81efd75f50b Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 10 Apr 2026 08:05:30 +0200 Subject: [PATCH 21/38] Zero AEAD stack AES state F/2418 --- src/psa_aead.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/psa_aead.c b/src/psa_aead.c index 58fa794..a6f4efe 100644 --- a/src/psa_aead.c +++ b/src/psa_aead.c @@ -502,6 +502,7 @@ static psa_status_t wolfpsa_aead_encrypt_final(wolfpsa_aead_ctx_t *ctx, ctx->aad, (word32)ctx->aad_length); } wc_AesFree(&aes); + wc_ForceZero(&aes, sizeof(aes)); if (ret != 0) { return wc_error_to_psa_status(ret); } @@ -523,6 +524,7 @@ static psa_status_t wolfpsa_aead_encrypt_final(wolfpsa_aead_ctx_t *ctx, ctx->aad, (word32)ctx->aad_length); } wc_AesFree(&aes); + wc_ForceZero(&aes, sizeof(aes)); if (ret != 0) { return wc_error_to_psa_status(ret); } @@ -607,6 +609,7 @@ static psa_status_t wolfpsa_aead_decrypt_final(wolfpsa_aead_ctx_t *ctx, ctx->aad, (word32)ctx->aad_length); } wc_AesFree(&aes); + wc_ForceZero(&aes, sizeof(aes)); if (ret == AES_GCM_AUTH_E || ret == MAC_CMP_FAILED_E) { return PSA_ERROR_INVALID_SIGNATURE; } @@ -631,6 +634,7 @@ static psa_status_t wolfpsa_aead_decrypt_final(wolfpsa_aead_ctx_t *ctx, ctx->aad, (word32)ctx->aad_length); } wc_AesFree(&aes); + wc_ForceZero(&aes, sizeof(aes)); if (ret == AES_CCM_AUTH_E || ret == MAC_CMP_FAILED_E) { return PSA_ERROR_INVALID_SIGNATURE; } From c0a069eb4da6909326bf4cb8c19370d62cce74c1 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 10 Apr 2026 08:06:49 +0200 Subject: [PATCH 22/38] Zero AEAD context before free F/2419 --- src/psa_aead.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/psa_aead.c b/src/psa_aead.c index a6f4efe..0a40f7e 100644 --- a/src/psa_aead.c +++ b/src/psa_aead.c @@ -873,6 +873,7 @@ psa_status_t psa_aead_abort(psa_aead_operation_t *operation) wc_ForceZero(ctx->key, ctx->key_length); XFREE(ctx->key, NULL, DYNAMIC_TYPE_TMP_BUFFER); } + wc_ForceZero(ctx, sizeof(*ctx)); XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER); operation->opaque = (uintptr_t)NULL; } From 7f6b0cc0141f6ecc091f03ededa31a78593bbf40 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 10 Apr 2026 08:10:01 +0200 Subject: [PATCH 23/38] Fix ChaCha20-Poly1305 multipart finish buffers F/2054 --- src/psa_aead.c | 25 ++++++- test/psa_server/psa_api_test.c | 128 +++++++++++++++++++++++++++++++++ 2 files changed, 151 insertions(+), 2 deletions(-) diff --git a/src/psa_aead.c b/src/psa_aead.c index 0a40f7e..15b8699 100644 --- a/src/psa_aead.c +++ b/src/psa_aead.c @@ -459,6 +459,7 @@ static psa_status_t wolfpsa_aead_encrypt_final(wolfpsa_aead_ctx_t *ctx, size_t *tag_length) { int ret; + size_t chacha_ciphertext_size = 0; if (ciphertext == NULL || ciphertext_length == NULL || tag == NULL || tag_length == NULL) { @@ -475,6 +476,13 @@ static psa_status_t wolfpsa_aead_encrypt_final(wolfpsa_aead_ctx_t *ctx, return PSA_ERROR_INVALID_ARGUMENT; } + if (PSA_ALG_AEAD_EQUAL(ctx->alg, PSA_ALG_CHACHA20_POLY1305)) { + if (ctx->input_length > SIZE_MAX - ctx->tag_length) { + return PSA_ERROR_BUFFER_TOO_SMALL; + } + chacha_ciphertext_size = ctx->input_length + ctx->tag_length; + } + if (ciphertext_size < ctx->input_length) { return PSA_ERROR_BUFFER_TOO_SMALL; } @@ -531,18 +539,31 @@ static psa_status_t wolfpsa_aead_encrypt_final(wolfpsa_aead_ctx_t *ctx, } else if (PSA_ALG_AEAD_EQUAL(ctx->alg, PSA_ALG_CHACHA20_POLY1305)) { size_t out_len = 0; + uint8_t *tmp = (uint8_t *)XMALLOC(chacha_ciphertext_size, NULL, + DYNAMIC_TYPE_TMP_BUFFER); + if (tmp == NULL) { + return PSA_ERROR_INSUFFICIENT_MEMORY; + } ret = psa_chacha20_poly1305_encrypt(ctx->key, ctx->key_length, ctx->alg, ctx->nonce, ctx->nonce_length, ctx->aad, ctx->aad_length, ctx->input, ctx->input_length, - ciphertext, ciphertext_size, &out_len); + tmp, chacha_ciphertext_size, + &out_len); if (ret != 0) { + wc_ForceZero(tmp, chacha_ciphertext_size); + XFREE(tmp, NULL, DYNAMIC_TYPE_TMP_BUFFER); return (psa_status_t)ret; } if (out_len < ctx->tag_length) { + wc_ForceZero(tmp, chacha_ciphertext_size); + XFREE(tmp, NULL, DYNAMIC_TYPE_TMP_BUFFER); return PSA_ERROR_GENERIC_ERROR; } - XMEMCPY(tag, ciphertext + ctx->input_length, ctx->tag_length); + XMEMCPY(ciphertext, tmp, ctx->input_length); + XMEMCPY(tag, tmp + ctx->input_length, ctx->tag_length); + wc_ForceZero(tmp, chacha_ciphertext_size); + XFREE(tmp, NULL, DYNAMIC_TYPE_TMP_BUFFER); } else { return PSA_ERROR_NOT_SUPPORTED; diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index cb4e31b..8928060 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -1751,6 +1751,128 @@ static int test_chacha20_import_rejects_invalid_key_size(void) return TEST_OK; } +static int test_chacha20_poly1305_multipart_finish_split_buffers(void) +{ + static const uint8_t key[32] = { + 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, + 0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f, + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97, + 0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f + }; + static const uint8_t nonce[12] = { + 0x07,0x00,0x00,0x00,0x40,0x41,0x42,0x43, + 0x44,0x45,0x46,0x47 + }; + static const uint8_t aad[12] = { + 0x50,0x51,0x52,0x53,0xc0,0xc1,0xc2,0xc3, + 0xc4,0xc5,0xc6,0xc7 + }; + static const uint8_t plaintext[16] = { + 0x4c,0x61,0x64,0x69,0x65,0x73,0x20,0x61, + 0x6e,0x64,0x20,0x47,0x65,0x6e,0x74,0x6c + }; + uint8_t ciphertext[sizeof(plaintext)]; + uint8_t tag[16]; + uint8_t combined[sizeof(plaintext) + sizeof(tag)]; + uint8_t update_out[sizeof(plaintext)]; + size_t ciphertext_len = 0; + size_t tag_len = 0; + size_t combined_len = 0; + size_t update_len = 0; + psa_key_id_t key_id = 0; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_aead_operation_t op = psa_aead_operation_init(); + psa_status_t st; + int ret = TEST_OK; + + psa_set_key_type(&attrs, PSA_KEY_TYPE_CHACHA20); + 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(ChaCha20 multipart)") != TEST_OK) { + return TEST_FAIL; + } + + st = psa_aead_encrypt_setup(&op, key_id, PSA_ALG_CHACHA20_POLY1305); + if (check_status(st, "psa_aead_encrypt_setup(ChaCha20 multipart)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + st = psa_aead_set_nonce(&op, nonce, sizeof(nonce)); + if (check_status(st, "psa_aead_set_nonce(ChaCha20 multipart)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + st = psa_aead_update_ad(&op, aad, sizeof(aad)); + if (check_status(st, "psa_aead_update_ad(ChaCha20 multipart)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + st = psa_aead_update(&op, plaintext, sizeof(plaintext), + update_out, sizeof(update_out), &update_len); + if (check_status(st, "psa_aead_update(ChaCha20 multipart)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + if (check_true(update_len == 0, + "psa_aead_update(ChaCha20 multipart) length") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + + st = psa_aead_finish(&op, ciphertext, sizeof(ciphertext), &ciphertext_len, + tag, sizeof(tag), &tag_len); + if (check_status(st, "psa_aead_finish(ChaCha20 split buffers)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + if (check_true(ciphertext_len == sizeof(ciphertext), + "psa_aead_finish(ChaCha20 ciphertext length)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + if (check_true(tag_len == sizeof(tag), + "psa_aead_finish(ChaCha20 tag length)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + + st = psa_aead_encrypt(key_id, PSA_ALG_CHACHA20_POLY1305, + nonce, sizeof(nonce), + aad, sizeof(aad), + plaintext, sizeof(plaintext), + combined, sizeof(combined), &combined_len); + if (check_status(st, "psa_aead_encrypt(ChaCha20 reference)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + if (check_true(combined_len == sizeof(combined), + "psa_aead_encrypt(ChaCha20 reference length)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + if (check_buf_eq("psa_aead_finish(ChaCha20 ciphertext matches reference)", + ciphertext, combined, sizeof(ciphertext)) != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + if (check_buf_eq("psa_aead_finish(ChaCha20 tag matches reference)", + tag, combined + sizeof(ciphertext), sizeof(tag)) != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + +cleanup: + psa_aead_abort(&op); + st = psa_destroy_key(key_id); + if (check_status(st, "psa_destroy_key(ChaCha20 multipart)") != TEST_OK) { + return TEST_FAIL; + } + return ret; +} + static int test_asym_ecc(void) { static const uint8_t msg[] = "psa ecc sign"; @@ -3276,6 +3398,12 @@ int main(int argc, char** argv) return TEST_FAIL; } } + if (only == NULL || strcmp(only, "chacha20_multipart_split_buffers") == 0) { + if (run_named_test("chacha20_multipart_split_buffers", + test_chacha20_poly1305_multipart_finish_split_buffers) == TEST_FAIL) { + return TEST_FAIL; + } + } if (only == NULL || strcmp(only, "asym_ecc") == 0) { if (run_named_test("asym_ecc", test_asym_ecc) == TEST_FAIL) return TEST_FAIL; } From ce2e7cf4fc01e9edd5b6cac274e82e73c525d197 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 10 Apr 2026 08:14:13 +0200 Subject: [PATCH 24/38] Guard volatile key ID wraparound F/2055 --- src/psa_key_storage.c | 13 ++++++++ test/psa_server/psa_api_test.c | 60 ++++++++++++++++++++++++++++++++++ wolfpsa.map | 2 ++ wolfpsa/psa_key_storage.h | 2 ++ 4 files changed, 77 insertions(+) diff --git a/src/psa_key_storage.c b/src/psa_key_storage.c index d1ec6b6..87e9066 100644 --- a/src/psa_key_storage.c +++ b/src/psa_key_storage.c @@ -568,6 +568,16 @@ void psa_key_storage_cleanup(void) g_key_storage_initialized = 0; } +psa_key_id_t wolfpsa_test_get_next_key_id(void) +{ + return g_next_key_id; +} + +void wolfpsa_test_set_next_key_id(psa_key_id_t key_id) +{ + g_next_key_id = key_id; +} + /* Check if the key storage is initialized */ static psa_status_t psa_key_storage_check_init(void) { @@ -873,6 +883,9 @@ psa_status_t psa_import_key( *key_id = attr_id; } else { + if (g_next_key_id == PSA_KEY_ID_NULL) { + return PSA_ERROR_INSUFFICIENT_STORAGE; + } *key_id = g_next_key_id++; } } diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index 8928060..6104a22 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -38,6 +38,9 @@ typedef int (*test_fn_t)(void); static int tests_passed = 0; static int tests_skipped = 0; +extern psa_key_id_t wolfpsa_test_get_next_key_id(void); +extern void wolfpsa_test_set_next_key_id(psa_key_id_t key_id); + static int check_status(psa_status_t st, const char* what) { if (st != PSA_SUCCESS) { @@ -1985,6 +1988,57 @@ static int test_generate_key_rejects_public_key_type(void) return TEST_OK; } +static int test_import_key_rejects_wrapped_volatile_key_id(void) +{ + static const uint8_t aes_key[16] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f + }; + const psa_key_id_t original_next_key_id = wolfpsa_test_get_next_key_id(); + const psa_key_id_t max_key_id = (psa_key_id_t)~(psa_key_id_t)0; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_key_id_t first_key_id = PSA_KEY_ID_NULL; + psa_key_id_t second_key_id = PSA_KEY_ID_NULL; + psa_status_t st; + int result = TEST_FAIL; + + 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_set_key_algorithm(&attrs, PSA_ALG_CTR); + psa_set_key_lifetime(&attrs, PSA_KEY_LIFETIME_VOLATILE); + + wolfpsa_test_set_next_key_id(max_key_id); + + st = psa_import_key(&attrs, aes_key, sizeof(aes_key), &first_key_id); + if (check_status(st, "psa_import_key(max volatile key id)") != TEST_OK) { + goto cleanup; + } + if (check_true(first_key_id == max_key_id, + "psa_import_key uses max volatile key id before wrap") != TEST_OK) { + goto cleanup; + } + + st = psa_import_key(&attrs, aes_key, sizeof(aes_key), &second_key_id); + if (check_true(st == PSA_ERROR_INSUFFICIENT_STORAGE, + "psa_import_key rejects wrapped volatile key id") != TEST_OK) { + goto cleanup; + } + if (check_true(second_key_id == PSA_KEY_ID_NULL, + "psa_import_key leaves key id null on volatile key id wrap") != TEST_OK) { + goto cleanup; + } + + result = TEST_OK; + +cleanup: + if (first_key_id != PSA_KEY_ID_NULL) { + (void)psa_destroy_key(first_key_id); + } + wolfpsa_test_set_next_key_id(original_next_key_id); + return result; +} + static int test_asym_rsa_oaep_usage_policy(void) { static const uint8_t plaintext[] = "psa rsa oaep"; @@ -3413,6 +3467,12 @@ int main(int argc, char** argv) return TEST_FAIL; } } + if (only == NULL || strcmp(only, "import_key_volatile_key_id_wrap") == 0) { + if (run_named_test("import_key_volatile_key_id_wrap", + test_import_key_rejects_wrapped_volatile_key_id) == TEST_FAIL) { + return TEST_FAIL; + } + } if (only == NULL || strcmp(only, "asym_rsa_oaep_policy") == 0) { if (run_named_test("asym_rsa_oaep_policy", test_asym_rsa_oaep_usage_policy) == TEST_FAIL) { diff --git a/wolfpsa.map b/wolfpsa.map index 130078d..081d22d 100644 --- a/wolfpsa.map +++ b/wolfpsa.map @@ -89,6 +89,8 @@ WOLFPSA_1.0 { psa_verify_message; wolfpsa_free_key_data; wolfpsa_get_key_data; + wolfpsa_test_get_next_key_id; + wolfpsa_test_set_next_key_id; local: *; }; diff --git a/wolfpsa/psa_key_storage.h b/wolfpsa/psa_key_storage.h index 8f87e8e..b9ee545 100644 --- a/wolfpsa/psa_key_storage.h +++ b/wolfpsa/psa_key_storage.h @@ -100,6 +100,8 @@ WOLFSSL_API psa_status_t wolfpsa_get_key_data(psa_key_id_t key_id, size_t* key_data_length); WOLFSSL_API void wolfpsa_forcezero_free_key_data(uint8_t* key_data, size_t key_data_length); +WOLFSSL_API psa_key_id_t wolfpsa_test_get_next_key_id(void); +WOLFSSL_API void wolfpsa_test_set_next_key_id(psa_key_id_t key_id); #ifdef __cplusplus } From d2e4a1dbbb5aedd61fd996c4ecc8f74cde498eb1 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 10 Apr 2026 08:18:32 +0200 Subject: [PATCH 25/38] reject unsupported secondary key algorithms F/2398 --- src/psa_key_storage.c | 6 ++++ test/psa_server/psa_api_test.c | 52 ++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/src/psa_key_storage.c b/src/psa_key_storage.c index 87e9066..7300dbb 100644 --- a/src/psa_key_storage.c +++ b/src/psa_key_storage.c @@ -823,6 +823,12 @@ psa_status_t psa_import_key( attr = *attributes; + if (attr.policy.alg2 != PSA_ALG_NONE) { + wolfpsa_debug_import_reason("unsupported secondary algorithm", &attr, + data_length); + return PSA_ERROR_NOT_SUPPORTED; + } + if (attr.type == PSA_KEY_TYPE_NONE) { wolfpsa_debug_import_reason("unsupported key type", &attr, data_length); return PSA_ERROR_NOT_SUPPORTED; diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index 6104a22..c5d4e96 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -346,6 +346,52 @@ static int test_algorithm_none_rejects_key_usage(void) return ret; } +static int test_secondary_algorithm_is_not_supported(void) +{ + static const uint8_t aes_key[16] = { + 0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6, + 0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c + }; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_key_id_t key_id = PSA_KEY_ID_NULL; + 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_set_key_algorithm(&attrs, PSA_ALG_CBC_NO_PADDING); + attrs.policy.alg2 = PSA_ALG_CTR; + + st = psa_import_key(&attrs, aes_key, sizeof(aes_key), &key_id); + if (check_true(st == PSA_ERROR_NOT_SUPPORTED, + "psa_import_key rejects unsupported secondary algorithm policy") != TEST_OK) { + return TEST_FAIL; + } + if (check_true(key_id == PSA_KEY_ID_NULL, + "psa_import_key leaves key id null when alg2 is unsupported") != TEST_OK) { + return TEST_FAIL; + } + + psa_reset_key_attributes(&attrs); + 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_set_key_algorithm(&attrs, PSA_ALG_CBC_NO_PADDING); + attrs.policy.alg2 = PSA_ALG_CTR; + + st = psa_generate_key(&attrs, &key_id); + if (check_true(st == PSA_ERROR_NOT_SUPPORTED, + "psa_generate_key rejects unsupported secondary algorithm policy") != TEST_OK) { + return TEST_FAIL; + } + if (check_true(key_id == PSA_KEY_ID_NULL, + "psa_generate_key leaves key id null when alg2 is unsupported") != TEST_OK) { + return TEST_FAIL; + } + + return TEST_OK; +} + static int test_mac_error_aborts_operation(void) { static const uint8_t key[] = { @@ -3350,6 +3396,12 @@ int main(int argc, char** argv) return TEST_FAIL; } } + if (only == NULL || strcmp(only, "secondary_algorithm_not_supported") == 0) { + if (run_named_test("secondary_algorithm_not_supported", + test_secondary_algorithm_is_not_supported) == TEST_FAIL) { + return TEST_FAIL; + } + } if (only == NULL || strcmp(only, "cipher_cbc") == 0) { if (run_named_test("cipher_cbc", test_cipher_cbc) == TEST_FAIL) return TEST_FAIL; } From 8c509625dd0c2eb96a7e05884fe277a6be4f8977 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 10 Apr 2026 08:22:17 +0200 Subject: [PATCH 26/38] Initialize wolfCrypt from psa_crypto_init F/2399 --- src/psa_crypto.c | 14 ++++++ test/Makefile | 6 ++- test/psa_server/psa_crypto_init_test.c | 62 ++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 test/psa_server/psa_crypto_init_test.c diff --git a/src/psa_crypto.c b/src/psa_crypto.c index 8bce30c..f68fcf5 100644 --- a/src/psa_crypto.c +++ b/src/psa_crypto.c @@ -29,6 +29,8 @@ #include #include "psa_trace.h" +#include +#include static int g_psa_crypto_initialized = 0; @@ -39,7 +41,19 @@ int wolfPSA_CryptoIsInitialized(void) psa_status_t psa_crypto_init(void) { + int ret; + wolfpsa_trace("psa_crypto_init()"); + + if (g_psa_crypto_initialized) { + return PSA_SUCCESS; + } + + ret = wolfCrypt_Init(); + if (ret != 0) { + return wc_error_to_psa_status(ret); + } + g_psa_crypto_initialized = 1; return PSA_SUCCESS; } diff --git a/test/Makefile b/test/Makefile index a8105f2..ce44e70 100644 --- a/test/Makefile +++ b/test/Makefile @@ -57,13 +57,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_des3_stack_scrub_test psa_ecc_bit_inference_test psa_ecc_curve_id_test psa_random_size_test psa_rsa_pss_interop_test psa_tls_client wolfcrypt-psa-benchmark +BINARIES = psa_api_test psa_crypto_init_test psa_des3_stack_scrub_test psa_ecc_bit_inference_test psa_ecc_curve_id_test psa_random_size_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_CRYPTO_INIT_TEST_OBJS = psa_server/psa_crypto_init_test.o PSA_DES3_STACK_SCRUB_TEST_OBJS = psa_server/psa_des3_stack_scrub_test.o PSA_ECC_BIT_INFERENCE_TEST_OBJS = psa_server/psa_ecc_bit_inference_test.o PSA_ECC_CURVE_ID_TEST_OBJS = psa_server/psa_ecc_curve_id_test.o @@ -86,6 +87,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_crypto_init_test: require-wolfssl-lib $(PSA_CRYPTO_INIT_TEST_OBJS) + $(CC) $(CFLAGS) -o $@ $(PSA_CRYPTO_INIT_TEST_OBJS) ../libwolfpsa.a $(LDFLAGS) -L../../wolfssl/src/.libs -lwolfssl -lpthread -lm $(RPATH_WOLFSSL) -Wl,--wrap=wolfCrypt_Init + psa_des3_stack_scrub_test: require-wolfssl-lib $(PSA_DES3_STACK_SCRUB_TEST_OBJS) $(CC) $(CFLAGS) -o $@ $(PSA_DES3_STACK_SCRUB_TEST_OBJS) $(LDFLAGS) $(LDLIBS) $(RPATH_WOLFPSA) $(RPATH_WOLFSSL) diff --git a/test/psa_server/psa_crypto_init_test.c b/test/psa_server/psa_crypto_init_test.c new file mode 100644 index 0000000..ce01627 --- /dev/null +++ b/test/psa_server/psa_crypto_init_test.c @@ -0,0 +1,62 @@ +#include "psa_api_test_user_settings.h" + +#ifndef WOLFSSL_USER_SETTINGS +#define WOLFSSL_USER_SETTINGS +#endif + +#include +#include + +#include + +#include + +static int g_wolfcrypt_init_calls; +static int g_wolfcrypt_init_result; + +int __wrap_wolfCrypt_Init(void) +{ + g_wolfcrypt_init_calls++; + return g_wolfcrypt_init_result; +} + +static int expect_status(const char* label, psa_status_t status, psa_status_t expected) +{ + if (status != expected) { + printf("FAIL %s status=%d expected=%d\n", label, (int)status, (int)expected); + return 1; + } + + return 0; +} + +int main(void) +{ + psa_status_t st; + + g_wolfcrypt_init_calls = 0; + g_wolfcrypt_init_result = RNG_FAILURE_E; + + st = psa_crypto_init(); + if (expect_status("psa_crypto_init rng failure", st, + PSA_ERROR_INSUFFICIENT_ENTROPY) != 0) { + return 1; + } + if (g_wolfcrypt_init_calls != 1) { + printf("FAIL wolfCrypt_Init calls=%d expected=1\n", g_wolfcrypt_init_calls); + return 1; + } + + g_wolfcrypt_init_result = 0; + st = psa_crypto_init(); + if (expect_status("psa_crypto_init success", st, PSA_SUCCESS) != 0) { + return 1; + } + if (g_wolfcrypt_init_calls != 2) { + printf("FAIL wolfCrypt_Init calls=%d expected=2\n", g_wolfcrypt_init_calls); + return 1; + } + + printf("PSA crypto init test: OK\n"); + return 0; +} From bfcd563f6e8560ebe1ebdf4a233e62891a8bd017 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 10 Apr 2026 08:24:10 +0200 Subject: [PATCH 27/38] Force zero key buffer on read failure F/2049 --- 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 7300dbb..b37b4d7 100644 --- a/src/psa_key_storage.c +++ b/src/psa_key_storage.c @@ -776,6 +776,7 @@ psa_status_t wolfpsa_get_key_data(psa_key_id_t key_id, wolfPSA_Store_Close(store); store = NULL; if (ret != (int)*key_data_length) { + wc_ForceZero(*key_data, *key_data_length); XFREE(*key_data, NULL, DYNAMIC_TYPE_TMP_BUFFER); *key_data = NULL; *key_data_length = 0; From ec67dc4193d4c92d1dffa812e132abf2115e9555 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 10 Apr 2026 08:29:28 +0200 Subject: [PATCH 28/38] Validate stored key lengths before reads F/2050 --- src/psa_key_storage.c | 31 ++++++++- test/psa_server/psa_api_test.c | 113 +++++++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+), 2 deletions(-) diff --git a/src/psa_key_storage.c b/src/psa_key_storage.c index b37b4d7..d21850d 100644 --- a/src/psa_key_storage.c +++ b/src/psa_key_storage.c @@ -31,6 +31,7 @@ #include #include #include "psa_trace.h" +#include "psa_size.h" #include #include #include @@ -39,6 +40,7 @@ #include #include #include +#include #ifdef WOLFPSA_DEBUG_IMPORT #include @@ -147,6 +149,15 @@ static psa_status_t psa_wc_error_to_psa_status(int ret) return status; } +static psa_status_t wolfpsa_validate_stored_key_data_length(size_t key_data_length) +{ + if (key_data_length == 0 || key_data_length > (size_t)INT_MAX) { + return PSA_ERROR_DATA_INVALID; + } + + return PSA_SUCCESS; +} + static psa_key_bits_t wolfpsa_ecc_bits_from_length(psa_ecc_family_t family, size_t length_bytes) { @@ -760,9 +771,10 @@ psa_status_t wolfpsa_get_key_data(psa_key_id_t key_id, } XMEMCPY(key_data_length, header + attr_length, sizeof(size_t)); - if (*key_data_length == 0) { + status = wolfpsa_validate_stored_key_data_length(*key_data_length); + if (status != PSA_SUCCESS) { wolfPSA_Store_Close(store); - return PSA_ERROR_DATA_INVALID; + return status; } *key_data = (uint8_t*)XMALLOC(*key_data_length, NULL, @@ -1253,6 +1265,11 @@ psa_status_t psa_export_key( /* Get key data length */ XMEMCPY(&key_data_length, header + attr_length, sizeof(size_t)); + status = wolfpsa_validate_stored_key_data_length(key_data_length); + if (status != PSA_SUCCESS) { + wolfPSA_Store_Close(store); + return status; + } /* Check if the output buffer is large enough */ if (data_size < key_data_length) { @@ -1349,6 +1366,11 @@ psa_status_t psa_export_public_key( } XMEMCPY(&key_data_length, header + attr_length, sizeof(size_t)); + status = wolfpsa_validate_stored_key_data_length(key_data_length); + if (status != PSA_SUCCESS) { + wolfPSA_Store_Close(store); + return status; + } key_data = (uint8_t*)XMALLOC(key_data_length, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (key_data == NULL) { @@ -1726,6 +1748,11 @@ psa_status_t psa_copy_key( psa_get_key_usage_flags(&dst_attr); XMEMCPY(&key_data_length, header + attr_length, sizeof(size_t)); + status = wolfpsa_validate_stored_key_data_length(key_data_length); + if (status != PSA_SUCCESS) { + wolfPSA_Store_Close(store); + return status; + } buffer = (uint8_t*)XMALLOC(key_data_length, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (buffer == NULL) { wolfPSA_Store_Close(store); diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index c5d4e96..1fe4869 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include "psa_aead_internal.h" @@ -668,6 +669,52 @@ static void setup_aes_key_attrs(psa_key_attributes_t* attrs, psa_key_usage_t usa psa_set_key_lifetime(attrs, lifetime); } +static int get_persistent_key_store_path(psa_key_id_t key_id, char* path, + size_t path_size) +{ + const char* root = getenv("WOLFPSA_TOKEN_PATH"); + + if (root == NULL) { + root = "./.store"; + } + + return snprintf(path, path_size, "%s/psa_key_%016lx_%016lx", root, + (unsigned long)key_id, 0ul); +} + +static int overwrite_persistent_key_data_length(psa_key_id_t key_id, + size_t key_data_length) +{ + char path[256]; + FILE* file; + long offset; + size_t written; + int len; + + len = get_persistent_key_store_path(key_id, path, sizeof(path)); + if (len <= 0 || (size_t)len >= sizeof(path)) { + return TEST_FAIL; + } + + offset = (long)(sizeof(psa_key_type_t) + sizeof(psa_key_bits_t) + + sizeof(psa_key_usage_t) + sizeof(psa_algorithm_t) + + sizeof(psa_key_lifetime_t)); + + file = fopen(path, "r+b"); + if (file == NULL) { + return TEST_FAIL; + } + + if (fseek(file, offset, SEEK_SET) != 0) { + fclose(file); + return TEST_FAIL; + } + + written = fwrite(&key_data_length, sizeof(key_data_length), 1, file); + fclose(file); + return written == 1 ? TEST_OK : TEST_FAIL; +} + static int test_copy_key_copies_material_and_attributes(void) { static const uint8_t key[16] = { @@ -758,6 +805,66 @@ static int test_copy_key_copies_material_and_attributes(void) return ret; } +static int test_export_key_rejects_oversized_persistent_length(void) +{ + static const uint8_t key[16] = { + 0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6, + 0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c + }; + uint8_t exported[sizeof(key)]; + size_t exported_len = 0; + psa_key_id_t key_id = 0; + psa_key_id_t persistent_id = PSA_KEY_ID_USER_MIN + 2406u; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_status_t st; + size_t oversized_length; + int ret = TEST_FAIL; + + if (sizeof(size_t) <= sizeof(int)) { + return TEST_SKIPPED; + } + + (void)psa_destroy_key(persistent_id); + + setup_aes_key_attrs(&attrs, + PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_ENCRYPT, + PSA_ALG_CBC_NO_PADDING, + PSA_KEY_LIFETIME_PERSISTENT); + psa_set_key_id(&attrs, persistent_id); + st = psa_import_key(&attrs, key, sizeof(key), &key_id); + if (check_status(st, "psa_import_key(AES persistent oversized length)") != TEST_OK) { + goto cleanup; + } + + oversized_length = (size_t)UINT32_MAX + 17u; + if (check_true(oversized_length > (size_t)INT_MAX, + "oversized persistent length exceeds INT_MAX") != TEST_OK) { + goto cleanup; + } + if (overwrite_persistent_key_data_length(key_id, oversized_length) != TEST_OK) { + printf("FAIL: overwrite_persistent_key_data_length\n"); + goto cleanup; + } + + st = psa_export_key(key_id, exported, sizeof(exported), &exported_len); + if (check_true(st == PSA_ERROR_DATA_INVALID, + "psa_export_key rejects oversized persistent length") != TEST_OK) { + goto cleanup; + } + + ret = TEST_OK; + +cleanup: + psa_reset_key_attributes(&attrs); + if (key_id != 0) { + (void)psa_destroy_key(key_id); + } + else { + (void)psa_destroy_key(persistent_id); + } + return ret; +} + static int test_copy_key_requires_copy_usage_flag(void) { static const uint8_t key[16] = { @@ -3423,6 +3530,12 @@ int main(int argc, char** argv) return TEST_FAIL; } } + if (only == NULL || strcmp(only, "export_oversized_persistent_length") == 0) { + if (run_named_test("export_oversized_persistent_length", + test_export_key_rejects_oversized_persistent_length) == TEST_FAIL) { + return TEST_FAIL; + } + } if (only == NULL || strcmp(only, "copy_key_success") == 0) { if (run_named_test("copy_key_success", test_copy_key_copies_material_and_attributes) == TEST_FAIL) { From e2e910b79c8f1390b95b68d512d97265139b370b Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 10 Apr 2026 10:26:30 +0200 Subject: [PATCH 29/38] zero hash ctx before abort free F/2420 --- src/psa_hash_engine.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/psa_hash_engine.c b/src/psa_hash_engine.c index 4477469..7329f05 100644 --- a/src/psa_hash_engine.c +++ b/src/psa_hash_engine.c @@ -688,6 +688,7 @@ psa_status_t psa_hash_abort(psa_hash_operation_t *operation) } psa_hash_cleanup_ctx(ctx); + wc_ForceZero(ctx, sizeof(*ctx)); XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER); operation->opaque = (uintptr_t)NULL; From 24750b857d0cce4c6391e830a9faf4870be8ed6e Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 10 Apr 2026 10:29:42 +0200 Subject: [PATCH 30/38] Require PSA init for hash entry points F/2400 --- src/psa_hash_engine.c | 14 +++++++++ test/psa_server/psa_crypto_init_test.c | 41 ++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/src/psa_hash_engine.c b/src/psa_hash_engine.c index 7329f05..ccecb39 100644 --- a/src/psa_hash_engine.c +++ b/src/psa_hash_engine.c @@ -71,6 +71,8 @@ #include #endif +extern int wolfPSA_CryptoIsInitialized(void); + typedef struct psa_hash_operation_ctx { psa_algorithm_t alg; /* Hash algorithm */ int initialized; /* Whether operation is initialized */ @@ -323,6 +325,10 @@ psa_status_t psa_hash_setup(psa_hash_operation_t *operation, wolfpsa_trace("psa_hash_setup(alg=0x%08x)", (unsigned)alg); + if (!wolfPSA_CryptoIsInitialized()) { + return PSA_ERROR_BAD_STATE; + } + if (!PSA_ALG_IS_HASH(alg)) { return PSA_ERROR_INVALID_ARGUMENT; } @@ -832,6 +838,10 @@ psa_status_t psa_hash_compute(psa_algorithm_t alg, { psa_hash_operation_t operation = PSA_HASH_OPERATION_INIT; psa_status_t status; + + if (!wolfPSA_CryptoIsInitialized()) { + return PSA_ERROR_BAD_STATE; + } /* Set up the operation */ status = psa_hash_setup(&operation, alg); @@ -867,6 +877,10 @@ psa_status_t psa_hash_compare(psa_algorithm_t alg, uint8_t computed_hash[PSA_HASH_MAX_SIZE]; size_t computed_hash_length; size_t expected_hash_size; + + if (!wolfPSA_CryptoIsInitialized()) { + return PSA_ERROR_BAD_STATE; + } /* Check if the reference hash length is valid */ if (!PSA_ALG_IS_HASH(alg)) { diff --git a/test/psa_server/psa_crypto_init_test.c b/test/psa_server/psa_crypto_init_test.c index ce01627..bd349c1 100644 --- a/test/psa_server/psa_crypto_init_test.c +++ b/test/psa_server/psa_crypto_init_test.c @@ -30,10 +30,51 @@ static int expect_status(const char* label, psa_status_t status, psa_status_t ex return 0; } +static int test_hash_requires_psa_crypto_init(void) +{ + static const uint8_t msg[] = "abc"; + static const uint8_t sha256_abc[] = { + 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, + 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23, + 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, + 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad + }; + psa_hash_operation_t op = PSA_HASH_OPERATION_INIT; + uint8_t hash[PSA_HASH_LENGTH(PSA_ALG_SHA_256)]; + size_t hash_length = 0; + psa_status_t st; + + st = psa_hash_setup(&op, PSA_ALG_SHA_256); + if (expect_status("psa_hash_setup before init", st, + PSA_ERROR_BAD_STATE) != 0) { + return 1; + } + + st = psa_hash_compute(PSA_ALG_SHA_256, msg, sizeof(msg) - 1, hash, + sizeof(hash), &hash_length); + if (expect_status("psa_hash_compute before init", st, + PSA_ERROR_BAD_STATE) != 0) { + return 1; + } + + st = psa_hash_compare(PSA_ALG_SHA_256, msg, sizeof(msg) - 1, sha256_abc, + sizeof(sha256_abc)); + if (expect_status("psa_hash_compare before init", st, + PSA_ERROR_BAD_STATE) != 0) { + return 1; + } + + return 0; +} + int main(void) { psa_status_t st; + if (test_hash_requires_psa_crypto_init() != 0) { + return 1; + } + g_wolfcrypt_init_calls = 0; g_wolfcrypt_init_result = RNG_FAILURE_E; From fd989c2537cce460678f7afd52410e021ca22289 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 10 Apr 2026 10:37:40 +0200 Subject: [PATCH 31/38] Guard psa_generate_random before init F/2403 --- src/psa_random.c | 8 +++++++- test/psa_server/psa_crypto_init_test.c | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/psa_random.c b/src/psa_random.c index 1ccc747..e93db88 100644 --- a/src/psa_random.c +++ b/src/psa_random.c @@ -39,12 +39,18 @@ #include #include +extern int wolfPSA_CryptoIsInitialized(void); + /* Generate random bytes */ psa_status_t psa_generate_random(uint8_t *output, size_t output_size) { int ret; WC_RNG rng; - + + if (!wolfPSA_CryptoIsInitialized()) { + return PSA_ERROR_BAD_STATE; + } + if (output == NULL && output_size > 0) { return PSA_ERROR_INVALID_ARGUMENT; } diff --git a/test/psa_server/psa_crypto_init_test.c b/test/psa_server/psa_crypto_init_test.c index bd349c1..e0b4002 100644 --- a/test/psa_server/psa_crypto_init_test.c +++ b/test/psa_server/psa_crypto_init_test.c @@ -67,6 +67,20 @@ static int test_hash_requires_psa_crypto_init(void) return 0; } +static int test_random_requires_psa_crypto_init(void) +{ + uint8_t buf[16]; + psa_status_t st; + + st = psa_generate_random(buf, sizeof(buf)); + if (expect_status("psa_generate_random before init", st, + PSA_ERROR_BAD_STATE) != 0) { + return 1; + } + + return 0; +} + int main(void) { psa_status_t st; @@ -75,6 +89,10 @@ int main(void) return 1; } + if (test_random_requires_psa_crypto_init() != 0) { + return 1; + } + g_wolfcrypt_init_calls = 0; g_wolfcrypt_init_result = RNG_FAILURE_E; From dfe9540345085b8215915c37675e23d705bb4501 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 10 Apr 2026 10:40:54 +0200 Subject: [PATCH 32/38] add asymmetric algorithm mismatch regression F/2407 --- test/psa_server/psa_api_test.c | 85 ++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index 1fe4869..dff8e48 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -2119,6 +2119,85 @@ static int test_asym_ecc(void) return TEST_OK; } +static int test_asym_algorithm_mismatch_policy(void) +{ + static const uint8_t msg[] = "wolfpsa asym mismatch"; + uint8_t hash[WC_SHA256_DIGEST_SIZE]; + uint8_t peer_pub[100]; + uint8_t secret[100]; + uint8_t sig[80]; + size_t peer_pub_len = 0; + size_t secret_len = 0; + size_t sig_len = 0; + psa_key_id_t ecdsa_key = PSA_KEY_ID_NULL; + psa_key_id_t ecdh_key = PSA_KEY_ID_NULL; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_status_t st; + wc_Sha256 sha; + int ret; + + ret = wc_InitSha256(&sha); + if (ret != 0) { + printf("FAIL: wc_InitSha256 (asym mismatch) (%d)\n", ret); + return TEST_FAIL; + } + wc_Sha256Update(&sha, msg, (word32)sizeof(msg) - 1u); + wc_Sha256Final(&sha, hash); + wc_Sha256Free(&sha); + + psa_set_key_type(&attrs, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); + psa_set_key_bits(&attrs, 256); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_SIGN_HASH | PSA_KEY_USAGE_EXPORT); + psa_set_key_algorithm(&attrs, PSA_ALG_ECDSA(PSA_ALG_SHA_256)); + + st = psa_generate_key(&attrs, &ecdsa_key); + if (check_status(st, "psa_generate_key(ECDSA mismatch)") != TEST_OK) { + goto cleanup; + } + + psa_reset_key_attributes(&attrs); + psa_set_key_type(&attrs, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); + psa_set_key_bits(&attrs, 256); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_DERIVE | PSA_KEY_USAGE_EXPORT); + psa_set_key_algorithm(&attrs, PSA_ALG_ECDH); + + st = psa_generate_key(&attrs, &ecdh_key); + if (check_status(st, "psa_generate_key(ECDH mismatch)") != TEST_OK) { + goto cleanup; + } + + st = psa_export_public_key(ecdh_key, peer_pub, sizeof(peer_pub), &peer_pub_len); + if (check_status(st, "psa_export_public_key(ECDH mismatch peer)") != TEST_OK) { + goto cleanup; + } + + st = psa_raw_key_agreement(PSA_ALG_ECDH, ecdsa_key, peer_pub, peer_pub_len, + secret, sizeof(secret), &secret_len); + if (check_true(st == PSA_ERROR_NOT_PERMITTED, + "psa_raw_key_agreement rejects ECDSA key policy") != TEST_OK) { + goto cleanup; + } + + st = psa_sign_hash(ecdh_key, PSA_ALG_ECDSA(PSA_ALG_SHA_256), + hash, sizeof(hash), sig, sizeof(sig), &sig_len); + if (check_true(st == PSA_ERROR_NOT_PERMITTED, + "psa_sign_hash rejects ECDH key policy") != TEST_OK) { + goto cleanup; + } + + ret = TEST_OK; + +cleanup: + if (ecdh_key != PSA_KEY_ID_NULL) { + (void)psa_destroy_key(ecdh_key); + } + if (ecdsa_key != PSA_KEY_ID_NULL) { + (void)psa_destroy_key(ecdsa_key); + } + + return ret; +} + static int test_generate_key_rejects_public_key_type(void) { psa_key_id_t key_id = PSA_KEY_ID_NULL; @@ -3626,6 +3705,12 @@ int main(int argc, char** argv) if (only == NULL || strcmp(only, "asym_ecc") == 0) { if (run_named_test("asym_ecc", test_asym_ecc) == TEST_FAIL) return TEST_FAIL; } + if (only == NULL || strcmp(only, "asym_algorithm_mismatch_policy") == 0) { + if (run_named_test("asym_algorithm_mismatch_policy", + test_asym_algorithm_mismatch_policy) == TEST_FAIL) { + return TEST_FAIL; + } + } if (only == NULL || strcmp(only, "generate_key_rejects_public_key_type") == 0) { if (run_named_test("generate_key_rejects_public_key_type", test_generate_key_rejects_public_key_type) == TEST_FAIL) { From 71ad72909ee77b5e6c5f3723db9994b8e9ad5627 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 10 Apr 2026 10:48:51 +0200 Subject: [PATCH 33/38] Normalize empty AEAD buffers F/2425 --- src/psa_aead.c | 44 ++++-- test/psa_server/psa_api_test.c | 251 +++++++++++++++++++++++++++++++++ 2 files changed, 284 insertions(+), 11 deletions(-) diff --git a/src/psa_aead.c b/src/psa_aead.c index 15b8699..7355488 100644 --- a/src/psa_aead.c +++ b/src/psa_aead.c @@ -78,6 +78,18 @@ static psa_status_t wolfpsa_aead_append(uint8_t **buf, size_t *len, return PSA_SUCCESS; } +static const uint8_t* wolfpsa_aead_nonnull_data(const uint8_t *data, + size_t data_length) +{ + static const uint8_t empty = 0; + + if (data == NULL && data_length == 0) { + return ∅ + } + + return data; +} + static size_t wolfpsa_aead_tag_length(psa_algorithm_t alg) { return PSA_ALG_AEAD_GET_TAG_LENGTH(alg); @@ -460,6 +472,8 @@ static psa_status_t wolfpsa_aead_encrypt_final(wolfpsa_aead_ctx_t *ctx, { int ret; size_t chacha_ciphertext_size = 0; + const uint8_t *input; + const uint8_t *aad; if (ciphertext == NULL || ciphertext_length == NULL || tag == NULL || tag_length == NULL) { @@ -496,6 +510,9 @@ static psa_status_t wolfpsa_aead_encrypt_final(wolfpsa_aead_ctx_t *ctx, return PSA_ERROR_INVALID_ARGUMENT; } + input = wolfpsa_aead_nonnull_data(ctx->input, ctx->input_length); + aad = wolfpsa_aead_nonnull_data(ctx->aad, ctx->aad_length); + if (PSA_ALG_AEAD_EQUAL(ctx->alg, PSA_ALG_GCM)) { Aes aes; ret = wc_AesInit(&aes, NULL, INVALID_DEVID); @@ -503,11 +520,11 @@ static psa_status_t wolfpsa_aead_encrypt_final(wolfpsa_aead_ctx_t *ctx, ret = wc_AesGcmSetKey(&aes, ctx->key, (word32)ctx->key_length); } if (ret == 0) { - ret = wc_AesGcmEncrypt(&aes, ciphertext, ctx->input, + ret = wc_AesGcmEncrypt(&aes, ciphertext, input, (word32)ctx->input_length, ctx->nonce, (word32)ctx->nonce_length, tag, (word32)ctx->tag_length, - ctx->aad, (word32)ctx->aad_length); + aad, (word32)ctx->aad_length); } wc_AesFree(&aes); wc_ForceZero(&aes, sizeof(aes)); @@ -525,11 +542,11 @@ static psa_status_t wolfpsa_aead_encrypt_final(wolfpsa_aead_ctx_t *ctx, ret = wc_AesCcmSetKey(&aes, ctx->key, (word32)ctx->key_length); } if (ret == 0) { - ret = wc_AesCcmEncrypt(&aes, ciphertext, ctx->input, + ret = wc_AesCcmEncrypt(&aes, ciphertext, input, (word32)ctx->input_length, ctx->nonce, (word32)ctx->nonce_length, tag, (word32)ctx->tag_length, - ctx->aad, (word32)ctx->aad_length); + aad, (word32)ctx->aad_length); } wc_AesFree(&aes); wc_ForceZero(&aes, sizeof(aes)); @@ -546,8 +563,8 @@ static psa_status_t wolfpsa_aead_encrypt_final(wolfpsa_aead_ctx_t *ctx, } ret = psa_chacha20_poly1305_encrypt(ctx->key, ctx->key_length, ctx->alg, ctx->nonce, ctx->nonce_length, - ctx->aad, ctx->aad_length, - ctx->input, ctx->input_length, + aad, ctx->aad_length, + input, ctx->input_length, tmp, chacha_ciphertext_size, &out_len); if (ret != 0) { @@ -582,6 +599,8 @@ static psa_status_t wolfpsa_aead_decrypt_final(wolfpsa_aead_ctx_t *ctx, size_t tag_length) { int ret; + const uint8_t *input; + const uint8_t *aad; if (plaintext == NULL || plaintext_length == NULL || tag == NULL) { return PSA_ERROR_INVALID_ARGUMENT; @@ -616,6 +635,9 @@ static psa_status_t wolfpsa_aead_decrypt_final(wolfpsa_aead_ctx_t *ctx, return PSA_ERROR_INVALID_ARGUMENT; } + input = wolfpsa_aead_nonnull_data(ctx->input, ctx->input_length); + aad = wolfpsa_aead_nonnull_data(ctx->aad, ctx->aad_length); + if (PSA_ALG_AEAD_EQUAL(ctx->alg, PSA_ALG_GCM)) { Aes aes; ret = wc_AesInit(&aes, NULL, INVALID_DEVID); @@ -623,11 +645,11 @@ static psa_status_t wolfpsa_aead_decrypt_final(wolfpsa_aead_ctx_t *ctx, ret = wc_AesGcmSetKey(&aes, ctx->key, (word32)ctx->key_length); } if (ret == 0) { - ret = wc_AesGcmDecrypt(&aes, plaintext, ctx->input, + ret = wc_AesGcmDecrypt(&aes, plaintext, input, (word32)ctx->input_length, ctx->nonce, (word32)ctx->nonce_length, tag, (word32)tag_length, - ctx->aad, (word32)ctx->aad_length); + aad, (word32)ctx->aad_length); } wc_AesFree(&aes); wc_ForceZero(&aes, sizeof(aes)); @@ -648,11 +670,11 @@ static psa_status_t wolfpsa_aead_decrypt_final(wolfpsa_aead_ctx_t *ctx, ret = wc_AesCcmSetKey(&aes, ctx->key, (word32)ctx->key_length); } if (ret == 0) { - ret = wc_AesCcmDecrypt(&aes, plaintext, ctx->input, + ret = wc_AesCcmDecrypt(&aes, plaintext, input, (word32)ctx->input_length, ctx->nonce, (word32)ctx->nonce_length, tag, (word32)tag_length, - ctx->aad, (word32)ctx->aad_length); + aad, (word32)ctx->aad_length); } wc_AesFree(&aes); wc_ForceZero(&aes, sizeof(aes)); @@ -676,7 +698,7 @@ static psa_status_t wolfpsa_aead_decrypt_final(wolfpsa_aead_ctx_t *ctx, XMEMCPY(tmp + ctx->input_length, tag, tag_length); ret = psa_chacha20_poly1305_decrypt(ctx->key, ctx->key_length, ctx->alg, ctx->nonce, ctx->nonce_length, - ctx->aad, ctx->aad_length, + aad, ctx->aad_length, tmp, ciphertext_len, plaintext, plaintext_size, &out_len); XFREE(tmp, NULL, DYNAMIC_TYPE_TMP_BUFFER); diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index dff8e48..2d767a7 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -1583,6 +1583,251 @@ static int test_aead_gcm(void) return TEST_OK; } +static int test_aead_gcm_multipart_zero_length_inputs(void) +{ + static const uint8_t key[16] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + static const uint8_t nonce[12] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00 + }; + static const uint8_t aad[12] = { + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, + 0x18,0x19,0x1a,0x1b + }; + static const uint8_t plaintext[16] = { + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, + 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f + }; + uint8_t combined[sizeof(plaintext) + 16]; + uint8_t ciphertext[sizeof(plaintext)]; + uint8_t tag[16]; + uint8_t decrypt_out[sizeof(plaintext)]; + uint8_t update_out[sizeof(plaintext)]; + uint8_t dummy[1] = { 0 }; + size_t combined_len = 0; + size_t ciphertext_len = 0; + size_t plaintext_len = 0; + size_t tag_len = 0; + size_t update_len = 0; + psa_key_id_t key_id = 0; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_aead_operation_t op = psa_aead_operation_init(); + psa_status_t st; + int ret = TEST_OK; + + 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_GCM); + + st = psa_import_key(&attrs, key, sizeof(key), &key_id); + if (check_status(st, "psa_import_key(GCM zero-length multipart)") != TEST_OK) { + return TEST_FAIL; + } + + st = psa_aead_encrypt(key_id, PSA_ALG_GCM, + nonce, sizeof(nonce), + aad, sizeof(aad), + dummy, 0, + combined, sizeof(combined), &combined_len); + if (check_status(st, "psa_aead_encrypt(GCM zero plaintext reference)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + if (check_true(combined_len == sizeof(tag), + "psa_aead_encrypt(GCM zero plaintext reference length)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + + st = psa_aead_encrypt_setup(&op, key_id, PSA_ALG_GCM); + if (check_status(st, "psa_aead_encrypt_setup(GCM zero plaintext)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + st = psa_aead_set_nonce(&op, nonce, sizeof(nonce)); + if (check_status(st, "psa_aead_set_nonce(GCM zero plaintext)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + st = psa_aead_update_ad(&op, aad, sizeof(aad)); + if (check_status(st, "psa_aead_update_ad(GCM zero plaintext)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + st = psa_aead_update(&op, dummy, 0, update_out, sizeof(update_out), &update_len); + if (check_status(st, "psa_aead_update(GCM zero plaintext)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + if (check_true(update_len == 0, "psa_aead_update(GCM zero plaintext) length") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + st = psa_aead_finish(&op, dummy, sizeof(dummy), &ciphertext_len, + tag, sizeof(tag), &tag_len); + if (check_status(st, "psa_aead_finish(GCM zero plaintext)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + if (check_true(ciphertext_len == 0, "psa_aead_finish(GCM zero plaintext) length") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + if (check_true(tag_len == sizeof(tag), "psa_aead_finish(GCM zero plaintext) tag length") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + if (check_buf_eq("psa_aead_finish(GCM zero plaintext) tag matches reference)", + tag, combined, sizeof(tag)) != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + + st = psa_aead_decrypt_setup(&op, key_id, PSA_ALG_GCM); + if (check_status(st, "psa_aead_decrypt_setup(GCM zero plaintext)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + st = psa_aead_set_nonce(&op, nonce, sizeof(nonce)); + if (check_status(st, "psa_aead_set_nonce(GCM zero plaintext verify)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + st = psa_aead_update_ad(&op, aad, sizeof(aad)); + if (check_status(st, "psa_aead_update_ad(GCM zero plaintext verify)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + st = psa_aead_update(&op, dummy, 0, update_out, sizeof(update_out), &update_len); + if (check_status(st, "psa_aead_update(GCM zero plaintext verify)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + st = psa_aead_verify(&op, dummy, sizeof(dummy), &plaintext_len, tag, tag_len); + if (check_status(st, "psa_aead_verify(GCM zero plaintext)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + if (check_true(plaintext_len == 0, "psa_aead_verify(GCM zero plaintext) length") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + + st = psa_aead_encrypt(key_id, PSA_ALG_GCM, + nonce, sizeof(nonce), + aad, 0, + plaintext, sizeof(plaintext), + combined, sizeof(combined), &combined_len); + if (check_status(st, "psa_aead_encrypt(GCM zero aad reference)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + if (check_true(combined_len == sizeof(plaintext) + sizeof(tag), + "psa_aead_encrypt(GCM zero aad reference length)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + + st = psa_aead_encrypt_setup(&op, key_id, PSA_ALG_GCM); + if (check_status(st, "psa_aead_encrypt_setup(GCM zero aad)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + st = psa_aead_set_nonce(&op, nonce, sizeof(nonce)); + if (check_status(st, "psa_aead_set_nonce(GCM zero aad)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + st = psa_aead_update_ad(&op, aad, 0); + if (check_status(st, "psa_aead_update_ad(GCM zero aad)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + st = psa_aead_update(&op, plaintext, sizeof(plaintext), + update_out, sizeof(update_out), &update_len); + if (check_status(st, "psa_aead_update(GCM zero aad)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + if (check_true(update_len == 0, "psa_aead_update(GCM zero aad) length") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + st = psa_aead_finish(&op, ciphertext, sizeof(ciphertext), &ciphertext_len, + tag, sizeof(tag), &tag_len); + if (check_status(st, "psa_aead_finish(GCM zero aad)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + if (check_true(ciphertext_len == sizeof(ciphertext), "psa_aead_finish(GCM zero aad) length") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + if (check_true(tag_len == sizeof(tag), "psa_aead_finish(GCM zero aad) tag length") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + if (check_buf_eq("psa_aead_finish(GCM zero aad) ciphertext matches reference)", + ciphertext, combined, sizeof(ciphertext)) != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + if (check_buf_eq("psa_aead_finish(GCM zero aad) tag matches reference)", + tag, combined + sizeof(ciphertext), sizeof(tag)) != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + + st = psa_aead_decrypt_setup(&op, key_id, PSA_ALG_GCM); + if (check_status(st, "psa_aead_decrypt_setup(GCM zero aad)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + st = psa_aead_set_nonce(&op, nonce, sizeof(nonce)); + if (check_status(st, "psa_aead_set_nonce(GCM zero aad verify)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + st = psa_aead_update_ad(&op, aad, 0); + if (check_status(st, "psa_aead_update_ad(GCM zero aad verify)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + st = psa_aead_update(&op, ciphertext, ciphertext_len, + update_out, sizeof(update_out), &update_len); + if (check_status(st, "psa_aead_update(GCM zero aad verify)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + st = psa_aead_verify(&op, decrypt_out, sizeof(decrypt_out), &plaintext_len, tag, tag_len); + if (check_status(st, "psa_aead_verify(GCM zero aad)") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + if (check_true(plaintext_len == sizeof(plaintext), "psa_aead_verify(GCM zero aad) length") != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + if (check_buf_eq("psa_aead_verify(GCM zero aad)", decrypt_out, plaintext, sizeof(plaintext)) != TEST_OK) { + ret = TEST_FAIL; + goto cleanup; + } + +cleanup: + psa_aead_abort(&op); + if (key_id != 0) { + st = psa_destroy_key(key_id); + if (check_status(st, "psa_destroy_key(GCM zero-length multipart)") != TEST_OK) { + return TEST_FAIL; + } + } + return ret; +} + static int test_aead_multipart_length_overflow_rejected(void) { static const uint8_t key[16] = { @@ -3666,6 +3911,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_FAIL) return TEST_FAIL; } + if (only == NULL || strcmp(only, "aead_gcm_multipart_zero_length") == 0) { + if (run_named_test("aead_gcm_multipart_zero_length", + test_aead_gcm_multipart_zero_length_inputs) == TEST_FAIL) { + return TEST_FAIL; + } + } if (only == NULL || strcmp(only, "aead_multipart_length_overflow") == 0) { if (run_named_test("aead_multipart_length_overflow", test_aead_multipart_length_overflow_rejected) == TEST_FAIL) { From 65d06737b4d9f33c9188857527e74930ef386042 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 10 Apr 2026 10:52:09 +0200 Subject: [PATCH 34/38] Guard zero-length PBKDF2 inputs F/2426 --- src/psa_key_derivation.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/psa_key_derivation.c b/src/psa_key_derivation.c index 1b72ee0..1ccd536 100644 --- a/src/psa_key_derivation.c +++ b/src/psa_key_derivation.c @@ -137,6 +137,17 @@ static psa_status_t wolfpsa_kdf_append(uint8_t **buf, size_t *len, return PSA_SUCCESS; } +static const uint8_t* wolfpsa_kdf_input_ptr(const uint8_t *buf, size_t len) +{ + static const uint8_t empty[1] = { 0 }; + + if (buf == NULL && len == 0) { + return empty; + } + + return buf; +} + static int wolfpsa_hash_type_from_alg(psa_algorithm_t alg) { psa_algorithm_t hash_alg = 0; @@ -897,6 +908,10 @@ static psa_status_t wolfpsa_kdf_pbkdf2(wolfpsa_kdf_ctx_t *ctx, uint8_t *output, size_t output_length) { + const uint8_t *password = wolfpsa_kdf_input_ptr(ctx->password, + ctx->password_length); + const uint8_t *salt = wolfpsa_kdf_input_ptr(ctx->salt, ctx->salt_length); + if (PSA_ALG_IS_PBKDF2_HMAC(ctx->alg)) { int hash_type = wolfpsa_hash_type_from_alg(ctx->alg); int ret; @@ -904,8 +919,8 @@ static psa_status_t wolfpsa_kdf_pbkdf2(wolfpsa_kdf_ctx_t *ctx, if (hash_type == WC_HASH_TYPE_NONE) { return PSA_ERROR_NOT_SUPPORTED; } - ret = wc_PBKDF2(output, ctx->password, (int)ctx->password_length, - ctx->salt, (int)ctx->salt_length, + ret = wc_PBKDF2(output, password, (int)ctx->password_length, + salt, (int)ctx->salt_length, (int)ctx->cost, (int)output_length, hash_type); if (ret != 0) { return wc_error_to_psa_status(ret); @@ -941,7 +956,7 @@ static psa_status_t wolfpsa_kdf_pbkdf2(wolfpsa_kdf_ctx_t *ctx, status = wc_error_to_psa_status(ret); goto cleanup; } - ret = wc_CmacUpdate(&cmac, ctx->password, (word32)ctx->password_length); + ret = wc_CmacUpdate(&cmac, password, (word32)ctx->password_length); if (ret != 0) { wc_CmacFree(&cmac); status = wc_error_to_psa_status(ret); @@ -969,7 +984,7 @@ static psa_status_t wolfpsa_kdf_pbkdf2(wolfpsa_kdf_ctx_t *ctx, blocks = (output_length + WC_AES_BLOCK_SIZE - 1) / WC_AES_BLOCK_SIZE; for (i = 1; i <= blocks; i++) { - XMEMCPY(block_input, ctx->salt, ctx->salt_length); + XMEMCPY(block_input, salt, ctx->salt_length); block_input[ctx->salt_length + 0] = (uint8_t)((i >> 24) & 0xff); block_input[ctx->salt_length + 1] = (uint8_t)((i >> 16) & 0xff); block_input[ctx->salt_length + 2] = (uint8_t)((i >> 8) & 0xff); From 3f8c5bde55adbc9b1ab8bdf3861afdf0068799df Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 10 Apr 2026 15:01:28 +0200 Subject: [PATCH 35/38] Addressed copilot's review comments --- src/psa_key_derivation.c | 45 ++++++++++++++++++++++++++++++++++++++- test/Makefile | 2 +- wolfpsa/psa_key_storage.h | 2 -- 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/psa_key_derivation.c b/src/psa_key_derivation.c index 1ccd536..3dbbbcf 100644 --- a/src/psa_key_derivation.c +++ b/src/psa_key_derivation.c @@ -68,6 +68,8 @@ typedef struct wolfpsa_kdf_ctx { int is_key_agreement; int is_raw_kdf; int output_started; + uint8_t *output_cache; + size_t output_cache_length; } wolfpsa_kdf_ctx_t; #define WOLFPSA_KDF_STEP_SECRET (1u << 0) @@ -427,6 +429,12 @@ psa_status_t psa_key_derivation_abort(psa_key_derivation_operation_t *operation) wolfpsa_kdf_free_buf(&ctx->label, &ctx->label_length); wolfpsa_kdf_free_buf(&ctx->seed, &ctx->seed_length); wolfpsa_kdf_free_buf(&ctx->password, &ctx->password_length); + if (ctx->output_cache != NULL) { + wc_ForceZero(ctx->output_cache, ctx->output_cache_length); + XFREE(ctx->output_cache, NULL, DYNAMIC_TYPE_TMP_BUFFER); + ctx->output_cache = NULL; + ctx->output_cache_length = 0; + } XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER); operation->opaque = (uintptr_t)NULL; } @@ -745,6 +753,9 @@ static psa_status_t wolfpsa_kdf_hkdf(wolfpsa_kdf_ctx_t *ctx, (wolfpsa_check_word32_length(ctx->secret_length) != PSA_SUCCESS)) { return PSA_ERROR_INVALID_ARGUMENT; } + if (output_length > (size_t)hash_len) { + return PSA_ERROR_INVALID_ARGUMENT; + } ret = wc_HKDF_Extract(hash_type, ctx->salt, (word32)ctx->salt_length, ctx->secret, (word32)ctx->secret_length, @@ -1164,7 +1175,39 @@ psa_status_t psa_key_derivation_output_bytes(psa_key_derivation_operation_t *ope return PSA_ERROR_INVALID_ARGUMENT; } - if (ctx->output_offset == 0) { + if (ctx->output_cache == NULL + && !ctx->is_raw_kdf + && ctx->capacity != PSA_KEY_DERIVATION_UNLIMITED_CAPACITY + && ctx->output_offset == 0) { + /* First output_bytes() call with a bounded capacity: compute the + * full derivation once and cache it. Subsequent calls serve slices + * from the cache, avoiding O(n^2) recomputation. */ + size_t cache_length = output_length + ctx->capacity; + + ctx->output_cache = (uint8_t *)XMALLOC(cache_length, NULL, + DYNAMIC_TYPE_TMP_BUFFER); + if (ctx->output_cache == NULL) { + return PSA_ERROR_INSUFFICIENT_MEMORY; + } + status = wolfpsa_kdf_compute_output(ctx, ctx->output_cache, + cache_length); + if (status != PSA_SUCCESS) { + wc_ForceZero(ctx->output_cache, cache_length); + XFREE(ctx->output_cache, NULL, DYNAMIC_TYPE_TMP_BUFFER); + ctx->output_cache = NULL; + return status; + } + ctx->output_cache_length = cache_length; + } + + if (ctx->output_cache != NULL) { + if (total_output_length > ctx->output_cache_length) { + return PSA_ERROR_INSUFFICIENT_DATA; + } + XMEMCPY(output, ctx->output_cache + ctx->output_offset, output_length); + status = PSA_SUCCESS; + } + else if (ctx->output_offset == 0) { status = wolfpsa_kdf_compute_output(ctx, output, output_length); } else { diff --git a/test/Makefile b/test/Makefile index ce44e70..78a0d73 100644 --- a/test/Makefile +++ b/test/Makefile @@ -88,7 +88,7 @@ psa_api_test: require-wolfssl-lib $(PSA_API_TEST_OBJS) $(CC) $(CFLAGS) -o $@ $(PSA_API_TEST_OBJS) $(LDFLAGS) $(LDLIBS) $(RPATH_WOLFPSA) $(RPATH_WOLFSSL) psa_crypto_init_test: require-wolfssl-lib $(PSA_CRYPTO_INIT_TEST_OBJS) - $(CC) $(CFLAGS) -o $@ $(PSA_CRYPTO_INIT_TEST_OBJS) ../libwolfpsa.a $(LDFLAGS) -L../../wolfssl/src/.libs -lwolfssl -lpthread -lm $(RPATH_WOLFSSL) -Wl,--wrap=wolfCrypt_Init + $(CC) $(CFLAGS) -o $@ $(PSA_CRYPTO_INIT_TEST_OBJS) $(WOLFPSA_PATH)/libwolfpsa.a $(LDFLAGS) -lwolfssl -lpthread -lm $(RPATH_WOLFSSL) -Wl,--wrap=wolfCrypt_Init psa_des3_stack_scrub_test: require-wolfssl-lib $(PSA_DES3_STACK_SCRUB_TEST_OBJS) $(CC) $(CFLAGS) -o $@ $(PSA_DES3_STACK_SCRUB_TEST_OBJS) $(LDFLAGS) $(LDLIBS) $(RPATH_WOLFPSA) $(RPATH_WOLFSSL) diff --git a/wolfpsa/psa_key_storage.h b/wolfpsa/psa_key_storage.h index b9ee545..8f87e8e 100644 --- a/wolfpsa/psa_key_storage.h +++ b/wolfpsa/psa_key_storage.h @@ -100,8 +100,6 @@ WOLFSSL_API psa_status_t wolfpsa_get_key_data(psa_key_id_t key_id, size_t* key_data_length); WOLFSSL_API void wolfpsa_forcezero_free_key_data(uint8_t* key_data, size_t key_data_length); -WOLFSSL_API psa_key_id_t wolfpsa_test_get_next_key_id(void); -WOLFSSL_API void wolfpsa_test_set_next_key_id(psa_key_id_t key_id); #ifdef __cplusplus } From 142c69bd57b7f3a456c544f5a5852588a9cb2aa3 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 10 Apr 2026 22:10:15 +0200 Subject: [PATCH 36/38] Addressed reviewer's comments --- src/psa_aead.c | 10 ++++++++-- src/psa_cipher.c | 3 +++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/psa_aead.c b/src/psa_aead.c index 7355488..60e0221 100644 --- a/src/psa_aead.c +++ b/src/psa_aead.c @@ -688,8 +688,13 @@ static psa_status_t wolfpsa_aead_decrypt_final(wolfpsa_aead_ctx_t *ctx, else if (PSA_ALG_AEAD_EQUAL(ctx->alg, PSA_ALG_CHACHA20_POLY1305)) { size_t out_len = 0; uint8_t *ciphertext = ctx->input; - size_t ciphertext_len = ctx->input_length + tag_length; - uint8_t *tmp = (uint8_t *)XMALLOC(ciphertext_len, NULL, + size_t ciphertext_len; + uint8_t *tmp; + if (ctx->input_length > SIZE_MAX - tag_length) { + return PSA_ERROR_INVALID_ARGUMENT; + } + ciphertext_len = ctx->input_length + tag_length; + tmp = (uint8_t *)XMALLOC(ciphertext_len, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (tmp == NULL) { return PSA_ERROR_INSUFFICIENT_MEMORY; @@ -701,6 +706,7 @@ static psa_status_t wolfpsa_aead_decrypt_final(wolfpsa_aead_ctx_t *ctx, aad, ctx->aad_length, tmp, ciphertext_len, plaintext, plaintext_size, &out_len); + wc_ForceZero(tmp, ciphertext_len); XFREE(tmp, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (ret != 0) { return PSA_ERROR_INVALID_SIGNATURE; diff --git a/src/psa_cipher.c b/src/psa_cipher.c index c47da34..31342e1 100644 --- a/src/psa_cipher.c +++ b/src/psa_cipher.c @@ -1260,6 +1260,7 @@ psa_status_t psa_cipher_finish(psa_cipher_operation_t *operation, } ctx->partial_len = 0; *output_length = block_size; + psa_cipher_abort(operation); return PSA_SUCCESS; } else { @@ -1319,6 +1320,7 @@ psa_status_t psa_cipher_finish(psa_cipher_operation_t *operation, if (status != PSA_SUCCESS) { return wolfpsa_cipher_fail(operation, status); } + psa_cipher_abort(operation); return status; } } @@ -1330,6 +1332,7 @@ psa_status_t psa_cipher_finish(psa_cipher_operation_t *operation, } *output_length = 0; + psa_cipher_abort(operation); return PSA_SUCCESS; } From c8717b6faf1f4bc863496ff6efad7c4a32b4f8c4 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 10 Apr 2026 22:10:43 +0200 Subject: [PATCH 37/38] Update gitignore (unit test binary) --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 08803db..7d49aca 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ test/psa_tls_server test/psa_des3_stack_scrub_test test/psa_random_size_test test/psa_rsa_pss_interop_test +test/psa_crypto_init_test psa_server/tls_client/psa_tls_client psa_tls_client wolfcrypt-benchmark/wolfcrypt-psa-benchmark From 219d5645838b75f56f7bfd779a3c372413a8ba39 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 10 Apr 2026 23:09:50 +0200 Subject: [PATCH 38/38] Fixed/updated copyright notice --- src/misc_impl.c | 18 +++++++++++++++- src/psa_aead_internal.h | 21 +++++++++++++++++++ src/psa_api_stub.c | 22 +++++++++++++++++++- src/psa_size.h | 21 +++++++++++++++++++ src/psa_trace.h | 18 +++++++++++++++- test/psa_server/psa_api_test.c | 19 ++++++++++++++++- test/psa_server/psa_api_test_user_settings.h | 21 +++++++++++++++++++ test/psa_server/psa_crypto_init_test.c | 21 +++++++++++++++++++ test/psa_server/psa_des3_stack_scrub_test.c | 21 +++++++++++++++++++ test/psa_server/psa_ecc_bit_inference_test.c | 21 +++++++++++++++++++ test/psa_server/psa_ecc_curve_id_test.c | 21 +++++++++++++++++++ test/psa_server/psa_random_size_test.c | 21 +++++++++++++++++++ test/psa_server/psa_rsa_pss_interop_test.c | 21 +++++++++++++++++++ test/psa_server/psa_tls_server.c | 19 ++++++++++++++++- test/psa_server/tls_client/user_settings.h | 21 +++++++++++++++++++ test/wolfcrypt-benchmark/main.c | 21 ++++++++++++++++++- 16 files changed, 321 insertions(+), 6 deletions(-) diff --git a/src/misc_impl.c b/src/misc_impl.c index b3c64e7..b6156d6 100644 --- a/src/misc_impl.c +++ b/src/misc_impl.c @@ -1,6 +1,22 @@ /* misc_impl.c * - * Provide misc.c helper implementations for NO_INLINE builds. + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfPSA. + * + * wolfPSA is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfPSA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ #ifdef HAVE_CONFIG_H diff --git a/src/psa_aead_internal.h b/src/psa_aead_internal.h index 1f71f54..149b001 100644 --- a/src/psa_aead_internal.h +++ b/src/psa_aead_internal.h @@ -1,3 +1,24 @@ +/* psa_aead_internal.h + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfPSA. + * + * wolfPSA is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfPSA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + #ifndef WOLFPSA_PSA_AEAD_INTERNAL_H #define WOLFPSA_PSA_AEAD_INTERNAL_H diff --git a/src/psa_api_stub.c b/src/psa_api_stub.c index bc70731..1d1798c 100644 --- a/src/psa_api_stub.c +++ b/src/psa_api_stub.c @@ -1,4 +1,24 @@ -/* psa_api_stub.c - auto-generated for test linkage */ +/* psa_api_stub.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfPSA. + * + * wolfPSA is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfPSA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + #include extern int wolfPSA_CryptoIsInitialized(void); diff --git a/src/psa_size.h b/src/psa_size.h index 059b851..621b8d2 100644 --- a/src/psa_size.h +++ b/src/psa_size.h @@ -1,3 +1,24 @@ +/* psa_size.h + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfPSA. + * + * wolfPSA is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfPSA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + #ifndef WOLFPSA_SIZE_H #define WOLFPSA_SIZE_H diff --git a/src/psa_trace.h b/src/psa_trace.h index 98e71f7..a449f5a 100644 --- a/src/psa_trace.h +++ b/src/psa_trace.h @@ -1,6 +1,22 @@ /* psa_trace.h * - * Lightweight tracing for PSA entry points. + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfPSA. + * + * wolfPSA is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfPSA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ #ifndef WOLFPSA_PSA_TRACE_H diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index 2d767a7..aa64352 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -1,5 +1,22 @@ /* psa_api_test.c - * Standalone PSA API coverage test for wolfPSA. + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfPSA. + * + * wolfPSA is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfPSA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ #include "psa_api_test_user_settings.h" diff --git a/test/psa_server/psa_api_test_user_settings.h b/test/psa_server/psa_api_test_user_settings.h index 4cda5a6..5ac64de 100644 --- a/test/psa_server/psa_api_test_user_settings.h +++ b/test/psa_server/psa_api_test_user_settings.h @@ -1,3 +1,24 @@ +/* psa_api_test_user_settings.h + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfPSA. + * + * wolfPSA is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfPSA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + #ifndef PSA_API_TEST_USER_SETTINGS_H #define PSA_API_TEST_USER_SETTINGS_H diff --git a/test/psa_server/psa_crypto_init_test.c b/test/psa_server/psa_crypto_init_test.c index e0b4002..c540aa2 100644 --- a/test/psa_server/psa_crypto_init_test.c +++ b/test/psa_server/psa_crypto_init_test.c @@ -1,3 +1,24 @@ +/* psa_crypto_init_test.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfPSA. + * + * wolfPSA is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfPSA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + #include "psa_api_test_user_settings.h" #ifndef WOLFSSL_USER_SETTINGS diff --git a/test/psa_server/psa_des3_stack_scrub_test.c b/test/psa_server/psa_des3_stack_scrub_test.c index 90d37d3..ce3f38c 100644 --- a/test/psa_server/psa_des3_stack_scrub_test.c +++ b/test/psa_server/psa_des3_stack_scrub_test.c @@ -1,3 +1,24 @@ +/* psa_des3_stack_scrub_test.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfPSA. + * + * wolfPSA is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfPSA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + #include "psa_api_test_user_settings.h" #ifndef WOLFSSL_USER_SETTINGS diff --git a/test/psa_server/psa_ecc_bit_inference_test.c b/test/psa_server/psa_ecc_bit_inference_test.c index bc76fab..51d463f 100644 --- a/test/psa_server/psa_ecc_bit_inference_test.c +++ b/test/psa_server/psa_ecc_bit_inference_test.c @@ -1,3 +1,24 @@ +/* psa_ecc_bit_inference_test.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfPSA. + * + * wolfPSA is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfPSA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + #include "psa_api_test_user_settings.h" #ifndef WOLFSSL_USER_SETTINGS diff --git a/test/psa_server/psa_ecc_curve_id_test.c b/test/psa_server/psa_ecc_curve_id_test.c index 8e6cf60..b8fe21b 100644 --- a/test/psa_server/psa_ecc_curve_id_test.c +++ b/test/psa_server/psa_ecc_curve_id_test.c @@ -1,3 +1,24 @@ +/* psa_ecc_curve_id_test.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfPSA. + * + * wolfPSA is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfPSA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + #include "psa_api_test_user_settings.h" #ifndef WOLFSSL_USER_SETTINGS diff --git a/test/psa_server/psa_random_size_test.c b/test/psa_server/psa_random_size_test.c index f4145fb..aedbac6 100644 --- a/test/psa_server/psa_random_size_test.c +++ b/test/psa_server/psa_random_size_test.c @@ -1,3 +1,24 @@ +/* psa_random_size_test.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfPSA. + * + * wolfPSA is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfPSA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + #include "psa_api_test_user_settings.h" #ifndef WOLFSSL_USER_SETTINGS diff --git a/test/psa_server/psa_rsa_pss_interop_test.c b/test/psa_server/psa_rsa_pss_interop_test.c index 692d859..c3e717b 100644 --- a/test/psa_server/psa_rsa_pss_interop_test.c +++ b/test/psa_server/psa_rsa_pss_interop_test.c @@ -1,3 +1,24 @@ +/* psa_rsa_pss_interop_test.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfPSA. + * + * wolfPSA is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfPSA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + #include #include diff --git a/test/psa_server/psa_tls_server.c b/test/psa_server/psa_tls_server.c index b377e6a..a9f8d1e 100644 --- a/test/psa_server/psa_tls_server.c +++ b/test/psa_server/psa_tls_server.c @@ -1,5 +1,22 @@ /* psa_tls_server.c - * TLS server that uses wolfSSL PSA PK callbacks for ECC operations. + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfPSA. + * + * wolfPSA is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfPSA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ #ifndef NO_FILESYSTEM diff --git a/test/psa_server/tls_client/user_settings.h b/test/psa_server/tls_client/user_settings.h index ffbac83..caee103 100644 --- a/test/psa_server/tls_client/user_settings.h +++ b/test/psa_server/tls_client/user_settings.h @@ -1,3 +1,24 @@ +/* user_settings.h + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfPSA. + * + * wolfPSA is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfPSA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + #ifndef WOLFSSL_USER_SETTINGS_H #define WOLFSSL_USER_SETTINGS_H diff --git a/test/wolfcrypt-benchmark/main.c b/test/wolfcrypt-benchmark/main.c index cda526b..a03dfc0 100644 --- a/test/wolfcrypt-benchmark/main.c +++ b/test/wolfcrypt-benchmark/main.c @@ -1,4 +1,23 @@ -/* wolfcrypt-benchmark main for wolfPSA + wolfSSL PSA path */ +/* main.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfPSA. + * + * wolfPSA is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfPSA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ #include #include