Skip to content

Translate SHA-2 intermediate state for cross-endian peers#468

Open
yosuke-wolfssl wants to merge 1 commit into
wolfSSL:mainfrom
yosuke-wolfssl:fix/f_4041
Open

Translate SHA-2 intermediate state for cross-endian peers#468
yosuke-wolfssl wants to merge 1 commit into
wolfSSL:mainfrom
yosuke-wolfssl:fix/f_4041

Conversation

@yosuke-wolfssl

Copy link
Copy Markdown

Problem

wolfHSM supports a client and server on cores with different byte order: every
message header carries an endian marker and the server byte-swaps each field via
wh_Translate*. The SHA-2 wire structs declare the resume state as
uint8_t hash[32]/[64], and the translation helpers skipped it as "just a byte
array". But that array is a verbatim copy of wc_Sha256.digest /
wc_Sha512.digest, which wolfCrypt keeps as host-order word32/word64. A
cross-endian pair therefore exchanged byte-flipped chaining words: every
non-final update compressed from garbage and the digest was silently wrong — no
error, no crash. Final digests are unaffected (wolfCrypt emits canonical
big-endian at finalize), so only multi-message streams broke, and same-endian
pairs never saw it. SHA-3 was already correct: whMessageCrypto_Sha3State is
uint64_t s[25], swapped per word.

Addressed by Fenrir 4041.

Fix (src/wh_message_crypto.c)

New _TranslateSha2State() swaps the eight chaining words, going through a local
word via memcpy (no alignment or aliasing hazard, safe when src == dest).

The response direction is ambiguous — hash[] holds host-order state on an
update but a canonical digest on the last block, and the struct has no
isLastBlock — so the caller now states which it is. The existing response
symbols remain as wrappers passing 0; no public signature changed.

Translator stateWordSize
...Sha256Request, ...Sha256DmaRequest 4, unconditional
...Sha512Request, ...Sha512DmaRequest 8, unconditional
...Sha2Response_ex, ...Sha2DmaResponse_ex (new) from caller; server passes req.isLastBlock ? 0 : 4/8

src/wh_client_crypto.c is untouched: only the server translates, so the
client's copy defines the wire format. Same-endian wire bytes are identical to
before, so there is no cross-version interop break. Closes 4041.

Tests

New test-refactor/misc/wh_test_message_crypto.c — the translate layer had no
coverage at all. A loopback test cannot observe peers that disagree on byte
order, so it drives the helpers directly with a foreign-endian magic and checks
per-word swapping, byte-for-byte identity under a native magic, involution,
in-place translation, and that a finalized digest is left alone. It covers the
Keccak state too, which validates the test's own swap detector against
already-correct code.

Verification

  • test-refactor: 38 passed / 0 failed of 63; DMA=1 42 passed; NOCRYPTO=1 12
    passed. Legacy test/ suite clean.
  • Clean under -std=c90 -Werror -Wall -Wextra.
  • Negative control: reducing the swap to a plain memcpy fails the new test
    (rc=-1).

@yosuke-wolfssl yosuke-wolfssl self-assigned this Jul 21, 2026
Copilot AI review requested due to automatic review settings July 21, 2026 07:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes mixed-endian client/server interoperability for multi-message SHA-2 hashing by correctly translating the intermediate chaining state (previously treated as an opaque byte array) while preserving the canonical byte order of finalized digests.

Changes:

  • Add _TranslateSha2State() and new wh_MessageCrypto_TranslateSha2Response_ex() / wh_MessageCrypto_TranslateSha2DmaResponse_ex() so callers can specify whether hash[] is intermediate state (swap per word) or a finalized digest (no swap).
  • Update server SHA-2 handlers to pass stateWordSize based on req.isLastBlock so only non-final responses have their chaining words swapped.
  • Add a new direct-translation unit test covering SHA-2 (32/64-bit words), in-place translation, involution, and SHA-3 Keccak state translation; update draft docs accordingly.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
wolfhsm/wh_message_crypto.h Adds _ex translator APIs documenting caller-provided stateWordSize.
src/wh_message_crypto.c Implements SHA-2 state per-word swapping and wraps legacy response translators as digest-only.
src/wh_server_crypto.c Uses _ex response translators with stateWordSize keyed off isLastBlock.
test-refactor/wh_test_list.c Registers the new translation-focused test in the misc suite.
test-refactor/misc/wh_test_message_crypto.c New tests for SHA-2/SHA-3 translation behavior, including foreign-endian and in-place cases.
docs/draft/async-crypto.md Documents the non-opaque nature and endian rules for resumeState.hash and response hashes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fenrir Automated Review — PR #468

Scan targets checked: wolfhsm-core-bugs, wolfhsm-crypto-bugs, wolfhsm-src

No new issues found in the changed files. ✅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants