From 20571a9beb4fc4604d571785f33b4a55c91cae7c Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Thu, 11 Jun 2026 16:02:57 -0400 Subject: [PATCH 1/4] Force-zero wc_AesSivDecrypt*() output buffer on authentication failure --- wolfcrypt/src/aes.c | 6 +++++- wolfcrypt/test/test.c | 28 +++++++++++++++++++--------- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index 021b5f84c3..0993f10fd5 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -17094,10 +17094,14 @@ static WARN_UNUSED_RESULT int AesSivCipher( WOLFSSL_MSG("S2V failed."); } - if (ConstantCompare(siv, sivTmp, WC_AES_BLOCK_SIZE) != 0) { + if (ret == 0 && ConstantCompare(siv, sivTmp, WC_AES_BLOCK_SIZE) != 0) { WOLFSSL_MSG("Computed SIV doesn't match received SIV."); ret = AES_SIV_AUTH_E; } + + if (ret != 0) { + ForceZero(out, dataSz); + } } #ifdef WOLFSSL_SMALL_STACK diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index d76dd112c9..413d7500e3 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -74789,28 +74789,38 @@ static wc_test_ret_t aes_siv_negative_test(const AesSivTestVector* testVectors) byte computedCiphertext[82]; byte computedPlaintext[82]; byte siv[WC_AES_BLOCK_SIZE]; + word32 j; wc_test_ret_t ret; /* Negative test: corrupted SIV must be rejected with AES_SIV_AUTH_E. */ - ret = wc_AesSivEncrypt(testVectors[0].key, testVectors[0].keySz, - testVectors[0].assoc1, testVectors[0].assoc1Sz, - testVectors[0].nonce, testVectors[0].nonceSz, - testVectors[0].plaintext, - testVectors[0].plaintextSz, siv, + ret = wc_AesSivEncrypt(testVectors[5].key, testVectors[5].keySz, + testVectors[5].assoc1, testVectors[5].assoc1Sz, + testVectors[5].nonce, testVectors[5].nonceSz, + testVectors[5].plaintext, + testVectors[5].plaintextSz, siv, computedCiphertext); if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); } + XMEMSET(computedPlaintext, 0xFF, sizeof(computedPlaintext)); /* Corrupt one byte of the SIV tag. */ siv[0] ^= 0x01; - ret = wc_AesSivDecrypt(testVectors[0].key, testVectors[0].keySz, - testVectors[0].assoc1, testVectors[0].assoc1Sz, - testVectors[0].nonce, testVectors[0].nonceSz, - computedCiphertext, testVectors[0].plaintextSz, + ret = wc_AesSivDecrypt(testVectors[5].key, testVectors[5].keySz, + testVectors[5].assoc1, testVectors[5].assoc1Sz, + testVectors[5].nonce, testVectors[5].nonceSz, + computedCiphertext, testVectors[5].plaintextSz, siv, computedPlaintext); if (ret != WC_NO_ERR_TRACE(AES_SIV_AUTH_E)) { return WC_TEST_RET_ENC_EC(ret); } + if (testVectors[5].plaintextSz == 0U) { + return WC_TEST_RET_ENC_NC; + } + for (j = 0; j < testVectors[5].plaintextSz; ++j) { + if (computedPlaintext[j] != 0) { + return WC_TEST_RET_ENC_NC; + } + } return 0; } From 7064eeca8907a967817389f1223d0fc88a1b88e3 Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Fri, 12 Jun 2026 12:19:06 -0400 Subject: [PATCH 2/4] Force-zero output buffer for AES-SIV decrypt for other AES errors as well --- wolfcrypt/src/aes.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index 0993f10fd5..9ce54277b7 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -17098,10 +17098,10 @@ static WARN_UNUSED_RESULT int AesSivCipher( WOLFSSL_MSG("Computed SIV doesn't match received SIV."); ret = AES_SIV_AUTH_E; } + } - if (ret != 0) { - ForceZero(out, dataSz); - } + if (ret != 0) { + ForceZero(out, dataSz); } #ifdef WOLFSSL_SMALL_STACK From 5bc5251c3e6df530890a3e908e5b4a7642f8e1f3 Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Fri, 12 Jun 2026 12:23:16 -0400 Subject: [PATCH 3/4] Dynamically find AES-SIV test vector index instead of hardcoding in unit test --- wolfcrypt/test/test.c | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 413d7500e3..ccb317342b 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -74784,20 +74784,32 @@ static wc_test_ret_t aes_siv_oneassoc_test(const AesSivTestVector* testVectors, return 0; } -static wc_test_ret_t aes_siv_negative_test(const AesSivTestVector* testVectors) +static wc_test_ret_t aes_siv_negative_test(const AesSivTestVector* testVectors, size_t n_vectors) { byte computedCiphertext[82]; byte computedPlaintext[82]; byte siv[WC_AES_BLOCK_SIZE]; word32 j; wc_test_ret_t ret; + size_t vector_idx; + + /* Find a test vector that has a non-empty plaintext size */ + for (vector_idx = 0U; vector_idx < n_vectors; vector_idx++) + { + if (testVectors[vector_idx].plaintextSz > 0U) + break; + } + if (vector_idx == n_vectors) + { + return WC_TEST_RET_ENC_NC; + } /* Negative test: corrupted SIV must be rejected with AES_SIV_AUTH_E. */ - ret = wc_AesSivEncrypt(testVectors[5].key, testVectors[5].keySz, - testVectors[5].assoc1, testVectors[5].assoc1Sz, - testVectors[5].nonce, testVectors[5].nonceSz, - testVectors[5].plaintext, - testVectors[5].plaintextSz, siv, + ret = wc_AesSivEncrypt(testVectors[vector_idx].key, testVectors[vector_idx].keySz, + testVectors[vector_idx].assoc1, testVectors[vector_idx].assoc1Sz, + testVectors[vector_idx].nonce, testVectors[vector_idx].nonceSz, + testVectors[vector_idx].plaintext, + testVectors[vector_idx].plaintextSz, siv, computedCiphertext); if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); @@ -74805,18 +74817,15 @@ static wc_test_ret_t aes_siv_negative_test(const AesSivTestVector* testVectors) XMEMSET(computedPlaintext, 0xFF, sizeof(computedPlaintext)); /* Corrupt one byte of the SIV tag. */ siv[0] ^= 0x01; - ret = wc_AesSivDecrypt(testVectors[5].key, testVectors[5].keySz, - testVectors[5].assoc1, testVectors[5].assoc1Sz, - testVectors[5].nonce, testVectors[5].nonceSz, - computedCiphertext, testVectors[5].plaintextSz, + ret = wc_AesSivDecrypt(testVectors[vector_idx].key, testVectors[vector_idx].keySz, + testVectors[vector_idx].assoc1, testVectors[vector_idx].assoc1Sz, + testVectors[vector_idx].nonce, testVectors[vector_idx].nonceSz, + computedCiphertext, testVectors[vector_idx].plaintextSz, siv, computedPlaintext); if (ret != WC_NO_ERR_TRACE(AES_SIV_AUTH_E)) { return WC_TEST_RET_ENC_EC(ret); } - if (testVectors[5].plaintextSz == 0U) { - return WC_TEST_RET_ENC_NC; - } - for (j = 0; j < testVectors[5].plaintextSz; ++j) { + for (j = 0; j < testVectors[vector_idx].plaintextSz; ++j) { if (computedPlaintext[j] != 0) { return WC_TEST_RET_ENC_NC; } @@ -75012,7 +75021,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t aes_siv_test(void) ret = aes_siv_multiassoc_test(testVectors, AES_SIV_TEST_VECTORS); if (ret != 0) return ret; - ret = aes_siv_negative_test(testVectors); + ret = aes_siv_negative_test(testVectors, AES_SIV_TEST_VECTORS); if (ret != 0) return ret; return 0; From f420c66c388550050b2e76b48e854acae3c2cb7c Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Fri, 12 Jun 2026 14:46:43 -0400 Subject: [PATCH 4/4] Fix aes_siv_negative_test unit test style for skoll --- wolfcrypt/test/test.c | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index ccb317342b..04f6a19c26 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -74784,44 +74784,48 @@ static wc_test_ret_t aes_siv_oneassoc_test(const AesSivTestVector* testVectors, return 0; } -static wc_test_ret_t aes_siv_negative_test(const AesSivTestVector* testVectors, size_t n_vectors) +static wc_test_ret_t aes_siv_negative_test(const AesSivTestVector* testVectors, + int n_vectors) { byte computedCiphertext[82]; byte computedPlaintext[82]; byte siv[WC_AES_BLOCK_SIZE]; word32 j; wc_test_ret_t ret; - size_t vector_idx; + int vector_idx; /* Find a test vector that has a non-empty plaintext size */ - for (vector_idx = 0U; vector_idx < n_vectors; vector_idx++) - { + for (vector_idx = 0; vector_idx < n_vectors; vector_idx++) { if (testVectors[vector_idx].plaintextSz > 0U) break; } - if (vector_idx == n_vectors) - { + if (vector_idx == n_vectors) { return WC_TEST_RET_ENC_NC; } /* Negative test: corrupted SIV must be rejected with AES_SIV_AUTH_E. */ - ret = wc_AesSivEncrypt(testVectors[vector_idx].key, testVectors[vector_idx].keySz, - testVectors[vector_idx].assoc1, testVectors[vector_idx].assoc1Sz, - testVectors[vector_idx].nonce, testVectors[vector_idx].nonceSz, - testVectors[vector_idx].plaintext, - testVectors[vector_idx].plaintextSz, siv, - computedCiphertext); + ret = wc_AesSivEncrypt( + testVectors[vector_idx].key, + testVectors[vector_idx].keySz, + testVectors[vector_idx].assoc1, + testVectors[vector_idx].assoc1Sz, + testVectors[vector_idx].nonce, + testVectors[vector_idx].nonceSz, + testVectors[vector_idx].plaintext, + testVectors[vector_idx].plaintextSz, + siv, computedCiphertext); if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); } XMEMSET(computedPlaintext, 0xFF, sizeof(computedPlaintext)); /* Corrupt one byte of the SIV tag. */ siv[0] ^= 0x01; - ret = wc_AesSivDecrypt(testVectors[vector_idx].key, testVectors[vector_idx].keySz, - testVectors[vector_idx].assoc1, testVectors[vector_idx].assoc1Sz, - testVectors[vector_idx].nonce, testVectors[vector_idx].nonceSz, - computedCiphertext, testVectors[vector_idx].plaintextSz, - siv, computedPlaintext); + ret = wc_AesSivDecrypt( + testVectors[vector_idx].key, testVectors[vector_idx].keySz, + testVectors[vector_idx].assoc1, testVectors[vector_idx].assoc1Sz, + testVectors[vector_idx].nonce, testVectors[vector_idx].nonceSz, + computedCiphertext, testVectors[vector_idx].plaintextSz, + siv, computedPlaintext); if (ret != WC_NO_ERR_TRACE(AES_SIV_AUTH_E)) { return WC_TEST_RET_ENC_EC(ret); }