From da763e5c7f412d81e35f99608921563e9aef402b Mon Sep 17 00:00:00 2001 From: kaleb-himes Date: Fri, 30 Oct 2026 16:13:07 -0600 Subject: [PATCH 1/8] 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..e3275d9c463 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 a copy, as the other FinalRaw paths do; never the state. */ + 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 de8bf60a4d6dedd77c0f6abb881ed56e20fd46e9 Mon Sep 17 00:00:00 2001 From: kaleb-himes Date: Fri, 30 Oct 2026 16:13:32 -0600 Subject: [PATCH 2/8] 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 e3275d9c463..a6cbda3feeb 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 b6cf2559eebb06f6f0e2bcee3ce7374701e418d5 Mon Sep 17 00:00:00 2001 From: kaleb-himes Date: Wed, 4 Nov 2026 11:11:31 -0700 Subject: [PATCH 3/8] 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 a6cbda3feeb..cb0394f984d 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 724853b12c75714c544aeda8bc1d44a310a1da95 Mon Sep 17 00:00:00 2001 From: kaleb-himes Date: Wed, 4 Nov 2026 11:11:31 -0700 Subject: [PATCH 4/8] sha512: SHA-512/224 FinalRaw restores the state instead of a stack copy --- wolfcrypt/src/sha512.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/wolfcrypt/src/sha512.c b/wolfcrypt/src/sha512.c index cb0394f984d..2a491edfe77 100644 --- a/wolfcrypt/src/sha512.c +++ b/wolfcrypt/src/sha512.c @@ -2716,18 +2716,12 @@ static int Sha512FinalRaw(wc_Sha512* sha512, byte* hash, word32 digestSz) if ((digestSz & 0x7) == 0) ByteReverseWords64((word64 *)hash, sha512->digest, digestSz); else { - /* Reverse a copy, as the other FinalRaw paths do; never the state. */ - 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 + /* Reverse for the copy, then reverse back to keep the state. */ + ByteReverseWords64(sha512->digest, sha512->digest, + WC_SHA512_DIGEST_SIZE); + XMEMCPY(hash, sha512->digest, digestSz); + ByteReverseWords64(sha512->digest, sha512->digest, + WC_SHA512_DIGEST_SIZE); } #else XMEMCPY(hash, sha512->digest, digestSz); From 4796d229aad362116ca4e3244d19d05d3f6c20c2 Mon Sep 17 00:00:00 2001 From: kaleb-himes Date: Wed, 4 Nov 2026 12:06:24 -0700 Subject: [PATCH 5/8] 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 2a491edfe77..37bdb8ec596 100644 --- a/wolfcrypt/src/sha512.c +++ b/wolfcrypt/src/sha512.c @@ -2912,9 +2912,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 0eb869fe981da1d66d6c47a5a8e4ef1271c1bdab Mon Sep 17 00:00:00 2001 From: kaleb-himes Date: Wed, 4 Nov 2026 14:54:55 -0700 Subject: [PATCH 6/8] 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 37bdb8ec596..aed52a2835e 100644 --- a/wolfcrypt/src/sha512.c +++ b/wolfcrypt/src/sha512.c @@ -2910,16 +2910,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 73c291a722a7bc25d5a8541a1060b60cdb4a059b Mon Sep 17 00:00:00 2001 From: kaleb-himes Date: Wed, 4 Nov 2026 15:15:49 -0700 Subject: [PATCH 7/8] 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 aed52a2835e..eddb1caeff6 100644 --- a/wolfcrypt/src/sha512.c +++ b/wolfcrypt/src/sha512.c @@ -2880,7 +2880,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); @@ -2911,9 +2911,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 c52785701bc34bb253075b6f586149ed26c336aa Mon Sep 17 00:00:00 2001 From: kaleb-himes Date: Wed, 4 Nov 2026 15:55:07 -0700 Subject: [PATCH 8/8] 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();