From 36e38aa669b5a340a1fd53653f0c2447d532a690 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 30 Mar 2026 07:39:59 +0200 Subject: [PATCH 01/15] Fix TLS 1.2 PSK premaster ordering F/1647 --- src/psa_key_derivation.c | 14 +++--- test/psa_server/psa_api_test.c | 78 ++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 7 deletions(-) diff --git a/src/psa_key_derivation.c b/src/psa_key_derivation.c index 9a29425..4e512c7 100644 --- a/src/psa_key_derivation.c +++ b/src/psa_key_derivation.c @@ -810,13 +810,13 @@ static psa_status_t wolfpsa_kdf_tls12_psk_to_ms(wolfpsa_kdf_ctx_t *ctx, return PSA_ERROR_INSUFFICIENT_MEMORY; } - premaster[0] = (uint8_t)((ctx->secret_length >> 8) & 0xff); - premaster[1] = (uint8_t)(ctx->secret_length & 0xff); - XMEMCPY(premaster + 2u, ctx->secret, ctx->secret_length); - premaster[2u + ctx->secret_length] = (uint8_t)((ctx->other_secret_length >> 8) & 0xff); - premaster[3u + ctx->secret_length] = (uint8_t)(ctx->other_secret_length & 0xff); - XMEMCPY(premaster + 4u + ctx->secret_length, ctx->other_secret, - ctx->other_secret_length); + premaster[0] = (uint8_t)((ctx->other_secret_length >> 8) & 0xff); + premaster[1] = (uint8_t)(ctx->other_secret_length & 0xff); + XMEMCPY(premaster + 2u, ctx->other_secret, ctx->other_secret_length); + premaster[2u + ctx->other_secret_length] = (uint8_t)((ctx->secret_length >> 8) & 0xff); + premaster[3u + ctx->other_secret_length] = (uint8_t)(ctx->secret_length & 0xff); + XMEMCPY(premaster + 4u + ctx->other_secret_length, ctx->secret, + ctx->secret_length); ret = wc_PRF_TLS(output, (word32)output_length, premaster, (word32)premaster_len, diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index fb71317..5c40517 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -903,6 +904,77 @@ static int test_kdf_null_capacity(void) return TEST_OK; } +static int test_kdf_tls12_psk_to_ms_rfc4279_order(void) +{ + static const uint8_t psk[] = { 0xa1, 0xb2, 0xc3, 0xd4, 0xe5 }; + static const uint8_t other_secret[] = { 0x10, 0x20, 0x30 }; + static const uint8_t seed[] = "clienthello||serverhello"; + uint8_t premaster[2u + sizeof(other_secret) + 2u + sizeof(psk)]; + uint8_t expected[48]; + uint8_t output[sizeof(expected)]; + size_t seed_len = sizeof(seed) - 1u; + psa_key_derivation_operation_t op = psa_key_derivation_operation_init(); + psa_status_t st; + int ret; + + premaster[0] = 0x00; + premaster[1] = (uint8_t)sizeof(other_secret); + memcpy(premaster + 2u, other_secret, sizeof(other_secret)); + premaster[2u + sizeof(other_secret)] = 0x00; + premaster[3u + sizeof(other_secret)] = (uint8_t)sizeof(psk); + memcpy(premaster + 4u + sizeof(other_secret), psk, sizeof(psk)); + + ret = wc_PRF_TLS(expected, (word32)sizeof(expected), + premaster, (word32)sizeof(premaster), + (const byte*)"master secret", 13u, + seed, (word32)seed_len, + 1, WC_HASH_TYPE_SHA256, NULL, INVALID_DEVID); + if (ret != 0) { + printf("FAIL: wc_PRF_TLS(TLS12_PSK_TO_MS reference) (%d)\n", ret); + return TEST_FAIL; + } + + st = psa_key_derivation_setup(&op, PSA_ALG_TLS12_PSK_TO_MS(PSA_ALG_SHA_256)); + if (check_status(st, "psa_key_derivation_setup(TLS12_PSK_TO_MS)") != TEST_OK) { + return TEST_FAIL; + } + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_SEED, + seed, seed_len); + if (check_status(st, "psa_key_derivation_input_bytes(SEED)") != TEST_OK) { + (void)psa_key_derivation_abort(&op); + return TEST_FAIL; + } + st = psa_key_derivation_input_bytes(&op, + PSA_KEY_DERIVATION_INPUT_OTHER_SECRET, + other_secret, sizeof(other_secret)); + if (check_status(st, "psa_key_derivation_input_bytes(OTHER_SECRET)") != TEST_OK) { + (void)psa_key_derivation_abort(&op); + return TEST_FAIL; + } + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_SECRET, + psk, sizeof(psk)); + if (check_status(st, "psa_key_derivation_input_bytes(SECRET)") != 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(TLS12_PSK_TO_MS)") != 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(TLS12_PSK_TO_MS)") != TEST_OK) { + return TEST_FAIL; + } + + if (check_buf_eq("psa_key_derivation_output_bytes(TLS12_PSK_TO_MS RFC4279)", + output, expected, sizeof(expected)) != TEST_OK) { + return TEST_FAIL; + } + + return TEST_OK; +} + static int run_named_test(const char* name, test_fn_t fn) { int ret; @@ -987,6 +1059,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_tls12_psk_to_ms") == 0) { + if (run_named_test("kdf_tls12_psk_to_ms", + test_kdf_tls12_psk_to_ms_rfc4279_order) == TEST_FAIL) { + return TEST_FAIL; + } + } printf("PSA API test: OK (passed=%d skipped=%d)\n", tests_passed, tests_skipped); From 7c9f862b126f241d7feb07a1957b164b06bcfa74 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 30 Mar 2026 07:41:51 +0200 Subject: [PATCH 02/15] Allow plain TLS 1.2 PSK derivation F/1648 --- src/psa_key_derivation.c | 34 ++++++++++++----- test/psa_server/psa_api_test.c | 67 ++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 10 deletions(-) diff --git a/src/psa_key_derivation.c b/src/psa_key_derivation.c index 4e512c7..897ee89 100644 --- a/src/psa_key_derivation.c +++ b/src/psa_key_derivation.c @@ -318,8 +318,7 @@ static psa_status_t wolfpsa_kdf_validate_step(wolfpsa_kdf_ctx_t *ctx, return PSA_ERROR_INVALID_ARGUMENT; } if (step == PSA_KEY_DERIVATION_INPUT_SECRET) { - if ((ctx->steps_set & WOLFPSA_KDF_STEP_SEED) == 0 || - (ctx->steps_set & WOLFPSA_KDF_STEP_OTHER_SECRET) == 0) { + if ((ctx->steps_set & WOLFPSA_KDF_STEP_SEED) == 0) { return PSA_ERROR_BAD_STATE; } } @@ -796,6 +795,8 @@ static psa_status_t wolfpsa_kdf_tls12_psk_to_ms(wolfpsa_kdf_ctx_t *ctx, size_t output_length) { int hash_type = wolfpsa_hash_type_from_alg(ctx->alg); + size_t other_secret_length; + const uint8_t *other_secret; uint8_t *premaster = NULL; size_t premaster_len; psa_status_t status; @@ -804,18 +805,32 @@ static psa_status_t wolfpsa_kdf_tls12_psk_to_ms(wolfpsa_kdf_ctx_t *ctx, if (hash_type == WC_HASH_TYPE_NONE) { return PSA_ERROR_NOT_SUPPORTED; } - premaster_len = 2u + ctx->secret_length + 2u + ctx->other_secret_length; + if ((ctx->steps_set & WOLFPSA_KDF_STEP_OTHER_SECRET) == 0) { + other_secret = NULL; + other_secret_length = ctx->secret_length; + } + else { + other_secret = ctx->other_secret; + other_secret_length = ctx->other_secret_length; + } + + premaster_len = 2u + ctx->secret_length + 2u + other_secret_length; premaster = (uint8_t *)XMALLOC(premaster_len, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (premaster == NULL) { return PSA_ERROR_INSUFFICIENT_MEMORY; } - premaster[0] = (uint8_t)((ctx->other_secret_length >> 8) & 0xff); - premaster[1] = (uint8_t)(ctx->other_secret_length & 0xff); - XMEMCPY(premaster + 2u, ctx->other_secret, ctx->other_secret_length); - premaster[2u + ctx->other_secret_length] = (uint8_t)((ctx->secret_length >> 8) & 0xff); - premaster[3u + ctx->other_secret_length] = (uint8_t)(ctx->secret_length & 0xff); - XMEMCPY(premaster + 4u + ctx->other_secret_length, ctx->secret, + premaster[0] = (uint8_t)((other_secret_length >> 8) & 0xff); + premaster[1] = (uint8_t)(other_secret_length & 0xff); + if (other_secret == NULL) { + XMEMSET(premaster + 2u, 0, other_secret_length); + } + else { + XMEMCPY(premaster + 2u, other_secret, other_secret_length); + } + premaster[2u + other_secret_length] = (uint8_t)((ctx->secret_length >> 8) & 0xff); + premaster[3u + other_secret_length] = (uint8_t)(ctx->secret_length & 0xff); + XMEMCPY(premaster + 4u + other_secret_length, ctx->secret, ctx->secret_length); ret = wc_PRF_TLS(output, (word32)output_length, @@ -1033,7 +1048,6 @@ psa_status_t psa_key_derivation_output_bytes(psa_key_derivation_operation_t *ope } else if (PSA_ALG_IS_TLS12_PSK_TO_MS(ctx->alg)) { if ((ctx->steps_set & WOLFPSA_KDF_STEP_SECRET) == 0 || - (ctx->steps_set & WOLFPSA_KDF_STEP_OTHER_SECRET) == 0 || (ctx->steps_set & WOLFPSA_KDF_STEP_SEED) == 0) { return PSA_ERROR_BAD_STATE; } diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index 5c40517..8116d36 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -975,6 +975,69 @@ static int test_kdf_tls12_psk_to_ms_rfc4279_order(void) return TEST_OK; } +static int test_kdf_tls12_psk_to_ms_plain_psk_optional_other_secret(void) +{ + static const uint8_t psk[] = { 0xa1, 0xb2, 0xc3, 0xd4, 0xe5 }; + static const uint8_t seed[] = "clienthello||serverhello"; + uint8_t premaster[2u + sizeof(psk) + 2u + sizeof(psk)]; + uint8_t expected[48]; + uint8_t output[sizeof(expected)]; + size_t seed_len = sizeof(seed) - 1u; + psa_key_derivation_operation_t op = psa_key_derivation_operation_init(); + psa_status_t st; + int ret; + + premaster[0] = 0x00; + premaster[1] = (uint8_t)sizeof(psk); + memset(premaster + 2u, 0, sizeof(psk)); + premaster[2u + sizeof(psk)] = 0x00; + premaster[3u + sizeof(psk)] = (uint8_t)sizeof(psk); + memcpy(premaster + 4u + sizeof(psk), psk, sizeof(psk)); + + ret = wc_PRF_TLS(expected, (word32)sizeof(expected), + premaster, (word32)sizeof(premaster), + (const byte*)"master secret", 13u, + seed, (word32)seed_len, + 1, WC_HASH_TYPE_SHA256, NULL, INVALID_DEVID); + if (ret != 0) { + printf("FAIL: wc_PRF_TLS(TLS12_PSK_TO_MS plain PSK reference) (%d)\n", ret); + return TEST_FAIL; + } + + st = psa_key_derivation_setup(&op, PSA_ALG_TLS12_PSK_TO_MS(PSA_ALG_SHA_256)); + if (check_status(st, "psa_key_derivation_setup(TLS12_PSK_TO_MS plain PSK)") != TEST_OK) { + return TEST_FAIL; + } + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_SEED, + seed, seed_len); + if (check_status(st, "psa_key_derivation_input_bytes(SEED plain PSK)") != TEST_OK) { + (void)psa_key_derivation_abort(&op); + return TEST_FAIL; + } + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_SECRET, + psk, sizeof(psk)); + if (check_status(st, "psa_key_derivation_input_bytes(SECRET plain PSK)") != 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(TLS12_PSK_TO_MS plain PSK)") != 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(TLS12_PSK_TO_MS plain PSK)") != TEST_OK) { + return TEST_FAIL; + } + + if (check_buf_eq("psa_key_derivation_output_bytes(TLS12_PSK_TO_MS plain PSK RFC4279)", + output, expected, sizeof(expected)) != TEST_OK) { + return TEST_FAIL; + } + + return TEST_OK; +} + static int run_named_test(const char* name, test_fn_t fn) { int ret; @@ -1064,6 +1127,10 @@ int main(int argc, char** argv) test_kdf_tls12_psk_to_ms_rfc4279_order) == TEST_FAIL) { return TEST_FAIL; } + if (run_named_test("kdf_tls12_psk_to_ms_plain_psk", + test_kdf_tls12_psk_to_ms_plain_psk_optional_other_secret) == TEST_FAIL) { + return TEST_FAIL; + } } printf("PSA API test: OK (passed=%d skipped=%d)\n", From 031821fd985b27dc1ce0f88069a782431842421b Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 30 Mar 2026 07:43:55 +0200 Subject: [PATCH 03/15] Allow HKDF extract without salt F/1649 --- src/psa_key_derivation.c | 6 +--- test/psa_server/psa_api_test.c | 52 ++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/src/psa_key_derivation.c b/src/psa_key_derivation.c index 897ee89..4367834 100644 --- a/src/psa_key_derivation.c +++ b/src/psa_key_derivation.c @@ -253,9 +253,6 @@ static psa_status_t wolfpsa_kdf_validate_step(wolfpsa_kdf_ctx_t *ctx, return PSA_SUCCESS; } if (step == PSA_KEY_DERIVATION_INPUT_SECRET) { - if ((ctx->steps_set & WOLFPSA_KDF_STEP_SALT) == 0) { - return PSA_ERROR_BAD_STATE; - } if ((ctx->steps_set & WOLFPSA_KDF_STEP_SECRET) != 0) { return PSA_ERROR_BAD_STATE; } @@ -1021,8 +1018,7 @@ psa_status_t psa_key_derivation_output_bytes(psa_key_derivation_operation_t *ope } else if (PSA_ALG_IS_ANY_HKDF(ctx->alg)) { if (PSA_ALG_IS_HKDF_EXTRACT(ctx->alg)) { - if ((ctx->steps_set & WOLFPSA_KDF_STEP_SECRET) == 0 || - (ctx->steps_set & WOLFPSA_KDF_STEP_SALT) == 0) { + if ((ctx->steps_set & WOLFPSA_KDF_STEP_SECRET) == 0) { return PSA_ERROR_BAD_STATE; } } diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index 8116d36..1bddf21 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -1038,6 +1038,52 @@ static int test_kdf_tls12_psk_to_ms_plain_psk_optional_other_secret(void) return TEST_OK; } +static int test_kdf_hkdf_extract_optional_salt(void) +{ + static const uint8_t secret[] = "hkdf extract secret"; + uint8_t expected[WC_SHA256_DIGEST_SIZE]; + uint8_t output[sizeof(expected)]; + 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 optional salt 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 optional salt)") != 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_EXTRACT optional salt)") != 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 optional salt)") != 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 optional salt)") != TEST_OK) { + return TEST_FAIL; + } + + if (check_buf_eq("psa_key_derivation_output_bytes(HKDF_EXTRACT optional salt)", + output, expected, sizeof(expected)) != TEST_OK) { + return TEST_FAIL; + } + + return TEST_OK; +} + static int run_named_test(const char* name, test_fn_t fn) { int ret; @@ -1132,6 +1178,12 @@ int main(int argc, char** argv) return TEST_FAIL; } } + if (only == NULL || strcmp(only, "kdf_hkdf_extract_optional_salt") == 0) { + if (run_named_test("kdf_hkdf_extract_optional_salt", + test_kdf_hkdf_extract_optional_salt) == TEST_FAIL) { + return TEST_FAIL; + } + } printf("PSA API test: OK (passed=%d skipped=%d)\n", tests_passed, tests_skipped); From 98d568ea0ff8193e25d8fb04d6bb7b98f565bd34 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 30 Mar 2026 07:46:30 +0200 Subject: [PATCH 04/15] Allow HKDF output without info F/1650 --- src/psa_key_derivation.c | 6 +- test/psa_server/psa_api_test.c | 110 +++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 4 deletions(-) diff --git a/src/psa_key_derivation.c b/src/psa_key_derivation.c index 4367834..33f2bbc 100644 --- a/src/psa_key_derivation.c +++ b/src/psa_key_derivation.c @@ -1023,14 +1023,12 @@ psa_status_t psa_key_derivation_output_bytes(psa_key_derivation_operation_t *ope } } else if (PSA_ALG_IS_HKDF_EXPAND(ctx->alg)) { - if ((ctx->steps_set & WOLFPSA_KDF_STEP_SECRET) == 0 || - (ctx->steps_set & WOLFPSA_KDF_STEP_INFO) == 0) { + if ((ctx->steps_set & WOLFPSA_KDF_STEP_SECRET) == 0) { return PSA_ERROR_BAD_STATE; } } else { - if ((ctx->steps_set & WOLFPSA_KDF_STEP_SECRET) == 0 || - (ctx->steps_set & WOLFPSA_KDF_STEP_INFO) == 0) { + if ((ctx->steps_set & WOLFPSA_KDF_STEP_SECRET) == 0) { return PSA_ERROR_BAD_STATE; } } diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index 1bddf21..8968ae7 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -1084,6 +1084,104 @@ static int test_kdf_hkdf_extract_optional_salt(void) return TEST_OK; } +static int test_kdf_hkdf_expand_optional_info(void) +{ + static const uint8_t prk[WC_SHA256_DIGEST_SIZE] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f + }; + uint8_t expected[42]; + uint8_t output[sizeof(expected)]; + psa_key_derivation_operation_t op = psa_key_derivation_operation_init(); + psa_status_t st; + int ret; + + ret = wc_HKDF_Expand(WC_HASH_TYPE_SHA256, + prk, (word32)sizeof(prk), + NULL, 0, + expected, (word32)sizeof(expected)); + if (ret != 0) { + printf("FAIL: wc_HKDF_Expand(HKDF_EXPAND optional info reference) (%d)\n", ret); + return TEST_FAIL; + } + + st = psa_key_derivation_setup(&op, PSA_ALG_HKDF_EXPAND(PSA_ALG_SHA_256)); + if (check_status(st, "psa_key_derivation_setup(HKDF_EXPAND optional info)") != TEST_OK) { + return TEST_FAIL; + } + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_SECRET, + prk, sizeof(prk)); + if (check_status(st, "psa_key_derivation_input_bytes(SECRET HKDF_EXPAND optional info)") != 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_EXPAND optional info)") != 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_EXPAND optional info)") != TEST_OK) { + return TEST_FAIL; + } + + if (check_buf_eq("psa_key_derivation_output_bytes(HKDF_EXPAND optional info)", + output, expected, sizeof(expected)) != TEST_OK) { + return TEST_FAIL; + } + + return TEST_OK; +} + +static int test_kdf_hkdf_optional_info(void) +{ + static const uint8_t secret[] = "hkdf secret"; + uint8_t expected[42]; + uint8_t output[sizeof(expected)]; + 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, + NULL, 0, + expected, (word32)sizeof(expected)); + if (ret != 0) { + printf("FAIL: wc_HKDF(HKDF optional info 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 optional info)") != 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 optional info)") != 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 optional info)") != 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 optional info)") != TEST_OK) { + return TEST_FAIL; + } + + if (check_buf_eq("psa_key_derivation_output_bytes(HKDF optional info)", + output, expected, sizeof(expected)) != TEST_OK) { + return TEST_FAIL; + } + + return TEST_OK; +} + static int run_named_test(const char* name, test_fn_t fn) { int ret; @@ -1184,6 +1282,18 @@ int main(int argc, char** argv) 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) { + return TEST_FAIL; + } + } + if (only == NULL || strcmp(only, "kdf_hkdf_optional_info") == 0) { + if (run_named_test("kdf_hkdf_optional_info", + test_kdf_hkdf_optional_info) == TEST_FAIL) { + return TEST_FAIL; + } + } printf("PSA API test: OK (passed=%d skipped=%d)\n", tests_passed, tests_skipped); From 0198520de0f1ed0abc15d8636c4c3c65b8c7c6d3 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 30 Mar 2026 07:51:57 +0200 Subject: [PATCH 05/15] Guard PSA word32 length conversions F/1656 --- src/psa_chacha20_poly1305.c | 10 ++++++++++ src/psa_cipher.c | 14 ++++++++++++++ src/psa_ecc.c | 16 ++++++++++++++++ src/psa_hash_engine.c | 4 ++++ src/psa_key_derivation.c | 35 +++++++++++++++++++++++++++++++++++ src/psa_lms_xmss.c | 35 +++++++++++++++++++++++++++++++++++ src/psa_mac.c | 9 +++++++++ src/psa_mldsa.c | 15 +++++++++++++++ src/psa_rsa.c | 31 +++++++++++++++++++++++++++++++ src/psa_size.h | 17 +++++++++++++++++ src/psa_tls_prf.c | 36 ++++++++++++++---------------------- 11 files changed, 200 insertions(+), 22 deletions(-) create mode 100644 src/psa_size.h diff --git a/src/psa_chacha20_poly1305.c b/src/psa_chacha20_poly1305.c index 67b731b..846682c 100644 --- a/src/psa_chacha20_poly1305.c +++ b/src/psa_chacha20_poly1305.c @@ -28,6 +28,7 @@ #if defined(WOLFSSL_PSA_ENGINE) && defined(HAVE_CHACHA) && defined(HAVE_POLY1305) #include +#include "psa_size.h" #include #include #include @@ -105,6 +106,10 @@ psa_status_t psa_chacha20_poly1305_encrypt( if (plaintext == NULL && plaintext_length > 0) { return PSA_ERROR_INVALID_ARGUMENT; } + if ((wolfpsa_check_word32_length(additional_data_length) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(plaintext_length) != PSA_SUCCESS)) { + return PSA_ERROR_INVALID_ARGUMENT; + } /* Check output buffer size */ if (ciphertext_size < plaintext_length + CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE) { @@ -164,6 +169,11 @@ psa_status_t psa_chacha20_poly1305_decrypt( if (ciphertext_length < CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE) { return PSA_ERROR_INVALID_ARGUMENT; } + if ((wolfpsa_check_word32_length(additional_data_length) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(ciphertext_length - + CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE) != PSA_SUCCESS)) { + return PSA_ERROR_INVALID_ARGUMENT; + } /* Check output buffer size */ if (plaintext_size < ciphertext_length - CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE) { diff --git a/src/psa_cipher.c b/src/psa_cipher.c index e198865..69fc374 100644 --- a/src/psa_cipher.c +++ b/src/psa_cipher.c @@ -29,6 +29,7 @@ #include #include "psa_trace.h" +#include "psa_size.h" #include #include #include @@ -296,6 +297,11 @@ psa_status_t psa_cipher_encrypt_setup(psa_cipher_operation_t *operation, ctx->block_size = wolfpsa_cipher_block_size(attributes.type); ctx->partial_len = 0; XMEMCPY(ctx->iv, zero_iv, sizeof(ctx->iv)); + if (wolfpsa_check_word32_length(key_data_length) != PSA_SUCCESS) { + wolfpsa_forcezero_free_key_data(key_data, key_data_length); + XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER); + return PSA_ERROR_INVALID_ARGUMENT; + } if (ctx->is_chacha) { ret = wc_Chacha_SetKey(&ctx->chacha, key_data, @@ -432,6 +438,11 @@ psa_status_t psa_cipher_decrypt_setup(psa_cipher_operation_t *operation, ctx->block_size = wolfpsa_cipher_block_size(attributes.type); ctx->partial_len = 0; XMEMCPY(ctx->iv, zero_iv, sizeof(ctx->iv)); + if (wolfpsa_check_word32_length(key_data_length) != PSA_SUCCESS) { + wolfpsa_forcezero_free_key_data(key_data, key_data_length); + XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER); + return PSA_ERROR_INVALID_ARGUMENT; + } if (ctx->is_chacha) { ret = wc_Chacha_SetKey(&ctx->chacha, key_data, @@ -658,6 +669,9 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, if (output == NULL && output_size > 0) { return PSA_ERROR_INVALID_ARGUMENT; } + if (wolfpsa_check_word32_length(input_length) != PSA_SUCCESS) { + return PSA_ERROR_INVALID_ARGUMENT; + } if (input_length == 0) { return PSA_SUCCESS; diff --git a/src/psa_ecc.c b/src/psa_ecc.c index e3ece59..1853d51 100644 --- a/src/psa_ecc.c +++ b/src/psa_ecc.c @@ -28,6 +28,7 @@ #if defined(WOLFSSL_PSA_ENGINE) && defined(HAVE_ECC) #include +#include "psa_size.h" #include #include #include @@ -83,6 +84,10 @@ psa_status_t psa_asymmetric_sign_ecc(psa_key_type_t key_type, if (curve_id == ECC_CURVE_INVALID) { return PSA_ERROR_NOT_SUPPORTED; } + if ((wolfpsa_check_word32_length(key_buffer_size) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(hash_length) != PSA_SUCCESS)) { + return PSA_ERROR_INVALID_ARGUMENT; + } /* Initialize ECC key */ ret = wc_ecc_init(&ecc); @@ -214,6 +219,10 @@ psa_status_t psa_asymmetric_verify_ecc(psa_key_type_t key_type, if (curve_id == ECC_CURVE_INVALID) { return PSA_ERROR_NOT_SUPPORTED; } + if ((wolfpsa_check_word32_length(key_buffer_size) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(hash_length) != PSA_SUCCESS)) { + return PSA_ERROR_INVALID_ARGUMENT; + } /* Initialize ECC key */ ret = wc_ecc_init(&ecc); @@ -304,6 +313,9 @@ psa_status_t psa_asymmetric_generate_key_ecc(psa_key_type_t key_type, if (curve_id == ECC_CURVE_INVALID) { return PSA_ERROR_NOT_SUPPORTED; } + if (wolfpsa_check_word32_length(public_key_size) != PSA_SUCCESS) { + return PSA_ERROR_INVALID_ARGUMENT; + } /* Initialize ECC key */ ret = wc_ecc_init(&ecc); @@ -388,6 +400,10 @@ psa_status_t psa_asymmetric_export_public_key_ecc(psa_key_type_t key_type, if (curve_id == ECC_CURVE_INVALID) { return PSA_ERROR_NOT_SUPPORTED; } + if ((wolfpsa_check_word32_length(key_buffer_size) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(output_size) != PSA_SUCCESS)) { + return PSA_ERROR_INVALID_ARGUMENT; + } /* Initialize ECC key */ ret = wc_ecc_init(&ecc); diff --git a/src/psa_hash_engine.c b/src/psa_hash_engine.c index c7aacd3..4228ff5 100644 --- a/src/psa_hash_engine.c +++ b/src/psa_hash_engine.c @@ -29,6 +29,7 @@ #include #include "psa_trace.h" +#include "psa_size.h" #include #include #include @@ -433,6 +434,9 @@ psa_status_t psa_hash_update(psa_hash_operation_t *operation, if (ctx->finalized) { return PSA_ERROR_BAD_STATE; } + if (wolfpsa_check_word32_length(input_length) != PSA_SUCCESS) { + return PSA_ERROR_INVALID_ARGUMENT; + } /* Update the hash context based on algorithm */ switch (ctx->alg) { diff --git a/src/psa_key_derivation.c b/src/psa_key_derivation.c index 33f2bbc..42856e6 100644 --- a/src/psa_key_derivation.c +++ b/src/psa_key_derivation.c @@ -29,6 +29,7 @@ #if defined(WOLFSSL_PSA_ENGINE) #include +#include "psa_size.h" #include #include #include @@ -714,6 +715,10 @@ static psa_status_t wolfpsa_kdf_hkdf(wolfpsa_kdf_ctx_t *ctx, if (hash_len <= 0) { 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; } @@ -725,6 +730,11 @@ static psa_status_t wolfpsa_kdf_hkdf(wolfpsa_kdf_ctx_t *ctx, } if (PSA_ALG_IS_HKDF_EXPAND(ctx->alg)) { + if ((wolfpsa_check_word32_length(ctx->secret_length) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(ctx->info_length) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(output_length) != PSA_SUCCESS)) { + return PSA_ERROR_INVALID_ARGUMENT; + } ret = wc_HKDF_Expand(hash_type, ctx->secret, (word32)ctx->secret_length, ctx->info, (word32)ctx->info_length, @@ -740,6 +750,12 @@ static psa_status_t wolfpsa_kdf_hkdf(wolfpsa_kdf_ctx_t *ctx, if (hash_len <= 0 || (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) || + (wolfpsa_check_word32_length(ctx->info_length) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(output_length) != PSA_SUCCESS)) { + return PSA_ERROR_INVALID_ARGUMENT; + } ret = wc_HKDF_Extract(hash_type, ctx->salt, (word32)ctx->salt_length, ctx->secret, (word32)ctx->secret_length, @@ -776,6 +792,12 @@ static psa_status_t wolfpsa_kdf_tls12_prf(wolfpsa_kdf_ctx_t *ctx, if (hash_type == WC_HASH_TYPE_NONE) { return PSA_ERROR_NOT_SUPPORTED; } + if ((wolfpsa_check_word32_length(output_length) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(ctx->secret_length) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(ctx->label_length) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(ctx->seed_length) != PSA_SUCCESS)) { + return PSA_ERROR_INVALID_ARGUMENT; + } ret = wc_PRF_TLS(output, (word32)output_length, ctx->secret, (word32)ctx->secret_length, ctx->label, (word32)ctx->label_length, @@ -812,6 +834,11 @@ static psa_status_t wolfpsa_kdf_tls12_psk_to_ms(wolfpsa_kdf_ctx_t *ctx, } premaster_len = 2u + ctx->secret_length + 2u + other_secret_length; + if ((wolfpsa_check_word32_length(output_length) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(premaster_len) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(ctx->seed_length) != PSA_SUCCESS)) { + return PSA_ERROR_INVALID_ARGUMENT; + } premaster = (uint8_t *)XMALLOC(premaster_len, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (premaster == NULL) { return PSA_ERROR_INSUFFICIENT_MEMORY; @@ -884,6 +911,10 @@ static psa_status_t wolfpsa_kdf_pbkdf2(wolfpsa_kdf_ctx_t *ctx, word32 out_sz = WC_AES_BLOCK_SIZE; psa_status_t status = PSA_SUCCESS; + if (wolfpsa_check_word32_length(ctx->password_length) != PSA_SUCCESS) { + return PSA_ERROR_INVALID_ARGUMENT; + } + XMEMSET(zero_key, 0, sizeof(zero_key)); ret = wc_InitCmac(&cmac, zero_key, (word32)sizeof(zero_key), WC_CMAC_AES, NULL); @@ -906,6 +937,10 @@ static psa_status_t wolfpsa_kdf_pbkdf2(wolfpsa_kdf_ctx_t *ctx, } block_input_len = ctx->salt_length + 4; + if (wolfpsa_check_word32_length(block_input_len) != PSA_SUCCESS) { + status = PSA_ERROR_INVALID_ARGUMENT; + goto cleanup; + } block_input = (uint8_t *)XMALLOC(block_input_len, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (block_input == NULL) { diff --git a/src/psa_lms_xmss.c b/src/psa_lms_xmss.c index 045e650..1cc284b 100644 --- a/src/psa_lms_xmss.c +++ b/src/psa_lms_xmss.c @@ -28,6 +28,7 @@ #if defined(WOLFSSL_PSA_ENGINE) && (defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS)) #include +#include "psa_size.h" #include #include #include @@ -53,6 +54,11 @@ psa_status_t psa_lms_generate_key(uint8_t *private_key, word32 priv_len32; word32 pub_len32; + if ((wolfpsa_check_word32_length(private_key_size) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(public_key_size) != PSA_SUCCESS)) { + return PSA_ERROR_INVALID_ARGUMENT; + } + /* Initialize LMS key */ ret = wc_LmsKey_Init(&key, NULL, INVALID_DEVID); if (ret != 0) { @@ -121,6 +127,12 @@ psa_status_t psa_lms_sign(const uint8_t *private_key, LmsKey key; word32 sig_len32; + if ((wolfpsa_check_word32_length(private_key_size) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(message_length) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(signature_size) != PSA_SUCCESS)) { + return PSA_ERROR_INVALID_ARGUMENT; + } + /* Initialize LMS key */ ret = wc_LmsKey_Init(&key, NULL, INVALID_DEVID); if (ret != 0) { @@ -162,6 +174,12 @@ psa_status_t psa_lms_verify(const uint8_t *public_key, LmsKey key; int verify_res = 0; + if ((wolfpsa_check_word32_length(public_key_size) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(message_length) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(signature_length) != PSA_SUCCESS)) { + return PSA_ERROR_INVALID_ARGUMENT; + } + /* Initialize LMS key */ ret = wc_LmsKey_Init(&key, NULL, INVALID_DEVID); if (ret != 0) { @@ -210,6 +228,11 @@ psa_status_t psa_xmss_generate_key(uint8_t *private_key, word32 priv_len32; word32 pub_len32; + if ((wolfpsa_check_word32_length(private_key_size) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(public_key_size) != PSA_SUCCESS)) { + return PSA_ERROR_INVALID_ARGUMENT; + } + /* Initialize XMSS key */ ret = wc_XmssKey_Init(&key, NULL, INVALID_DEVID); if (ret != 0) { @@ -278,6 +301,12 @@ psa_status_t psa_xmss_sign(const uint8_t *private_key, XmssKey key; word32 sig_len32; + if ((wolfpsa_check_word32_length(private_key_size) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(message_length) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(signature_size) != PSA_SUCCESS)) { + return PSA_ERROR_INVALID_ARGUMENT; + } + /* Initialize XMSS key */ ret = wc_XmssKey_Init(&key, NULL, INVALID_DEVID); if (ret != 0) { @@ -319,6 +348,12 @@ psa_status_t psa_xmss_verify(const uint8_t *public_key, XmssKey key; int verify_res = 0; + if ((wolfpsa_check_word32_length(public_key_size) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(message_length) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(signature_length) != PSA_SUCCESS)) { + return PSA_ERROR_INVALID_ARGUMENT; + } + /* Initialize XMSS key */ ret = wc_XmssKey_Init(&key, NULL, INVALID_DEVID); if (ret != 0) { diff --git a/src/psa_mac.c b/src/psa_mac.c index 9b28d70..f59f358 100644 --- a/src/psa_mac.c +++ b/src/psa_mac.c @@ -28,6 +28,7 @@ #if defined(WOLFSSL_PSA_ENGINE) #include +#include "psa_size.h" #include #include #include @@ -241,6 +242,11 @@ static psa_status_t wolfpsa_mac_setup(psa_mac_operation_t *operation, ctx->mac_length = PSA_MAC_LENGTH(attributes.type, attributes.bits, alg); ctx->full_length = PSA_MAC_LENGTH(attributes.type, attributes.bits, PSA_ALG_FULL_LENGTH_MAC(alg)); + if (wolfpsa_check_word32_length(key_data_length) != PSA_SUCCESS) { + wolfpsa_forcezero_free_key_data(key_data, key_data_length); + XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER); + return PSA_ERROR_INVALID_ARGUMENT; + } if (PSA_ALG_IS_HMAC(alg)) { int hash_type = wolfpsa_hash_type_from_alg(alg); @@ -322,6 +328,9 @@ psa_status_t psa_mac_update(psa_mac_operation_t *operation, if (input == NULL && input_length > 0) { return PSA_ERROR_INVALID_ARGUMENT; } + if (wolfpsa_check_word32_length(input_length) != PSA_SUCCESS) { + return PSA_ERROR_INVALID_ARGUMENT; + } if (input_length == 0) { return PSA_SUCCESS; diff --git a/src/psa_mldsa.c b/src/psa_mldsa.c index 24c843b..dd5f2f8 100644 --- a/src/psa_mldsa.c +++ b/src/psa_mldsa.c @@ -28,6 +28,7 @@ #if defined(WOLFSSL_PSA_ENGINE) && defined(WOLFSSL_HAVE_DILITHIUM) #include +#include "psa_size.h" #include #include #include @@ -73,6 +74,10 @@ psa_status_t psa_ml_dsa_generate_key(psa_ml_dsa_parameter_t parameter, if (type < 0) { return PSA_ERROR_NOT_SUPPORTED; } + if ((wolfpsa_check_word32_length(private_key_size) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(public_key_size) != PSA_SUCCESS)) { + return PSA_ERROR_INVALID_ARGUMENT; + } /* Initialize ML-DSA key */ ret = wc_dilithium_init_ex(&key, type, NULL, INVALID_DEVID); @@ -139,6 +144,11 @@ psa_status_t psa_ml_dsa_sign(psa_ml_dsa_parameter_t parameter, if (type < 0) { return PSA_ERROR_NOT_SUPPORTED; } + if ((wolfpsa_check_word32_length(private_key_size) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(message_length) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(signature_size) != PSA_SUCCESS)) { + return PSA_ERROR_INVALID_ARGUMENT; + } /* Initialize ML-DSA key */ ret = wc_dilithium_init_ex(&key, type, NULL, INVALID_DEVID); @@ -193,6 +203,11 @@ psa_status_t psa_ml_dsa_verify(psa_ml_dsa_parameter_t parameter, if (type < 0) { return PSA_ERROR_NOT_SUPPORTED; } + if ((wolfpsa_check_word32_length(public_key_size) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(message_length) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(signature_length) != PSA_SUCCESS)) { + return PSA_ERROR_INVALID_ARGUMENT; + } /* Initialize ML-DSA key */ ret = wc_dilithium_init_ex(&key, type, NULL, INVALID_DEVID); diff --git a/src/psa_rsa.c b/src/psa_rsa.c index 5b2b09c..5fb10c7 100644 --- a/src/psa_rsa.c +++ b/src/psa_rsa.c @@ -28,6 +28,7 @@ #if defined(WOLFSSL_PSA_ENGINE) && !defined(NO_RSA) #include +#include "psa_size.h" #include #include #include @@ -121,6 +122,11 @@ psa_status_t psa_asymmetric_sign_rsa(psa_key_type_t key_type, if (key_type != PSA_KEY_TYPE_RSA_KEY_PAIR) { return PSA_ERROR_INVALID_ARGUMENT; } + if ((wolfpsa_check_word32_length(key_buffer_size) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(hash_length) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(signature_size) != PSA_SUCCESS)) { + return PSA_ERROR_INVALID_ARGUMENT; + } /* Initialize RSA key */ ret = wc_InitRsaKey(&rsa_key, NULL); @@ -228,6 +234,11 @@ psa_status_t psa_asymmetric_verify_rsa(psa_key_type_t key_type, key_type != PSA_KEY_TYPE_RSA_KEY_PAIR) { return PSA_ERROR_INVALID_ARGUMENT; } + if ((wolfpsa_check_word32_length(key_buffer_size) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(hash_length) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(signature_length) != PSA_SUCCESS)) { + return PSA_ERROR_INVALID_ARGUMENT; + } /* Initialize RSA key */ ret = wc_InitRsaKey(&rsa_key, NULL); @@ -359,6 +370,12 @@ psa_status_t psa_asymmetric_encrypt_rsa(psa_key_type_t key_type, key_type != PSA_KEY_TYPE_RSA_KEY_PAIR) { return PSA_ERROR_INVALID_ARGUMENT; } + if ((wolfpsa_check_word32_length(key_buffer_size) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(input_length) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(output_size) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(salt_length) != PSA_SUCCESS)) { + return PSA_ERROR_INVALID_ARGUMENT; + } /* Initialize RSA key */ ret = wc_InitRsaKey(&rsa_key, NULL); @@ -460,6 +477,12 @@ psa_status_t psa_asymmetric_decrypt_rsa(psa_key_type_t key_type, if (key_type != PSA_KEY_TYPE_RSA_KEY_PAIR) { return PSA_ERROR_INVALID_ARGUMENT; } + if ((wolfpsa_check_word32_length(key_buffer_size) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(input_length) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(output_size) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(salt_length) != PSA_SUCCESS)) { + return PSA_ERROR_INVALID_ARGUMENT; + } /* Initialize RSA key */ ret = wc_InitRsaKey(&rsa_key, NULL); @@ -549,6 +572,10 @@ psa_status_t psa_asymmetric_generate_key_rsa(psa_key_type_t key_type, if (key_type != PSA_KEY_TYPE_RSA_KEY_PAIR) { return PSA_ERROR_INVALID_ARGUMENT; } + if ((wolfpsa_check_word32_length(private_key_size) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(public_key_size) != PSA_SUCCESS)) { + return PSA_ERROR_INVALID_ARGUMENT; + } /* Initialize RSA key */ ret = wc_InitRsaKey(&rsa_key, NULL); @@ -624,6 +651,10 @@ psa_status_t psa_asymmetric_export_public_key_rsa(psa_key_type_t key_type, key_type != PSA_KEY_TYPE_RSA_PUBLIC_KEY) { return PSA_ERROR_INVALID_ARGUMENT; } + if ((wolfpsa_check_word32_length(key_buffer_size) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(output_size) != PSA_SUCCESS)) { + return PSA_ERROR_INVALID_ARGUMENT; + } /* Initialize RSA key */ ret = wc_InitRsaKey(&rsa_key, NULL); diff --git a/src/psa_size.h b/src/psa_size.h new file mode 100644 index 0000000..059b851 --- /dev/null +++ b/src/psa_size.h @@ -0,0 +1,17 @@ +#ifndef WOLFPSA_SIZE_H +#define WOLFPSA_SIZE_H + +#include + +#include + +static inline psa_status_t wolfpsa_check_word32_length(size_t length) +{ + if (length > UINT32_MAX) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + return PSA_SUCCESS; +} + +#endif /* WOLFPSA_SIZE_H */ diff --git a/src/psa_tls_prf.c b/src/psa_tls_prf.c index b554ec1..57589c2 100644 --- a/src/psa_tls_prf.c +++ b/src/psa_tls_prf.c @@ -28,6 +28,7 @@ #if defined(WOLFSSL_PSA_ENGINE) && defined(WOLFSSL_TLS13) #include +#include "psa_size.h" #include #include #include @@ -74,15 +75,6 @@ static int psa_tls13_get_hash_type(psa_algorithm_t alg, enum wc_HashType* hashTy } } -static psa_status_t psa_tls13_check_word32_length(size_t length) -{ - if (length > UINT32_MAX) { - return PSA_ERROR_INVALID_ARGUMENT; - } - - return PSA_SUCCESS; -} - /* TLS 1.3 PRF (HKDF) */ psa_status_t psa_tls13_prf( psa_algorithm_t alg, @@ -114,10 +106,10 @@ psa_status_t psa_tls13_prf( if (output == NULL || output_length == 0) { return PSA_ERROR_INVALID_ARGUMENT; } - if ((psa_tls13_check_word32_length(secret_length) != PSA_SUCCESS) || - (psa_tls13_check_word32_length(label_length) != PSA_SUCCESS) || - (psa_tls13_check_word32_length(context_length) != PSA_SUCCESS) || - (psa_tls13_check_word32_length(output_length) != PSA_SUCCESS)) { + if ((wolfpsa_check_word32_length(secret_length) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(label_length) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(context_length) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(output_length) != PSA_SUCCESS)) { return PSA_ERROR_INVALID_ARGUMENT; } @@ -171,8 +163,8 @@ psa_status_t psa_tls13_hkdf_extract( if (output == NULL || output_size == 0 || output_length == NULL) { return PSA_ERROR_INVALID_ARGUMENT; } - if ((psa_tls13_check_word32_length(salt_length) != PSA_SUCCESS) || - (psa_tls13_check_word32_length(ikm_length) != PSA_SUCCESS)) { + if ((wolfpsa_check_word32_length(salt_length) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(ikm_length) != PSA_SUCCESS)) { return PSA_ERROR_INVALID_ARGUMENT; } @@ -237,9 +229,9 @@ psa_status_t psa_tls13_hkdf_expand( if (output == NULL || output_length == 0) { return PSA_ERROR_INVALID_ARGUMENT; } - if ((psa_tls13_check_word32_length(prk_length) != PSA_SUCCESS) || - (psa_tls13_check_word32_length(info_length) != PSA_SUCCESS) || - (psa_tls13_check_word32_length(output_length) != PSA_SUCCESS)) { + if ((wolfpsa_check_word32_length(prk_length) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(info_length) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(output_length) != PSA_SUCCESS)) { return PSA_ERROR_INVALID_ARGUMENT; } @@ -295,10 +287,10 @@ psa_status_t psa_tls13_hkdf_expand_label( if (output == NULL || output_length == 0) { return PSA_ERROR_INVALID_ARGUMENT; } - if ((psa_tls13_check_word32_length(secret_length) != PSA_SUCCESS) || - (psa_tls13_check_word32_length(label_length) != PSA_SUCCESS) || - (psa_tls13_check_word32_length(context_length) != PSA_SUCCESS) || - (psa_tls13_check_word32_length(output_length) != PSA_SUCCESS)) { + if ((wolfpsa_check_word32_length(secret_length) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(label_length) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(context_length) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(output_length) != PSA_SUCCESS)) { return PSA_ERROR_INVALID_ARGUMENT; } From 4913be8a0de724fe045881f42eba35a23acf1d21 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 30 Mar 2026 07:55:01 +0200 Subject: [PATCH 06/15] Guard ChaCha20-Poly1305 output length F/1657 --- src/psa_chacha20_poly1305.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/psa_chacha20_poly1305.c b/src/psa_chacha20_poly1305.c index 846682c..5b3a424 100644 --- a/src/psa_chacha20_poly1305.c +++ b/src/psa_chacha20_poly1305.c @@ -83,6 +83,7 @@ psa_status_t psa_chacha20_poly1305_encrypt( uint8_t *ciphertext, size_t ciphertext_size, size_t *ciphertext_length) { int ret; + size_t required_size; /* Check parameters */ if (key == NULL || key_length != CHACHA20_POLY1305_AEAD_KEYSIZE) { @@ -111,8 +112,13 @@ psa_status_t psa_chacha20_poly1305_encrypt( return PSA_ERROR_INVALID_ARGUMENT; } + if (plaintext_length > SIZE_MAX - CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE) { + return PSA_ERROR_BUFFER_TOO_SMALL; + } + required_size = plaintext_length + CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE; + /* Check output buffer size */ - if (ciphertext_size < plaintext_length + CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE) { + if (ciphertext_size < required_size) { return PSA_ERROR_BUFFER_TOO_SMALL; } @@ -131,7 +137,7 @@ psa_status_t psa_chacha20_poly1305_encrypt( } /* Set output length */ - *ciphertext_length = plaintext_length + CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE; + *ciphertext_length = required_size; return PSA_SUCCESS; } @@ -235,8 +241,13 @@ psa_status_t psa_xchacha20_poly1305_encrypt( return PSA_ERROR_INVALID_ARGUMENT; } + if (plaintext_length > SIZE_MAX - CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE) { + return PSA_ERROR_BUFFER_TOO_SMALL; + } + required_size = plaintext_length + CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE; + /* Check output buffer size */ - if (ciphertext_size < plaintext_length + CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE) { + if (ciphertext_size < required_size) { return PSA_ERROR_BUFFER_TOO_SMALL; } @@ -254,7 +265,7 @@ psa_status_t psa_xchacha20_poly1305_encrypt( } /* Set output length */ - *ciphertext_length = plaintext_length + CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE; + *ciphertext_length = required_size; return PSA_SUCCESS; } From 7c55d7b28d865e9faa76aab516d9aacec2bec6a8 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 30 Mar 2026 07:56:18 +0200 Subject: [PATCH 07/15] Fix XChaCha output length guard F/1657 --- src/psa_chacha20_poly1305.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/psa_chacha20_poly1305.c b/src/psa_chacha20_poly1305.c index 5b3a424..c6d136e 100644 --- a/src/psa_chacha20_poly1305.c +++ b/src/psa_chacha20_poly1305.c @@ -217,6 +217,7 @@ psa_status_t psa_xchacha20_poly1305_encrypt( uint8_t *ciphertext, size_t ciphertext_size, size_t *ciphertext_length) { int ret; + size_t required_size; /* Check parameters */ if (key == NULL || key_length != CHACHA20_POLY1305_AEAD_KEYSIZE) { From cc2af5bb52b29bb1d91ff94289c4fadf6cf6c258 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 30 Mar 2026 07:57:53 +0200 Subject: [PATCH 08/15] zeroize ECC DER signature buffer F/1658 --- src/psa_ecc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/psa_ecc.c b/src/psa_ecc.c index 1853d51..c965879 100644 --- a/src/psa_ecc.c +++ b/src/psa_ecc.c @@ -151,12 +151,14 @@ psa_status_t psa_asymmetric_sign_ecc(psa_key_type_t key_type, wc_ecc_free(&ecc); if (ret != 0) { + wc_ForceZero(der_sig, sig_len); XFREE(der_sig, NULL, DYNAMIC_TYPE_TMP_BUFFER); return wc_error_to_psa_status(ret); } rs = (byte*)XMALLOC(raw_sig_len, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (rs == NULL) { + wc_ForceZero(der_sig, sig_len); XFREE(der_sig, NULL, DYNAMIC_TYPE_TMP_BUFFER); return PSA_ERROR_INSUFFICIENT_MEMORY; } @@ -164,6 +166,7 @@ psa_status_t psa_asymmetric_sign_ecc(psa_key_type_t key_type, r_len = (word32)key_bytes; s_len = (word32)key_bytes; ret = wc_ecc_sig_to_rs(der_sig, der_len, rs, &r_len, rs + key_bytes, &s_len); + wc_ForceZero(der_sig, sig_len); XFREE(der_sig, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (ret != 0) { XFREE(rs, NULL, DYNAMIC_TYPE_TMP_BUFFER); From cda46734af2957c2056ba203e4cf1af8b8eca23f Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 30 Mar 2026 07:58:44 +0200 Subject: [PATCH 09/15] Zero RSA PKCS#1 v1.5 sign buffer F/1659 --- src/psa_rsa.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/psa_rsa.c b/src/psa_rsa.c index 5fb10c7..cf8dbd7 100644 --- a/src/psa_rsa.c +++ b/src/psa_rsa.c @@ -114,6 +114,7 @@ psa_status_t psa_asymmetric_sign_rsa(psa_key_type_t key_type, int hash_oid; byte* sig_input = NULL; word32 sig_input_len = 0; + word32 sig_input_alloc_len = 0; WC_RNG rng; (void)key_bits; @@ -175,6 +176,7 @@ psa_status_t psa_asymmetric_sign_rsa(psa_key_type_t key_type, wc_FreeRsaKey(&rsa_key); return PSA_ERROR_INSUFFICIENT_MEMORY; } + sig_input_alloc_len = PSA_HASH_MAX_SIZE + 32; sig_input_len = wc_EncodeSignature(sig_input, hash, (word32)hash_length, hash_oid); } @@ -196,6 +198,7 @@ psa_status_t psa_asymmetric_sign_rsa(psa_key_type_t key_type, } if (sig_input != NULL && sig_input != hash) { + wc_ForceZero(sig_input, sig_input_alloc_len); XFREE(sig_input, NULL, DYNAMIC_TYPE_TMP_BUFFER); } wc_FreeRng(&rng); From a810ac2951ef11adee569c53abfe9d4b67ab8747 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 30 Mar 2026 08:03:25 +0200 Subject: [PATCH 10/15] Guard AEAD final word32 lengths F/1663 --- src/psa_aead.c | 11 ++++ test/psa_server/psa_api_test.c | 106 +++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+) diff --git a/src/psa_aead.c b/src/psa_aead.c index d3c6451..0ba7e74 100644 --- a/src/psa_aead.c +++ b/src/psa_aead.c @@ -34,6 +34,7 @@ #include #include #include "psa_aead_internal.h" +#include "psa_size.h" static wolfpsa_aead_ctx_t* wolfpsa_aead_get_ctx(psa_aead_operation_t *operation) { @@ -475,6 +476,11 @@ static psa_status_t wolfpsa_aead_encrypt_final(wolfpsa_aead_ctx_t *ctx, return PSA_ERROR_BUFFER_TOO_SMALL; } + if ((wolfpsa_check_word32_length(ctx->input_length) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(ctx->aad_length) != PSA_SUCCESS)) { + return PSA_ERROR_INVALID_ARGUMENT; + } + if (PSA_ALG_AEAD_EQUAL(ctx->alg, PSA_ALG_GCM)) { Aes aes; ret = wc_AesInit(&aes, NULL, INVALID_DEVID); @@ -575,6 +581,11 @@ static psa_status_t wolfpsa_aead_decrypt_final(wolfpsa_aead_ctx_t *ctx, return PSA_ERROR_INVALID_SIGNATURE; } + if ((wolfpsa_check_word32_length(ctx->input_length) != PSA_SUCCESS) || + (wolfpsa_check_word32_length(ctx->aad_length) != PSA_SUCCESS)) { + return PSA_ERROR_INVALID_ARGUMENT; + } + if (PSA_ALG_AEAD_EQUAL(ctx->alg, PSA_ALG_GCM)) { Aes aes; ret = wc_AesInit(&aes, NULL, INVALID_DEVID); diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index 8968ae7..b988083 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -609,6 +609,106 @@ static int test_aead_multipart_length_overflow_rejected(void) return ret; } +static int test_aead_finish_verify_word32_overflow_rejected(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 empty_gcm_tag[16] = { + 0x58,0xe2,0xfc,0xce,0xfa,0x7e,0x30,0x61, + 0x36,0x7f,0x1d,0x57,0xa4,0xe7,0x45,0x5a + }; + uint8_t out[1]; + uint8_t tag[sizeof(empty_gcm_tag)]; + size_t out_len = 0; + size_t tag_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(); + wolfpsa_aead_ctx_t *ctx; + int ret = TEST_OK; + psa_status_t st; + + psa_set_key_type(&attrs, PSA_KEY_TYPE_AES); + psa_set_key_bits(&attrs, 128); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT); + psa_set_key_algorithm(&attrs, PSA_ALG_GCM); + + st = psa_import_key(&attrs, key, sizeof(key), &key_id); + if (check_status(st, "psa_import_key(GCM word32 overflow)") != TEST_OK) { + return TEST_FAIL; + } + + st = psa_aead_encrypt_setup(&op, key_id, PSA_ALG_GCM); + if (check_status(st, "psa_aead_encrypt_setup(word32 input)") != TEST_OK) goto cleanup; + st = psa_aead_set_nonce(&op, nonce, sizeof(nonce)); + if (check_status(st, "psa_aead_set_nonce(word32 input)") != TEST_OK) goto cleanup; + ctx = wolfpsa_aead_get_ctx_ptr(&op); + ctx->input = NULL; + ctx->input_length = (size_t)UINT32_MAX + 1u; + st = psa_aead_finish(&op, out, SIZE_MAX, &out_len, tag, sizeof(tag), &tag_len); + if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, + "psa_aead_finish rejects input_length above word32") != 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(word32 aad)") != TEST_OK) goto cleanup; + st = psa_aead_set_nonce(&op, nonce, sizeof(nonce)); + if (check_status(st, "psa_aead_set_nonce(word32 aad)") != TEST_OK) goto cleanup; + ctx = wolfpsa_aead_get_ctx_ptr(&op); + ctx->aad = NULL; + ctx->aad_length = (size_t)UINT32_MAX + 1u; + st = psa_aead_finish(&op, out, sizeof(out), &out_len, tag, sizeof(tag), &tag_len); + if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, + "psa_aead_finish rejects aad_length above word32") != 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(word32 input)") != TEST_OK) goto cleanup; + st = psa_aead_set_nonce(&op, nonce, sizeof(nonce)); + if (check_status(st, "psa_aead_set_nonce(word32 verify input)") != TEST_OK) goto cleanup; + ctx = wolfpsa_aead_get_ctx_ptr(&op); + ctx->input = NULL; + ctx->input_length = (size_t)UINT32_MAX + 1u; + st = psa_aead_verify(&op, out, SIZE_MAX, &out_len, empty_gcm_tag, sizeof(empty_gcm_tag)); + if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, + "psa_aead_verify rejects input_length above word32") != 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(word32 aad)") != TEST_OK) goto cleanup; + st = psa_aead_set_nonce(&op, nonce, sizeof(nonce)); + if (check_status(st, "psa_aead_set_nonce(word32 verify aad)") != TEST_OK) goto cleanup; + ctx = wolfpsa_aead_get_ctx_ptr(&op); + ctx->aad = NULL; + ctx->aad_length = (size_t)UINT32_MAX + 1u; + st = psa_aead_verify(&op, out, sizeof(out), &out_len, empty_gcm_tag, sizeof(empty_gcm_tag)); + if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, + "psa_aead_verify rejects aad_length above word32") != 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 word32 overflow)") != TEST_OK) { + return TEST_FAIL; + } + return ret; +} + static int test_chacha20_poly1305_rejects_aes_key(void) { static const uint8_t key[32] = { @@ -1239,6 +1339,12 @@ int main(int argc, char** argv) return TEST_FAIL; } } + if (only == NULL || strcmp(only, "aead_word32_overflow") == 0) { + if (run_named_test("aead_word32_overflow", + test_aead_finish_verify_word32_overflow_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 b404aeca5f2a29e5fae232727f921ff917e0fc57 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 30 Mar 2026 08:04:25 +0200 Subject: [PATCH 11/15] zeroize ECC raw rs buffer before free F/1664 --- src/psa_ecc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/psa_ecc.c b/src/psa_ecc.c index c965879..d55d20d 100644 --- a/src/psa_ecc.c +++ b/src/psa_ecc.c @@ -169,10 +169,12 @@ psa_status_t psa_asymmetric_sign_ecc(psa_key_type_t key_type, wc_ForceZero(der_sig, sig_len); XFREE(der_sig, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (ret != 0) { + wc_ForceZero(rs, raw_sig_len); XFREE(rs, NULL, DYNAMIC_TYPE_TMP_BUFFER); return wc_error_to_psa_status(ret); } if (r_len > key_bytes || s_len > key_bytes) { + wc_ForceZero(rs, raw_sig_len); XFREE(rs, NULL, DYNAMIC_TYPE_TMP_BUFFER); return PSA_ERROR_INVALID_SIGNATURE; } @@ -181,6 +183,7 @@ psa_status_t psa_asymmetric_sign_ecc(psa_key_type_t key_type, XMEMCPY(signature + (key_bytes - r_len), rs, r_len); XMEMCPY(signature + key_bytes + (key_bytes - s_len), rs + key_bytes, s_len); + wc_ForceZero(rs, raw_sig_len); XFREE(rs, NULL, DYNAMIC_TYPE_TMP_BUFFER); *signature_length = raw_sig_len; From 005238e9da76dee3440cd7471ef9dfef195a1aac Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 30 Mar 2026 08:05:37 +0200 Subject: [PATCH 12/15] zeroize RSA verify decoded buffers F/1660 --- src/psa_rsa.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/psa_rsa.c b/src/psa_rsa.c index cf8dbd7..ba2abe1 100644 --- a/src/psa_rsa.c +++ b/src/psa_rsa.c @@ -292,6 +292,7 @@ psa_status_t psa_asymmetric_verify_rsa(psa_key_type_t key_type, ret = SIG_VERIFY_E; } } + wc_ForceZero(decoded, sizeof(decoded)); } else if (padding == WC_RSA_PSS_PAD) { #ifdef WC_RSA_PSS @@ -305,6 +306,7 @@ psa_status_t psa_asymmetric_verify_rsa(psa_key_type_t key_type, #else if (PSA_ALG_IS_RSA_PSS_ANY_SALT(alg)) { ret = NOT_COMPILED_IN; + wc_ForceZero(decoded, sizeof(decoded)); wc_FreeRsaKey(&rsa_key); return wc_error_to_psa_status(ret); } @@ -322,6 +324,7 @@ psa_status_t psa_asymmetric_verify_rsa(psa_key_type_t key_type, ret = (int)hash_length; } } + wc_ForceZero(decoded, sizeof(decoded)); #else ret = NOT_COMPILED_IN; #endif From a0e471e761b35ab72a72ae1009bff1d46e2db8ab Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 30 Mar 2026 08:07:05 +0200 Subject: [PATCH 13/15] zero message hashes before return F/1661 --- src/psa_asymmetric_api.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/psa_asymmetric_api.c b/src/psa_asymmetric_api.c index 3000683..c61f69d 100644 --- a/src/psa_asymmetric_api.c +++ b/src/psa_asymmetric_api.c @@ -452,23 +452,22 @@ psa_status_t psa_sign_message(psa_key_id_t key, hash_alg = 0; hash_length = input_length; if (hash_length > sizeof(hash)) { - wolfpsa_forcezero_free_key_data(key_data, key_data_length); - return PSA_ERROR_INVALID_ARGUMENT; + status = PSA_ERROR_INVALID_ARGUMENT; + goto cleanup; } XMEMCPY(hash, input, hash_length); } else { hash_alg = PSA_ALG_SIGN_GET_HASH(alg); if (hash_alg == 0) { - wolfpsa_forcezero_free_key_data(key_data, key_data_length); - return PSA_ERROR_NOT_SUPPORTED; + status = PSA_ERROR_NOT_SUPPORTED; + goto cleanup; } status = psa_hash_compute(hash_alg, input, input_length, hash, sizeof(hash), &hash_length); if (status != PSA_SUCCESS) { - wolfpsa_forcezero_free_key_data(key_data, key_data_length); - return status; + goto cleanup; } } @@ -517,6 +516,8 @@ psa_status_t psa_sign_message(psa_key_id_t key, status = PSA_ERROR_NOT_SUPPORTED; } +cleanup: + wc_ForceZero(hash, sizeof(hash)); wolfpsa_forcezero_free_key_data(key_data, key_data_length); return status; } @@ -550,23 +551,22 @@ psa_status_t psa_verify_message(psa_key_id_t key, hash_alg = 0; hash_length = input_length; if (hash_length > sizeof(hash)) { - wolfpsa_forcezero_free_key_data(key_data, key_data_length); - return PSA_ERROR_INVALID_ARGUMENT; + status = PSA_ERROR_INVALID_ARGUMENT; + goto cleanup; } XMEMCPY(hash, input, hash_length); } else { hash_alg = PSA_ALG_SIGN_GET_HASH(alg); if (hash_alg == 0) { - wolfpsa_forcezero_free_key_data(key_data, key_data_length); - return PSA_ERROR_NOT_SUPPORTED; + status = PSA_ERROR_NOT_SUPPORTED; + goto cleanup; } status = psa_hash_compute(hash_alg, input, input_length, hash, sizeof(hash), &hash_length); if (status != PSA_SUCCESS) { - wolfpsa_forcezero_free_key_data(key_data, key_data_length); - return status; + goto cleanup; } } @@ -612,6 +612,8 @@ psa_status_t psa_verify_message(psa_key_id_t key, status = PSA_ERROR_NOT_SUPPORTED; } +cleanup: + wc_ForceZero(hash, sizeof(hash)); wolfpsa_forcezero_free_key_data(key_data, key_data_length); return status; } From b730c0a4d59e87aa24b4754747593e7811577e4b Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 30 Mar 2026 08:08:47 +0200 Subject: [PATCH 14/15] zero computed hash buffers in hash checks F/1665 --- src/psa_hash_engine.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/psa_hash_engine.c b/src/psa_hash_engine.c index 4228ff5..5db9ecd 100644 --- a/src/psa_hash_engine.c +++ b/src/psa_hash_engine.c @@ -644,18 +644,24 @@ psa_status_t psa_hash_verify(psa_hash_operation_t *operation, status = psa_hash_finish(operation, computed_hash, sizeof(computed_hash), &computed_hash_length); if (status != PSA_SUCCESS) { - return status; + goto cleanup; } if (hash_length != computed_hash_length) { - return PSA_ERROR_INVALID_SIGNATURE; + status = PSA_ERROR_INVALID_SIGNATURE; + goto cleanup; } if (ConstantCompare(computed_hash, hash, (int)computed_hash_length) != 0) { - return PSA_ERROR_INVALID_SIGNATURE; + status = PSA_ERROR_INVALID_SIGNATURE; + goto cleanup; } - return PSA_SUCCESS; + status = PSA_SUCCESS; + +cleanup: + wc_ForceZero(computed_hash, sizeof(computed_hash)); + return status; } /* Abort a hash operation */ @@ -870,15 +876,20 @@ psa_status_t psa_hash_compare(psa_algorithm_t alg, status = psa_hash_compute(alg, input, input_length, computed_hash, sizeof(computed_hash), &computed_hash_length); if (status != PSA_SUCCESS) { - return status; + goto cleanup; } /* Compare the computed hash with the reference hash */ if (ConstantCompare(computed_hash, hash, (int)computed_hash_length) != 0) { - return PSA_ERROR_INVALID_SIGNATURE; + status = PSA_ERROR_INVALID_SIGNATURE; + goto cleanup; } - return PSA_SUCCESS; + status = PSA_SUCCESS; + +cleanup: + wc_ForceZero(computed_hash, sizeof(computed_hash)); + return status; } #endif /* WOLFSSL_PSA_ENGINE */ From 00d3232657d70dd68316c614a933d7142c0b70a0 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 30 Mar 2026 10:56:26 +0200 Subject: [PATCH 15/15] Fix regression on KDF state machine --- src/psa_key_derivation.c | 14 ++++++++++---- test/psa_server/psa_api_test.c | 15 +++++++++++++++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/psa_key_derivation.c b/src/psa_key_derivation.c index 42856e6..0f8e57c 100644 --- a/src/psa_key_derivation.c +++ b/src/psa_key_derivation.c @@ -248,12 +248,15 @@ static psa_status_t wolfpsa_kdf_validate_step(wolfpsa_kdf_ctx_t *ctx, return PSA_ERROR_INVALID_ARGUMENT; } if (step == PSA_KEY_DERIVATION_INPUT_SALT) { - if (ctx->steps_set != 0) { + if ((ctx->steps_set & WOLFPSA_KDF_STEP_SECRET) != 0) { return PSA_ERROR_BAD_STATE; } return PSA_SUCCESS; } if (step == PSA_KEY_DERIVATION_INPUT_SECRET) { + if ((ctx->steps_set & WOLFPSA_KDF_STEP_SALT) == 0) { + return PSA_ERROR_BAD_STATE; + } if ((ctx->steps_set & WOLFPSA_KDF_STEP_SECRET) != 0) { return PSA_ERROR_BAD_STATE; } @@ -1053,17 +1056,20 @@ psa_status_t psa_key_derivation_output_bytes(psa_key_derivation_operation_t *ope } else if (PSA_ALG_IS_ANY_HKDF(ctx->alg)) { if (PSA_ALG_IS_HKDF_EXTRACT(ctx->alg)) { - if ((ctx->steps_set & WOLFPSA_KDF_STEP_SECRET) == 0) { + if ((ctx->steps_set & WOLFPSA_KDF_STEP_SALT) == 0 || + (ctx->steps_set & WOLFPSA_KDF_STEP_SECRET) == 0) { return PSA_ERROR_BAD_STATE; } } else if (PSA_ALG_IS_HKDF_EXPAND(ctx->alg)) { - if ((ctx->steps_set & WOLFPSA_KDF_STEP_SECRET) == 0) { + if ((ctx->steps_set & WOLFPSA_KDF_STEP_SECRET) == 0 || + (ctx->steps_set & WOLFPSA_KDF_STEP_INFO) == 0) { return PSA_ERROR_BAD_STATE; } } else { - if ((ctx->steps_set & WOLFPSA_KDF_STEP_SECRET) == 0) { + if ((ctx->steps_set & WOLFPSA_KDF_STEP_SECRET) == 0 || + (ctx->steps_set & WOLFPSA_KDF_STEP_INFO) == 0) { return PSA_ERROR_BAD_STATE; } } diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index b988083..47a5ffb 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -1160,6 +1160,11 @@ static int test_kdf_hkdf_extract_optional_salt(void) if (check_status(st, "psa_key_derivation_setup(HKDF_EXTRACT optional salt)") != 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 optional salt)") != 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 optional salt)") != TEST_OK) { @@ -1217,6 +1222,11 @@ static int test_kdf_hkdf_expand_optional_info(void) (void)psa_key_derivation_abort(&op); return TEST_FAIL; } + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_INFO, NULL, 0); + if (check_status(st, "psa_key_derivation_input_bytes(INFO HKDF_EXPAND optional info)") != 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_EXPAND optional info)") != TEST_OK) { (void)psa_key_derivation_abort(&op); @@ -1264,6 +1274,11 @@ static int test_kdf_hkdf_optional_info(void) (void)psa_key_derivation_abort(&op); return TEST_FAIL; } + st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_INFO, NULL, 0); + if (check_status(st, "psa_key_derivation_input_bytes(INFO HKDF optional info)") != 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 optional info)") != TEST_OK) { (void)psa_key_derivation_abort(&op);