Bound LMS, XMSS and ML-KEM DMA response frames in the crypto client#476
Bound LMS, XMSS and ML-KEM DMA response frames in the crypto client#476yosuke-wolfssl wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR hardens the crypto client’s DMA response parsing for LMS, XMSS, and ML-KEM by validating that the received response frame is large enough to safely overlay and read fixed-size response structs, preventing stale request bytes (from the reused comm buffer) from being interpreted as response fields. It also adds a malformed-response test harness that simulates truncated frames and header-only error replies.
Changes:
- Add per-handler
res_lenreceive lengths and fixed-size frame bounds before reading response struct fields insrc/wh_client_crypto.c. - Add a scripted-transport misc test (
whTest_ClientMalformedResp) that exercises exact-fit, truncation, server-error header-only replies, and caller-capacity rejection cases. - Register the new misc test in
test-refactor/wh_test_list.c.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/wh_client_crypto.c | Adds response-length bounds (via res_len) for LMS/XMSS/ML-KEM DMA response parsing to prevent stale-buffer overlay reads. |
| test-refactor/misc/wh_test_client_malformed_resp.c | Adds a scripted transport + subtests to validate client behavior on truncated/invalid response frames. |
| test-refactor/wh_test_list.c | Registers the new malformed-response misc test entry point. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
33bee76 to
a110692
Compare
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #476
Scan targets checked: wolfhsm-crypto-bugs, wolfhsm-src
No new issues found in the changed files. ✅
Problem
wh_Client_RecvResponsereports the received payload length but does not validate it against the message being parsed. The comm client reuses one buffer for request and response, so a reply shorter than the response struct leaves the tail holding the client's own outbound request bytes. An unchecked overlay read then returns stale data instead of failing.Nine DMA response handlers in
wh_client_crypto.coverlaid their response struct with no frame bound. What a short frame could fabricate:wh_Client_{Lms,Xmss}MakeKeyDmares->keyId, stored into the caller's out-param and the keywh_Client_{Lms,Xmss}SignDmares->sigLen, bounded against the caller's buffer but not the framewh_Client_{Lms,Xmss}VerifyDmaresp->res, the verify verdict itselfwh_Client_{Lms,Xmss}SigsLeftDmares->sigsLeft, which gates whether a one-time key is reused_MlKemMakeKeyDmares->keyId, andres->keySizebounded only by the staging bufferFollow-up to #457, continuing the family split started in #474 (CMAC) and #475 (AES DMA).
Fix (
src/wh_client_crypto.c)Every one of these responses is fixed size with no trailing payload, so a single bound covers each site:
These nine functions previously reused
req_lenas the out-param ofwh_Client_RecvResponse. Each now receives into its ownres_len, matching every other handler in the file and keeping a response-size comparison off a variable named for the request.wh_server_crypto.c:8837), so an unconditional bound would convert every legitimate error reply intoWH_ERROR_ABORTEDand hide the server'src. The bound sits inside the existingif (ret >= 0)block; these server handlers return0or a negative error, never a positive value.ret, never returns early.MakeKeyDmaandSignDmareconcilepostRetafter the parse block,MakeKey/Sign/Verifyunmap their DMA buffers on the way out, and_MlKemMakeKeyDmazeroizes its staging buffer at the bottom.res->sigLen > sigCapandres->keySize > buffer_lenbound the caller's buffer, a different bound from the frame, and still returnWH_ERROR_BADARGS.Test harness (
test-refactor/misc/wh_test_client_malformed_resp.c)New sub-tests driven by a scripted transport that echoes the request comm header, always writes the response body, and reports whatever frame length the sub-test asks for. Each handler gets:
hdr + sizeof(resp)WH_ERROR_ABORTED, out-param untouchedrc = WH_ERROR_NOTFOUNDWH_ERROR_NOTFOUND, not a rejectionWH_ERROR_BADARGSThe ML-KEM sub-test always claims an oversized key length, because a staging buffer of zeros cannot deserialize into a real key. An accepted frame is therefore
WH_ERROR_BADARGSfrom the capacity check, and only a frame rejection isWH_ERROR_ABORTED.The transport is family-agnostic: a sub-test supplies
structSz,claimOffandclaimVal, so stamping a field into the response struct needs no per-algorithm branch.This file also exists on #464, #474 and #475 with different contents. Whichever lands last resolves the add/add conflict by concatenating sub-tests and keeping the single
whTest_ClientMalformedRespregistry entry.Verification
make checkclean on every matrix: default,SHE=1 DMA=1(48 passed, 15 skipped, 0 failed of 63),DMA=1withLMS_VERIFY_ONLY=1,XMSS_VERIFY_ONLY=1, and both together. Legacytest/suite passes withSHE=1.hdr_sz, flip<to>,hdr_sz - 1), all caught. Two hoist controls that move a bound out of the success branch also fail the suite, so the header-only error reply is genuinely covered.