From f01d1a5ded3d0ebfb086a35e3bf96318e36798e4 Mon Sep 17 00:00:00 2001 From: kaleb-himes Date: Fri, 30 Oct 2026 16:13:07 -0600 Subject: [PATCH 1/7] sha512: SHA-512/224 FinalRaw reverses a copy like the other FinalRaw paths --- tests/api/test_sha512.c | 18 ++++++++++++++++++ wolfcrypt/src/sha512.c | 15 ++++++++++++--- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/tests/api/test_sha512.c b/tests/api/test_sha512.c index e44aac331d9..ad74b42ecf4 100644 --- a/tests/api/test_sha512.c +++ b/tests/api/test_sha512.c @@ -342,6 +342,24 @@ int test_wc_Sha512_224FinalRaw(void) "\x73\xe1\x99\x66\x89\xdc\xd4\xd6" "\x1d\xfa\xb7\xae\x32\xff\x9c\x82" "\x67\x9d\xd5\x14"); + { + /* Hashing continues after FinalRaw: "ab", raw read, "c" is "abc". */ + wc_Sha512 raw; + byte rawHash[WC_SHA512_224_DIGEST_SIZE]; + + XMEMSET(&raw, 0, sizeof(raw)); + ExpectIntEQ(wc_InitSha512_224(&raw), 0); + ExpectIntEQ(wc_Sha512_224Update(&raw, (const byte*)"ab", 2), 0); + ExpectIntEQ(wc_Sha512_224FinalRaw(&raw, rawHash), 0); + ExpectIntEQ(wc_Sha512_224Update(&raw, (const byte*)"c", 1), 0); + ExpectIntEQ(wc_Sha512_224Final(&raw, rawHash), 0); + ExpectBufEQ(rawHash, + "\x46\x34\x27\x0f\x70\x7b\x6a\x54" + "\xda\xae\x75\x30\x46\x08\x42\xe2" + "\x0e\x37\xed\x26\x5c\xee\xe9\xa4" + "\x3e\x89\x24\xaa", WC_SHA512_224_DIGEST_SIZE); + wc_Sha512_224Free(&raw); + } #endif return EXPECT_RESULT(); } /* END test_wc_Sha512_224Final */ diff --git a/wolfcrypt/src/sha512.c b/wolfcrypt/src/sha512.c index 2e048f0e72a..f689cbd80a5 100644 --- a/wolfcrypt/src/sha512.c +++ b/wolfcrypt/src/sha512.c @@ -2716,9 +2716,18 @@ static int Sha512FinalRaw(wc_Sha512* sha512, byte* hash, word32 digestSz) if ((digestSz & 0x7) == 0) ByteReverseWords64((word64 *)hash, sha512->digest, digestSz); else { - ByteReverseWords64(sha512->digest, sha512->digest, - WC_SHA512_DIGEST_SIZE); - XMEMCPY(hash, sha512->digest, digestSz); + /* Reverse into a copy so the state is never left reversed. */ + word64 digest[WC_SHA512_DIGEST_SIZE / sizeof(word64)]; + + ByteReverseWords64(digest, sha512->digest, WC_SHA512_DIGEST_SIZE); + #ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("Sha512FinalRaw digest", digest, sizeof(digest)); + #endif + XMEMCPY(hash, digest, digestSz); + ForceZero(digest, sizeof(digest)); + #ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Check(digest, sizeof(digest)); + #endif } #else XMEMCPY(hash, sha512->digest, digestSz); From c11f82b011739fba97a2c00b0b562d099eef8a06 Mon Sep 17 00:00:00 2001 From: kaleb-himes Date: Fri, 30 Oct 2026 16:13:32 -0600 Subject: [PATCH 2/7] sha512: Transform no longer byte-reverses the caller's const input block --- tests/api/test_sha512.c | 4 ++++ wolfcrypt/src/sha512.c | 33 +++++++++++++++++++-------------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/tests/api/test_sha512.c b/tests/api/test_sha512.c index ad74b42ecf4..1abd7c7caa7 100644 --- a/tests/api/test_sha512.c +++ b/tests/api/test_sha512.c @@ -274,6 +274,10 @@ int test_wc_Sha512Transform(void) "\x68\x75\xde\x70\x09\x4d\xcd\xfe" "\xbe\x11\x20\xd6\xe7\x7c\x49\xd3" "\x5b\xd7\x07\x75\x19\xc9\x8a\xfa"); +#if !defined(HAVE_FIPS) || FIPS_VERSION_GE(7,0) + /* Transform must not modify the caller's block (v5/v6 modules do). */ + ExpectBufEQ(abcData, abcSha512Data, WC_SHA512_BLOCK_SIZE); +#endif #endif return EXPECT_RESULT(); } diff --git a/wolfcrypt/src/sha512.c b/wolfcrypt/src/sha512.c index f689cbd80a5..76c2f538e82 100644 --- a/wolfcrypt/src/sha512.c +++ b/wolfcrypt/src/sha512.c @@ -2916,28 +2916,33 @@ int wc_Sha512Transform(wc_Sha512* sha, const unsigned char* data) wc_MemZero_Add("wc_Sha512Transform buffer", buffer, WC_SHA512_BLOCK_SIZE); #endif + /* data is const: byte-reverse a copy, never the caller's block. */ +#if defined(WOLFSSL_ARMASM) || defined(WOLFSSL_RISCV_ASM) #if defined(LITTLE_ENDIAN_ORDER) && !defined(WC_SHA512_RAW_BE_BUFFER) -#if defined(WOLFSSL_X86_64_BUILD) && defined(USE_INTEL_SPEEDUP) && \ - (defined(HAVE_INTEL_AVX1) || defined(HAVE_INTEL_AVX2)) - if (!IS_INTEL_AVX1(intel_flags) && !IS_INTEL_AVX2(intel_flags)) + XMEMCPY(buffer, data, WC_SHA512_BLOCK_SIZE); +#else + ByteReverseWords64(buffer, (const word64*)data, WC_SHA512_BLOCK_SIZE); #endif - { - ByteReverseWords64((word64*)data, (word64*)data, - WC_SHA512_BLOCK_SIZE); - } -#endif /* LITTLE_ENDIAN_ORDER && !WC_SHA512_RAW_BE_BUFFER */ - -#if defined(WOLFSSL_ARMASM) || defined(WOLFSSL_RISCV_ASM) - ByteReverseWords64(buffer, (word64*)data, WC_SHA512_BLOCK_SIZE); ret = Transform_Sha512(sha, (const byte*)buffer); #elif defined(WOLFSSL_PPC64_ASM) || defined(WOLFSSL_PPC32_ASM) - /* PPC assembly uses the (sha, data) form and reads the block directly - * (big-endian native - any little-endian reversal was done above). */ - (void)buffer; +#if defined(LITTLE_ENDIAN_ORDER) && !defined(WC_SHA512_RAW_BE_BUFFER) + ByteReverseWords64(buffer, (const word64*)data, WC_SHA512_BLOCK_SIZE); + ret = Transform_Sha512(sha, (const byte*)buffer); +#else ret = Transform_Sha512(sha, data); +#endif #else XMEMCPY(buffer, sha->buffer, WC_SHA512_BLOCK_SIZE); XMEMCPY(sha->buffer, data, WC_SHA512_BLOCK_SIZE); +#if defined(LITTLE_ENDIAN_ORDER) && !defined(WC_SHA512_RAW_BE_BUFFER) +#if defined(WOLFSSL_X86_64_BUILD) && defined(USE_INTEL_SPEEDUP) && \ + (defined(HAVE_INTEL_AVX1) || defined(HAVE_INTEL_AVX2)) + if (!IS_INTEL_AVX1(intel_flags) && !IS_INTEL_AVX2(intel_flags)) +#endif + { + ByteReverseWords64(sha->buffer, sha->buffer, WC_SHA512_BLOCK_SIZE); + } +#endif ret = Transform_Sha512(sha); From 8a3b8b13034f07c99fb1843556ffa47c79cb443c Mon Sep 17 00:00:00 2001 From: kaleb-himes Date: Wed, 4 Nov 2026 11:11:31 -0700 Subject: [PATCH 3/7] sha512: Fenrir F-17178, F-3343, F-17180: Transform input handling --- wolfcrypt/src/sha512.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/wolfcrypt/src/sha512.c b/wolfcrypt/src/sha512.c index 76c2f538e82..e53ee534087 100644 --- a/wolfcrypt/src/sha512.c +++ b/wolfcrypt/src/sha512.c @@ -2917,20 +2917,15 @@ int wc_Sha512Transform(wc_Sha512* sha, const unsigned char* data) #endif /* data is const: byte-reverse a copy, never the caller's block. */ -#if defined(WOLFSSL_ARMASM) || defined(WOLFSSL_RISCV_ASM) -#if defined(LITTLE_ENDIAN_ORDER) && !defined(WC_SHA512_RAW_BE_BUFFER) +#if (defined(WOLFSSL_ARMASM) || defined(WOLFSSL_RISCV_ASM)) && \ + !defined(LITTLE_ENDIAN_ORDER) XMEMCPY(buffer, data, WC_SHA512_BLOCK_SIZE); -#else - ByteReverseWords64(buffer, (const word64*)data, WC_SHA512_BLOCK_SIZE); -#endif - ret = Transform_Sha512(sha, (const byte*)buffer); -#elif defined(WOLFSSL_PPC64_ASM) || defined(WOLFSSL_PPC32_ASM) -#if defined(LITTLE_ENDIAN_ORDER) && !defined(WC_SHA512_RAW_BE_BUFFER) - ByteReverseWords64(buffer, (const word64*)data, WC_SHA512_BLOCK_SIZE); + ByteReverseWords64(buffer, buffer, WC_SHA512_BLOCK_SIZE); ret = Transform_Sha512(sha, (const byte*)buffer); -#else +#elif defined(WOLFSSL_ARMASM) || defined(WOLFSSL_RISCV_ASM) || \ + defined(WOLFSSL_PPC64_ASM) || defined(WOLFSSL_PPC32_ASM) + /* The asm reads data as Sha512Update passes it: unreversed. */ ret = Transform_Sha512(sha, data); -#endif #else XMEMCPY(buffer, sha->buffer, WC_SHA512_BLOCK_SIZE); XMEMCPY(sha->buffer, data, WC_SHA512_BLOCK_SIZE); From 9f4089bfe2cbab5e6faa936530bb59af918dfe86 Mon Sep 17 00:00:00 2001 From: kaleb-himes Date: Wed, 4 Nov 2026 12:06:24 -0700 Subject: [PATCH 4/7] sha512: ARMv5 Transform copies the block so the asm LDRs are aligned --- wolfcrypt/src/sha512.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/wolfcrypt/src/sha512.c b/wolfcrypt/src/sha512.c index e53ee534087..eb26e00d034 100644 --- a/wolfcrypt/src/sha512.c +++ b/wolfcrypt/src/sha512.c @@ -2918,9 +2918,13 @@ int wc_Sha512Transform(wc_Sha512* sha, const unsigned char* data) /* data is const: byte-reverse a copy, never the caller's block. */ #if (defined(WOLFSSL_ARMASM) || defined(WOLFSSL_RISCV_ASM)) && \ - !defined(LITTLE_ENDIAN_ORDER) + (!defined(LITTLE_ENDIAN_ORDER) || \ + (defined(WOLFSSL_ARM_ARCH) && (WOLFSSL_ARM_ARCH < 6))) + /* ARMv5 asm loads words with LDR, so it needs an aligned copy. */ XMEMCPY(buffer, data, WC_SHA512_BLOCK_SIZE); +#ifndef LITTLE_ENDIAN_ORDER ByteReverseWords64(buffer, buffer, WC_SHA512_BLOCK_SIZE); +#endif ret = Transform_Sha512(sha, (const byte*)buffer); #elif defined(WOLFSSL_ARMASM) || defined(WOLFSSL_RISCV_ASM) || \ defined(WOLFSSL_PPC64_ASM) || defined(WOLFSSL_PPC32_ASM) From e48e819b71f0c21a7e2bd82e4fd16cf37e5ca889 Mon Sep 17 00:00:00 2001 From: kaleb-himes Date: Wed, 4 Nov 2026 14:54:55 -0700 Subject: [PATCH 5/7] sha512: AArch64 Transform keeps pending input; big-endian block not reversed --- wolfcrypt/src/sha512.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/wolfcrypt/src/sha512.c b/wolfcrypt/src/sha512.c index eb26e00d034..da20b684195 100644 --- a/wolfcrypt/src/sha512.c +++ b/wolfcrypt/src/sha512.c @@ -2916,16 +2916,17 @@ int wc_Sha512Transform(wc_Sha512* sha, const unsigned char* data) wc_MemZero_Add("wc_Sha512Transform buffer", buffer, WC_SHA512_BLOCK_SIZE); #endif - /* data is const: byte-reverse a copy, never the caller's block. */ -#if (defined(WOLFSSL_ARMASM) || defined(WOLFSSL_RISCV_ASM)) && \ - (!defined(LITTLE_ENDIAN_ORDER) || \ - (defined(WOLFSSL_ARM_ARCH) && (WOLFSSL_ARM_ARCH < 6))) + /* data is const: never modify the caller's block. */ +#if defined(WOLFSSL_ARMASM) && defined(WOLFSSL_ARM_ARCH) && \ + (WOLFSSL_ARM_ARCH < 6) /* ARMv5 asm loads words with LDR, so it needs an aligned copy. */ XMEMCPY(buffer, data, WC_SHA512_BLOCK_SIZE); -#ifndef LITTLE_ENDIAN_ORDER - ByteReverseWords64(buffer, buffer, WC_SHA512_BLOCK_SIZE); -#endif ret = Transform_Sha512(sha, (const byte*)buffer); +#elif defined(WOLFSSL_ARMASM) && defined(__aarch64__) + /* The C CPU step writes sha->buffer, which may hold pending input. */ + XMEMCPY(buffer, sha->buffer, WC_SHA512_BLOCK_SIZE); + ret = Transform_Sha512(sha, data); + XMEMCPY(sha->buffer, buffer, WC_SHA512_BLOCK_SIZE); #elif defined(WOLFSSL_ARMASM) || defined(WOLFSSL_RISCV_ASM) || \ defined(WOLFSSL_PPC64_ASM) || defined(WOLFSSL_PPC32_ASM) /* The asm reads data as Sha512Update passes it: unreversed. */ From 4b45c53de389234c8b45fb5ab68908580849bfe8 Mon Sep 17 00:00:00 2001 From: kaleb-himes Date: Wed, 4 Nov 2026 15:15:49 -0700 Subject: [PATCH 6/7] sha512: RISC-V vector Transform copies the block so vl8re64.v is aligned --- wolfcrypt/src/sha512.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/wolfcrypt/src/sha512.c b/wolfcrypt/src/sha512.c index da20b684195..c51e11fb503 100644 --- a/wolfcrypt/src/sha512.c +++ b/wolfcrypt/src/sha512.c @@ -2886,7 +2886,7 @@ void wc_Sha512Free(wc_Sha512* sha512) int wc_Sha512Transform(wc_Sha512* sha, const unsigned char* data) { int ret; - /* back up buffer */ + /* aligned copy of data, or backup of sha->buffer */ WC_DECLARE_VAR(buffer, word64, WC_SHA512_BLOCK_SIZE / sizeof(word64), 0); @@ -2917,9 +2917,10 @@ int wc_Sha512Transform(wc_Sha512* sha, const unsigned char* data) #endif /* data is const: never modify the caller's block. */ -#if defined(WOLFSSL_ARMASM) && defined(WOLFSSL_ARM_ARCH) && \ - (WOLFSSL_ARM_ARCH < 6) - /* ARMv5 asm loads words with LDR, so it needs an aligned copy. */ +#if (defined(WOLFSSL_ARMASM) && defined(WOLFSSL_ARM_ARCH) && \ + (WOLFSSL_ARM_ARCH < 6)) || \ + (defined(WOLFSSL_RISCV_ASM) && defined(WOLFSSL_RISCV_VECTOR_CRYPTO_ASM)) + /* ARMv5 LDR and RISC-V vl8re64.v loads need an aligned copy. */ XMEMCPY(buffer, data, WC_SHA512_BLOCK_SIZE); ret = Transform_Sha512(sha, (const byte*)buffer); #elif defined(WOLFSSL_ARMASM) && defined(__aarch64__) From 5b8aa51905f207c8d365da4f03fb6e2ca45a78c9 Mon Sep 17 00:00:00 2001 From: kaleb-himes Date: Wed, 4 Nov 2026 15:55:07 -0700 Subject: [PATCH 7/7] sha512: test that Transform keeps input pending from an earlier Update --- tests/api/test_sha512.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/api/test_sha512.c b/tests/api/test_sha512.c index 1abd7c7caa7..17bb329f392 100644 --- a/tests/api/test_sha512.c +++ b/tests/api/test_sha512.c @@ -277,6 +277,28 @@ int test_wc_Sha512Transform(void) #if !defined(HAVE_FIPS) || FIPS_VERSION_GE(7,0) /* Transform must not modify the caller's block (v5/v6 modules do). */ ExpectBufEQ(abcData, abcSha512Data, WC_SHA512_BLOCK_SIZE); + /* Transform must keep input pending from an earlier Update. */ + { + wc_Sha512 shaA; + wc_Sha512 shaB; + byte hashA[WC_SHA512_DIGEST_SIZE]; + byte hashB[WC_SHA512_DIGEST_SIZE]; + + XMEMSET(&shaA, 0, sizeof(shaA)); + XMEMSET(&shaB, 0, sizeof(shaB)); + ExpectIntEQ(wc_InitSha512(&shaA), 0); + ExpectIntEQ(wc_Sha512Update(&shaA, (const byte*)"ab", 2), 0); + ExpectIntEQ(wc_Sha512Transform(&shaA, abcData), 0); + ExpectIntEQ(wc_Sha512Update(&shaA, (const byte*)"c", 1), 0); + ExpectIntEQ(wc_Sha512Final(&shaA, hashA), 0); + ExpectIntEQ(wc_InitSha512(&shaB), 0); + ExpectIntEQ(wc_Sha512Transform(&shaB, abcData), 0); + ExpectIntEQ(wc_Sha512Update(&shaB, (const byte*)"abc", 3), 0); + ExpectIntEQ(wc_Sha512Final(&shaB, hashB), 0); + ExpectBufEQ(hashA, hashB, WC_SHA512_DIGEST_SIZE); + wc_Sha512Free(&shaA); + wc_Sha512Free(&shaB); + } #endif #endif return EXPECT_RESULT();