From ab8adb4d5bd3c5ff9d03af699f32cf1bc2d75cf8 Mon Sep 17 00:00:00 2001 From: "Ganyu (Bruce) Xu" Date: Wed, 20 May 2026 15:40:22 +0000 Subject: [PATCH 1/4] tests: SHA3-512 inc absorb at rate boundary [skip ci] The existing test absorbed the 200-byte message in a single call, which exercises the multi-permutation path but always aligns segment boundaries with the Keccak rate (72 bytes for SHA3-512). It therefore misses the case where a segment straddles a rate boundary mid-absorption. Add a loop that absorbs in chunks of (RATE - 1) bytes, forcing every permutation trigger to occur in the middle of a segment. Absorbing at exactly RATE does not expose the bug; the error only surfaces when a segment boundary falls inside a rate block. Signed-off-by: Ganyu (Bruce) Xu --- tests/test_sha3.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/test_sha3.c b/tests/test_sha3.c index b402ddc0b6..f17e704832 100644 --- a/tests/test_sha3.c +++ b/tests/test_sha3.c @@ -458,9 +458,26 @@ int sha3_512_kat_test(void) { OQS_SHA3_sha3_512_inc_ctx_reset(&state); OQS_SHA3_sha3_512_inc_absorb(&state, msg1600, 200); OQS_SHA3_sha3_512_inc_finalize(hash, &state); + + if (are_equal8(hash, exp1600, 64) == EXIT_FAILURE) { + status = EXIT_FAILURE; + } + + /* Special test case: absorb chunks of nasty sizes */ + clear8(hash, 200); + size_t absorbed = 0; + size_t max_seglen = OQS_SHA3_SHA3_512_RATE - 1; + OQS_SHA3_sha3_512_inc_ctx_reset(&state); + while (absorbed < 200) { + size_t seglen = 200 - absorbed < max_seglen ? 200 - absorbed : max_seglen; + OQS_SHA3_sha3_512_inc_absorb(&state, msg1600, seglen); + absorbed += seglen; + } + OQS_SHA3_sha3_512_inc_finalize(hash, &state); OQS_SHA3_sha3_512_inc_ctx_release(&state); if (are_equal8(hash, exp1600, 64) == EXIT_FAILURE) { + printf("ERROR: SHA3-512 non-rate-multiple absorption incorrect output\n"); status = EXIT_FAILURE; } From da6000ea01f83ec0730740fa67737ed46d0a7009 Mon Sep 17 00:00:00 2001 From: Douglas Stebila Date: Wed, 13 May 2026 15:17:23 -0400 Subject: [PATCH 2/4] Fix off-by-N in AVX512VL sha3_absorb at rate boundary The partial-fill-and-permute path in the sha3_absorb macro consumed \`capacity\` bytes via keccak_1600_partial_add (which advances arg2 and clobbers %r12) but did not decrement the remaining message-length tracker. The post-permute path at label 3 then treated the original mlen as still pending, causing the bytes already consumed in the partial fill to be 're-absorbed' (reading past the end of the input buffer) into the post-permute state and leaving s[25] off by \`capacity\`. Symptom: SHA3-{256,384,512} digests are wrong whenever an incremental absorb call exactly fills, or crosses, the rate boundary - reproduced via HQC-3 / HQC-5 KAT failures on AVX512 runners (HQC-1 is unaffected because its hash_g input is 65 bytes, which never crosses the 72-byte SHA3-512 rate). The matching shake_absorb macro in the same file already has the correct pattern (subq %r12, arg3 before the partial_add call); this just mirrors it in sha3_absorb. Co-Authored-By: Claude Opus 4.7 (1M context) Signed-off-by: Douglas Stebila --- src/common/sha3/avx512vl_low/SHA3-AVX512VL.S | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/common/sha3/avx512vl_low/SHA3-AVX512VL.S b/src/common/sha3/avx512vl_low/SHA3-AVX512VL.S index 5a7e610947..dab115b466 100644 --- a/src/common/sha3/avx512vl_low/SHA3-AVX512VL.S +++ b/src/common/sha3/avx512vl_low/SHA3-AVX512VL.S @@ -407,12 +407,19 @@ cmp %r12, arg3 # if mlen < capacity then cannot permute yet jb 1f # skip permute - movq arg3, %r10 + # Partial-fill-and-permute path. + # We are about to absorb `capacity` (= rate - s[25]) bytes from the + # input into the state, which will exactly fill the rate, then permute. + # `keccak_1600_partial_add` reads its length from %r12 (which holds + # `capacity` here), advances arg2 by that many bytes, and clobbers %r12. + # We must therefore decrement arg3 by `capacity` BEFORE the call, so that + # the post-permute path at label `3:` continues with the correct number + # of remaining input bytes. Forgetting this (the previous behavior) + # caused `capacity` bytes to be double-absorbed into the post-permute + # state and read out of bounds past the input buffer. + subq %r12, arg3 # arg3 -= capacity (bytes remaining after fill) leaq (arg1, %r14), %r13 # %r13 = state + s[25] - movq arg2, arg3 - call keccak_1600_partial_add - - movq %r10, arg3 + call keccak_1600_partial_add # arg2 updated, %r12 clobbered call keccak_1600_load_state call keccak_1600_permute From f3fa81c2f471c3ce7899fd2c2a240c5e4d44b69a Mon Sep 17 00:00:00 2001 From: "Ganyu (Bruce) Xu" Date: Wed, 20 May 2026 13:51:38 -0400 Subject: [PATCH 3/4] Code formatting Signed-off-by: Ganyu (Bruce) Xu --- tests/test_sha3.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/test_sha3.c b/tests/test_sha3.c index f17e704832..aea83d73ed 100644 --- a/tests/test_sha3.c +++ b/tests/test_sha3.c @@ -463,21 +463,21 @@ int sha3_512_kat_test(void) { status = EXIT_FAILURE; } - /* Special test case: absorb chunks of nasty sizes */ + /* Special test case: absorb chunks of nasty sizes */ clear8(hash, 200); - size_t absorbed = 0; - size_t max_seglen = OQS_SHA3_SHA3_512_RATE - 1; + size_t absorbed = 0; + size_t max_seglen = OQS_SHA3_SHA3_512_RATE - 1; OQS_SHA3_sha3_512_inc_ctx_reset(&state); - while (absorbed < 200) { - size_t seglen = 200 - absorbed < max_seglen ? 200 - absorbed : max_seglen; - OQS_SHA3_sha3_512_inc_absorb(&state, msg1600, seglen); - absorbed += seglen; - } + while (absorbed < 200) { + size_t seglen = 200 - absorbed < max_seglen ? 200 - absorbed : max_seglen; + OQS_SHA3_sha3_512_inc_absorb(&state, msg1600, seglen); + absorbed += seglen; + } OQS_SHA3_sha3_512_inc_finalize(hash, &state); OQS_SHA3_sha3_512_inc_ctx_release(&state); if (are_equal8(hash, exp1600, 64) == EXIT_FAILURE) { - printf("ERROR: SHA3-512 non-rate-multiple absorption incorrect output\n"); + printf("ERROR: SHA3-512 non-rate-multiple absorption incorrect output\n"); status = EXIT_FAILURE; } From 19fd5b783412aac835451cd79bab837996c52cc3 Mon Sep 17 00:00:00 2001 From: Douglas Stebila Date: Mon, 25 May 2026 20:09:07 -0400 Subject: [PATCH 4/4] Fix test_sha3 absorb length See https://github.com/open-quantum-safe/liboqs/pull/2442#discussion_r3299067105 Signed-off-by: Douglas Stebila --- tests/test_sha3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_sha3.c b/tests/test_sha3.c index aea83d73ed..fc2c5230c1 100644 --- a/tests/test_sha3.c +++ b/tests/test_sha3.c @@ -470,7 +470,7 @@ int sha3_512_kat_test(void) { OQS_SHA3_sha3_512_inc_ctx_reset(&state); while (absorbed < 200) { size_t seglen = 200 - absorbed < max_seglen ? 200 - absorbed : max_seglen; - OQS_SHA3_sha3_512_inc_absorb(&state, msg1600, seglen); + OQS_SHA3_sha3_512_inc_absorb(&state, msg1600 + absorbed, seglen); absorbed += seglen; } OQS_SHA3_sha3_512_inc_finalize(hash, &state);