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 diff --git a/tests/test_sha3.c b/tests/test_sha3.c index b402ddc0b6..fc2c5230c1 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 + absorbed, 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; }