From 9a8256711577c1bec764c9cd04ee4d45894426ec Mon Sep 17 00:00:00 2001 From: Brett Nicholas <7547222+bigbrett@users.noreply.github.com> Date: Wed, 8 Apr 2026 15:19:04 -0600 Subject: [PATCH 1/3] Add native async API for SHAx --- docs/draft/async-crypto.md | 470 +++ docs/draft/posix-shm.md | 67 + examples/posix/wh_posix_cfg.h | 8 +- examples/posix/wh_posix_client/wolfhsm_cfg.h | 2 +- examples/posix/wh_posix_server/wolfhsm_cfg.h | 4 +- src/wh_client_crypto.c | 2769 ++++++++++++------ src/wh_message_crypto.c | 75 +- src/wh_server_crypto.c | 914 +++--- test/wh_test_check_struct_padding.c | 3 +- test/wh_test_crypto.c | 1725 ++++++++++- wolfhsm/wh_client.h | 20 + wolfhsm/wh_client_crypto.h | 302 ++ wolfhsm/wh_message_crypto.h | 151 +- 13 files changed, 5065 insertions(+), 1445 deletions(-) create mode 100644 docs/draft/async-crypto.md create mode 100644 docs/draft/posix-shm.md diff --git a/docs/draft/async-crypto.md b/docs/draft/async-crypto.md new file mode 100644 index 000000000..0f386de3f --- /dev/null +++ b/docs/draft/async-crypto.md @@ -0,0 +1,470 @@ +# Async Crypto API + +**Status: Work in Progress** + +## Background: Crypto in wolfHSM Today + +wolfHSM offloads cryptographic operations from a client application to a secure +server (typically running on an HSM or trusted core) using a request/response +protocol over a shared communication buffer. Today this works through +wolfCrypt's **crypto callback** mechanism: + +1. The application initializes a wolfCrypt context with `devId = WH_DEV_ID`. +2. When a wolfCrypt function (`wc_Sha256Update`, `wc_AesCbcEncrypt`, etc.) is + called, wolfCrypt invokes the registered callback `wh_Client_CryptoCb`. +3. The callback serializes the operation into the comm buffer, sends the request + to the server, and **blocks** polling `wh_Client_RecvResponse()` until the + server replies. +4. The result is deserialized and returned to the caller. + +This is transparent to application code -- standard wolfCrypt API calls "just +work" -- but every crypto operation is **synchronous and blocking**. The client +thread cannot do useful work while the server is processing. On embedded +targets where the transport is shared memory and the server runs on a different +core, this means the client core sits idle for the entire round-trip. + +### Why Blocking is a Problem + +- **CPU waste**: the client spins in a polling loop while the HSM computes. +- **No pipelining**: multi-step operations (e.g., hashing a large file followed + by signing the digest) cannot overlap. +- **RTOS integration**: a blocking call cannot yield to higher-priority tasks + or cooperate with event-driven schedulers. + +## The Async Crypto API + +The async crypto API introduces a **non-blocking request/response split** for +each cryptographic operation. Every blocking function is decomposed into: + +- **`*Request()`** -- serializes and sends the request. Returns immediately. +- **`*Response()`** -- attempts a single non-blocking receive. Returns + `WH_ERROR_NOTREADY` if the server has not yet replied, or the final result + on completion. + +The existing blocking functions are retained as thin wrappers that call +`Request()` then poll `Response()` in a loop. The crypto callback path +(`wh_Client_CryptoCb`) continues to use these blocking wrappers, so existing +application code is unaffected. + +``` + +-----------+ + Application | | + (async) | wolfHSM | + | | Server | + |-- Request() ---->| | + | | (compute)| + | (do other work) | | + | | | + |<-- Response() ---| | + | WH_ERROR_NOTREADY | + | | | + |<-- Response() ---| | + | WH_ERROR_OK | | + | (result) +-----------+ +``` + +### Design Principles + +- **Stateless responses**: output buffers are passed as parameters to the + Response function, not stored in `whClientContext`. +- **No server-side changes**: the server already handles each request + independently -- it doesn't know or care whether the client blocked. +- **Preserve existing wire formats where possible**: for operations whose + request/response layout is already suitable, the async API only changes the + client-side calling pattern. Some algorithms (notably the SHA family) still + require new message layouts to carry async-specific inputs such as + intermediate state, variable-length trailing input, and DMA metadata. +- **Pre-cached keys required**: async Request functions require keys to already + be cached on the server. The blocking wrappers retain automatic key import + for convenience. +- **One outstanding request per client context**: only one async crypto + request may be in flight at a time on a given `whClientContext`. + +### Usage Pattern + +```c +/* Send the request */ +ret = wh_Client_EccSignRequest(ctx, key, hash, hashLen); +if (ret != WH_ERROR_OK) { /* handle error */ } + +/* ... do other work while server computes ... */ + +/* Poll for completion */ +do { + ret = wh_Client_EccSignResponse(ctx, sig, &sigLen); + if (ret == WH_ERROR_NOTREADY) { + /* yield to scheduler, do other work, etc. */ + } +} while (ret == WH_ERROR_NOTREADY); +/* ret has final result, sig/sigLen are populated */ +``` + +## SHA: The First Async Algorithm + +SHA hash functions are the first algorithm family to receive the async +treatment. All four SHA-2 variants are supported: SHA-224, SHA-256, SHA-384, +and SHA-512. + +SHA is a particularly interesting case because hashing is inherently a +**streaming, multi-call** operation (`Init` / `Update*` / `Final`), unlike +single-shot operations like RSA sign or AES-CBC encrypt where one +request/response round-trip suffices. The async SHA API must handle: + +- Inputs that vastly exceed the communication buffer size +- Partial-block buffering on the client +- Intermediate hash state that must be preserved across round-trips +- A stateless server that reconstructs state from each request + +### Wire Protocol + +Each SHA request carries the **full intermediate hash state** inline so the +server can process the data statelessly. The wire layout in the comm buffer +is: + +``` ++------------------------------------------+ +| GenericRequestHeader (12 bytes) | algo type, affinity ++------------------------------------------+ +| Sha256Request / Sha512Request | resumeState + control fields +| resumeState.hiLen (4 bytes) | +| resumeState.loLen (4 bytes) | +| resumeState.hash (32 or 64 bytes) | intermediate digest +| [resumeState.hashType (4 bytes)] | SHA-512 family only +| isLastBlock (4 bytes) | +| inSz (4 bytes) | ++------------------------------------------+ +| uint8_t in[inSz] | variable-length input data ++------------------------------------------+ +``` + +The response carries the updated state (or final digest) back: + +``` ++------------------------------------------+ +| GenericResponseHeader (12 bytes) | algo type, return code ++------------------------------------------+ +| Sha2Response | +| hiLen, loLen (8 bytes) | +| hash (64 bytes) | updated/final digest +| hashType (4 bytes) | ++------------------------------------------+ +``` + +### Block Alignment and MTU Filling + +The comm buffer has a fixed size (`WOLFHSM_CFG_COMM_DATA_LEN`, default 1280 +bytes). The async SHA design maximizes throughput by packing as many **whole +hash blocks** into each message as possible. + +SHA-256 and SHA-224 use a 64-byte block size. SHA-384 and SHA-512 use 128 +bytes. The maximum inline data capacity per message is: + +```c +#define WH_MESSAGE_CRYPTO_SHA256_MAX_INLINE_UPDATE_SZ \ + (((WOLFHSM_CFG_COMM_DATA_LEN \ + - sizeof(whMessageCrypto_GenericRequestHeader) \ + - sizeof(whMessageCrypto_Sha256Request)) \ + / 64u) * 64u) +``` + +This rounds **down** to the nearest block boundary so that non-final Update +messages always carry whole blocks. + +With the default 1280-byte comm buffer: + +| Variant | Header Overhead | Block Size | Max Inline Data | Blocks/Message | +|----------------|-----------------|------------|-----------------|----------------| +| SHA-256/224 | 60 bytes | 64 bytes | 1216 bytes | 19 blocks | +| SHA-512/384 | 96 bytes | 128 bytes | 1152 bytes | 9 blocks | + +> *Header overhead = GenericRequestHeader (12 bytes) + algorithm-specific +> request struct (48 bytes for SHA-256, 84 bytes for SHA-512).* + +The per-call capacity is slightly larger than the inline wire capacity because +the client can absorb up to `BLOCK_SIZE - 1` additional tail bytes into its +local buffer without needing to send them: + +```c +capacity = MAX_INLINE_UPDATE_SZ + (BLOCK_SIZE - 1 - sha->buffLen) +``` + +### Client-Side Partial-Block Buffering + +The SHA block cipher operates on fixed-size blocks (64 or 128 bytes). When the +caller provides input that isn't block-aligned, the client must buffer the +partial tail locally until enough data arrives to form a complete block. This +buffering uses the `buffer` and `buffLen` fields already present in wolfCrypt's +`wc_Sha256` (and related) structures -- no additional memory is needed. + +The Update request function performs three steps: + +1. **Top up the existing partial block**: if there are already bytes buffered + from a previous call (`buffLen > 0`), pull bytes from the new input until + either a full block is assembled or the input is exhausted. If a full block + is formed, it becomes the first inline block on the wire. + +2. **Pack whole blocks from input**: copy as many remaining complete blocks from + the caller's input as fit in the inline data area. + +3. **Stash the tail**: any leftover bytes (less than one block) go into the + local buffer for the next call. + +``` + Caller input (e.g., 200 bytes, buffLen=30 from prior call): + ┌──────────────────────────────────────────────────────────┐ + │ input data (200 bytes) │ + └──────────────────────────────────────────────────────────┘ + + Step 1: Top up partial block (34 bytes from input complete the block) + ┌────────┬──────────────────────────────────────────────────┐ + │buff(30)│ 34 bytes │ │ + └────────┴──────────┘ remaining: 166 bytes │ + ↓ │ + [Block 0: 64 bytes] → wire │ + │ + Step 2: Pack whole blocks (2 more blocks = 128 bytes) │ + [Block 1: 64 bytes] → wire │ + [Block 2: 64 bytes] → wire │ + │ + Step 3: Stash tail (166 - 128 = 38 bytes) │ + buffLen = 38 │ + │ + Wire payload: 192 bytes (3 blocks) │ + └───────────────────────────────────────────────────────────┘ +``` + +If the total input is small enough to fit entirely in the partial-block buffer +without completing a block, no server round-trip is issued at all. The +`requestSent` output flag tells the caller whether a matching `*Response()` call +is needed: + +```c +bool requestSent; +ret = wh_Client_Sha256UpdateRequest(ctx, sha, smallData, 10, &requestSent); +/* requestSent == false: data absorbed locally, no Response needed */ +``` + +### State Rollback on Send Failure + +Before mutating the buffer state, the Request function snapshots `buffLen` and +the partial buffer contents. If `wh_Client_SendRequest()` fails (e.g., +transport error), the snapshot is restored so the caller can retry without data +loss: + +```c +/* Save state before mutation */ +savedBuffLen = sha->buffLen; +memcpy(savedBuffer, sha->buffer, sha->buffLen); + +/* ... mutate buffer, assemble wire payload ... */ + +ret = wh_Client_SendRequest(...); +if (ret != 0) { + /* Restore -- SHA state is as if the call never happened */ + sha->buffLen = savedBuffLen; + memcpy(sha->buffer, savedBuffer, savedBuffLen); +} +``` + +### Finalization + +The Final request sends whatever partial data remains in the client's buffer +(0 to `BLOCK_SIZE - 1` bytes) with `isLastBlock = 1`. The server handles +MD5-style padding and produces the final digest. The Final response copies the +digest to the caller's output buffer and resets the `wc_Sha*` context (via +`wc_InitSha*_ex`, preserving `devId`). + +### Stateless Server + +The server is fully stateless with respect to SHA operations. Each request +carries the complete intermediate hash state (`digest`, `loLen`, `hiLen`) in +the `resumeState` field. The server: + +1. Initializes a fresh `wc_Sha256` (or variant) context. +2. Restores `digest`, `loLen`, `hiLen` from the request. +3. Calls `wc_Sha256Update()` with the inline data. +4. If `isLastBlock`, calls `wc_Sha256Final()` and returns the digest. +5. Otherwise, returns the updated intermediate state. + +This design has a key benefit: **no server-side per-client hash state is +needed**. The server can handle SHA requests from multiple clients +interleaved without any context tracking. The tradeoff is larger messages +(~40-84 bytes of state overhead per request), which is negligible relative to +the data payload. + +The server also enforces invariants: +- Non-final updates: `inSz` must be a multiple of the block size. +- Final: `inSz` must be strictly less than one block. +- After processing a non-final update, `buffLen` must be 0 (sanity check). + +### Blocking Wrapper + +The existing `wh_Client_Sha256()` function is retained as a blocking wrapper +that loops over the async primitives: + +```c +int wh_Client_Sha256(whClientContext* ctx, wc_Sha256* sha256, + const uint8_t* in, uint32_t inLen, uint8_t* out) +{ + /* Update phase: chunk input to fit per-call capacity */ + while (consumed < inLen) { + capacity = _Sha256UpdatePerCallCapacity(sha256); + chunk = min(remaining, capacity); + + wh_Client_Sha256UpdateRequest(ctx, sha256, in + consumed, chunk, &sent); + if (sent) { + do { + ret = wh_Client_Sha256UpdateResponse(ctx, sha256); + } while (ret == WH_ERROR_NOTREADY); + } + consumed += chunk; + } + + /* Final phase */ + wh_Client_Sha256FinalRequest(ctx, sha256); + do { + ret = wh_Client_Sha256FinalResponse(ctx, sha256, out); + } while (ret == WH_ERROR_NOTREADY); +} +``` + +The crypto callback (`wh_Client_CryptoCb`) calls this blocking wrapper, so +existing code using `wc_Sha256Update()` / `wc_Sha256Final()` with +`devId = WH_DEV_ID` continues to work identically. + +### DMA Variant + +When `WOLFHSM_CFG_DMA` is enabled, a parallel set of DMA async functions is +available. The DMA variant differs from the inline variant in how bulk data +reaches the server: + +- **Inline (non-DMA)**: all input data is copied into the comm buffer message. +- **DMA**: whole blocks are referenced by address via a `DmaBuffer` descriptor + (the server reads them directly from client memory). Only the assembled first + block (from the partial buffer) or the final tail travels inline. + +The hash state (`resumeState`) always travels **inline**, not via DMA, for +cross-architecture concerns (endian translation, address space isolation). + +DMA async functions require the client to stash the translated DMA address +across the Request/Response boundary for POST cleanup. This context is stored +in `whClientContext.dma.asyncCtx.sha`: + +```c +typedef struct { + uintptr_t ioAddr; /* translated DMA address for POST */ + uintptr_t clientAddr; /* original client address for POST */ + uint64_t ioSz; /* DMA'd size for POST */ +} whClientDmaAsyncSha; +``` + +### API Reference + +All variants follow the same pattern. SHA-224 uses the SHA-256 wire format +(same block size); SHA-384 uses the SHA-512 wire format. + +#### Non-DMA + +```c +/* SHA-256 */ +int wh_Client_Sha256UpdateRequest(whClientContext* ctx, wc_Sha256* sha, + const uint8_t* in, uint32_t inLen, + bool* requestSent); +int wh_Client_Sha256UpdateResponse(whClientContext* ctx, wc_Sha256* sha); +int wh_Client_Sha256FinalRequest(whClientContext* ctx, wc_Sha256* sha); +int wh_Client_Sha256FinalResponse(whClientContext* ctx, wc_Sha256* sha, + uint8_t* out); + +/* SHA-224: identical pattern, s/256/224/ */ +/* SHA-384: identical pattern, s/256/384/, uses SHA-512 wire format */ +/* SHA-512: identical pattern, s/256/512/ */ +``` + +#### DMA + +```c +/* SHA-256 DMA (requires WOLFHSM_CFG_DMA) */ +int wh_Client_Sha256DmaUpdateRequest(whClientContext* ctx, wc_Sha256* sha, + const uint8_t* in, uint32_t inLen, + bool* requestSent); +int wh_Client_Sha256DmaUpdateResponse(whClientContext* ctx, wc_Sha256* sha); +int wh_Client_Sha256DmaFinalRequest(whClientContext* ctx, wc_Sha256* sha); +int wh_Client_Sha256DmaFinalResponse(whClientContext* ctx, wc_Sha256* sha, + uint8_t* out); + +/* SHA-224, SHA-384, SHA-512: same pattern */ +``` + +#### Blocking (unchanged, now wraps async internally) + +```c +int wh_Client_Sha256(whClientContext* ctx, wc_Sha256* sha, const uint8_t* in, + uint32_t inLen, uint8_t* out); +int wh_Client_Sha256Dma(whClientContext* ctx, wc_Sha256* sha, const uint8_t* in, + uint32_t inLen, uint8_t* out); +/* SHA-224, SHA-384, SHA-512: same pattern */ +``` + +### Design Tradeoffs + +| Decision | Tradeoff | +|----------|----------| +| **State on wire** | Larger messages (~40-84 bytes overhead), but the server is fully stateless and needs no per-client hash context | +| **Whole-block alignment** | Wastes up to `BLOCK_SIZE - 1` bytes of comm buffer capacity per message, but guarantees the server never has a partial block (simplifies server logic and invariant checking) | +| **Client-side partial buffering** | Requires wolfCrypt's buffer/buffLen fields, but avoids allocating separate storage and enables the `requestSent` optimization for small inputs | +| **Per-call capacity limit** | Callers of the async API must respect the capacity and chunk large inputs themselves (the blocking wrapper handles this automatically), but each call is bounded and predictable | +| **`requestSent` flag** | Adds a parameter to the API, but avoids unnecessary round-trips when input is absorbed entirely into the local buffer | +| **Snapshot/rollback on send failure** | Small CPU cost to copy the partial buffer, but guarantees SHA state consistency even on transport failures | + +## Roadmap: Remaining Algorithms + +The async split pattern will be applied algorithm by algorithm to all crypto +operations currently handled by `wh_Client_CryptoCb`. The table below shows +the full set of operations and their planned async status. + +**Completed:** + +| Algorithm | Functions | Notes | +|----------------|----------------------------------|-------| +| SHA-256 | Update/Final Request/Response | Non-DMA and DMA variants | +| SHA-224 | Update/Final Request/Response | Shares SHA-256 wire format | +| SHA-384 | Update/Final Request/Response | Shares SHA-512 wire format | +| SHA-512 | Update/Final Request/Response | Non-DMA and DMA variants | + +**Planned:** + +| Algorithm | Functions | Complexity | Notes | +|-------------------|--------------------------------------------|------------|-------| +| AES-CBC | `wh_Client_AesCbc{Request,Response}` | Low | Single-shot; straightforward split | +| AES-CTR | `wh_Client_AesCtr{Request,Response}` | Low | Single-shot | +| AES-ECB | `wh_Client_AesEcb{Request,Response}` | Low | Single-shot | +| AES-GCM | `wh_Client_AesGcm{Request,Response}` | Low | Single-shot; AAD + ciphertext in one message | +| RSA Sign/Verify | `wh_Client_RsaFunction{Request,Response}` | Low | Single-shot; may need auto-import removed from Request | +| RSA Get Size | `wh_Client_RsaGetSize{Request,Response}` | Low | Trivial query | +| ECDSA Sign | `wh_Client_EccSign{Request,Response}` | Low | Single-shot | +| ECDSA Verify | `wh_Client_EccVerify{Request,Response}` | Low | Single-shot | +| ECDH | `wh_Client_EccSharedSecret{Request,Response}` | Low | Single-shot | +| Curve25519 | `wh_Client_Curve25519SharedSecret{Request,Response}` | Low | Single-shot | +| Ed25519 Sign | `wh_Client_Ed25519Sign{Request,Response}` | Low | Single-shot | +| Ed25519 Verify | `wh_Client_Ed25519Verify{Request,Response}`| Low | Single-shot | +| CMAC | `wh_Client_Cmac{Request,Response}` | Low | Already has partial split pattern | +| ML-DSA Sign | `wh_Client_MlDsaSign{Request,Response}` | Low | Post-quantum; single-shot | +| ML-DSA Verify | `wh_Client_MlDsaVerify{Request,Response}` | Low | Post-quantum; single-shot | +| RNG Generate | `wh_Client_RngGenerate{Request,Response}` | Medium | Chunking needed for large requests; async callers must handle chunking themselves | + +Most remaining algorithms are **single-shot** operations (one request, one +response) and are straightforward to split compared to SHA's streaming +semantics. SHA was done first because it exercises the hardest design +constraints: multi-round-trip streaming, partial-block buffering, and state +resumption. + +### Future: Async Crypto Callbacks + +The long-term goal is to also make the **crypto callback path itself +asynchronous**, so that standard wolfCrypt API calls (`wc_Sha256Update`, +`wc_AesCbcEncrypt`, etc.) can return a "not ready" indicator and be resumed +later, rather than blocking. This requires changes in wolfCrypt's crypto +callback infrastructure and is outside the scope of the current native async +API work. The native async API being introduced here is a prerequisite: it +establishes the per-algorithm Request/Response split that a future async +callback mechanism will build upon. diff --git a/docs/draft/posix-shm.md b/docs/draft/posix-shm.md new file mode 100644 index 000000000..be4035a84 --- /dev/null +++ b/docs/draft/posix-shm.md @@ -0,0 +1,67 @@ +# POSIX SHM DMA Transport + +## Overview + +There are two independent features at play in the POSIX SHM transport port. Understanding which is which is key. + +## 1. The Transport: POSIX Shared Memory (`posix_transport_shm`) + +This is purely a **transport layer** -- it moves request/response messages between client and server processes. It works like this: + +- **Server** creates a POSIX shared memory object (`shm_open`) with a layout of: + ``` + [ 64-byte header | request buffer | response buffer | optional DMA section ] + ``` +- **Client** opens the same named object and `mmap`s it into its address space +- Both sides then delegate to `wh_transport_mem` (the generic memory-based transport) for actual message passing via CSR registers in the request/response buffers +- The header contains PIDs for RT-signal-based async notification + +The transport's job is **only** to shuttle serialized request/response packets. It knows nothing about crypto, keys, or DMA semantics. + +The optional **DMA section** at the end of the shared memory region is the transport providing a chunk of shared address space that *both* processes can access. This is just raw shared memory -- the transport allocates it but doesn't use it itself. It's plumbing for the DMA feature. + +## 2. The Feature: DMA (`WOLFHSM_CFG_DMA`) + +DMA is a **separate, transport-agnostic feature** in wolfHSM core (`wh_dma.h`, `wh_server_dma.c`, `wh_client_dma.c`). It allows crypto operations to reference client memory **by address** rather than copying data into the transport's request/response buffers. This matters because: + +- Standard messages are limited by `WOLFHSM_CFG_COMM_DATA_LEN` (typically ~4KB) +- DMA messages send *addresses* in the request, and the server reads/writes client memory directly + +The DMA feature has a callback-based architecture: +- `wh_Server_DmaProcessClientAddress()` -- server calls this with a client address, the registered callback transforms it to something the server can dereference +- `wh_Client_DmaProcessClientAddress()` -- client calls this to transform its local address into whatever the server will receive in the message +- PRE/POST operations handle setup and teardown (cache flush/invalidate, temporary buffer allocation, etc.) + +On real hardware (e.g. Infineon TC3xx), this is literal hardware DMA -- client and server are on different cores with different address maps, and the callbacks handle the MMU/bus address translation. + +## 3. The Glue: Static Memory Pool Allocator in the SHM DMA Callbacks + +The `posixTransportShm_ClientStaticMemDmaCallback` and `posixTransportShm_ServerStaticMemDmaCallback` in `posix_transport_shm.c` are the **port-specific DMA callbacks** that bridge the POSIX SHM transport with the DMA feature. Here's the clever part: + +**Problem:** On POSIX, client and server are separate processes with separate virtual address spaces. A raw client pointer like `0x7fff12345000` means nothing to the server. But the DMA section in shared memory is mapped into *both* processes (at potentially different virtual addresses). + +**Solution using the pool allocator:** + +1. wolfCrypt's `WOLFSSL_STATIC_MEMORY` pool allocator (`wc_LoadStaticMemory_ex`) is initialized with the DMA section as its backing memory pool +2. When the client DMA callback gets a PRE operation with a client address that's **not** already in the DMA area, it: + - Allocates a temporary buffer from the pool (`XMALLOC` with the heap hint) + - Copies client data into it + - Returns an **offset** from the DMA base (not a pointer) -- this is what gets sent to the server +3. The server DMA callback simply takes that offset, validates it's in bounds, and returns `dma_base + offset` +4. On POST, the client callback copies results back (for writes) and frees the temporary buffer + +If the client address **is already** in the DMA section (the client allocated directly from the pool), it skips the copy and just computes the offset -- zero-copy. + +The pool allocator here is used as a **bump/slab allocator for the shared DMA region**. It has nothing to do with the transport itself -- it's the DMA callback's strategy for managing the shared buffer. wolfHSM could use a different allocator; the pool allocator was chosen because it's already available in wolfCrypt and works without `malloc`. + +## Summary Table + +| Aspect | Transport (SHM) | DMA Feature | Pool Allocator | +|--------|-----------------|-------------|----------------| +| **Layer** | Communication | Application/Crypto | Memory management | +| **Scope** | Port-specific (POSIX) | Core wolfHSM | DMA callback impl detail | +| **Purpose** | Move request/response packets | Let server access client memory by address | Manage temporary buffers in shared DMA area | +| **Config** | `posixTransportShmConfig` | `WOLFHSM_CFG_DMA` | `WOLFSSL_STATIC_MEMORY` | +| **Without it** | No communication | Data must fit in request/response buffers | Would need a different allocator for DMA region | + +The DMA section is **allocated by the transport** but **used by the DMA callbacks**. The pool allocator is **used by the DMA callbacks** to subdivide that DMA section. Three layers, three concerns. diff --git a/examples/posix/wh_posix_cfg.h b/examples/posix/wh_posix_cfg.h index e5a563b34..04fcb57d0 100644 --- a/examples/posix/wh_posix_cfg.h +++ b/examples/posix/wh_posix_cfg.h @@ -23,9 +23,11 @@ * DMA AND BUFFER SIZES * =========================================== */ -/* Request and Response Buffer Sizes */ -#define WH_POSIX_REQ_SIZE 2048 -#define WH_POSIX_RESP_SIZE 2048 +/* Request and Response Buffer Sizes. Must be at least + * sizeof(whTransportMemCsr) + sizeof(whCommHeader) + WOLFHSM_CFG_COMM_DATA_LEN + * to carry a full comm packet through the SHM transport. */ +#define WH_POSIX_REQ_SIZE (16 + (1024 * 8)) +#define WH_POSIX_RESP_SIZE (16 + (1024 * 8)) #define WH_POSIX_DMA_SIZE 8000 /* Data Buffer Sizes */ diff --git a/examples/posix/wh_posix_client/wolfhsm_cfg.h b/examples/posix/wh_posix_client/wolfhsm_cfg.h index a07936b83..2b44e653c 100644 --- a/examples/posix/wh_posix_client/wolfhsm_cfg.h +++ b/examples/posix/wh_posix_client/wolfhsm_cfg.h @@ -32,7 +32,7 @@ /** wolfHSM settings */ #define WOLFHSM_CFG_ENABLE_CLIENT #define WOLFHSM_CFG_HEXDUMP -#define WOLFHSM_CFG_COMM_DATA_LEN 5000 +#define WOLFHSM_CFG_COMM_DATA_LEN (1024 * 8) #ifndef WOLFHSM_CFG_NO_CRYPTO #define WOLFHSM_CFG_KEYWRAP #define WOLFHSM_CFG_GLOBAL_KEYS diff --git a/examples/posix/wh_posix_server/wolfhsm_cfg.h b/examples/posix/wh_posix_server/wolfhsm_cfg.h index 2c0e4e3fa..c1799d5b1 100644 --- a/examples/posix/wh_posix_server/wolfhsm_cfg.h +++ b/examples/posix/wh_posix_server/wolfhsm_cfg.h @@ -34,8 +34,8 @@ #define WOLFHSM_CFG_HEXDUMP -/* Large enough for ML-DSA level 5 key */ -#define WOLFHSM_CFG_COMM_DATA_LEN 5000 +/* Must match client WOLFHSM_CFG_COMM_DATA_LEN */ +#define WOLFHSM_CFG_COMM_DATA_LEN (1024 * 8) #define WOLFHSM_CFG_NVM_OBJECT_COUNT 30 #define WOLFHSM_CFG_SERVER_KEYCACHE_COUNT 9 diff --git a/src/wh_client_crypto.c b/src/wh_client_crypto.c index e47ae92f8..1f6c5d4d9 100644 --- a/src/wh_client_crypto.c +++ b/src/wh_client_crypto.c @@ -124,20 +124,6 @@ static int _MlDsaMakeKeyDma(whClientContext* ctx, int level, #endif /* WOLFHSM_CFG_DMA */ #endif /* HAVE_DILITHIUM */ -#ifndef NO_SHA256 -/* Helper function to transfer SHA256 block and update digest */ -static int _xferSha256BlockAndUpdateDigest(whClientContext* ctx, - wc_Sha256* sha256, - uint32_t isLastBlock); -#endif /* !NO_SHA256 */ - -#if defined(WOLFSSL_SHA224) -/* Helper function to transfer SHA224 block and update digest */ -static int _xferSha224BlockAndUpdateDigest(whClientContext* ctx, - wc_Sha224* sha224, - uint32_t isLastBlock); -#endif /* WOLFSSL_SHA224 */ - static uint8_t* _createCryptoRequest(uint8_t* reqBuf, uint16_t type, uint32_t affinity); static uint8_t* _createCryptoRequestWithSubtype(uint8_t* reqBuf, uint16_t type, @@ -4310,17 +4296,47 @@ int wh_Client_CmacDma(whClientContext* ctx, Cmac* cmac, CmacType type, #ifndef NO_SHA256 -static int _xferSha256BlockAndUpdateDigest(whClientContext* ctx, - wc_Sha256* sha256, - uint32_t isLastBlock) +/* Maximum number of input bytes that wh_Client_Sha256UpdateRequest can absorb + * in a single call: the inline-data wire capacity, plus whatever room is left + * in the partial-block buffer (we can stash up to BLOCK_SIZE-1-buffLen tail + * bytes locally without producing a new full block). */ +static uint32_t _Sha256UpdatePerCallCapacity(const wc_Sha256* sha) { - uint16_t group = WH_MESSAGE_GROUP_CRYPTO; - uint16_t action = WH_MESSAGE_ACTION_NONE; - int ret = 0; - uint16_t dataSz = 0; - whMessageCrypto_Sha256Request* req = NULL; - whMessageCrypto_Sha2Response* res = NULL; - uint8_t* dataPtr = NULL; + return WH_MESSAGE_CRYPTO_SHA256_MAX_INLINE_UPDATE_SZ + + (uint32_t)(WC_SHA256_BLOCK_SIZE - 1u - sha->buffLen); +} + +int wh_Client_Sha256UpdateRequest(whClientContext* ctx, wc_Sha256* sha, + const uint8_t* in, uint32_t inLen, + bool* requestSent) +{ + int ret = 0; + whMessageCrypto_Sha256Request* req = NULL; + uint8_t* inlineData; + uint8_t* dataPtr = NULL; + uint8_t* sha256BufferBytes; + uint32_t capacity; + uint32_t wirePos = 0; + uint32_t i = 0; + /* Snapshot of buffer state for rollback if SendRequest fails */ + uint32_t savedBuffLen; + uint8_t savedBuffer[WC_SHA256_BLOCK_SIZE]; + + if (ctx == NULL || sha == NULL || requestSent == NULL || + (in == NULL && inLen != 0)) { + return WH_ERROR_BADARGS; + } + *requestSent = false; + + capacity = _Sha256UpdatePerCallCapacity(sha); + if (inLen > capacity) { + return WH_ERROR_BADARGS; + } + + /* Empty update: nothing to send, no state to mutate. */ + if (inLen == 0) { + return WH_ERROR_OK; + } /* Get data buffer */ dataPtr = wh_CommClient_GetDataPtr(ctx->comm); @@ -4331,564 +4347,538 @@ static int _xferSha256BlockAndUpdateDigest(whClientContext* ctx, /* Setup generic header and get pointer to request data */ req = (whMessageCrypto_Sha256Request*)_createCryptoRequest( dataPtr, WC_HASH_TYPE_SHA256, ctx->cryptoAffinity); + inlineData = (uint8_t*)(req + 1); + sha256BufferBytes = (uint8_t*)sha->buffer; + + /* Save the buffer state before mutation so we can restore it if + * SendRequest fails, preventing silent SHA state corruption. */ + savedBuffLen = sha->buffLen; + memcpy(savedBuffer, sha256BufferBytes, sha->buffLen); + + /* If there's a partial block already buffered, top it up from the input. + * If we manage to fill a full block, copy the completed block into the + * wire payload as the first inline block. */ + if (sha->buffLen > 0) { + while (i < inLen && sha->buffLen < WC_SHA256_BLOCK_SIZE) { + sha256BufferBytes[sha->buffLen++] = in[i++]; + } + if (sha->buffLen == WC_SHA256_BLOCK_SIZE) { + memcpy(inlineData + wirePos, sha256BufferBytes, + WC_SHA256_BLOCK_SIZE); + wirePos += WC_SHA256_BLOCK_SIZE; + sha->buffLen = 0; + } + } + + /* Copy as many full blocks from the input as fit in the inline area. */ + while ((inLen - i) >= WC_SHA256_BLOCK_SIZE && + (wirePos + WC_SHA256_BLOCK_SIZE) <= + WH_MESSAGE_CRYPTO_SHA256_MAX_INLINE_UPDATE_SZ) { + memcpy(inlineData + wirePos, in + i, WC_SHA256_BLOCK_SIZE); + wirePos += WC_SHA256_BLOCK_SIZE; + i += WC_SHA256_BLOCK_SIZE; + } + + /* Stash any remaining tail bytes into the buffer for next time. The + * capacity check above guarantees this fits without overflow. */ + while (i < inLen) { + sha256BufferBytes[sha->buffLen++] = in[i++]; + } + + /* Pure-buffer-fill update: nothing to send. */ + if (wirePos == 0) { + return WH_ERROR_OK; + } + + /* Populate fixed request fields */ + req->isLastBlock = 0; + req->inSz = wirePos; + memcpy(req->resumeState.hash, sha->digest, WC_SHA256_DIGEST_SIZE); + req->resumeState.hiLen = sha->hiLen; + req->resumeState.loLen = sha->loLen; + ret = wh_Client_SendRequest(ctx, WH_MESSAGE_GROUP_CRYPTO, WC_ALGO_TYPE_HASH, + sizeof(whMessageCrypto_GenericRequestHeader) + + sizeof(*req) + wirePos, + dataPtr); - /* Send the full block to the server, along with the - * current hash state if needed. Finalization/padding of last block is up to - * the server, we just need to let it know we are done and sending an - * incomplete last block */ - if (isLastBlock) { - req->isLastBlock = 1; - req->lastBlockLen = sha256->buffLen; + if (ret == 0) { + *requestSent = true; } else { - req->isLastBlock = 0; + /* SendRequest failed — restore buffer state so the caller can retry + * or continue hashing without data loss. */ + sha->buffLen = savedBuffLen; + memcpy(sha256BufferBytes, savedBuffer, savedBuffLen); } - memcpy(req->inBlock, sha256->buffer, - (isLastBlock) ? sha256->buffLen : WC_SHA256_BLOCK_SIZE); + return ret; +} - /* Send the hash state - this will be 0 on the first block on a properly - * initialized sha256 struct */ - memcpy(req->resumeState.hash, sha256->digest, WC_SHA256_DIGEST_SIZE); - req->resumeState.hiLen = sha256->hiLen; - req->resumeState.loLen = sha256->loLen; +int wh_Client_Sha256UpdateResponse(whClientContext* ctx, wc_Sha256* sha) +{ + uint16_t group = WH_MESSAGE_GROUP_CRYPTO; + uint16_t action = WH_MESSAGE_ACTION_NONE; + uint16_t dataSz = 0; + int ret = 0; + whMessageCrypto_Sha2Response* res = NULL; + uint8_t* dataPtr; - uint32_t req_len = - sizeof(whMessageCrypto_GenericRequestHeader) + sizeof(*req); + if (ctx == NULL || sha == NULL) { + return WH_ERROR_BADARGS; + } - ret = wh_Client_SendRequest(ctx, group, WC_ALGO_TYPE_HASH, req_len, - (uint8_t*)dataPtr); + dataPtr = wh_CommClient_GetDataPtr(ctx->comm); + if (dataPtr == NULL) { + return WH_ERROR_BADARGS; + } - WH_DEBUG_CLIENT_VERBOSE("send SHA256 Req:\n"); - WH_DEBUG_VERBOSE_HEXDUMP("[client] inBlock: ", req->inBlock, WC_SHA256_BLOCK_SIZE); - if (req->resumeState.hiLen != 0 || req->resumeState.loLen != 0) { - WH_DEBUG_VERBOSE_HEXDUMP(" [client] resumeHash: ", req->resumeState.hash, - (isLastBlock) ? req->lastBlockLen - : WC_SHA256_BLOCK_SIZE); - WH_DEBUG_CLIENT_VERBOSE(" hiLen: %u, loLen: %u\n", - (unsigned int)req->resumeState.hiLen, - (unsigned int)req->resumeState.loLen); + ret = wh_Client_RecvResponse(ctx, &group, &action, &dataSz, dataPtr); + if (ret != WH_ERROR_OK) { + return ret; } - WH_DEBUG_CLIENT_VERBOSE(" ret = %d\n", ret); - if (ret == 0) { - do { - ret = wh_Client_RecvResponse(ctx, &group, &action, &dataSz, - (uint8_t*)dataPtr); - } while (ret == WH_ERROR_NOTREADY); + ret = _getCryptoResponse(dataPtr, WC_HASH_TYPE_SHA256, (uint8_t**)&res); + if (ret >= 0) { + memcpy(sha->digest, res->hash, WC_SHA256_DIGEST_SIZE); + sha->hiLen = res->hiLen; + sha->loLen = res->loLen; } - if (ret == 0) { - /* Get response */ - ret = _getCryptoResponse(dataPtr, WC_HASH_TYPE_SHA256, (uint8_t**)&res); - /* wolfCrypt allows positive error codes on success in some scenarios */ - if (ret >= 0) { - WH_DEBUG_CLIENT_VERBOSE("Client SHA256 Res recv: ret=%d", ret); - /* Store the received intermediate hash in the sha256 - * context and indicate the field is now valid and - * should be passed back and forth to the server */ - memcpy(sha256->digest, res->hash, WC_SHA256_DIGEST_SIZE); - sha256->hiLen = res->hiLen; - sha256->loLen = res->loLen; - WH_DEBUG_CLIENT_VERBOSE("Client SHA256 Res recv:\n"); - WH_DEBUG_VERBOSE_HEXDUMP("[client] hash: ", (uint8_t*)sha256->digest, - WC_SHA256_DIGEST_SIZE); - } + return ret; +} + +int wh_Client_Sha256FinalRequest(whClientContext* ctx, wc_Sha256* sha) +{ + int ret; + whMessageCrypto_Sha256Request* req; + uint8_t* inlineData; + uint8_t* dataPtr; + + if (ctx == NULL || sha == NULL) { + return WH_ERROR_BADARGS; + } + + dataPtr = wh_CommClient_GetDataPtr(ctx->comm); + if (dataPtr == NULL) { + return WH_ERROR_BADARGS; + } + + req = (whMessageCrypto_Sha256Request*)_createCryptoRequest( + dataPtr, WC_HASH_TYPE_SHA256, ctx->cryptoAffinity); + inlineData = (uint8_t*)(req + 1); + + req->isLastBlock = 1; + req->inSz = sha->buffLen; + memcpy(req->resumeState.hash, sha->digest, WC_SHA256_DIGEST_SIZE); + req->resumeState.hiLen = sha->hiLen; + req->resumeState.loLen = sha->loLen; + + if (sha->buffLen > 0) { + memcpy(inlineData, sha->buffer, sha->buffLen); + } + + ret = wh_Client_SendRequest(ctx, WH_MESSAGE_GROUP_CRYPTO, WC_ALGO_TYPE_HASH, + sizeof(whMessageCrypto_GenericRequestHeader) + + sizeof(*req) + sha->buffLen, + dataPtr); + return ret; +} + +int wh_Client_Sha256FinalResponse(whClientContext* ctx, wc_Sha256* sha, + uint8_t* out) +{ + uint16_t group = WH_MESSAGE_GROUP_CRYPTO; + uint16_t action = WH_MESSAGE_ACTION_NONE; + uint16_t dataSz = 0; + int ret; + whMessageCrypto_Sha2Response* res = NULL; + uint8_t* dataPtr; + + if (ctx == NULL || sha == NULL || out == NULL) { + return WH_ERROR_BADARGS; + } + + dataPtr = wh_CommClient_GetDataPtr(ctx->comm); + if (dataPtr == NULL) { + return WH_ERROR_BADARGS; + } + + ret = wh_Client_RecvResponse(ctx, &group, &action, &dataSz, dataPtr); + if (ret != 0) { + return ret; } + ret = _getCryptoResponse(dataPtr, WC_HASH_TYPE_SHA256, (uint8_t**)&res); + if (ret >= 0) { + memcpy(out, res->hash, WC_SHA256_DIGEST_SIZE); + /* Reset state without blowing away devId */ + (void)wc_InitSha256_ex(sha, NULL, sha->devId); + } return ret; } int wh_Client_Sha256(whClientContext* ctx, wc_Sha256* sha256, const uint8_t* in, uint32_t inLen, uint8_t* out) { - int ret = 0; - uint8_t* sha256BufferBytes = (uint8_t*)sha256->buffer; + int ret = WH_ERROR_OK; /* Caller invoked SHA Update: * wc_CryptoCb_Sha256Hash(sha256, data, len, NULL) */ - if (in != NULL) { - size_t i = 0; - - /* Process the partial blocks directly from the input data. If there - * is enough input data to fill a full block, transfer it to the - * server */ - if (sha256->buffLen > 0) { - while (i < inLen && sha256->buffLen < WC_SHA256_BLOCK_SIZE) { - sha256BufferBytes[sha256->buffLen++] = in[i++]; + if (in != NULL && inLen > 0) { + uint32_t consumed = 0; + while (ret == WH_ERROR_OK && consumed < inLen) { + uint32_t capacity = _Sha256UpdatePerCallCapacity(sha256); + uint32_t remaining = inLen - consumed; + uint32_t chunk = (remaining < capacity) ? remaining : capacity; + bool sent = false; + + ret = wh_Client_Sha256UpdateRequest(ctx, sha256, in + consumed, + chunk, &sent); + if (ret != WH_ERROR_OK) { + break; } - if (sha256->buffLen == WC_SHA256_BLOCK_SIZE) { - ret = _xferSha256BlockAndUpdateDigest(ctx, sha256, 0); - sha256->buffLen = 0; + if (sent) { + do { + ret = wh_Client_Sha256UpdateResponse(ctx, sha256); + } while (ret == WH_ERROR_NOTREADY); + if (ret != WH_ERROR_OK) { + break; + } } - } - - /* Process as many full blocks from the input data as we can */ - while (ret == 0 && (inLen - i) >= WC_SHA256_BLOCK_SIZE) { - memcpy(sha256BufferBytes, in + i, WC_SHA256_BLOCK_SIZE); - ret = _xferSha256BlockAndUpdateDigest(ctx, sha256, 0); - i += WC_SHA256_BLOCK_SIZE; - } - - /* Copy any remaining data into the buffer to be sent in a - * subsequent call when we have enough input data to send a full - * block */ - while (i < inLen) { - sha256BufferBytes[sha256->buffLen++] = in[i++]; + consumed += chunk; } } /* Caller invoked SHA finalize: - * wc_CryptoCb_Sha256Hash(sha256, NULL, 0, * hash) */ - if (ret == 0 && out != NULL) { - ret = _xferSha256BlockAndUpdateDigest(ctx, sha256, 1); - - /* Copy out the final hash value */ - if (ret == 0) { - memcpy(out, sha256->digest, WC_SHA256_DIGEST_SIZE); + * wc_CryptoCb_Sha256Hash(sha256, NULL, 0, *hash) */ + if (ret == WH_ERROR_OK && out != NULL) { + ret = wh_Client_Sha256FinalRequest(ctx, sha256); + if (ret == WH_ERROR_OK) { + do { + ret = wh_Client_Sha256FinalResponse(ctx, sha256, out); + } while (ret == WH_ERROR_NOTREADY); } - - /* reset the state of the sha context (without blowing away devId) */ - wc_InitSha256_ex(sha256, NULL, sha256->devId); } return ret; } #ifdef WOLFHSM_CFG_DMA -int wh_Client_Sha256Dma(whClientContext* ctx, wc_Sha256* sha, const uint8_t* in, - uint32_t inLen, uint8_t* out) +int wh_Client_Sha256DmaUpdateRequest(whClientContext* ctx, wc_Sha256* sha, + const uint8_t* in, uint32_t inLen, + bool* requestSent) { - int ret = WH_ERROR_OK; - wc_Sha256* sha256 = sha; - uint16_t respSz = 0; - uint16_t group = WH_MESSAGE_GROUP_CRYPTO_DMA; - uint8_t* dataPtr = NULL; - whMessageCrypto_Sha2DmaRequest* req = NULL; - whMessageCrypto_Sha2DmaResponse* resp = NULL; - uintptr_t inAddr = 0; /* The req->input.addr is reused elsewhere, this - local variable is to keep track of the resulting - DMA translation to pass back to the callback on - POST operations. */ - uintptr_t outAddr = 0; - uintptr_t stateAddr = 0; + int ret = WH_ERROR_OK; + uint8_t* dataPtr = NULL; + whMessageCrypto_Sha256DmaRequest* req = NULL; + uint8_t* inlineData; + uint8_t* sha256BufferBytes; + uint32_t wirePos = 0; + uint32_t i = 0; + uintptr_t inAddr = 0; + bool inAddrAcquired = false; + const uint8_t* dmaBase = NULL; + uint32_t dmaSz = 0; + /* Snapshot of buffer state for rollback if SendRequest fails */ + uint32_t savedBuffLen; + uint8_t savedBuffer[WC_SHA256_BLOCK_SIZE]; + + if (ctx == NULL || sha == NULL || requestSent == NULL || + (in == NULL && inLen != 0)) { + return WH_ERROR_BADARGS; + } + *requestSent = false; + + if (inLen == 0) { + return WH_ERROR_OK; + } - /* Get data pointer from the context to use as request/response storage */ dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm); if (dataPtr == NULL) { return WH_ERROR_BADARGS; } - /* Setup generic header and get pointer to request data */ - req = (whMessageCrypto_Sha2DmaRequest*)_createCryptoRequest( + req = (whMessageCrypto_Sha256DmaRequest*)_createCryptoRequest( dataPtr, WC_HASH_TYPE_SHA256, ctx->cryptoAffinity); + inlineData = (uint8_t*)(req + 1); + sha256BufferBytes = (uint8_t*)sha->buffer; - /* map addresses and setup default request structure */ - if (in != NULL || out != NULL) { - req->finalize = 0; - req->state.sz = sizeof(*sha256); - req->input.sz = inLen; - req->output.sz = WC_SHA256_DIGEST_SIZE; /* not needed, but YOLO */ + /* Save buffer state for rollback */ + savedBuffLen = sha->buffLen; + memcpy(savedBuffer, sha256BufferBytes, sha->buffLen); - /* Perform address translations */ - ret = wh_Client_DmaProcessClientAddress( - ctx, (uintptr_t)sha256, (void**)&stateAddr, req->state.sz, - WH_DMA_OPER_CLIENT_WRITE_PRE, (whDmaFlags){0}); - if (ret == WH_ERROR_OK) { - req->state.addr = stateAddr; + /* If there's a partial block already buffered, top it up from input. + * If we complete a full block, copy it to the inline trailing area. */ + if (sha->buffLen > 0) { + while (i < inLen && sha->buffLen < WC_SHA256_BLOCK_SIZE) { + sha256BufferBytes[sha->buffLen++] = in[i++]; } - - if (ret == WH_ERROR_OK) { - ret = wh_Client_DmaProcessClientAddress( - ctx, (uintptr_t)in, (void**)&inAddr, req->input.sz, - WH_DMA_OPER_CLIENT_READ_PRE, (whDmaFlags){0}); - if (ret == WH_ERROR_OK) { - req->input.addr = inAddr; - } + if (sha->buffLen == WC_SHA256_BLOCK_SIZE) { + memcpy(inlineData, sha256BufferBytes, WC_SHA256_BLOCK_SIZE); + wirePos = WC_SHA256_BLOCK_SIZE; + sha->buffLen = 0; } + } - if (ret == WH_ERROR_OK) { - ret = wh_Client_DmaProcessClientAddress( - ctx, (uintptr_t)out, (void**)&outAddr, req->output.sz, - WH_DMA_OPER_CLIENT_WRITE_PRE, (whDmaFlags){0}); - if (ret == WH_ERROR_OK) { - req->output.addr = outAddr; - } - } + /* Remaining whole blocks from input go via DMA */ + if ((inLen - i) >= WC_SHA256_BLOCK_SIZE) { + dmaBase = in + i; + dmaSz = ((inLen - i) / WC_SHA256_BLOCK_SIZE) * WC_SHA256_BLOCK_SIZE; + i += dmaSz; } - /* Caller invoked SHA Update: - * wc_CryptoCb_Sha256Hash(sha256, data, len, NULL) */ - if ((ret == WH_ERROR_OK) && (in != NULL)) { - WH_DEBUG_CLIENT_VERBOSE("SHA256 DMA UPDATE: inAddr=%p, inSz=%u\n", in, - (unsigned int)inLen); + /* Stash any remaining tail bytes into the buffer */ + while (i < inLen) { + sha256BufferBytes[sha->buffLen++] = in[i++]; + } - ret = wh_Client_SendRequest( - ctx, group, WC_ALGO_TYPE_HASH, - sizeof(whMessageCrypto_GenericRequestHeader) + sizeof(*req), - (uint8_t*)dataPtr); + /* If no blocks to send, nothing to do */ + if (wirePos == 0 && dmaSz == 0) { + return WH_ERROR_OK; + } - if (ret == WH_ERROR_OK) { - do { - ret = wh_Client_RecvResponse(ctx, NULL, NULL, &respSz, - (uint8_t*)dataPtr); - } while (ret == WH_ERROR_NOTREADY); - } + /* Populate request fields */ + req->isLastBlock = 0; + req->inSz = wirePos; + memcpy(req->resumeState.hash, sha->digest, WC_SHA256_DIGEST_SIZE); + req->resumeState.hiLen = sha->hiLen; + req->resumeState.loLen = sha->loLen; + req->input.sz = dmaSz; + req->input.addr = 0; + /* DMA PRE for input data if there are DMA blocks */ + if (dmaSz > 0) { + ret = wh_Client_DmaProcessClientAddress( + ctx, (uintptr_t)dmaBase, (void**)&inAddr, dmaSz, + WH_DMA_OPER_CLIENT_READ_PRE, (whDmaFlags){0}); if (ret == WH_ERROR_OK) { - /* Get response structure pointer, validates generic header - * rc */ - ret = _getCryptoResponse(dataPtr, WC_HASH_TYPE_SHA256, - (uint8_t**)&resp); - /* Nothing to do on success, as server will have updated the context - * in client memory */ + inAddrAcquired = true; + req->input.addr = inAddr; } } - /* Caller invoked SHA finalize: - * wc_CryptoCb_Sha256Hash(sha256, NULL, 0, * hash) */ - if ((ret == WH_ERROR_OK) && (out != NULL)) { - /* Packet will have been trashed, so re-populate all fields */ - req->finalize = 1; - WH_DEBUG_CLIENT_VERBOSE("SHA256 DMA FINAL: outAddr=%p\n", out); - /* send the request to the server */ - ret = wh_Client_SendRequest( - ctx, group, WC_ALGO_TYPE_HASH, - sizeof(whMessageCrypto_GenericRequestHeader) + sizeof(*req), - (uint8_t*)dataPtr); + if (ret == WH_ERROR_OK) { + WH_DEBUG_CLIENT_VERBOSE("SHA256 DMA UPDATE: inlineSz=%u, dmaSz=%u\n", + (unsigned int)wirePos, (unsigned int)dmaSz); - if (ret == WH_ERROR_OK) { - do { - ret = wh_Client_RecvResponse(ctx, NULL, NULL, &respSz, - (uint8_t*)dataPtr); - } while (ret == WH_ERROR_NOTREADY); - } + /* Stash DMA info for Response POST cleanup */ + ctx->dma.asyncCtx.sha.ioAddr = inAddr; + ctx->dma.asyncCtx.sha.clientAddr = (uintptr_t)dmaBase; + ctx->dma.asyncCtx.sha.ioSz = dmaSz; - /* Copy out the final hash value */ - if (ret == WH_ERROR_OK) { - /* Get response structure pointer, validates generic header - * rc */ - ret = _getCryptoResponse(dataPtr, WC_HASH_TYPE_SHA256, - (uint8_t**)&resp); - /* Nothing to do on success, as server will have updated the output - * hash in client memory */ - } + ret = wh_Client_SendRequest( + ctx, WH_MESSAGE_GROUP_CRYPTO_DMA, WC_ALGO_TYPE_HASH, + sizeof(whMessageCrypto_GenericRequestHeader) + sizeof(*req) + + wirePos, + dataPtr); } - /* This is called regardless of successful operation to give the callback a - * chance for cleanup. i.e if XMALLOC had been used and XFREE call is - * needed. Don't override return value with closing process address calls.*/ - if (in != NULL || out != NULL) { - /* post operation address translations */ - (void)wh_Client_DmaProcessClientAddress( - ctx, (uintptr_t)sha256, (void**)&stateAddr, sizeof(*sha256), - WH_DMA_OPER_CLIENT_WRITE_POST, (whDmaFlags){0}); - (void)wh_Client_DmaProcessClientAddress( - ctx, (uintptr_t)in, (void**)&inAddr, inLen, - WH_DMA_OPER_CLIENT_READ_POST, (whDmaFlags){0}); - (void)wh_Client_DmaProcessClientAddress( - ctx, (uintptr_t)out, (void**)&outAddr, WC_SHA256_DIGEST_SIZE, - WH_DMA_OPER_CLIENT_WRITE_POST, (whDmaFlags){0}); + if (ret == WH_ERROR_OK) { + *requestSent = true; + } + else { + /* Rollback buffer state and release DMA on failure */ + sha->buffLen = savedBuffLen; + memcpy(sha256BufferBytes, savedBuffer, savedBuffLen); + if (inAddrAcquired) { + (void)wh_Client_DmaProcessClientAddress( + ctx, (uintptr_t)dmaBase, (void**)&inAddr, dmaSz, + WH_DMA_OPER_CLIENT_READ_POST, (whDmaFlags){0}); + } } - return ret; } -#endif /* WOLFHSM_CFG_DMA */ -#endif /* !NO_SHA256 */ - -#ifdef WOLFSSL_SHA224 -static int _xferSha224BlockAndUpdateDigest(whClientContext* ctx, - wc_Sha224* sha224, - uint32_t isLastBlock) +int wh_Client_Sha256DmaUpdateResponse(whClientContext* ctx, wc_Sha256* sha) { - uint16_t group = WH_MESSAGE_GROUP_CRYPTO; - uint16_t action = WH_MESSAGE_ACTION_NONE; - int ret = 0; - uint16_t dataSz = 0; - whMessageCrypto_Sha256Request* req = NULL; - whMessageCrypto_Sha2Response* res = NULL; - uint8_t* dataPtr = NULL; + int ret = WH_ERROR_OK; + uint8_t* dataPtr = NULL; + whMessageCrypto_Sha2DmaResponse* resp = NULL; + uint16_t respSz = 0; - /* Get data buffer */ - dataPtr = wh_CommClient_GetDataPtr(ctx->comm); - if (dataPtr == NULL) { + if (ctx == NULL || sha == NULL) { return WH_ERROR_BADARGS; } - /* Setup generic header and get pointer to request data */ - req = (whMessageCrypto_Sha256Request*)_createCryptoRequest( - dataPtr, WC_HASH_TYPE_SHA224, ctx->cryptoAffinity); - - - /* Send the full block to the server, along with the - * current hash state if needed. Finalization/padding of last block is up to - * the server, we just need to let it know we are done and sending an - * incomplete last block */ - if (isLastBlock) { - req->isLastBlock = 1; - req->lastBlockLen = sha224->buffLen; - } - else { - req->isLastBlock = 0; + dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm); + if (dataPtr == NULL) { + return WH_ERROR_BADARGS; } - memcpy(req->inBlock, sha224->buffer, - (isLastBlock) ? sha224->buffLen : WC_SHA224_BLOCK_SIZE); - - /* Send the hash state - this will be 0 on the first block on a properly - * initialized sha224 struct */ - memcpy(req->resumeState.hash, sha224->digest, WC_SHA256_DIGEST_SIZE); - req->resumeState.hiLen = sha224->hiLen; - req->resumeState.loLen = sha224->loLen; - - uint32_t req_len = - sizeof(whMessageCrypto_GenericRequestHeader) + sizeof(*req); - - ret = wh_Client_SendRequest(ctx, group, WC_ALGO_TYPE_HASH, req_len, - (uint8_t*)dataPtr); - WH_DEBUG_CLIENT_VERBOSE("send SHA224 Req:\n"); - WH_DEBUG_VERBOSE_HEXDUMP("[client] inBlock: ", req->inBlock, WC_SHA224_BLOCK_SIZE); - if (req->resumeState.hiLen != 0 || req->resumeState.loLen != 0) { - WH_DEBUG_VERBOSE_HEXDUMP(" [client] resumeHash: ", req->resumeState.hash, - (isLastBlock) ? req->lastBlockLen - : WC_SHA224_BLOCK_SIZE); - WH_DEBUG_CLIENT_VERBOSE(" hiLen: %u, loLen: %u\n", req->resumeState.hiLen, - req->resumeState.loLen); + ret = wh_Client_RecvResponse(ctx, NULL, NULL, &respSz, dataPtr); + if (ret == WH_ERROR_NOTREADY) { + return ret; } - WH_DEBUG_CLIENT_VERBOSE(" ret = %d\n", ret); - if (ret == 0) { - do { - ret = wh_Client_RecvResponse(ctx, &group, &action, &dataSz, - (uint8_t*)dataPtr); - } while (ret == WH_ERROR_NOTREADY); - } - if (ret == 0) { - /* Get response */ - ret = _getCryptoResponse(dataPtr, WC_HASH_TYPE_SHA224, (uint8_t**)&res); - /* wolfCrypt allows positive error codes on success in some scenarios */ + if (ret == WH_ERROR_OK) { + ret = + _getCryptoResponse(dataPtr, WC_HASH_TYPE_SHA256, (uint8_t**)&resp); if (ret >= 0) { - WH_DEBUG_CLIENT_VERBOSE("Client SHA224 Res recv: ret=%d", ret); - /* Store the received intermediate hash in the sha224 - * context and indicate the field is now valid and - * should be passed back and forth to the server. - * The digest length is the same as sha256 - * for intermediate operation. Final output will be - * truncated to WC_SHA224_DIGEST_SIZE. - */ - memcpy(sha224->digest, res->hash, WC_SHA256_DIGEST_SIZE); - sha224->hiLen = res->hiLen; - sha224->loLen = res->loLen; - WH_DEBUG_CLIENT_VERBOSE("Client SHA224 Res recv:\n"); - WH_DEBUG_VERBOSE_HEXDUMP("[client] hash: ", (uint8_t*)sha224->digest, - WC_SHA224_DIGEST_SIZE); + memcpy(sha->digest, resp->hash, WC_SHA256_DIGEST_SIZE); + sha->hiLen = resp->hiLen; + sha->loLen = resp->loLen; } } + /* POST-process DMA input using stashed async context */ + if (ctx->dma.asyncCtx.sha.ioSz > 0) { + uintptr_t ioAddr = ctx->dma.asyncCtx.sha.ioAddr; + (void)wh_Client_DmaProcessClientAddress( + ctx, ctx->dma.asyncCtx.sha.clientAddr, (void**)&ioAddr, + ctx->dma.asyncCtx.sha.ioSz, WH_DMA_OPER_CLIENT_READ_POST, + (whDmaFlags){0}); + ctx->dma.asyncCtx.sha.ioSz = 0; + } return ret; } -int wh_Client_Sha224(whClientContext* ctx, wc_Sha224* sha224, const uint8_t* in, - uint32_t inLen, uint8_t* out) +int wh_Client_Sha256DmaFinalRequest(whClientContext* ctx, wc_Sha256* sha) { - int ret = 0; - uint8_t* sha224BufferBytes = (uint8_t*)sha224->buffer; - - /* Caller invoked SHA Update: - * wc_CryptoCb_Sha224Hash(sha224, data, len, NULL) */ - if (in != NULL) { - size_t i = 0; - - /* Process the partial blocks directly from the input data. If there - * is enough input data to fill a full block, transfer it to the - * server */ - if (sha224->buffLen > 0) { - while (i < inLen && sha224->buffLen < WC_SHA224_BLOCK_SIZE) { - sha224BufferBytes[sha224->buffLen++] = in[i++]; - } - if (sha224->buffLen == WC_SHA224_BLOCK_SIZE) { - ret = _xferSha224BlockAndUpdateDigest(ctx, sha224, 0); - sha224->buffLen = 0; - } - } + int ret = WH_ERROR_OK; + uint8_t* dataPtr = NULL; + whMessageCrypto_Sha256DmaRequest* req = NULL; + uint8_t* inlineData; - /* Process as many full blocks from the input data as we can */ - while (ret == 0 && (inLen - i) >= WC_SHA224_BLOCK_SIZE) { - memcpy(sha224BufferBytes, in + i, WC_SHA224_BLOCK_SIZE); - ret = _xferSha224BlockAndUpdateDigest(ctx, sha224, 0); - i += WC_SHA224_BLOCK_SIZE; - } + if (ctx == NULL || sha == NULL) { + return WH_ERROR_BADARGS; + } - /* Copy any remaining data into the buffer to be sent in a - * subsequent call when we have enough input data to send a full - * block */ - while (i < inLen) { - sha224BufferBytes[sha224->buffLen++] = in[i++]; - } + dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm); + if (dataPtr == NULL) { + return WH_ERROR_BADARGS; } - /* Caller invoked SHA finalize: - * wc_CryptoCb_Sha224Hash(sha224, NULL, 0, * hash) */ - if (ret == 0 && out != NULL) { - ret = _xferSha224BlockAndUpdateDigest(ctx, sha224, 1); + req = (whMessageCrypto_Sha256DmaRequest*)_createCryptoRequest( + dataPtr, WC_HASH_TYPE_SHA256, ctx->cryptoAffinity); + inlineData = (uint8_t*)(req + 1); - /* Copy out the final hash value */ - if (ret == 0) { - memcpy(out, sha224->digest, WC_SHA224_DIGEST_SIZE); - } + req->isLastBlock = 1; + req->inSz = sha->buffLen; + memcpy(req->resumeState.hash, sha->digest, WC_SHA256_DIGEST_SIZE); + req->resumeState.hiLen = sha->hiLen; + req->resumeState.loLen = sha->loLen; + req->input.sz = 0; + req->input.addr = 0; - /* reset the state of the sha context (without blowing away devId) */ - wc_InitSha224_ex(sha224, NULL, sha224->devId); + /* Copy partial-block tail as inline data */ + if (sha->buffLen > 0) { + memcpy(inlineData, sha->buffer, sha->buffLen); } + WH_DEBUG_CLIENT_VERBOSE("SHA256 DMA FINAL: buffLen=%u\n", + (unsigned int)sha->buffLen); + + ret = wh_Client_SendRequest(ctx, WH_MESSAGE_GROUP_CRYPTO_DMA, + WC_ALGO_TYPE_HASH, + sizeof(whMessageCrypto_GenericRequestHeader) + + sizeof(*req) + sha->buffLen, + dataPtr); return ret; } -#ifdef WOLFHSM_CFG_DMA -int wh_Client_Sha224Dma(whClientContext* ctx, wc_Sha224* sha, const uint8_t* in, - uint32_t inLen, uint8_t* out) +int wh_Client_Sha256DmaFinalResponse(whClientContext* ctx, wc_Sha256* sha, + uint8_t* out) { int ret = WH_ERROR_OK; - wc_Sha224* sha224 = sha; - uint16_t respSz = 0; - uint16_t group = WH_MESSAGE_GROUP_CRYPTO_DMA; uint8_t* dataPtr = NULL; - whMessageCrypto_Sha2DmaRequest* req = NULL; whMessageCrypto_Sha2DmaResponse* resp = NULL; - uintptr_t inAddr = 0; - uintptr_t outAddr = 0; - uintptr_t stateAddr = 0; + uint16_t respSz = 0; + + if (ctx == NULL || sha == NULL || out == NULL) { + return WH_ERROR_BADARGS; + } - /* Get data pointer from the context to use as request/response storage */ dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm); if (dataPtr == NULL) { return WH_ERROR_BADARGS; } - /* Setup generic header and get pointer to request data */ - req = (whMessageCrypto_Sha2DmaRequest*)_createCryptoRequest( - dataPtr, WC_HASH_TYPE_SHA224, ctx->cryptoAffinity); - - if (in != NULL || out != NULL) { - req->state.sz = sizeof(*sha224); - req->input.sz = inLen; - req->output.sz = WC_SHA224_DIGEST_SIZE; /* not needed, but YOLO */ + ret = wh_Client_RecvResponse(ctx, NULL, NULL, &respSz, dataPtr); + if (ret == WH_ERROR_NOTREADY) { + return ret; + } - /* Perform address translations */ - ret = wh_Client_DmaProcessClientAddress( - ctx, (uintptr_t)sha224, (void**)&stateAddr, req->state.sz, - WH_DMA_OPER_CLIENT_WRITE_PRE, (whDmaFlags){0}); - if (ret == WH_ERROR_OK) { - req->state.addr = stateAddr; - } - - if (ret == WH_ERROR_OK) { - ret = wh_Client_DmaProcessClientAddress( - ctx, (uintptr_t)in, (void**)&inAddr, req->input.sz, - WH_DMA_OPER_CLIENT_READ_PRE, (whDmaFlags){0}); - if (ret == WH_ERROR_OK) { - req->input.addr = inAddr; - } - } - - if (ret == WH_ERROR_OK) { - ret = wh_Client_DmaProcessClientAddress( - ctx, (uintptr_t)out, (void**)&outAddr, req->output.sz, - WH_DMA_OPER_CLIENT_WRITE_PRE, (whDmaFlags){0}); - if (ret == WH_ERROR_OK) { - req->output.addr = outAddr; - } + if (ret == WH_ERROR_OK) { + ret = + _getCryptoResponse(dataPtr, WC_HASH_TYPE_SHA256, (uint8_t**)&resp); + if (ret >= 0) { + memcpy(out, resp->hash, WC_SHA256_DIGEST_SIZE); + /* Reset state without blowing away devId */ + (void)wc_InitSha256_ex(sha, NULL, sha->devId); } } + return ret; +} - /* Caller invoked SHA Update: - * wc_CryptoCb_Sha224Hash(sha224, data, len, NULL) */ - if (in != NULL && ret == WH_ERROR_OK) { - req->finalize = 0; - WH_DEBUG_CLIENT_VERBOSE("SHA224 DMA UPDATE: inAddr=%p, inSz=%u\n", in, - (unsigned int)inLen); - ret = wh_Client_SendRequest( - ctx, group, WC_ALGO_TYPE_HASH, - sizeof(whMessageCrypto_GenericRequestHeader) + sizeof(*req), - (uint8_t*)dataPtr); +int wh_Client_Sha256Dma(whClientContext* ctx, wc_Sha256* sha, const uint8_t* in, + uint32_t inLen, uint8_t* out) +{ + int ret = WH_ERROR_OK; - if (ret == WH_ERROR_OK) { + if (in != NULL && inLen > 0) { + bool sent = false; + ret = wh_Client_Sha256DmaUpdateRequest(ctx, sha, in, inLen, &sent); + if (ret == WH_ERROR_OK && sent) { do { - ret = wh_Client_RecvResponse(ctx, NULL, NULL, &respSz, - (uint8_t*)dataPtr); + ret = wh_Client_Sha256DmaUpdateResponse(ctx, sha); } while (ret == WH_ERROR_NOTREADY); } - - if (ret == WH_ERROR_OK) { - /* Get response structure pointer, validates generic header - * rc */ - ret = _getCryptoResponse(dataPtr, WC_HASH_TYPE_SHA224, - (uint8_t**)&resp); - /* Nothing to do on success, as server will have updated the context - * in client memory */ - } } - - /* Caller invoked SHA finalize: - * wc_CryptoCb_Sha224Hash(sha224, NULL, 0, * hash) */ - if ((ret == WH_ERROR_OK) && (out != NULL)) { - /* Packet will have been trashed, so re-populate all fields */ - req->finalize = 1; - - WH_DEBUG_CLIENT_VERBOSE("SHA224 DMA FINAL: outAddr=%p\n", out); - /* send the request to the server */ - ret = wh_Client_SendRequest( - ctx, group, WC_ALGO_TYPE_HASH, - sizeof(whMessageCrypto_GenericRequestHeader) + sizeof(*req), - (uint8_t*)dataPtr); - + if (ret == WH_ERROR_OK && out != NULL) { + ret = wh_Client_Sha256DmaFinalRequest(ctx, sha); if (ret == WH_ERROR_OK) { do { - ret = wh_Client_RecvResponse(ctx, NULL, NULL, &respSz, - (uint8_t*)dataPtr); + ret = wh_Client_Sha256DmaFinalResponse(ctx, sha, out); } while (ret == WH_ERROR_NOTREADY); } - - /* Copy out the final hash value */ - if (ret == WH_ERROR_OK) { - /* Get response structure pointer, validates generic header - * rc */ - ret = _getCryptoResponse(dataPtr, WC_HASH_TYPE_SHA224, - (uint8_t**)&resp); - /* Nothing to do on success, as server will have updated the output - * hash in client memory */ - } - } - - if (in != NULL || out != NULL) { - (void)wh_Client_DmaProcessClientAddress( - ctx, (uintptr_t)sha224, (void**)&stateAddr, sizeof(*sha224), - WH_DMA_OPER_CLIENT_WRITE_POST, (whDmaFlags){0}); - (void)wh_Client_DmaProcessClientAddress( - ctx, (uintptr_t)in, (void**)&inAddr, inLen, - WH_DMA_OPER_CLIENT_READ_POST, (whDmaFlags){0}); - (void)wh_Client_DmaProcessClientAddress( - ctx, (uintptr_t)out, (void**)&outAddr, WC_SHA224_DIGEST_SIZE, - WH_DMA_OPER_CLIENT_WRITE_POST, (whDmaFlags){0}); } return ret; } #endif /* WOLFHSM_CFG_DMA */ -#endif /* WOLFSSL_SHA224 */ +#endif /* !NO_SHA256 */ -#ifdef WOLFSSL_SHA384 +#ifdef WOLFSSL_SHA224 + +/* Maximum number of input bytes that wh_Client_Sha224UpdateRequest can absorb + * in a single call: the inline-data wire capacity, plus whatever room is left + * in the partial-block buffer (we can stash up to BLOCK_SIZE-1-buffLen tail + * bytes locally without producing a new full block). */ +static uint32_t _Sha224UpdatePerCallCapacity(const wc_Sha224* sha) +{ + return WH_MESSAGE_CRYPTO_SHA224_MAX_INLINE_UPDATE_SZ + + (uint32_t)(WC_SHA224_BLOCK_SIZE - 1u - sha->buffLen); +} -static int _xferSha384BlockAndUpdateDigest(whClientContext* ctx, - wc_Sha384* sha384, - uint32_t isLastBlock) +int wh_Client_Sha224UpdateRequest(whClientContext* ctx, wc_Sha224* sha, + const uint8_t* in, uint32_t inLen, + bool* requestSent) { - uint16_t group = WH_MESSAGE_GROUP_CRYPTO; - uint16_t action = WH_MESSAGE_ACTION_NONE; - int ret = 0; - uint16_t dataSz = 0; - whMessageCrypto_Sha512Request* req = NULL; - whMessageCrypto_Sha2Response* res = NULL; + int ret = 0; + whMessageCrypto_Sha256Request* req = NULL; + uint8_t* inlineData; uint8_t* dataPtr = NULL; + uint8_t* sha224BufferBytes; + uint32_t capacity; + uint32_t wirePos = 0; + uint32_t i = 0; + /* Snapshot of buffer state for rollback if SendRequest fails */ + uint32_t savedBuffLen; + uint8_t savedBuffer[WC_SHA224_BLOCK_SIZE]; + + if (ctx == NULL || sha == NULL || requestSent == NULL || + (in == NULL && inLen != 0)) { + return WH_ERROR_BADARGS; + } + *requestSent = false; + + capacity = _Sha224UpdatePerCallCapacity(sha); + if (inLen > capacity) { + return WH_ERROR_BADARGS; + } + + /* Empty update: nothing to send, no state to mutate. */ + if (inLen == 0) { + return WH_ERROR_OK; + } /* Get data buffer */ dataPtr = wh_CommClient_GetDataPtr(ctx->comm); @@ -4897,554 +4887,1579 @@ static int _xferSha384BlockAndUpdateDigest(whClientContext* ctx, } /* Setup generic header and get pointer to request data */ - req = (whMessageCrypto_Sha512Request*)_createCryptoRequest( - dataPtr, WC_HASH_TYPE_SHA384, ctx->cryptoAffinity); + req = (whMessageCrypto_Sha256Request*)_createCryptoRequest( + dataPtr, WC_HASH_TYPE_SHA224, ctx->cryptoAffinity); + inlineData = (uint8_t*)(req + 1); + sha224BufferBytes = (uint8_t*)sha->buffer; + /* Save the buffer state before mutation so we can restore it if + * SendRequest fails, preventing silent SHA state corruption. */ + savedBuffLen = sha->buffLen; + memcpy(savedBuffer, sha224BufferBytes, sha->buffLen); - /* Send the full block to the server, along with the - * current hash state if needed. Finalization/padding of last block is up to - * the server, we just need to let it know we are done and sending an - * incomplete last block */ - if (isLastBlock) { - req->isLastBlock = 1; - req->lastBlockLen = sha384->buffLen; + /* If there's a partial block already buffered, top it up from the input. + * If we manage to fill a full block, copy the completed block into the + * wire payload as the first inline block. */ + if (sha->buffLen > 0) { + while (i < inLen && sha->buffLen < WC_SHA224_BLOCK_SIZE) { + sha224BufferBytes[sha->buffLen++] = in[i++]; + } + if (sha->buffLen == WC_SHA224_BLOCK_SIZE) { + memcpy(inlineData + wirePos, sha224BufferBytes, + WC_SHA224_BLOCK_SIZE); + wirePos += WC_SHA224_BLOCK_SIZE; + sha->buffLen = 0; + } } - else { - req->isLastBlock = 0; + + /* Copy as many full blocks from the input as fit in the inline area. */ + while ((inLen - i) >= WC_SHA224_BLOCK_SIZE && + (wirePos + WC_SHA224_BLOCK_SIZE) <= + WH_MESSAGE_CRYPTO_SHA224_MAX_INLINE_UPDATE_SZ) { + memcpy(inlineData + wirePos, in + i, WC_SHA224_BLOCK_SIZE); + wirePos += WC_SHA224_BLOCK_SIZE; + i += WC_SHA224_BLOCK_SIZE; } - memcpy(req->inBlock, sha384->buffer, - (isLastBlock) ? sha384->buffLen : WC_SHA384_BLOCK_SIZE); - /* Send the hash state - this will be 0 on the first block on a properly - * initialized sha384 struct */ - memcpy(req->resumeState.hash, sha384->digest, WC_SHA512_DIGEST_SIZE); - req->resumeState.hiLen = sha384->hiLen; - req->resumeState.loLen = sha384->loLen; + /* Stash any remaining tail bytes into the buffer for next time. The + * capacity check above guarantees this fits without overflow. */ + while (i < inLen) { + sha224BufferBytes[sha->buffLen++] = in[i++]; + } - uint32_t req_len = - sizeof(whMessageCrypto_GenericRequestHeader) + sizeof(*req); + /* Pure-buffer-fill update: nothing to send. */ + if (wirePos == 0) { + return WH_ERROR_OK; + } - ret = wh_Client_SendRequest(ctx, group, WC_ALGO_TYPE_HASH, req_len, - (uint8_t*)dataPtr); + /* Populate fixed request fields. Intermediate hash state uses the full + * SHA256 digest size (32 bytes) on the wire; the final truncation to + * WC_SHA224_DIGEST_SIZE happens only in FinalResponse. */ + req->isLastBlock = 0; + req->inSz = wirePos; + memcpy(req->resumeState.hash, sha->digest, WC_SHA256_DIGEST_SIZE); + req->resumeState.hiLen = sha->hiLen; + req->resumeState.loLen = sha->loLen; - WH_DEBUG_CLIENT_VERBOSE("send SHA384 Req:\n"); - WH_DEBUG_VERBOSE_HEXDUMP("[client] inBlock: ", req->inBlock, WC_SHA384_BLOCK_SIZE); - if (req->resumeState.hiLen != 0 || req->resumeState.loLen != 0) { - WH_DEBUG_VERBOSE_HEXDUMP(" [client] resumeHash: ", req->resumeState.hash, - (isLastBlock) ? req->lastBlockLen - : WC_SHA384_BLOCK_SIZE); - WH_DEBUG_CLIENT_VERBOSE(" hiLen: %u, loLen: %u\n", req->resumeState.hiLen, - req->resumeState.loLen); - } - WH_DEBUG_CLIENT_VERBOSE(" ret = %d\n", ret); + ret = wh_Client_SendRequest(ctx, WH_MESSAGE_GROUP_CRYPTO, WC_ALGO_TYPE_HASH, + sizeof(whMessageCrypto_GenericRequestHeader) + + sizeof(*req) + wirePos, + dataPtr); if (ret == 0) { - do { - ret = wh_Client_RecvResponse(ctx, &group, &action, &dataSz, - (uint8_t*)dataPtr); - } while (ret == WH_ERROR_NOTREADY); + *requestSent = true; } - if (ret == 0) { - /* Get response */ - ret = _getCryptoResponse(dataPtr, WC_HASH_TYPE_SHA384, (uint8_t**)&res); - /* wolfCrypt allows positive error codes on success in some scenarios */ - if (ret >= 0) { - WH_DEBUG_CLIENT_VERBOSE("Client SHA384 Res recv: ret=%d", ret); - /* Store the received intermediate hash in the sha384 - * context and indicate the field is now valid and - * should be passed back and forth to the server - * The digest length is the same as sha512 - * for intermediate operation. Final output will be - * truncated to WC_SHA384_DIGEST_SIZE. - */ - memcpy(sha384->digest, res->hash, WC_SHA512_DIGEST_SIZE); - sha384->hiLen = res->hiLen; - sha384->loLen = res->loLen; - WH_DEBUG_CLIENT_VERBOSE("Client SHA384 Res recv:\n"); - WH_DEBUG_VERBOSE_HEXDUMP("[client] hash: ", (uint8_t*)sha384->digest, - WC_SHA384_DIGEST_SIZE); - } + else { + /* SendRequest failed — restore buffer state so the caller can retry + * or continue hashing without data loss. */ + sha->buffLen = savedBuffLen; + memcpy(sha224BufferBytes, savedBuffer, savedBuffLen); } - return ret; } -int wh_Client_Sha384(whClientContext* ctx, wc_Sha384* sha384, const uint8_t* in, - uint32_t inLen, uint8_t* out) +int wh_Client_Sha224UpdateResponse(whClientContext* ctx, wc_Sha224* sha) { - int ret = 0; - uint8_t* sha384BufferBytes = (uint8_t*)sha384->buffer; - - /* Caller invoked SHA Update: - * wc_CryptoCb_Sha384Hash(sha384, data, len, NULL) */ - if (in != NULL) { - size_t i = 0; - - /* Process the partial blocks directly from the input data. If there - * is enough input data to fill a full block, transfer it to the - * server */ - if (sha384->buffLen > 0) { - while (i < inLen && sha384->buffLen < WC_SHA384_BLOCK_SIZE) { - sha384BufferBytes[sha384->buffLen++] = in[i++]; - } - if (sha384->buffLen == WC_SHA384_BLOCK_SIZE) { - ret = _xferSha384BlockAndUpdateDigest(ctx, sha384, 0); - sha384->buffLen = 0; - } - } - - /* Process as many full blocks from the input data as we can */ - while (ret == 0 && (inLen - i) >= WC_SHA384_BLOCK_SIZE) { - memcpy(sha384BufferBytes, in + i, WC_SHA384_BLOCK_SIZE); - ret = _xferSha384BlockAndUpdateDigest(ctx, sha384, 0); - i += WC_SHA384_BLOCK_SIZE; - } + uint16_t group = WH_MESSAGE_GROUP_CRYPTO; + uint16_t action = WH_MESSAGE_ACTION_NONE; + uint16_t dataSz = 0; + int ret = 0; + whMessageCrypto_Sha2Response* res = NULL; + uint8_t* dataPtr; - /* Copy any remaining data into the buffer to be sent in a - * subsequent call when we have enough input data to send a full - * block */ - while (i < inLen) { - sha384BufferBytes[sha384->buffLen++] = in[i++]; - } + if (ctx == NULL || sha == NULL) { + return WH_ERROR_BADARGS; } - /* Caller invoked SHA finalize: - * wc_CryptoCb_Sha384Hash(sha384, NULL, 0, * hash) */ - if (ret == 0 && out != NULL) { - ret = _xferSha384BlockAndUpdateDigest(ctx, sha384, 1); - - /* Copy out the final hash value */ - if (ret == 0) { - memcpy(out, sha384->digest, WC_SHA384_DIGEST_SIZE); - } + dataPtr = wh_CommClient_GetDataPtr(ctx->comm); + if (dataPtr == NULL) { + return WH_ERROR_BADARGS; + } - /* reset the state of the sha context (without blowing away devId) */ - wc_InitSha384_ex(sha384, NULL, sha384->devId); + ret = wh_Client_RecvResponse(ctx, &group, &action, &dataSz, dataPtr); + if (ret != WH_ERROR_OK) { + return ret; } + ret = _getCryptoResponse(dataPtr, WC_HASH_TYPE_SHA224, (uint8_t**)&res); + if (ret >= 0) { + /* Intermediate hash state is stored at full SHA256 digest width */ + memcpy(sha->digest, res->hash, WC_SHA256_DIGEST_SIZE); + sha->hiLen = res->hiLen; + sha->loLen = res->loLen; + } return ret; } -#ifdef WOLFHSM_CFG_DMA -int wh_Client_Sha384Dma(whClientContext* ctx, wc_Sha384* sha, const uint8_t* in, - uint32_t inLen, uint8_t* out) +int wh_Client_Sha224FinalRequest(whClientContext* ctx, wc_Sha224* sha) { - int ret = WH_ERROR_OK; - wc_Sha384* sha384 = sha; - uint16_t respSz = 0; - uint16_t group = WH_MESSAGE_GROUP_CRYPTO_DMA; - uint8_t* dataPtr = NULL; - whMessageCrypto_Sha2DmaRequest* req = NULL; - whMessageCrypto_Sha2DmaResponse* resp = NULL; - uintptr_t inAddr = 0; - uintptr_t outAddr = 0; - uintptr_t stateAddr = 0; + int ret; + whMessageCrypto_Sha256Request* req; + uint8_t* inlineData; + uint8_t* dataPtr; - /* Get data pointer from the context to use as request/response storage */ - dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm); + if (ctx == NULL || sha == NULL) { + return WH_ERROR_BADARGS; + } + + dataPtr = wh_CommClient_GetDataPtr(ctx->comm); if (dataPtr == NULL) { return WH_ERROR_BADARGS; } - /* Setup generic header and get pointer to request data */ - req = (whMessageCrypto_Sha2DmaRequest*)_createCryptoRequest( - dataPtr, WC_HASH_TYPE_SHA384, ctx->cryptoAffinity); + req = (whMessageCrypto_Sha256Request*)_createCryptoRequest( + dataPtr, WC_HASH_TYPE_SHA224, ctx->cryptoAffinity); + inlineData = (uint8_t*)(req + 1); - if (in != NULL || out != NULL) { - req->state.sz = sizeof(*sha384); - req->input.sz = inLen; - req->output.sz = WC_SHA384_DIGEST_SIZE; /* not needed, but YOLO */ + req->isLastBlock = 1; + req->inSz = sha->buffLen; + memcpy(req->resumeState.hash, sha->digest, WC_SHA256_DIGEST_SIZE); + req->resumeState.hiLen = sha->hiLen; + req->resumeState.loLen = sha->loLen; - /* Perform address translations */ - ret = wh_Client_DmaProcessClientAddress( - ctx, (uintptr_t)sha384, (void**)&stateAddr, req->state.sz, - WH_DMA_OPER_CLIENT_WRITE_PRE, (whDmaFlags){0}); - if (ret == WH_ERROR_OK) { - req->state.addr = stateAddr; - } + if (sha->buffLen > 0) { + memcpy(inlineData, sha->buffer, sha->buffLen); + } - if (ret == WH_ERROR_OK) { - ret = wh_Client_DmaProcessClientAddress( - ctx, (uintptr_t)in, (void**)&inAddr, req->input.sz, - WH_DMA_OPER_CLIENT_READ_PRE, (whDmaFlags){0}); - if (ret == WH_ERROR_OK) { - req->input.addr = inAddr; - } - } + ret = wh_Client_SendRequest(ctx, WH_MESSAGE_GROUP_CRYPTO, WC_ALGO_TYPE_HASH, + sizeof(whMessageCrypto_GenericRequestHeader) + + sizeof(*req) + sha->buffLen, + dataPtr); + return ret; +} - if (ret == WH_ERROR_OK) { - ret = wh_Client_DmaProcessClientAddress( - ctx, (uintptr_t)out, (void**)&outAddr, req->output.sz, - WH_DMA_OPER_CLIENT_WRITE_PRE, (whDmaFlags){0}); - if (ret == WH_ERROR_OK) { - req->output.addr = outAddr; - } - } +int wh_Client_Sha224FinalResponse(whClientContext* ctx, wc_Sha224* sha, + uint8_t* out) +{ + uint16_t group = WH_MESSAGE_GROUP_CRYPTO; + uint16_t action = WH_MESSAGE_ACTION_NONE; + uint16_t dataSz = 0; + int ret; + whMessageCrypto_Sha2Response* res = NULL; + uint8_t* dataPtr; + + if (ctx == NULL || sha == NULL || out == NULL) { + return WH_ERROR_BADARGS; } - /* Caller invoked SHA Update: - * wc_CryptoCb_Sha384Hash(sha384, data, len, NULL) */ - if (in != NULL && ret == WH_ERROR_OK) { - req->finalize = 0; - WH_DEBUG_CLIENT_VERBOSE("SHA384 DMA UPDATE: inAddr=%p, inSz=%u\n", in, - (unsigned int)inLen); - ret = wh_Client_SendRequest( - ctx, group, WC_ALGO_TYPE_HASH, - sizeof(whMessageCrypto_GenericRequestHeader) + sizeof(*req), - (uint8_t*)dataPtr); + dataPtr = wh_CommClient_GetDataPtr(ctx->comm); + if (dataPtr == NULL) { + return WH_ERROR_BADARGS; + } - if (ret == WH_ERROR_OK) { - do { - ret = wh_Client_RecvResponse(ctx, NULL, NULL, &respSz, - (uint8_t*)dataPtr); - } while (ret == WH_ERROR_NOTREADY); - } + ret = wh_Client_RecvResponse(ctx, &group, &action, &dataSz, dataPtr); + if (ret != 0) { + return ret; + } - if (ret == WH_ERROR_OK) { - /* Get response structure pointer, validates generic header - * rc */ - ret = _getCryptoResponse(dataPtr, WC_HASH_TYPE_SHA384, - (uint8_t**)&resp); - /* Nothing to do on success, as server will have updated the context - * in client memory */ - } + ret = _getCryptoResponse(dataPtr, WC_HASH_TYPE_SHA224, (uint8_t**)&res); + if (ret >= 0) { + /* Final output is truncated to WC_SHA224_DIGEST_SIZE */ + memcpy(out, res->hash, WC_SHA224_DIGEST_SIZE); + /* Reset state without blowing away devId */ + (void)wc_InitSha224_ex(sha, NULL, sha->devId); } + return ret; +} - /* Caller invoked SHA finalize: - * wc_CryptoCb_Sha384Hash(sha384, NULL, 0, * hash) */ - if ((ret == WH_ERROR_OK) && (out != NULL)) { - /* Packet will have been trashed, so re-populate all fields */ - req->finalize = 1; +int wh_Client_Sha224(whClientContext* ctx, wc_Sha224* sha224, const uint8_t* in, + uint32_t inLen, uint8_t* out) +{ + int ret = WH_ERROR_OK; - WH_DEBUG_CLIENT_VERBOSE("SHA384 DMA FINAL: outAddr=%p\n", out); - /* send the request to the server */ - ret = wh_Client_SendRequest( - ctx, group, WC_ALGO_TYPE_HASH, - sizeof(whMessageCrypto_GenericRequestHeader) + sizeof(*req), - (uint8_t*)dataPtr); + /* Caller invoked SHA Update: + * wc_CryptoCb_Sha224Hash(sha224, data, len, NULL) */ + if (in != NULL && inLen > 0) { + uint32_t consumed = 0; + while (ret == WH_ERROR_OK && consumed < inLen) { + uint32_t capacity = _Sha224UpdatePerCallCapacity(sha224); + uint32_t remaining = inLen - consumed; + uint32_t chunk = (remaining < capacity) ? remaining : capacity; + bool sent = false; + + ret = wh_Client_Sha224UpdateRequest(ctx, sha224, in + consumed, + chunk, &sent); + if (ret != WH_ERROR_OK) { + break; + } + if (sent) { + do { + ret = wh_Client_Sha224UpdateResponse(ctx, sha224); + } while (ret == WH_ERROR_NOTREADY); + if (ret != WH_ERROR_OK) { + break; + } + } + consumed += chunk; + } + } + /* Caller invoked SHA finalize: + * wc_CryptoCb_Sha224Hash(sha224, NULL, 0, *hash) */ + if (ret == WH_ERROR_OK && out != NULL) { + ret = wh_Client_Sha224FinalRequest(ctx, sha224); if (ret == WH_ERROR_OK) { do { - ret = wh_Client_RecvResponse(ctx, NULL, NULL, &respSz, - (uint8_t*)dataPtr); + ret = wh_Client_Sha224FinalResponse(ctx, sha224, out); } while (ret == WH_ERROR_NOTREADY); } - - /* Copy out the final hash value */ - if (ret == WH_ERROR_OK) { - /* Get response structure pointer, validates generic header - * rc */ - ret = _getCryptoResponse(dataPtr, WC_HASH_TYPE_SHA384, - (uint8_t**)&resp); - /* Nothing to do on success, as server will have updated the output - * hash in client memory */ - } } - if (in != NULL || out != NULL) { - (void)wh_Client_DmaProcessClientAddress( - ctx, (uintptr_t)sha384, (void**)&stateAddr, sizeof(*sha384), - WH_DMA_OPER_CLIENT_WRITE_POST, (whDmaFlags){0}); - (void)wh_Client_DmaProcessClientAddress( - ctx, (uintptr_t)in, (void**)&inAddr, inLen, - WH_DMA_OPER_CLIENT_READ_POST, (whDmaFlags){0}); - (void)wh_Client_DmaProcessClientAddress( - ctx, (uintptr_t)out, (void**)&outAddr, WC_SHA384_DIGEST_SIZE, - WH_DMA_OPER_CLIENT_WRITE_POST, (whDmaFlags){0}); - } return ret; } -#endif /* WOLFHSM_CFG_DMA */ -#endif /* WOLFSSL_SHA384 */ +#ifdef WOLFHSM_CFG_DMA +int wh_Client_Sha224DmaUpdateRequest(whClientContext* ctx, wc_Sha224* sha, + const uint8_t* in, uint32_t inLen, + bool* requestSent) +{ + int ret = WH_ERROR_OK; + uint8_t* dataPtr = NULL; + whMessageCrypto_Sha256DmaRequest* req = NULL; + uint8_t* inlineData; + uint8_t* shaBufferBytes; + uint32_t wirePos = 0; + uint32_t i = 0; + uintptr_t inAddr = 0; + bool inAddrAcquired = false; + const uint8_t* dmaBase = NULL; + uint32_t dmaSz = 0; + uint32_t savedBuffLen; + uint8_t savedBuffer[WC_SHA224_BLOCK_SIZE]; -#if defined(WOLFSSL_SHA512) && defined(WOLFSSL_SHA512_HASHTYPE) + if (ctx == NULL || sha == NULL || requestSent == NULL || + (in == NULL && inLen != 0)) { + return WH_ERROR_BADARGS; + } + *requestSent = false; -static int _xferSha512BlockAndUpdateDigest(whClientContext* ctx, - wc_Sha512* sha512, - uint32_t isLastBlock) -{ - uint16_t group = WH_MESSAGE_GROUP_CRYPTO; - uint16_t action = WH_MESSAGE_ACTION_NONE; - int ret = 0; - uint16_t dataSz = 0; - whMessageCrypto_Sha512Request* req = NULL; - whMessageCrypto_Sha2Response* res = NULL; - uint8_t* dataPtr = NULL; + if (inLen == 0) { + return WH_ERROR_OK; + } - /* Get data buffer */ - dataPtr = wh_CommClient_GetDataPtr(ctx->comm); + dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm); if (dataPtr == NULL) { return WH_ERROR_BADARGS; } - /* Setup generic header and get pointer to request data */ - req = (whMessageCrypto_Sha512Request*)_createCryptoRequest( - dataPtr, WC_HASH_TYPE_SHA512, ctx->cryptoAffinity); + req = (whMessageCrypto_Sha256DmaRequest*)_createCryptoRequest( + dataPtr, WC_HASH_TYPE_SHA224, ctx->cryptoAffinity); + inlineData = (uint8_t*)(req + 1); + shaBufferBytes = (uint8_t*)sha->buffer; + savedBuffLen = sha->buffLen; + memcpy(savedBuffer, shaBufferBytes, sha->buffLen); - /* Send the full block to the server, along with the - * current hash state if needed. Finalization/padding of last block is up to - * the server, we just need to let it know we are done and sending an - * incomplete last block */ - if (isLastBlock) { - req->isLastBlock = 1; - req->lastBlockLen = sha512->buffLen; + if (sha->buffLen > 0) { + while (i < inLen && sha->buffLen < WC_SHA224_BLOCK_SIZE) { + shaBufferBytes[sha->buffLen++] = in[i++]; + } + if (sha->buffLen == WC_SHA224_BLOCK_SIZE) { + memcpy(inlineData, shaBufferBytes, WC_SHA224_BLOCK_SIZE); + wirePos = WC_SHA224_BLOCK_SIZE; + sha->buffLen = 0; + } } - else { - req->isLastBlock = 0; - } - memcpy(req->inBlock, sha512->buffer, - (isLastBlock) ? sha512->buffLen : WC_SHA512_BLOCK_SIZE); - - /* Send the hash state - this will be 0 on the first block on a properly - * initialized sha512 struct */ - memcpy(req->resumeState.hash, sha512->digest, WC_SHA512_DIGEST_SIZE); - req->resumeState.hiLen = sha512->hiLen; - req->resumeState.loLen = sha512->loLen; - req->resumeState.hashType = sha512->hashType; - uint32_t req_len = - sizeof(whMessageCrypto_GenericRequestHeader) + sizeof(*req); - ret = wh_Client_SendRequest(ctx, group, WC_ALGO_TYPE_HASH, req_len, - (uint8_t*)dataPtr); + if ((inLen - i) >= WC_SHA224_BLOCK_SIZE) { + dmaBase = in + i; + dmaSz = ((inLen - i) / WC_SHA224_BLOCK_SIZE) * WC_SHA224_BLOCK_SIZE; + i += dmaSz; + } - WH_DEBUG_CLIENT_VERBOSE("send SHA512 Req:\n"); - WH_DEBUG_VERBOSE_HEXDUMP("[client] inBlock: ", req->inBlock, WC_SHA512_BLOCK_SIZE); - if (req->resumeState.hiLen != 0 || req->resumeState.loLen != 0) { - WH_DEBUG_VERBOSE_HEXDUMP(" [client] resumeHash: ", req->resumeState.hash, - (isLastBlock) ? req->lastBlockLen - : WC_SHA512_BLOCK_SIZE); - WH_DEBUG_CLIENT_VERBOSE(" hiLen: %u, loLen: %u\n", req->resumeState.hiLen, - req->resumeState.loLen); + while (i < inLen) { + shaBufferBytes[sha->buffLen++] = in[i++]; } - WH_DEBUG_CLIENT_VERBOSE(" ret = %d\n", ret); - if (ret == 0) { - do { - ret = wh_Client_RecvResponse(ctx, &group, &action, &dataSz, - (uint8_t*)dataPtr); - } while (ret == WH_ERROR_NOTREADY); + if (wirePos == 0 && dmaSz == 0) { + return WH_ERROR_OK; } - if (ret == 0) { - /* Get response */ - ret = _getCryptoResponse(dataPtr, WC_HASH_TYPE_SHA512, (uint8_t**)&res); - /* wolfCrypt allows positive error codes on success in some scenarios */ - if (ret >= 0) { - WH_DEBUG_CLIENT_VERBOSE("Client SHA512 Res recv: ret=%d", ret); - WH_DEBUG_CLIENT_VERBOSE("hashType: %d\n", sha512->hashType); - /* Store the received intermediate hash in the sha512 - * context and indicate the field is now valid and - * should be passed back and forth to the server */ - memcpy(sha512->digest, res->hash, WC_SHA512_DIGEST_SIZE); - sha512->hiLen = res->hiLen; - sha512->loLen = res->loLen; - WH_DEBUG_CLIENT_VERBOSE("Client SHA512 Res recv:\n"); - WH_DEBUG_VERBOSE_HEXDUMP("[client] hash: ", (uint8_t*)sha512->digest, - WC_SHA512_DIGEST_SIZE); + + req->isLastBlock = 0; + req->inSz = wirePos; + /* SHA224 shares SHA256's internal 32-byte digest state */ + memcpy(req->resumeState.hash, sha->digest, WC_SHA256_DIGEST_SIZE); + req->resumeState.hiLen = sha->hiLen; + req->resumeState.loLen = sha->loLen; + req->input.sz = dmaSz; + req->input.addr = 0; + + if (dmaSz > 0) { + ret = wh_Client_DmaProcessClientAddress( + ctx, (uintptr_t)dmaBase, (void**)&inAddr, dmaSz, + WH_DMA_OPER_CLIENT_READ_PRE, (whDmaFlags){0}); + if (ret == WH_ERROR_OK) { + inAddrAcquired = true; + req->input.addr = inAddr; } } + if (ret == WH_ERROR_OK) { + ctx->dma.asyncCtx.sha.ioAddr = inAddr; + ctx->dma.asyncCtx.sha.clientAddr = (uintptr_t)dmaBase; + ctx->dma.asyncCtx.sha.ioSz = dmaSz; + + ret = wh_Client_SendRequest( + ctx, WH_MESSAGE_GROUP_CRYPTO_DMA, WC_ALGO_TYPE_HASH, + sizeof(whMessageCrypto_GenericRequestHeader) + sizeof(*req) + + wirePos, + dataPtr); + } + + if (ret == WH_ERROR_OK) { + *requestSent = true; + } + else { + sha->buffLen = savedBuffLen; + memcpy(shaBufferBytes, savedBuffer, savedBuffLen); + if (inAddrAcquired) { + (void)wh_Client_DmaProcessClientAddress( + ctx, (uintptr_t)dmaBase, (void**)&inAddr, dmaSz, + WH_DMA_OPER_CLIENT_READ_POST, (whDmaFlags){0}); + } + } + return ret; +} + +int wh_Client_Sha224DmaUpdateResponse(whClientContext* ctx, wc_Sha224* sha) +{ + int ret = WH_ERROR_OK; + uint8_t* dataPtr = NULL; + whMessageCrypto_Sha2DmaResponse* resp = NULL; + uint16_t respSz = 0; + + if (ctx == NULL || sha == NULL) { + return WH_ERROR_BADARGS; + } + + dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm); + if (dataPtr == NULL) { + return WH_ERROR_BADARGS; + } + + ret = wh_Client_RecvResponse(ctx, NULL, NULL, &respSz, dataPtr); + if (ret == WH_ERROR_NOTREADY) { + return ret; + } + + if (ret == WH_ERROR_OK) { + ret = + _getCryptoResponse(dataPtr, WC_HASH_TYPE_SHA224, (uint8_t**)&resp); + if (ret >= 0) { + memcpy(sha->digest, resp->hash, WC_SHA256_DIGEST_SIZE); + sha->hiLen = resp->hiLen; + sha->loLen = resp->loLen; + } + } + + if (ctx->dma.asyncCtx.sha.ioSz > 0) { + uintptr_t ioAddr = ctx->dma.asyncCtx.sha.ioAddr; + (void)wh_Client_DmaProcessClientAddress( + ctx, ctx->dma.asyncCtx.sha.clientAddr, (void**)&ioAddr, + ctx->dma.asyncCtx.sha.ioSz, WH_DMA_OPER_CLIENT_READ_POST, + (whDmaFlags){0}); + ctx->dma.asyncCtx.sha.ioSz = 0; + } + return ret; +} + +int wh_Client_Sha224DmaFinalRequest(whClientContext* ctx, wc_Sha224* sha) +{ + int ret = WH_ERROR_OK; + uint8_t* dataPtr = NULL; + whMessageCrypto_Sha256DmaRequest* req = NULL; + uint8_t* inlineData; + + if (ctx == NULL || sha == NULL) { + return WH_ERROR_BADARGS; + } + + dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm); + if (dataPtr == NULL) { + return WH_ERROR_BADARGS; + } + + req = (whMessageCrypto_Sha256DmaRequest*)_createCryptoRequest( + dataPtr, WC_HASH_TYPE_SHA224, ctx->cryptoAffinity); + inlineData = (uint8_t*)(req + 1); + + req->isLastBlock = 1; + req->inSz = sha->buffLen; + /* SHA224 shares SHA256's internal 32-byte digest state */ + memcpy(req->resumeState.hash, sha->digest, WC_SHA256_DIGEST_SIZE); + req->resumeState.hiLen = sha->hiLen; + req->resumeState.loLen = sha->loLen; + req->input.sz = 0; + req->input.addr = 0; + + if (sha->buffLen > 0) { + memcpy(inlineData, sha->buffer, sha->buffLen); + } + + ret = wh_Client_SendRequest(ctx, WH_MESSAGE_GROUP_CRYPTO_DMA, + WC_ALGO_TYPE_HASH, + sizeof(whMessageCrypto_GenericRequestHeader) + + sizeof(*req) + sha->buffLen, + dataPtr); + return ret; +} + +int wh_Client_Sha224DmaFinalResponse(whClientContext* ctx, wc_Sha224* sha, + uint8_t* out) +{ + int ret = WH_ERROR_OK; + uint8_t* dataPtr = NULL; + whMessageCrypto_Sha2DmaResponse* resp = NULL; + uint16_t respSz = 0; + + if (ctx == NULL || sha == NULL || out == NULL) { + return WH_ERROR_BADARGS; + } + + dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm); + if (dataPtr == NULL) { + return WH_ERROR_BADARGS; + } + + ret = wh_Client_RecvResponse(ctx, NULL, NULL, &respSz, dataPtr); + if (ret == WH_ERROR_NOTREADY) { + return ret; + } + + if (ret == WH_ERROR_OK) { + ret = + _getCryptoResponse(dataPtr, WC_HASH_TYPE_SHA224, (uint8_t**)&resp); + if (ret >= 0) { + memcpy(out, resp->hash, WC_SHA224_DIGEST_SIZE); + (void)wc_InitSha224_ex(sha, NULL, sha->devId); + } + } + return ret; +} + +int wh_Client_Sha224Dma(whClientContext* ctx, wc_Sha224* sha, const uint8_t* in, + uint32_t inLen, uint8_t* out) +{ + int ret = WH_ERROR_OK; + + if (in != NULL && inLen > 0) { + bool sent = false; + ret = wh_Client_Sha224DmaUpdateRequest(ctx, sha, in, inLen, &sent); + if (ret == WH_ERROR_OK && sent) { + do { + ret = wh_Client_Sha224DmaUpdateResponse(ctx, sha); + } while (ret == WH_ERROR_NOTREADY); + } + } + if (ret == WH_ERROR_OK && out != NULL) { + ret = wh_Client_Sha224DmaFinalRequest(ctx, sha); + if (ret == WH_ERROR_OK) { + do { + ret = wh_Client_Sha224DmaFinalResponse(ctx, sha, out); + } while (ret == WH_ERROR_NOTREADY); + } + } + return ret; +} +#endif /* WOLFHSM_CFG_DMA */ +#endif /* WOLFSSL_SHA224 */ + +#ifdef WOLFSSL_SHA384 + +/* Maximum number of input bytes that wh_Client_Sha384UpdateRequest can absorb + * in a single call: the inline-data wire capacity, plus whatever room is left + * in the partial-block buffer (we can stash up to BLOCK_SIZE-1-buffLen tail + * bytes locally without producing a new full block). */ +static uint32_t _Sha384UpdatePerCallCapacity(const wc_Sha384* sha) +{ + return WH_MESSAGE_CRYPTO_SHA384_MAX_INLINE_UPDATE_SZ + + (uint32_t)(WC_SHA384_BLOCK_SIZE - 1u - sha->buffLen); +} + +int wh_Client_Sha384UpdateRequest(whClientContext* ctx, wc_Sha384* sha, + const uint8_t* in, uint32_t inLen, + bool* requestSent) +{ + int ret = 0; + whMessageCrypto_Sha512Request* req = NULL; + uint8_t* inlineData; + uint8_t* dataPtr = NULL; + uint8_t* sha384BufferBytes; + uint32_t capacity; + uint32_t wirePos = 0; + uint32_t i = 0; + /* Snapshot of buffer state for rollback if SendRequest fails */ + uint32_t savedBuffLen; + uint8_t savedBuffer[WC_SHA384_BLOCK_SIZE]; + + if (ctx == NULL || sha == NULL || requestSent == NULL || + (in == NULL && inLen != 0)) { + return WH_ERROR_BADARGS; + } + *requestSent = false; + + capacity = _Sha384UpdatePerCallCapacity(sha); + if (inLen > capacity) { + return WH_ERROR_BADARGS; + } + + /* Empty update: nothing to send, no state to mutate. */ + if (inLen == 0) { + return WH_ERROR_OK; + } + + /* Get data buffer */ + dataPtr = wh_CommClient_GetDataPtr(ctx->comm); + if (dataPtr == NULL) { + return WH_ERROR_BADARGS; + } + + /* Setup generic header and get pointer to request data */ + req = (whMessageCrypto_Sha512Request*)_createCryptoRequest( + dataPtr, WC_HASH_TYPE_SHA384, ctx->cryptoAffinity); + inlineData = (uint8_t*)(req + 1); + sha384BufferBytes = (uint8_t*)sha->buffer; + + /* Save the buffer state before mutation so we can restore it if + * SendRequest fails, preventing silent SHA state corruption. */ + savedBuffLen = sha->buffLen; + memcpy(savedBuffer, sha384BufferBytes, sha->buffLen); + + /* If there's a partial block already buffered, top it up from the input. + * If we manage to fill a full block, copy the completed block into the + * wire payload as the first inline block. */ + if (sha->buffLen > 0) { + while (i < inLen && sha->buffLen < WC_SHA384_BLOCK_SIZE) { + sha384BufferBytes[sha->buffLen++] = in[i++]; + } + if (sha->buffLen == WC_SHA384_BLOCK_SIZE) { + memcpy(inlineData + wirePos, sha384BufferBytes, + WC_SHA384_BLOCK_SIZE); + wirePos += WC_SHA384_BLOCK_SIZE; + sha->buffLen = 0; + } + } + + /* Copy as many full blocks from the input as fit in the inline area. */ + while ((inLen - i) >= WC_SHA384_BLOCK_SIZE && + (wirePos + WC_SHA384_BLOCK_SIZE) <= + WH_MESSAGE_CRYPTO_SHA384_MAX_INLINE_UPDATE_SZ) { + memcpy(inlineData + wirePos, in + i, WC_SHA384_BLOCK_SIZE); + wirePos += WC_SHA384_BLOCK_SIZE; + i += WC_SHA384_BLOCK_SIZE; + } + + /* Stash any remaining tail bytes into the buffer for next time. The + * capacity check above guarantees this fits without overflow. */ + while (i < inLen) { + sha384BufferBytes[sha->buffLen++] = in[i++]; + } + + /* Pure-buffer-fill update: nothing to send. */ + if (wirePos == 0) { + return WH_ERROR_OK; + } + + /* Populate fixed request fields. Intermediate hash state uses the full + * SHA512 digest size (64 bytes) on the wire; the final truncation to + * WC_SHA384_DIGEST_SIZE happens only in FinalResponse. */ + req->isLastBlock = 0; + req->inSz = wirePos; + memcpy(req->resumeState.hash, sha->digest, WC_SHA512_DIGEST_SIZE); + req->resumeState.hiLen = sha->hiLen; + req->resumeState.loLen = sha->loLen; + req->resumeState.hashType = WC_HASH_TYPE_SHA384; + + ret = wh_Client_SendRequest(ctx, WH_MESSAGE_GROUP_CRYPTO, WC_ALGO_TYPE_HASH, + sizeof(whMessageCrypto_GenericRequestHeader) + + sizeof(*req) + wirePos, + dataPtr); + + if (ret == 0) { + *requestSent = true; + } + else { + /* SendRequest failed — restore buffer state so the caller can retry + * or continue hashing without data loss. */ + sha->buffLen = savedBuffLen; + memcpy(sha384BufferBytes, savedBuffer, savedBuffLen); + } + return ret; +} + +int wh_Client_Sha384UpdateResponse(whClientContext* ctx, wc_Sha384* sha) +{ + uint16_t group = WH_MESSAGE_GROUP_CRYPTO; + uint16_t action = WH_MESSAGE_ACTION_NONE; + uint16_t dataSz = 0; + int ret = 0; + whMessageCrypto_Sha2Response* res = NULL; + uint8_t* dataPtr; + + if (ctx == NULL || sha == NULL) { + return WH_ERROR_BADARGS; + } + + dataPtr = wh_CommClient_GetDataPtr(ctx->comm); + if (dataPtr == NULL) { + return WH_ERROR_BADARGS; + } + + ret = wh_Client_RecvResponse(ctx, &group, &action, &dataSz, dataPtr); + if (ret != WH_ERROR_OK) { + return ret; + } + + ret = _getCryptoResponse(dataPtr, WC_HASH_TYPE_SHA384, (uint8_t**)&res); + if (ret >= 0) { + /* Intermediate hash state is stored at full SHA512 digest width */ + memcpy(sha->digest, res->hash, WC_SHA512_DIGEST_SIZE); + sha->hiLen = res->hiLen; + sha->loLen = res->loLen; + } + return ret; +} + +int wh_Client_Sha384FinalRequest(whClientContext* ctx, wc_Sha384* sha) +{ + int ret; + whMessageCrypto_Sha512Request* req; + uint8_t* inlineData; + uint8_t* dataPtr; + + if (ctx == NULL || sha == NULL) { + return WH_ERROR_BADARGS; + } + + dataPtr = wh_CommClient_GetDataPtr(ctx->comm); + if (dataPtr == NULL) { + return WH_ERROR_BADARGS; + } + + req = (whMessageCrypto_Sha512Request*)_createCryptoRequest( + dataPtr, WC_HASH_TYPE_SHA384, ctx->cryptoAffinity); + inlineData = (uint8_t*)(req + 1); + + req->isLastBlock = 1; + req->inSz = sha->buffLen; + memcpy(req->resumeState.hash, sha->digest, WC_SHA512_DIGEST_SIZE); + req->resumeState.hiLen = sha->hiLen; + req->resumeState.loLen = sha->loLen; + req->resumeState.hashType = WC_HASH_TYPE_SHA384; + + if (sha->buffLen > 0) { + memcpy(inlineData, sha->buffer, sha->buffLen); + } + + ret = wh_Client_SendRequest(ctx, WH_MESSAGE_GROUP_CRYPTO, WC_ALGO_TYPE_HASH, + sizeof(whMessageCrypto_GenericRequestHeader) + + sizeof(*req) + sha->buffLen, + dataPtr); + return ret; +} + +int wh_Client_Sha384FinalResponse(whClientContext* ctx, wc_Sha384* sha, + uint8_t* out) +{ + uint16_t group = WH_MESSAGE_GROUP_CRYPTO; + uint16_t action = WH_MESSAGE_ACTION_NONE; + uint16_t dataSz = 0; + int ret; + whMessageCrypto_Sha2Response* res = NULL; + uint8_t* dataPtr; + + if (ctx == NULL || sha == NULL || out == NULL) { + return WH_ERROR_BADARGS; + } + + dataPtr = wh_CommClient_GetDataPtr(ctx->comm); + if (dataPtr == NULL) { + return WH_ERROR_BADARGS; + } + + ret = wh_Client_RecvResponse(ctx, &group, &action, &dataSz, dataPtr); + if (ret != 0) { + return ret; + } + + ret = _getCryptoResponse(dataPtr, WC_HASH_TYPE_SHA384, (uint8_t**)&res); + if (ret >= 0) { + /* Final output is truncated to WC_SHA384_DIGEST_SIZE */ + memcpy(out, res->hash, WC_SHA384_DIGEST_SIZE); + /* Reset state without blowing away devId */ + (void)wc_InitSha384_ex(sha, NULL, sha->devId); + } + return ret; +} + +int wh_Client_Sha384(whClientContext* ctx, wc_Sha384* sha384, const uint8_t* in, + uint32_t inLen, uint8_t* out) +{ + int ret = WH_ERROR_OK; + + /* Caller invoked SHA Update: + * wc_CryptoCb_Sha384Hash(sha384, data, len, NULL) */ + if (in != NULL && inLen > 0) { + uint32_t consumed = 0; + while (ret == WH_ERROR_OK && consumed < inLen) { + uint32_t capacity = _Sha384UpdatePerCallCapacity(sha384); + uint32_t remaining = inLen - consumed; + uint32_t chunk = (remaining < capacity) ? remaining : capacity; + bool sent = false; + + ret = wh_Client_Sha384UpdateRequest(ctx, sha384, in + consumed, + chunk, &sent); + if (ret != WH_ERROR_OK) { + break; + } + if (sent) { + do { + ret = wh_Client_Sha384UpdateResponse(ctx, sha384); + } while (ret == WH_ERROR_NOTREADY); + if (ret != WH_ERROR_OK) { + break; + } + } + consumed += chunk; + } + } + + /* Caller invoked SHA finalize: + * wc_CryptoCb_Sha384Hash(sha384, NULL, 0, *hash) */ + if (ret == WH_ERROR_OK && out != NULL) { + ret = wh_Client_Sha384FinalRequest(ctx, sha384); + if (ret == WH_ERROR_OK) { + do { + ret = wh_Client_Sha384FinalResponse(ctx, sha384, out); + } while (ret == WH_ERROR_NOTREADY); + } + } + + return ret; +} + +#ifdef WOLFHSM_CFG_DMA +int wh_Client_Sha384DmaUpdateRequest(whClientContext* ctx, wc_Sha384* sha, + const uint8_t* in, uint32_t inLen, + bool* requestSent) +{ + int ret = WH_ERROR_OK; + uint8_t* dataPtr = NULL; + whMessageCrypto_Sha512DmaRequest* req = NULL; + uint8_t* inlineData; + uint8_t* shaBufferBytes; + uint32_t wirePos = 0; + uint32_t i = 0; + uintptr_t inAddr = 0; + bool inAddrAcquired = false; + const uint8_t* dmaBase = NULL; + uint32_t dmaSz = 0; + uint32_t savedBuffLen; + uint8_t savedBuffer[WC_SHA384_BLOCK_SIZE]; + + if (ctx == NULL || sha == NULL || requestSent == NULL || + (in == NULL && inLen != 0)) { + return WH_ERROR_BADARGS; + } + *requestSent = false; + + if (inLen == 0) { + return WH_ERROR_OK; + } + + dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm); + if (dataPtr == NULL) { + return WH_ERROR_BADARGS; + } + + req = (whMessageCrypto_Sha512DmaRequest*)_createCryptoRequest( + dataPtr, WC_HASH_TYPE_SHA384, ctx->cryptoAffinity); + inlineData = (uint8_t*)(req + 1); + shaBufferBytes = (uint8_t*)sha->buffer; + + savedBuffLen = sha->buffLen; + memcpy(savedBuffer, shaBufferBytes, sha->buffLen); + + if (sha->buffLen > 0) { + while (i < inLen && sha->buffLen < WC_SHA384_BLOCK_SIZE) { + shaBufferBytes[sha->buffLen++] = in[i++]; + } + if (sha->buffLen == WC_SHA384_BLOCK_SIZE) { + memcpy(inlineData, shaBufferBytes, WC_SHA384_BLOCK_SIZE); + wirePos = WC_SHA384_BLOCK_SIZE; + sha->buffLen = 0; + } + } + + if ((inLen - i) >= WC_SHA384_BLOCK_SIZE) { + dmaBase = in + i; + dmaSz = ((inLen - i) / WC_SHA384_BLOCK_SIZE) * WC_SHA384_BLOCK_SIZE; + i += dmaSz; + } + + while (i < inLen) { + shaBufferBytes[sha->buffLen++] = in[i++]; + } + + if (wirePos == 0 && dmaSz == 0) { + return WH_ERROR_OK; + } + + req->isLastBlock = 0; + req->inSz = wirePos; + /* SHA384 shares SHA512's internal 64-byte digest state */ + memcpy(req->resumeState.hash, sha->digest, WC_SHA512_DIGEST_SIZE); + req->resumeState.hiLen = sha->hiLen; + req->resumeState.loLen = sha->loLen; + req->resumeState.hashType = WC_HASH_TYPE_SHA384; + req->input.sz = dmaSz; + req->input.addr = 0; + + if (dmaSz > 0) { + ret = wh_Client_DmaProcessClientAddress( + ctx, (uintptr_t)dmaBase, (void**)&inAddr, dmaSz, + WH_DMA_OPER_CLIENT_READ_PRE, (whDmaFlags){0}); + if (ret == WH_ERROR_OK) { + inAddrAcquired = true; + req->input.addr = inAddr; + } + } + + if (ret == WH_ERROR_OK) { + ctx->dma.asyncCtx.sha.ioAddr = inAddr; + ctx->dma.asyncCtx.sha.clientAddr = (uintptr_t)dmaBase; + ctx->dma.asyncCtx.sha.ioSz = dmaSz; + + ret = wh_Client_SendRequest( + ctx, WH_MESSAGE_GROUP_CRYPTO_DMA, WC_ALGO_TYPE_HASH, + sizeof(whMessageCrypto_GenericRequestHeader) + sizeof(*req) + + wirePos, + dataPtr); + } + + if (ret == WH_ERROR_OK) { + *requestSent = true; + } + else { + sha->buffLen = savedBuffLen; + memcpy(shaBufferBytes, savedBuffer, savedBuffLen); + if (inAddrAcquired) { + (void)wh_Client_DmaProcessClientAddress( + ctx, (uintptr_t)dmaBase, (void**)&inAddr, dmaSz, + WH_DMA_OPER_CLIENT_READ_POST, (whDmaFlags){0}); + } + } + return ret; +} + +int wh_Client_Sha384DmaUpdateResponse(whClientContext* ctx, wc_Sha384* sha) +{ + int ret = WH_ERROR_OK; + uint8_t* dataPtr = NULL; + whMessageCrypto_Sha2DmaResponse* resp = NULL; + uint16_t respSz = 0; + + if (ctx == NULL || sha == NULL) { + return WH_ERROR_BADARGS; + } + + dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm); + if (dataPtr == NULL) { + return WH_ERROR_BADARGS; + } + + ret = wh_Client_RecvResponse(ctx, NULL, NULL, &respSz, dataPtr); + if (ret == WH_ERROR_NOTREADY) { + return ret; + } + + if (ret == WH_ERROR_OK) { + ret = + _getCryptoResponse(dataPtr, WC_HASH_TYPE_SHA384, (uint8_t**)&resp); + if (ret >= 0) { + memcpy(sha->digest, resp->hash, WC_SHA512_DIGEST_SIZE); + sha->hiLen = resp->hiLen; + sha->loLen = resp->loLen; + } + } + + if (ctx->dma.asyncCtx.sha.ioSz > 0) { + uintptr_t ioAddr = ctx->dma.asyncCtx.sha.ioAddr; + (void)wh_Client_DmaProcessClientAddress( + ctx, ctx->dma.asyncCtx.sha.clientAddr, (void**)&ioAddr, + ctx->dma.asyncCtx.sha.ioSz, WH_DMA_OPER_CLIENT_READ_POST, + (whDmaFlags){0}); + ctx->dma.asyncCtx.sha.ioSz = 0; + } + return ret; +} + +int wh_Client_Sha384DmaFinalRequest(whClientContext* ctx, wc_Sha384* sha) +{ + int ret = WH_ERROR_OK; + uint8_t* dataPtr = NULL; + whMessageCrypto_Sha512DmaRequest* req = NULL; + uint8_t* inlineData; + + if (ctx == NULL || sha == NULL) { + return WH_ERROR_BADARGS; + } + + dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm); + if (dataPtr == NULL) { + return WH_ERROR_BADARGS; + } + + req = (whMessageCrypto_Sha512DmaRequest*)_createCryptoRequest( + dataPtr, WC_HASH_TYPE_SHA384, ctx->cryptoAffinity); + inlineData = (uint8_t*)(req + 1); + + req->isLastBlock = 1; + req->inSz = sha->buffLen; + /* SHA384 shares SHA512's internal 64-byte digest state */ + memcpy(req->resumeState.hash, sha->digest, WC_SHA512_DIGEST_SIZE); + req->resumeState.hiLen = sha->hiLen; + req->resumeState.loLen = sha->loLen; + req->resumeState.hashType = WC_HASH_TYPE_SHA384; + req->input.sz = 0; + req->input.addr = 0; + + if (sha->buffLen > 0) { + memcpy(inlineData, sha->buffer, sha->buffLen); + } + + ret = wh_Client_SendRequest(ctx, WH_MESSAGE_GROUP_CRYPTO_DMA, + WC_ALGO_TYPE_HASH, + sizeof(whMessageCrypto_GenericRequestHeader) + + sizeof(*req) + sha->buffLen, + dataPtr); + return ret; +} + +int wh_Client_Sha384DmaFinalResponse(whClientContext* ctx, wc_Sha384* sha, + uint8_t* out) +{ + int ret = WH_ERROR_OK; + uint8_t* dataPtr = NULL; + whMessageCrypto_Sha2DmaResponse* resp = NULL; + uint16_t respSz = 0; + + if (ctx == NULL || sha == NULL || out == NULL) { + return WH_ERROR_BADARGS; + } + + dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm); + if (dataPtr == NULL) { + return WH_ERROR_BADARGS; + } + + ret = wh_Client_RecvResponse(ctx, NULL, NULL, &respSz, dataPtr); + if (ret == WH_ERROR_NOTREADY) { + return ret; + } + + if (ret == WH_ERROR_OK) { + ret = + _getCryptoResponse(dataPtr, WC_HASH_TYPE_SHA384, (uint8_t**)&resp); + if (ret >= 0) { + memcpy(out, resp->hash, WC_SHA384_DIGEST_SIZE); + (void)wc_InitSha384_ex(sha, NULL, sha->devId); + } + } + return ret; +} + +int wh_Client_Sha384Dma(whClientContext* ctx, wc_Sha384* sha, const uint8_t* in, + uint32_t inLen, uint8_t* out) +{ + int ret = WH_ERROR_OK; + + if (in != NULL && inLen > 0) { + bool sent = false; + ret = wh_Client_Sha384DmaUpdateRequest(ctx, sha, in, inLen, &sent); + if (ret == WH_ERROR_OK && sent) { + do { + ret = wh_Client_Sha384DmaUpdateResponse(ctx, sha); + } while (ret == WH_ERROR_NOTREADY); + } + } + if (ret == WH_ERROR_OK && out != NULL) { + ret = wh_Client_Sha384DmaFinalRequest(ctx, sha); + if (ret == WH_ERROR_OK) { + do { + ret = wh_Client_Sha384DmaFinalResponse(ctx, sha, out); + } while (ret == WH_ERROR_NOTREADY); + } + } + return ret; +} +#endif /* WOLFHSM_CFG_DMA */ +#endif /* WOLFSSL_SHA384 */ + + +#if defined(WOLFSSL_SHA512) && defined(WOLFSSL_SHA512_HASHTYPE) + +/* Maximum number of input bytes that wh_Client_Sha512UpdateRequest can absorb + * in a single call: the inline-data wire capacity, plus whatever room is left + * in the partial-block buffer (we can stash up to BLOCK_SIZE-1-buffLen tail + * bytes locally without producing a new full block). */ +static uint32_t _Sha512UpdatePerCallCapacity(const wc_Sha512* sha) +{ + return WH_MESSAGE_CRYPTO_SHA512_MAX_INLINE_UPDATE_SZ + + (uint32_t)(WC_SHA512_BLOCK_SIZE - 1u - sha->buffLen); +} + +int wh_Client_Sha512UpdateRequest(whClientContext* ctx, wc_Sha512* sha, + const uint8_t* in, uint32_t inLen, + bool* requestSent) +{ + int ret = 0; + whMessageCrypto_Sha512Request* req = NULL; + uint8_t* inlineData; + uint8_t* dataPtr = NULL; + uint8_t* sha512BufferBytes; + uint32_t capacity; + uint32_t wirePos = 0; + uint32_t i = 0; + /* Snapshot of buffer state for rollback if SendRequest fails */ + uint32_t savedBuffLen; + uint8_t savedBuffer[WC_SHA512_BLOCK_SIZE]; + + if (ctx == NULL || sha == NULL || requestSent == NULL || + (in == NULL && inLen != 0)) { + return WH_ERROR_BADARGS; + } + *requestSent = false; + + capacity = _Sha512UpdatePerCallCapacity(sha); + if (inLen > capacity) { + return WH_ERROR_BADARGS; + } + + /* Empty update: nothing to send, no state to mutate. */ + if (inLen == 0) { + return WH_ERROR_OK; + } + + /* Get data buffer */ + dataPtr = wh_CommClient_GetDataPtr(ctx->comm); + if (dataPtr == NULL) { + return WH_ERROR_BADARGS; + } + + /* Setup generic header and get pointer to request data */ + req = (whMessageCrypto_Sha512Request*)_createCryptoRequest( + dataPtr, WC_HASH_TYPE_SHA512, ctx->cryptoAffinity); + inlineData = (uint8_t*)(req + 1); + sha512BufferBytes = (uint8_t*)sha->buffer; + + /* Save the buffer state before mutation so we can restore it if + * SendRequest fails, preventing silent SHA state corruption. */ + savedBuffLen = sha->buffLen; + memcpy(savedBuffer, sha512BufferBytes, sha->buffLen); + + /* If there's a partial block already buffered, top it up from the input. + * If we manage to fill a full block, copy the completed block into the + * wire payload as the first inline block. */ + if (sha->buffLen > 0) { + while (i < inLen && sha->buffLen < WC_SHA512_BLOCK_SIZE) { + sha512BufferBytes[sha->buffLen++] = in[i++]; + } + if (sha->buffLen == WC_SHA512_BLOCK_SIZE) { + memcpy(inlineData + wirePos, sha512BufferBytes, + WC_SHA512_BLOCK_SIZE); + wirePos += WC_SHA512_BLOCK_SIZE; + sha->buffLen = 0; + } + } + + /* Copy as many full blocks from the input as fit in the inline area. */ + while ((inLen - i) >= WC_SHA512_BLOCK_SIZE && + (wirePos + WC_SHA512_BLOCK_SIZE) <= + WH_MESSAGE_CRYPTO_SHA512_MAX_INLINE_UPDATE_SZ) { + memcpy(inlineData + wirePos, in + i, WC_SHA512_BLOCK_SIZE); + wirePos += WC_SHA512_BLOCK_SIZE; + i += WC_SHA512_BLOCK_SIZE; + } + + /* Stash any remaining tail bytes into the buffer for next time. The + * capacity check above guarantees this fits without overflow. */ + while (i < inLen) { + sha512BufferBytes[sha->buffLen++] = in[i++]; + } + + /* Pure-buffer-fill update: nothing to send. */ + if (wirePos == 0) { + return WH_ERROR_OK; + } + + /* Populate fixed request fields */ + req->isLastBlock = 0; + req->inSz = wirePos; + memcpy(req->resumeState.hash, sha->digest, WC_SHA512_DIGEST_SIZE); + req->resumeState.hiLen = sha->hiLen; + req->resumeState.loLen = sha->loLen; + req->resumeState.hashType = sha->hashType; + + ret = wh_Client_SendRequest(ctx, WH_MESSAGE_GROUP_CRYPTO, WC_ALGO_TYPE_HASH, + sizeof(whMessageCrypto_GenericRequestHeader) + + sizeof(*req) + wirePos, + dataPtr); + + if (ret == 0) { + *requestSent = true; + } + else { + /* SendRequest failed — restore buffer state so the caller can retry + * or continue hashing without data loss. */ + sha->buffLen = savedBuffLen; + memcpy(sha512BufferBytes, savedBuffer, savedBuffLen); + } return ret; } -int wh_Client_Sha512(whClientContext* ctx, wc_Sha512* sha512, const uint8_t* in, - uint32_t inLen, uint8_t* out) +int wh_Client_Sha512UpdateResponse(whClientContext* ctx, wc_Sha512* sha) { - int ret = 0; - uint8_t* sha512BufferBytes = (uint8_t*)sha512->buffer; - int hashType = WC_HASH_TYPE_SHA512; + uint16_t group = WH_MESSAGE_GROUP_CRYPTO; + uint16_t action = WH_MESSAGE_ACTION_NONE; + uint16_t dataSz = 0; + int ret = 0; + whMessageCrypto_Sha2Response* res = NULL; + uint8_t* dataPtr; - /* Caller invoked SHA Update: - * wc_CryptoCb_Sha512Hash(sha512, data, len, NULL) */ - if (in != NULL) { - size_t i = 0; - - /* Process the partial blocks directly from the input data. If there - * is enough input data to fill a full block, transfer it to the - * server */ - if (sha512->buffLen > 0) { - while (i < inLen && sha512->buffLen < WC_SHA512_BLOCK_SIZE) { - sha512BufferBytes[sha512->buffLen++] = in[i++]; - } - if (sha512->buffLen == WC_SHA512_BLOCK_SIZE) { - ret = _xferSha512BlockAndUpdateDigest(ctx, sha512, 0); - sha512->buffLen = 0; - } - } + if (ctx == NULL || sha == NULL) { + return WH_ERROR_BADARGS; + } - /* Process as many full blocks from the input data as we can */ - while (ret == 0 && (inLen - i) >= WC_SHA512_BLOCK_SIZE) { - memcpy(sha512BufferBytes, in + i, WC_SHA512_BLOCK_SIZE); - ret = _xferSha512BlockAndUpdateDigest(ctx, sha512, 0); - i += WC_SHA512_BLOCK_SIZE; - } + dataPtr = wh_CommClient_GetDataPtr(ctx->comm); + if (dataPtr == NULL) { + return WH_ERROR_BADARGS; + } - /* Copy any remaining data into the buffer to be sent in a - * subsequent call when we have enough input data to send a full - * block */ - while (i < inLen) { - sha512BufferBytes[sha512->buffLen++] = in[i++]; - } + ret = wh_Client_RecvResponse(ctx, &group, &action, &dataSz, dataPtr); + if (ret != WH_ERROR_OK) { + return ret; } - /* Caller invoked SHA finalize: - * wc_CryptoCb_Sha512Hash(sha512, NULL, 0, * hash) */ - if (ret == 0 && out != NULL) { - ret = _xferSha512BlockAndUpdateDigest(ctx, sha512, 1); + ret = _getCryptoResponse(dataPtr, WC_HASH_TYPE_SHA512, (uint8_t**)&res); + if (ret >= 0) { + memcpy(sha->digest, res->hash, WC_SHA512_DIGEST_SIZE); + sha->hiLen = res->hiLen; + sha->loLen = res->loLen; + } + return ret; +} - /* Copy out the final hash value */ - if (ret == 0) { - memcpy(out, sha512->digest, WC_SHA512_DIGEST_SIZE); - } +int wh_Client_Sha512FinalRequest(whClientContext* ctx, wc_Sha512* sha) +{ + int ret; + whMessageCrypto_Sha512Request* req; + uint8_t* inlineData; + uint8_t* dataPtr; + + if (ctx == NULL || sha == NULL) { + return WH_ERROR_BADARGS; + } + + dataPtr = wh_CommClient_GetDataPtr(ctx->comm); + if (dataPtr == NULL) { + return WH_ERROR_BADARGS; + } + + req = (whMessageCrypto_Sha512Request*)_createCryptoRequest( + dataPtr, WC_HASH_TYPE_SHA512, ctx->cryptoAffinity); + inlineData = (uint8_t*)(req + 1); + + req->isLastBlock = 1; + req->inSz = sha->buffLen; + memcpy(req->resumeState.hash, sha->digest, WC_SHA512_DIGEST_SIZE); + req->resumeState.hiLen = sha->hiLen; + req->resumeState.loLen = sha->loLen; + req->resumeState.hashType = sha->hashType; + + if (sha->buffLen > 0) { + memcpy(inlineData, sha->buffer, sha->buffLen); + } + + ret = wh_Client_SendRequest(ctx, WH_MESSAGE_GROUP_CRYPTO, WC_ALGO_TYPE_HASH, + sizeof(whMessageCrypto_GenericRequestHeader) + + sizeof(*req) + sha->buffLen, + dataPtr); + return ret; +} + +int wh_Client_Sha512FinalResponse(whClientContext* ctx, wc_Sha512* sha, + uint8_t* out) +{ + uint16_t group = WH_MESSAGE_GROUP_CRYPTO; + uint16_t action = WH_MESSAGE_ACTION_NONE; + uint16_t dataSz = 0; + int ret; + whMessageCrypto_Sha2Response* res = NULL; + uint8_t* dataPtr; + int hashType; + + if (ctx == NULL || sha == NULL || out == NULL) { + return WH_ERROR_BADARGS; + } + + dataPtr = wh_CommClient_GetDataPtr(ctx->comm); + if (dataPtr == NULL) { + return WH_ERROR_BADARGS; + } + + ret = wh_Client_RecvResponse(ctx, &group, &action, &dataSz, dataPtr); + if (ret != 0) { + return ret; + } + + ret = _getCryptoResponse(dataPtr, WC_HASH_TYPE_SHA512, (uint8_t**)&res); + if (ret >= 0) { /* keep hashtype before initialization */ - hashType = sha512->hashType; + hashType = sha->hashType; /* reset the state of the sha context (without blowing away devId and - * hashType) - */ + * hashType), and copy only the digest bytes for the active variant */ switch (hashType) { +#ifndef WOLFSSL_NOSHA512_224 case WC_HASH_TYPE_SHA512_224: - (void)wc_InitSha512_224_ex(sha512, NULL, sha512->devId); + memcpy(out, res->hash, WC_SHA512_224_DIGEST_SIZE); + (void)wc_InitSha512_224_ex(sha, NULL, sha->devId); break; +#endif +#ifndef WOLFSSL_NOSHA512_256 case WC_HASH_TYPE_SHA512_256: - (void)wc_InitSha512_256_ex(sha512, NULL, sha512->devId); + memcpy(out, res->hash, WC_SHA512_256_DIGEST_SIZE); + (void)wc_InitSha512_256_ex(sha, NULL, sha->devId); break; +#endif default: - (void)wc_InitSha512_ex(sha512, NULL, sha512->devId); + memcpy(out, res->hash, WC_SHA512_DIGEST_SIZE); + (void)wc_InitSha512_ex(sha, NULL, sha->devId); break; } } + return ret; +} + +int wh_Client_Sha512(whClientContext* ctx, wc_Sha512* sha512, const uint8_t* in, + uint32_t inLen, uint8_t* out) +{ + int ret = WH_ERROR_OK; + + /* Caller invoked SHA Update: + * wc_CryptoCb_Sha512Hash(sha512, data, len, NULL) */ + if (in != NULL && inLen > 0) { + uint32_t consumed = 0; + while (ret == WH_ERROR_OK && consumed < inLen) { + uint32_t capacity = _Sha512UpdatePerCallCapacity(sha512); + uint32_t remaining = inLen - consumed; + uint32_t chunk = (remaining < capacity) ? remaining : capacity; + bool sent = false; + + ret = wh_Client_Sha512UpdateRequest(ctx, sha512, in + consumed, + chunk, &sent); + if (ret != WH_ERROR_OK) { + break; + } + if (sent) { + do { + ret = wh_Client_Sha512UpdateResponse(ctx, sha512); + } while (ret == WH_ERROR_NOTREADY); + if (ret != WH_ERROR_OK) { + break; + } + } + consumed += chunk; + } + } + + /* Caller invoked SHA finalize: + * wc_CryptoCb_Sha512Hash(sha512, NULL, 0, *hash) */ + if (ret == WH_ERROR_OK && out != NULL) { + ret = wh_Client_Sha512FinalRequest(ctx, sha512); + if (ret == WH_ERROR_OK) { + do { + ret = wh_Client_Sha512FinalResponse(ctx, sha512, out); + } while (ret == WH_ERROR_NOTREADY); + } + } return ret; } #ifdef WOLFHSM_CFG_DMA -int wh_Client_Sha512Dma(whClientContext* ctx, wc_Sha512* sha, const uint8_t* in, - uint32_t inLen, uint8_t* out) +int wh_Client_Sha512DmaUpdateRequest(whClientContext* ctx, wc_Sha512* sha, + const uint8_t* in, uint32_t inLen, + bool* requestSent) { - int ret = WH_ERROR_OK; - wc_Sha512* sha512 = sha; - uint16_t respSz = 0; - uint16_t group = WH_MESSAGE_GROUP_CRYPTO_DMA; - uint8_t* dataPtr = NULL; - whMessageCrypto_Sha2DmaRequest* req = NULL; - whMessageCrypto_Sha2DmaResponse* resp = NULL; - uintptr_t inAddr = 0; - uintptr_t outAddr = 0; - uintptr_t stateAddr = 0; + int ret = WH_ERROR_OK; + uint8_t* dataPtr = NULL; + whMessageCrypto_Sha512DmaRequest* req = NULL; + uint8_t* inlineData; + uint8_t* shaBufferBytes; + uint32_t wirePos = 0; + uint32_t i = 0; + uintptr_t inAddr = 0; + bool inAddrAcquired = false; + const uint8_t* dmaBase = NULL; + uint32_t dmaSz = 0; + uint32_t savedBuffLen; + uint8_t savedBuffer[WC_SHA512_BLOCK_SIZE]; + + if (ctx == NULL || sha == NULL || requestSent == NULL || + (in == NULL && inLen != 0)) { + return WH_ERROR_BADARGS; + } + *requestSent = false; + + if (inLen == 0) { + return WH_ERROR_OK; + } - /* Get data pointer from the context to use as request/response storage */ dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm); if (dataPtr == NULL) { return WH_ERROR_BADARGS; } - /* Setup generic header and get pointer to request data */ - req = (whMessageCrypto_Sha2DmaRequest*)_createCryptoRequest( + req = (whMessageCrypto_Sha512DmaRequest*)_createCryptoRequest( dataPtr, WC_HASH_TYPE_SHA512, ctx->cryptoAffinity); + inlineData = (uint8_t*)(req + 1); + shaBufferBytes = (uint8_t*)sha->buffer; - if (in != NULL || out != NULL) { - req->state.sz = sizeof(*sha512); - req->input.sz = inLen; - req->output.sz = WC_SHA512_DIGEST_SIZE; /* not needed, but YOLO */ + savedBuffLen = sha->buffLen; + memcpy(savedBuffer, shaBufferBytes, sha->buffLen); - /* Perform address translations */ - ret = wh_Client_DmaProcessClientAddress( - ctx, (uintptr_t)sha512, (void**)&stateAddr, req->state.sz, - WH_DMA_OPER_CLIENT_WRITE_PRE, (whDmaFlags){0}); - if (ret == WH_ERROR_OK) { - req->state.addr = stateAddr; + if (sha->buffLen > 0) { + while (i < inLen && sha->buffLen < WC_SHA512_BLOCK_SIZE) { + shaBufferBytes[sha->buffLen++] = in[i++]; } - - if (ret == WH_ERROR_OK) { - ret = wh_Client_DmaProcessClientAddress( - ctx, (uintptr_t)in, (void**)&inAddr, req->input.sz, - WH_DMA_OPER_CLIENT_READ_PRE, (whDmaFlags){0}); - if (ret == WH_ERROR_OK) { - req->input.addr = inAddr; - } + if (sha->buffLen == WC_SHA512_BLOCK_SIZE) { + memcpy(inlineData, shaBufferBytes, WC_SHA512_BLOCK_SIZE); + wirePos = WC_SHA512_BLOCK_SIZE; + sha->buffLen = 0; } + } + + if ((inLen - i) >= WC_SHA512_BLOCK_SIZE) { + dmaBase = in + i; + dmaSz = ((inLen - i) / WC_SHA512_BLOCK_SIZE) * WC_SHA512_BLOCK_SIZE; + i += dmaSz; + } + + while (i < inLen) { + shaBufferBytes[sha->buffLen++] = in[i++]; + } + + if (wirePos == 0 && dmaSz == 0) { + return WH_ERROR_OK; + } + req->isLastBlock = 0; + req->inSz = wirePos; + memcpy(req->resumeState.hash, sha->digest, WC_SHA512_DIGEST_SIZE); + req->resumeState.hiLen = sha->hiLen; + req->resumeState.loLen = sha->loLen; + req->resumeState.hashType = sha->hashType; + req->input.sz = dmaSz; + req->input.addr = 0; + + if (dmaSz > 0) { + ret = wh_Client_DmaProcessClientAddress( + ctx, (uintptr_t)dmaBase, (void**)&inAddr, dmaSz, + WH_DMA_OPER_CLIENT_READ_PRE, (whDmaFlags){0}); if (ret == WH_ERROR_OK) { - ret = wh_Client_DmaProcessClientAddress( - ctx, (uintptr_t)out, (void**)&outAddr, req->output.sz, - WH_DMA_OPER_CLIENT_WRITE_PRE, (whDmaFlags){0}); - if (ret == WH_ERROR_OK) { - req->output.addr = outAddr; - } + inAddrAcquired = true; + req->input.addr = inAddr; } } - /* Caller invoked SHA Update: - * wc_CryptoCb_Sha512Hash(sha512, data, len, NULL) */ - if (in != NULL && ret == WH_ERROR_OK) { - req->finalize = 0; - WH_DEBUG_CLIENT_VERBOSE("SHA512 DMA UPDATE: inAddr=%p, inSz=%u\n", in, - (unsigned int)inLen); + if (ret == WH_ERROR_OK) { + ctx->dma.asyncCtx.sha.ioAddr = inAddr; + ctx->dma.asyncCtx.sha.clientAddr = (uintptr_t)dmaBase; + ctx->dma.asyncCtx.sha.ioSz = dmaSz; + ret = wh_Client_SendRequest( - ctx, group, WC_ALGO_TYPE_HASH, - sizeof(whMessageCrypto_GenericRequestHeader) + sizeof(*req), - (uint8_t*)dataPtr); + ctx, WH_MESSAGE_GROUP_CRYPTO_DMA, WC_ALGO_TYPE_HASH, + sizeof(whMessageCrypto_GenericRequestHeader) + sizeof(*req) + + wirePos, + dataPtr); + } - if (ret == WH_ERROR_OK) { - do { - ret = wh_Client_RecvResponse(ctx, NULL, NULL, &respSz, - (uint8_t*)dataPtr); - } while (ret == WH_ERROR_NOTREADY); + if (ret == WH_ERROR_OK) { + *requestSent = true; + } + else { + sha->buffLen = savedBuffLen; + memcpy(shaBufferBytes, savedBuffer, savedBuffLen); + if (inAddrAcquired) { + (void)wh_Client_DmaProcessClientAddress( + ctx, (uintptr_t)dmaBase, (void**)&inAddr, dmaSz, + WH_DMA_OPER_CLIENT_READ_POST, (whDmaFlags){0}); } + } + return ret; +} - if (ret == WH_ERROR_OK) { - /* Get response structure pointer, validates generic header - * rc */ - ret = _getCryptoResponse(dataPtr, WC_HASH_TYPE_SHA512, - (uint8_t**)&resp); - /* Nothing to do on success, as server will have updated the context - * in client memory */ +int wh_Client_Sha512DmaUpdateResponse(whClientContext* ctx, wc_Sha512* sha) +{ + int ret = WH_ERROR_OK; + uint8_t* dataPtr = NULL; + whMessageCrypto_Sha2DmaResponse* resp = NULL; + uint16_t respSz = 0; + + if (ctx == NULL || sha == NULL) { + return WH_ERROR_BADARGS; + } + + dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm); + if (dataPtr == NULL) { + return WH_ERROR_BADARGS; + } + + ret = wh_Client_RecvResponse(ctx, NULL, NULL, &respSz, dataPtr); + if (ret == WH_ERROR_NOTREADY) { + return ret; + } + + if (ret == WH_ERROR_OK) { + ret = + _getCryptoResponse(dataPtr, WC_HASH_TYPE_SHA512, (uint8_t**)&resp); + if (ret >= 0) { + memcpy(sha->digest, resp->hash, WC_SHA512_DIGEST_SIZE); + sha->hiLen = resp->hiLen; + sha->loLen = resp->loLen; } } - /* Caller invoked SHA finalize: - * wc_CryptoCb_Sha512Hash(sha512, NULL, 0, * hash) */ - if ((ret == WH_ERROR_OK) && (out != NULL)) { - /* Packet will have been trashed, so re-populate all fields */ - req->finalize = 1; + if (ctx->dma.asyncCtx.sha.ioSz > 0) { + uintptr_t ioAddr = ctx->dma.asyncCtx.sha.ioAddr; + (void)wh_Client_DmaProcessClientAddress( + ctx, ctx->dma.asyncCtx.sha.clientAddr, (void**)&ioAddr, + ctx->dma.asyncCtx.sha.ioSz, WH_DMA_OPER_CLIENT_READ_POST, + (whDmaFlags){0}); + ctx->dma.asyncCtx.sha.ioSz = 0; + } + return ret; +} - WH_DEBUG_CLIENT_VERBOSE("SHA512 DMA FINAL: outAddr=%p\n", out); - /* send the request to the server */ - ret = wh_Client_SendRequest( - ctx, group, WC_ALGO_TYPE_HASH, - sizeof(whMessageCrypto_GenericRequestHeader) + sizeof(*req), - (uint8_t*)dataPtr); +int wh_Client_Sha512DmaFinalRequest(whClientContext* ctx, wc_Sha512* sha) +{ + int ret = WH_ERROR_OK; + uint8_t* dataPtr = NULL; + whMessageCrypto_Sha512DmaRequest* req = NULL; + uint8_t* inlineData; - if (ret == WH_ERROR_OK) { + if (ctx == NULL || sha == NULL) { + return WH_ERROR_BADARGS; + } + + dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm); + if (dataPtr == NULL) { + return WH_ERROR_BADARGS; + } + + req = (whMessageCrypto_Sha512DmaRequest*)_createCryptoRequest( + dataPtr, WC_HASH_TYPE_SHA512, ctx->cryptoAffinity); + inlineData = (uint8_t*)(req + 1); + + req->isLastBlock = 1; + req->inSz = sha->buffLen; + memcpy(req->resumeState.hash, sha->digest, WC_SHA512_DIGEST_SIZE); + req->resumeState.hiLen = sha->hiLen; + req->resumeState.loLen = sha->loLen; + req->resumeState.hashType = sha->hashType; + req->input.sz = 0; + req->input.addr = 0; + + if (sha->buffLen > 0) { + memcpy(inlineData, sha->buffer, sha->buffLen); + } + + ret = wh_Client_SendRequest(ctx, WH_MESSAGE_GROUP_CRYPTO_DMA, + WC_ALGO_TYPE_HASH, + sizeof(whMessageCrypto_GenericRequestHeader) + + sizeof(*req) + sha->buffLen, + dataPtr); + return ret; +} + +int wh_Client_Sha512DmaFinalResponse(whClientContext* ctx, wc_Sha512* sha, + uint8_t* out) +{ + int ret = WH_ERROR_OK; + uint8_t* dataPtr = NULL; + whMessageCrypto_Sha2DmaResponse* resp = NULL; + uint16_t respSz = 0; + int hashType = 0; + + if (ctx == NULL || sha == NULL || out == NULL) { + return WH_ERROR_BADARGS; + } + + dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm); + if (dataPtr == NULL) { + return WH_ERROR_BADARGS; + } + + ret = wh_Client_RecvResponse(ctx, NULL, NULL, &respSz, dataPtr); + if (ret == WH_ERROR_NOTREADY) { + return ret; + } + + if (ret == WH_ERROR_OK) { + ret = + _getCryptoResponse(dataPtr, WC_HASH_TYPE_SHA512, (uint8_t**)&resp); + if (ret >= 0) { + /* keep hashtype before initialization */ + hashType = sha->hashType; + /* reset the state of the sha context (without blowing away devId + * and hashType), and copy only the digest bytes for the active + * variant */ + switch (hashType) { +#ifndef WOLFSSL_NOSHA512_224 + case WC_HASH_TYPE_SHA512_224: + memcpy(out, resp->hash, WC_SHA512_224_DIGEST_SIZE); + (void)wc_InitSha512_224_ex(sha, NULL, sha->devId); + break; +#endif +#ifndef WOLFSSL_NOSHA512_256 + case WC_HASH_TYPE_SHA512_256: + memcpy(out, resp->hash, WC_SHA512_256_DIGEST_SIZE); + (void)wc_InitSha512_256_ex(sha, NULL, sha->devId); + break; +#endif + default: + memcpy(out, resp->hash, WC_SHA512_DIGEST_SIZE); + (void)wc_InitSha512_ex(sha, NULL, sha->devId); + break; + } + } + } + return ret; +} + +int wh_Client_Sha512Dma(whClientContext* ctx, wc_Sha512* sha, const uint8_t* in, + uint32_t inLen, uint8_t* out) +{ + int ret = WH_ERROR_OK; + + if (in != NULL && inLen > 0) { + bool sent = false; + ret = wh_Client_Sha512DmaUpdateRequest(ctx, sha, in, inLen, &sent); + if (ret == WH_ERROR_OK && sent) { do { - ret = wh_Client_RecvResponse(ctx, NULL, NULL, &respSz, - (uint8_t*)dataPtr); + ret = wh_Client_Sha512DmaUpdateResponse(ctx, sha); } while (ret == WH_ERROR_NOTREADY); } - - /* Copy out the final hash value */ + } + if (ret == WH_ERROR_OK && out != NULL) { + ret = wh_Client_Sha512DmaFinalRequest(ctx, sha); if (ret == WH_ERROR_OK) { - /* Get response structure pointer, validates generic header - * rc */ - ret = _getCryptoResponse(dataPtr, WC_HASH_TYPE_SHA512, - (uint8_t**)&resp); - /* Nothing to do on success, as server will have updated the output - * hash in client memory */ + do { + ret = wh_Client_Sha512DmaFinalResponse(ctx, sha, out); + } while (ret == WH_ERROR_NOTREADY); } } - - if (in != NULL || out != NULL) { - (void)wh_Client_DmaProcessClientAddress( - ctx, (uintptr_t)sha512, (void**)&stateAddr, sizeof(*sha512), - WH_DMA_OPER_CLIENT_WRITE_POST, (whDmaFlags){0}); - (void)wh_Client_DmaProcessClientAddress( - ctx, (uintptr_t)in, (void**)&inAddr, inLen, - WH_DMA_OPER_CLIENT_READ_POST, (whDmaFlags){0}); - (void)wh_Client_DmaProcessClientAddress( - ctx, (uintptr_t)out, (void**)&outAddr, WC_SHA512_DIGEST_SIZE, - WH_DMA_OPER_CLIENT_WRITE_POST, (whDmaFlags){0}); - } return ret; } #endif /* WOLFHSM_CFG_DMA */ diff --git a/src/wh_message_crypto.c b/src/wh_message_crypto.c index 83289caf1..a4e6d697b 100644 --- a/src/wh_message_crypto.c +++ b/src/wh_message_crypto.c @@ -627,7 +627,9 @@ int wh_MessageCrypto_TranslateEd25519VerifyResponse( return 0; } -/* SHA256 Request translation */ +/* SHA256 Request translation. Only the fixed-size header is translated; any + * trailing variable-length input bytes (uint8_t in[inSz]) are raw bytes that + * do not need endian translation. */ int wh_MessageCrypto_TranslateSha256Request( uint16_t magic, const whMessageCrypto_Sha256Request* src, whMessageCrypto_Sha256Request* dest) @@ -643,16 +645,14 @@ int wh_MessageCrypto_TranslateSha256Request( sizeof(src->resumeState.hash)); } WH_T32(magic, dest, src, isLastBlock); - WH_T32(magic, dest, src, lastBlockLen); - /* Input block is just a byte array, no translation needed */ - if (src != dest) { - memcpy(dest->inBlock, src->inBlock, sizeof(src->inBlock)); - } + WH_T32(magic, dest, src, inSz); return 0; } #if defined(WOLFSSL_SHA512) || defined(WOLFSSL_SHA384) -/* SHA512 Request translation */ +/* SHA512 Request translation. Only the fixed-size header is translated; any + * trailing variable-length input bytes (uint8_t in[inSz]) are raw bytes that + * do not need endian translation. */ int wh_MessageCrypto_TranslateSha512Request( uint16_t magic, const whMessageCrypto_Sha512Request* src, whMessageCrypto_Sha512Request* dest) @@ -669,11 +669,7 @@ int wh_MessageCrypto_TranslateSha512Request( sizeof(src->resumeState.hash)); } WH_T32(magic, dest, src, isLastBlock); - WH_T32(magic, dest, src, lastBlockLen); - /* Input block is just a byte array, no translation needed */ - if (src != dest) { - memcpy(dest->inBlock, src->inBlock, sizeof(src->inBlock)); - } + WH_T32(magic, dest, src, inSz); return 0; } #endif /* WOLFSSL_SHA512 || WOLFSSL_SHA384 */ @@ -865,10 +861,10 @@ static int wh_MessageCrypto_TranslateDmaAddrStatus( &dest->badAddr); } -/* SHA224 DMA Request translation */ -int wh_MessageCrypto_TranslateSha2DmaRequest( - uint16_t magic, const whMessageCrypto_Sha2DmaRequest* src, - whMessageCrypto_Sha2DmaRequest* dest) +/* SHA256/SHA224 DMA Request translation */ +int wh_MessageCrypto_TranslateSha256DmaRequest( + uint16_t magic, const whMessageCrypto_Sha256DmaRequest* src, + whMessageCrypto_Sha256DmaRequest* dest) { int ret; @@ -876,24 +872,51 @@ int wh_MessageCrypto_TranslateSha2DmaRequest( return WH_ERROR_BADARGS; } - WH_T64(magic, dest, src, finalize); + WH_T32(magic, dest, src, resumeState.hiLen); + WH_T32(magic, dest, src, resumeState.loLen); + if (src != dest) { + memcpy(dest->resumeState.hash, src->resumeState.hash, + sizeof(src->resumeState.hash)); + } ret = wh_MessageCrypto_TranslateDmaBuffer(magic, &src->input, &dest->input); if (ret != 0) { return ret; } - ret = wh_MessageCrypto_TranslateDmaBuffer(magic, &src->state, &dest->state); - if (ret != 0) { - return ret; + WH_T32(magic, dest, src, isLastBlock); + WH_T32(magic, dest, src, inSz); + + return 0; +} + +/* SHA512/SHA384 DMA Request translation */ +int wh_MessageCrypto_TranslateSha512DmaRequest( + uint16_t magic, const whMessageCrypto_Sha512DmaRequest* src, + whMessageCrypto_Sha512DmaRequest* dest) +{ + int ret; + + if ((src == NULL) || (dest == NULL)) { + return WH_ERROR_BADARGS; } - ret = - wh_MessageCrypto_TranslateDmaBuffer(magic, &src->output, &dest->output); + WH_T32(magic, dest, src, resumeState.hiLen); + WH_T32(magic, dest, src, resumeState.loLen); + if (src != dest) { + memcpy(dest->resumeState.hash, src->resumeState.hash, + sizeof(src->resumeState.hash)); + } + WH_T32(magic, dest, src, resumeState.hashType); + + ret = wh_MessageCrypto_TranslateDmaBuffer(magic, &src->input, &dest->input); if (ret != 0) { return ret; } + WH_T32(magic, dest, src, isLastBlock); + WH_T32(magic, dest, src, inSz); + return 0; } @@ -905,6 +928,14 @@ int wh_MessageCrypto_TranslateSha2DmaResponse( if ((src == NULL) || (dest == NULL)) { return WH_ERROR_BADARGS; } + + WH_T32(magic, dest, src, hiLen); + WH_T32(magic, dest, src, loLen); + if (src != dest) { + memcpy(dest->hash, src->hash, sizeof(src->hash)); + } + WH_T32(magic, dest, src, hashType); + return wh_MessageCrypto_TranslateDmaAddrStatus(magic, &src->dmaAddrStatus, &dest->dmaAddrStatus); } diff --git a/src/wh_server_crypto.c b/src/wh_server_crypto.c index 3cad89146..712d2fd19 100644 --- a/src/wh_server_crypto.c +++ b/src/wh_server_crypto.c @@ -3669,11 +3669,13 @@ static int _HandleSha256(whServerContext* ctx, uint16_t magic, int devId, const void* cryptoDataIn, uint16_t inSize, void* cryptoDataOut, uint16_t* outSize) { - int ret = 0; - wc_Sha256 sha256[1]; + int ret = 0; + wc_Sha256 sha256[1]; + whMessageCrypto_Sha256Request req; + whMessageCrypto_Sha2Response res = {0}; + const uint8_t* inData; + (void)ctx; - whMessageCrypto_Sha256Request req; - whMessageCrypto_Sha2Response res = {0}; /* Validate minimum size */ if (inSize < sizeof(whMessageCrypto_Sha256Request)) { @@ -3685,39 +3687,57 @@ static int _HandleSha256(whServerContext* ctx, uint16_t magic, int devId, if (ret != 0) { return ret; } + + /* Validate inSz fits inside the received payload */ + if ((uint32_t)req.inSz > + (uint32_t)(inSize - sizeof(whMessageCrypto_Sha256Request))) { + return WH_ERROR_BADARGS; + } + /* Non-final updates must be multiples of WC_SHA256_BLOCK_SIZE */ + if (!req.isLastBlock && (req.inSz % WC_SHA256_BLOCK_SIZE) != 0) { + return WH_ERROR_BADARGS; + } + /* Final block must be strictly less than one block (client always buffers + * full blocks and sends only the partial tail on finalize). */ + if (req.isLastBlock && req.inSz >= WC_SHA256_BLOCK_SIZE) { + return WH_ERROR_BADARGS; + } + + inData = + (const uint8_t*)cryptoDataIn + sizeof(whMessageCrypto_Sha256Request); + /* always init sha2 struct with the devid */ ret = wc_InitSha256_ex(sha256, NULL, devId); if (ret != 0) { return ret; } - /* restore the hash state from the client */ + + /* Restore intermediate state from client; server is stateless otherwise. + * The partial-block buffer lives only on the client. */ memcpy(sha256->digest, req.resumeState.hash, WC_SHA256_DIGEST_SIZE); - sha256->loLen = req.resumeState.loLen; - sha256->hiLen = req.resumeState.hiLen; + sha256->loLen = req.resumeState.loLen; + sha256->hiLen = req.resumeState.hiLen; + sha256->buffLen = 0; - if (req.isLastBlock) { - /* Validate lastBlockLen to prevent potential buffer overread */ - if ((unsigned int)req.lastBlockLen > WC_SHA256_BLOCK_SIZE) { - return WH_ERROR_BADARGS; - } - /* wolfCrypt (or cryptoCb) is responsible for last block padding */ - if (ret == 0) { - ret = wc_Sha256Update(sha256, req.inBlock, req.lastBlockLen); - } - if (ret == 0) { - ret = wc_Sha256Final(sha256, res.hash); - } + if (req.inSz > 0) { + ret = wc_Sha256Update(sha256, inData, req.inSz); } - else { - /* Client always sends full blocks, unless it's the last block */ - if (ret == 0) { - ret = wc_Sha256Update(sha256, req.inBlock, WC_SHA256_BLOCK_SIZE); + if (ret == 0) { + if (req.isLastBlock) { + /* wolfCrypt is responsible for last block padding */ + ret = wc_Sha256Final(sha256, res.hash); } - /* Send the hash state back to the client */ - if (ret == 0) { - memcpy(res.hash, sha256->digest, WC_SHA256_DIGEST_SIZE); - res.loLen = sha256->loLen; - res.hiLen = sha256->hiLen; + else { + /* Post-condition: non-final updates MUST leave buffLen == 0, + * since we validated inSz is a multiple of the block size. */ + if (sha256->buffLen != 0) { + ret = WH_ERROR_ABORTED; + } + else { + memcpy(res.hash, sha256->digest, WC_SHA256_DIGEST_SIZE); + res.loLen = sha256->loLen; + res.hiLen = sha256->hiLen; + } } } @@ -3741,9 +3761,11 @@ static int _HandleSha224(whServerContext* ctx, uint16_t magic, int devId, { int ret = 0; wc_Sha224 sha224[1]; - (void)ctx; whMessageCrypto_Sha256Request req; whMessageCrypto_Sha2Response res = {0}; + const uint8_t* inData; + + (void)ctx; /* Validate minimum size */ if (inSize < sizeof(whMessageCrypto_Sha256Request)) { @@ -3756,10 +3778,23 @@ static int _HandleSha224(whServerContext* ctx, uint16_t magic, int devId, return ret; } - /* Validate lastBlockLen is reasonable */ - if (req.isLastBlock && req.lastBlockLen > WC_SHA224_BLOCK_SIZE) { + /* Validate inSz fits inside the received payload */ + if ((uint32_t)req.inSz > + (uint32_t)(inSize - sizeof(whMessageCrypto_Sha256Request))) { return WH_ERROR_BADARGS; } + /* Non-final updates must be multiples of WC_SHA224_BLOCK_SIZE */ + if (!req.isLastBlock && (req.inSz % WC_SHA224_BLOCK_SIZE) != 0) { + return WH_ERROR_BADARGS; + } + /* Final block must be strictly less than one block */ + if (req.isLastBlock && req.inSz >= WC_SHA224_BLOCK_SIZE) { + return WH_ERROR_BADARGS; + } + + inData = + (const uint8_t*)cryptoDataIn + sizeof(whMessageCrypto_Sha256Request); + ret = wc_InitSha224_ex(sha224, NULL, devId); if (ret != 0) { return ret; @@ -3768,31 +3803,30 @@ static int _HandleSha224(whServerContext* ctx, uint16_t magic, int devId, * intermediate hash data. */ memcpy(sha224->digest, req.resumeState.hash, WC_SHA256_DIGEST_SIZE); - sha224->loLen = req.resumeState.loLen; - sha224->hiLen = req.resumeState.hiLen; + sha224->loLen = req.resumeState.loLen; + sha224->hiLen = req.resumeState.hiLen; + sha224->buffLen = 0; - if (req.isLastBlock) { - /* wolfCrypt (or cryptoCb) is responsible for last block padding */ - if (ret == 0) { - ret = wc_Sha224Update(sha224, req.inBlock, req.lastBlockLen); - } - if (ret == 0) { - ret = wc_Sha224Final(sha224, res.hash); - } + if (req.inSz > 0) { + ret = wc_Sha224Update(sha224, inData, req.inSz); } - else { - /* Client always sends full blocks, unless it's the last block */ - if (ret == 0) { - ret = wc_Sha224Update(sha224, req.inBlock, WC_SHA224_BLOCK_SIZE); + if (ret == 0) { + if (req.isLastBlock) { + /* wolfCrypt is responsible for last block padding */ + ret = wc_Sha224Final(sha224, res.hash); } - /* Send the hash state back to the client */ - if (ret == 0) { - /* return back the digest which has the same length of sha256 - * for further operation - */ - memcpy(res.hash, sha224->digest, WC_SHA256_DIGEST_SIZE); - res.loLen = sha224->loLen; - res.hiLen = sha224->hiLen; + else { + /* Post-condition: non-final updates MUST leave buffLen == 0 */ + if (sha224->buffLen != 0) { + ret = WH_ERROR_ABORTED; + } + else { + /* return back the digest which has the same length of sha256 + * for further operation */ + memcpy(res.hash, sha224->digest, WC_SHA256_DIGEST_SIZE); + res.loLen = sha224->loLen; + res.hiLen = sha224->hiLen; + } } } @@ -3816,9 +3850,11 @@ static int _HandleSha384(whServerContext* ctx, uint16_t magic, int devId, { int ret = 0; wc_Sha384 sha384[1]; - (void)ctx; whMessageCrypto_Sha512Request req; whMessageCrypto_Sha2Response res = {0}; + const uint8_t* inData; + + (void)ctx; /* Validate minimum size */ if (inSize < sizeof(whMessageCrypto_Sha512Request)) { @@ -3831,10 +3867,23 @@ static int _HandleSha384(whServerContext* ctx, uint16_t magic, int devId, return ret; } - /* Validate lastBlockLen is reasonable */ - if (req.isLastBlock && req.lastBlockLen > WC_SHA384_BLOCK_SIZE) { + /* Validate inSz fits inside the received payload */ + if ((uint32_t)req.inSz > + (uint32_t)(inSize - sizeof(whMessageCrypto_Sha512Request))) { return WH_ERROR_BADARGS; } + /* Non-final updates must be multiples of WC_SHA384_BLOCK_SIZE */ + if (!req.isLastBlock && (req.inSz % WC_SHA384_BLOCK_SIZE) != 0) { + return WH_ERROR_BADARGS; + } + /* Final block must be strictly less than one block (client always buffers + * full blocks and sends only the partial tail on finalize). */ + if (req.isLastBlock && req.inSz >= WC_SHA384_BLOCK_SIZE) { + return WH_ERROR_BADARGS; + } + + inData = + (const uint8_t*)cryptoDataIn + sizeof(whMessageCrypto_Sha512Request); /* init sha2 struct with the devid */ ret = wc_InitSha384_ex(sha384, NULL, devId); @@ -3842,36 +3891,36 @@ static int _HandleSha384(whServerContext* ctx, uint16_t magic, int devId, return ret; } - /* restore the hash state from the client */ - /* sha384 is a part of sha512. It expects to have sha512 digest - * size of intermediate hash data. - */ + /* Restore intermediate state from client; server is stateless otherwise. + * The partial-block buffer lives only on the client. + * sha384 is a part of sha512. It expects to have sha512 digest + * size of intermediate hash data. */ memcpy(sha384->digest, req.resumeState.hash, WC_SHA512_DIGEST_SIZE); - sha384->loLen = req.resumeState.loLen; - sha384->hiLen = req.resumeState.hiLen; + sha384->loLen = req.resumeState.loLen; + sha384->hiLen = req.resumeState.hiLen; + sha384->buffLen = 0; - if (req.isLastBlock) { - /* wolfCrypt (or cryptoCb) is responsible for last block padding */ - if (ret == 0) { - ret = wc_Sha384Update(sha384, req.inBlock, req.lastBlockLen); - } - if (ret == 0) { - ret = wc_Sha384Final(sha384, res.hash); - } + if (req.inSz > 0) { + ret = wc_Sha384Update(sha384, inData, req.inSz); } - else { - /* Client always sends full blocks, unless it's the last block */ - if (ret == 0) { - ret = wc_Sha384Update(sha384, req.inBlock, WC_SHA384_BLOCK_SIZE); + if (ret == 0) { + if (req.isLastBlock) { + /* wolfCrypt is responsible for last block padding */ + ret = wc_Sha384Final(sha384, res.hash); } - /* Send the hash state back to the client */ - if (ret == 0) { - /* return back the digest which has the same length of sha512 - * for further operation - */ - memcpy(res.hash, sha384->digest, WC_SHA512_DIGEST_SIZE); - res.loLen = sha384->loLen; - res.hiLen = sha384->hiLen; + else { + /* Post-condition: non-final updates MUST leave buffLen == 0, + * since we validated inSz is a multiple of the block size. */ + if (sha384->buffLen != 0) { + ret = WH_ERROR_ABORTED; + } + else { + /* return back the digest which has the same length of sha512 + * for further operation */ + memcpy(res.hash, sha384->digest, WC_SHA512_DIGEST_SIZE); + res.loLen = sha384->loLen; + res.hiLen = sha384->hiLen; + } } } @@ -3894,10 +3943,12 @@ static int _HandleSha512(whServerContext* ctx, uint16_t magic, int devId, { int ret = 0; wc_Sha512 sha512[1]; - (void)ctx; whMessageCrypto_Sha512Request req; - whMessageCrypto_Sha2Response res = {0}; + whMessageCrypto_Sha2Response res = {0}; int hashType = WC_HASH_TYPE_SHA512; + const uint8_t* inData; + + (void)ctx; /* Validate minimum size */ if (inSize < sizeof(whMessageCrypto_Sha512Request)) { @@ -3910,10 +3961,23 @@ static int _HandleSha512(whServerContext* ctx, uint16_t magic, int devId, return ret; } - /* Validate lastBlockLen is reasonable */ - if (req.isLastBlock && req.lastBlockLen > WC_SHA512_BLOCK_SIZE) { + /* Validate inSz fits inside the received payload */ + if ((uint32_t)req.inSz > + (uint32_t)(inSize - sizeof(whMessageCrypto_Sha512Request))) { + return WH_ERROR_BADARGS; + } + /* Non-final updates must be multiples of WC_SHA512_BLOCK_SIZE */ + if (!req.isLastBlock && (req.inSz % WC_SHA512_BLOCK_SIZE) != 0) { + return WH_ERROR_BADARGS; + } + /* Final block must be strictly less than one block */ + if (req.isLastBlock && req.inSz >= WC_SHA512_BLOCK_SIZE) { return WH_ERROR_BADARGS; } + + inData = + (const uint8_t*)cryptoDataIn + sizeof(whMessageCrypto_Sha512Request); + /* init sha2 struct with devid */ hashType = req.resumeState.hashType; switch (hashType) { @@ -3935,16 +3999,19 @@ static int _HandleSha512(whServerContext* ctx, uint16_t magic, int devId, return ret; } + /* Restore intermediate state from client; server is stateless otherwise. + * The partial-block buffer lives only on the client. */ memcpy(sha512->digest, req.resumeState.hash, WC_SHA512_DIGEST_SIZE); - sha512->loLen = req.resumeState.loLen; - sha512->hiLen = req.resumeState.hiLen; + sha512->loLen = req.resumeState.loLen; + sha512->hiLen = req.resumeState.hiLen; + sha512->buffLen = 0; - if (req.isLastBlock) { - /* wolfCrypt (or cryptoCb) is responsible for last block padding */ - if (ret == 0) { - ret = wc_Sha512Update(sha512, req.inBlock, req.lastBlockLen); - } - if (ret == 0) { + if (req.inSz > 0) { + ret = wc_Sha512Update(sha512, inData, req.inSz); + } + if (ret == 0) { + if (req.isLastBlock) { + /* wolfCrypt is responsible for last block padding */ switch (hashType) { #ifndef WOLFSSL_NOSHA512_224 case WC_HASH_TYPE_SHA512_224: @@ -3961,17 +4028,17 @@ static int _HandleSha512(whServerContext* ctx, uint16_t magic, int devId, break; } } - } - else { - /* Client always sends full blocks, unless it's the last block */ - if (ret == 0) { - ret = wc_Sha512Update(sha512, req.inBlock, WC_SHA512_BLOCK_SIZE); - } - /* Send the hash state back to the client */ - if (ret == 0) { - memcpy(res.hash, sha512->digest, WC_SHA512_DIGEST_SIZE); - res.loLen = sha512->loLen; - res.hiLen = sha512->hiLen; + else { + /* Post-condition: non-final updates MUST leave buffLen == 0, + * since we validated inSz is a multiple of the block size. */ + if (sha512->buffLen != 0) { + ret = WH_ERROR_ABORTED; + } + else { + memcpy(res.hash, sha512->digest, WC_SHA512_DIGEST_SIZE); + res.loLen = sha512->loLen; + res.hiLen = sha512->hiLen; + } } } @@ -4753,123 +4820,100 @@ static int _HandleSha256Dma(whServerContext* ctx, uint16_t magic, int devId, { (void)seq; - int ret = 0; - whMessageCrypto_Sha2DmaRequest req; - whMessageCrypto_Sha2DmaResponse res; - wc_Sha256 sha256[1]; - int clientDevId; + int ret = 0; + int preOk = 0; + whMessageCrypto_Sha256DmaRequest req; + whMessageCrypto_Sha2DmaResponse res = {0}; + wc_Sha256 sha256[1]; + const uint8_t* inlineData; + void* inAddr = NULL; - if (inSize < sizeof(whMessageCrypto_Sha2DmaRequest)) { + if (inSize < sizeof(whMessageCrypto_Sha256DmaRequest)) { return WH_ERROR_BADARGS; } - /* Translate the request */ - ret = wh_MessageCrypto_TranslateSha2DmaRequest( - magic, (whMessageCrypto_Sha2DmaRequest*)cryptoDataIn, &req); + ret = wh_MessageCrypto_TranslateSha256DmaRequest( + magic, (const whMessageCrypto_Sha256DmaRequest*)cryptoDataIn, &req); if (ret != WH_ERROR_OK) { return ret; } - /* Ensure state sizes are the same */ - if (req.state.sz != sizeof(*sha256)) { - res.dmaAddrStatus.badAddr = req.state; - ret = WH_ERROR_BADARGS; + /* Validate inSz fits inside the received payload */ + if ((uint32_t)req.inSz > + (uint32_t)(inSize - sizeof(whMessageCrypto_Sha256DmaRequest))) { + return WH_ERROR_BADARGS; } - - if (ret == WH_ERROR_OK) { - /* Copy the SHA256 context from client address space */ - ret = whServerDma_CopyFromClient(ctx, sha256, req.state.addr, - req.state.sz, (whServerDmaFlags){0}); - if (ret != WH_ERROR_OK) { - res.dmaAddrStatus.badAddr = req.state; - } - else { - /* Validate buffLen from untrusted context */ - if (sha256->buffLen >= WC_SHA256_BLOCK_SIZE) { - ret = WH_ERROR_BADARGS; - } - else { - /* Save the client devId to be restored later, when the context - * is copied back into client memory. */ - clientDevId = sha256->devId; - /* overwrite the devId to that of the server for local crypto */ - sha256->devId = devId; - } - } + /* Non-final: inline and DMA input must be multiples of block size */ + if (!req.isLastBlock && ((req.inSz % WC_SHA256_BLOCK_SIZE) != 0 || + (req.input.sz % WC_SHA256_BLOCK_SIZE) != 0)) { + return WH_ERROR_BADARGS; + } + /* Final: inline data must be less than one block, no DMA input */ + if (req.isLastBlock && + (req.inSz >= WC_SHA256_BLOCK_SIZE || req.input.sz != 0)) { + return WH_ERROR_BADARGS; } - /* TODO: perhaps we should sequentially update and finalize (need individual - * flags as 0x0 could be a valid address?) just to future-proof, even though - * sha256 cryptoCb doesn't currently have a one-shot*/ - - /* If finalize requested, finalize the SHA256 operation, wrapping client - * address accesses with the associated DMA address processing */ - if (ret == WH_ERROR_OK && req.finalize) { - void* outAddr; - ret = wh_Server_DmaProcessClientAddress( - ctx, req.output.addr, &outAddr, req.output.sz, - WH_DMA_OPER_CLIENT_WRITE_PRE, (whServerDmaFlags){0}); + inlineData = + (const uint8_t*)cryptoDataIn + sizeof(whMessageCrypto_Sha256DmaRequest); - /* Finalize the SHA256 operation */ - if (ret == WH_ERROR_OK) { - WH_DEBUG_SERVER_VERBOSE(" wc_Sha256Final: outAddr=%p\n", outAddr); - ret = wc_Sha256Final(sha256, outAddr); - } + ret = wc_InitSha256_ex(sha256, NULL, devId); + if (ret != 0) { + return ret; + } - if (ret == WH_ERROR_OK) { - ret = wh_Server_DmaProcessClientAddress( - ctx, req.output.addr, &outAddr, req.output.sz, - WH_DMA_OPER_CLIENT_WRITE_POST, (whServerDmaFlags){0}); - } + /* Restore intermediate state from request */ + memcpy(sha256->digest, req.resumeState.hash, WC_SHA256_DIGEST_SIZE); + sha256->loLen = req.resumeState.loLen; + sha256->hiLen = req.resumeState.hiLen; + sha256->buffLen = 0; - if (ret == WH_ERROR_ACCESS) { - res.dmaAddrStatus.badAddr = req.output; - } + /* Process inline trailing data (assembled first block or final tail) */ + if (ret == 0 && req.inSz > 0) { + ret = wc_Sha256Update(sha256, inlineData, req.inSz); } - else if (ret == WH_ERROR_OK) { - /* Update requested, update the SHA256 operation, wrapping client - * address accesses with the associated DMA address processing */ - void* inAddr; + + /* Process DMA input (whole blocks from Update) */ + if (ret == 0 && req.input.sz > 0) { ret = wh_Server_DmaProcessClientAddress( ctx, req.input.addr, &inAddr, req.input.sz, WH_DMA_OPER_CLIENT_READ_PRE, (whServerDmaFlags){0}); - - /* Update the SHA256 operation */ - if (ret == WH_ERROR_OK) { - WH_DEBUG_SERVER_VERBOSE(" wc_Sha256Update: inAddr=%p, sz=%llu\n", inAddr, - (long long unsigned int)req.input.sz); - ret = wc_Sha256Update(sha256, inAddr, req.input.sz); - } - if (ret == WH_ERROR_OK) { - ret = wh_Server_DmaProcessClientAddress( - ctx, req.input.addr, &inAddr, req.input.sz, - WH_DMA_OPER_CLIENT_READ_POST, (whServerDmaFlags){0}); + preOk = 1; + ret = wc_Sha256Update(sha256, inAddr, req.input.sz); } - if (ret == WH_ERROR_ACCESS) { res.dmaAddrStatus.badAddr = req.input; } } + /* Pair every successful PRE with a POST so DMA callbacks can release any + * resources they acquired, even if the Update failed. */ + if (preOk) { + (void)wh_Server_DmaProcessClientAddress( + ctx, req.input.addr, &inAddr, req.input.sz, + WH_DMA_OPER_CLIENT_READ_POST, (whServerDmaFlags){0}); + } - if (ret == WH_ERROR_OK) { - /* Reset the devId in the local context to ensure it isn't copied back - * to client memory */ - sha256->devId = clientDevId; - /* Copy SHA256 context back into client memory */ - ret = whServerDma_CopyToClient(ctx, req.state.addr, sha256, - req.state.sz, (whServerDmaFlags){0}); - if (ret != WH_ERROR_OK) { - res.dmaAddrStatus.badAddr = req.state; + if (ret == 0) { + if (req.isLastBlock) { + ret = wc_Sha256Final(sha256, res.hash); + } + else { + if (sha256->buffLen != 0) { + ret = WH_ERROR_ABORTED; + } + else { + memcpy(res.hash, sha256->digest, WC_SHA256_DIGEST_SIZE); + res.loLen = sha256->loLen; + res.hiLen = sha256->hiLen; + } } } - /* Translate the response */ (void)wh_MessageCrypto_TranslateSha2DmaResponse( magic, &res, (whMessageCrypto_Sha2DmaResponse*)cryptoDataOut); *outSize = sizeof(res); - /* return value populates rc in response message */ return ret; } #endif /* ! NO_SHA256 */ @@ -4881,123 +4925,97 @@ static int _HandleSha224Dma(whServerContext* ctx, uint16_t magic, int devId, uint16_t* outSize) { (void)seq; - int ret = 0; - whMessageCrypto_Sha2DmaRequest req; - whMessageCrypto_Sha2DmaResponse res; - wc_Sha224 sha224[1]; - int clientDevId; - - if (inSize < sizeof(whMessageCrypto_Sha2DmaRequest)) { + int ret = 0; + int preOk = 0; + whMessageCrypto_Sha256DmaRequest req; + whMessageCrypto_Sha2DmaResponse res = {0}; + wc_Sha224 sha224[1]; + const uint8_t* inlineData; + void* inAddr = NULL; + + if (inSize < sizeof(whMessageCrypto_Sha256DmaRequest)) { return WH_ERROR_BADARGS; } - /* Translate the request */ - ret = wh_MessageCrypto_TranslateSha2DmaRequest( - magic, (whMessageCrypto_Sha2DmaRequest*)cryptoDataIn, &req); + ret = wh_MessageCrypto_TranslateSha256DmaRequest( + magic, (const whMessageCrypto_Sha256DmaRequest*)cryptoDataIn, &req); if (ret != WH_ERROR_OK) { return ret; } - /* Ensure state sizes are the same */ - if (req.state.sz != sizeof(*sha224)) { - res.dmaAddrStatus.badAddr = req.state; - ret = WH_ERROR_BADARGS; + if ((uint32_t)req.inSz > + (uint32_t)(inSize - sizeof(whMessageCrypto_Sha256DmaRequest))) { + return WH_ERROR_BADARGS; } - - if (ret == WH_ERROR_OK) { - /* Copy the SHA224 context from client address space */ - ret = whServerDma_CopyFromClient(ctx, sha224, req.state.addr, - req.state.sz, (whServerDmaFlags){0}); - if (ret != WH_ERROR_OK) { - res.dmaAddrStatus.badAddr = req.state; - } - else { - /* Validate buffLen from untrusted context */ - if (sha224->buffLen >= WC_SHA224_BLOCK_SIZE) { - ret = WH_ERROR_BADARGS; - } - else { - /* Save the client devId to be restored later, when the context - * is copied back into client memory. */ - clientDevId = sha224->devId; - /* overwrite the devId to that of the server for local crypto */ - sha224->devId = devId; - } - } + /* Non-final: inline and DMA input must be multiples of block size */ + if (!req.isLastBlock && ((req.inSz % WC_SHA224_BLOCK_SIZE) != 0 || + (req.input.sz % WC_SHA224_BLOCK_SIZE) != 0)) { + return WH_ERROR_BADARGS; + } + /* Final: inline data must be less than one block, no DMA input */ + if (req.isLastBlock && + (req.inSz >= WC_SHA224_BLOCK_SIZE || req.input.sz != 0)) { + return WH_ERROR_BADARGS; } - /* TODO: perhaps we should sequentially update and finalize (need individual - * flags as 0x0 could be a valid address?) just to future-proof, even though - * sha224 cryptoCb doesn't currently have a one-shot*/ - - /* If finalize requested, finalize the SHA224 operation, wrapping client - * address accesses with the associated DMA address processing */ - if (ret == WH_ERROR_OK && req.finalize) { - void* outAddr; - ret = wh_Server_DmaProcessClientAddress( - ctx, req.output.addr, &outAddr, req.output.sz, - WH_DMA_OPER_CLIENT_WRITE_PRE, (whServerDmaFlags){0}); + inlineData = + (const uint8_t*)cryptoDataIn + sizeof(whMessageCrypto_Sha256DmaRequest); - /* Finalize the SHA224 operation */ - if (ret == WH_ERROR_OK) { - WH_DEBUG_SERVER_VERBOSE(" wc_Sha224Final: outAddr=%p\n", outAddr); - ret = wc_Sha224Final(sha224, outAddr); - } + ret = wc_InitSha224_ex(sha224, NULL, devId); + if (ret != 0) { + return ret; + } - if (ret == WH_ERROR_OK) { - ret = wh_Server_DmaProcessClientAddress( - ctx, req.output.addr, &outAddr, req.output.sz, - WH_DMA_OPER_CLIENT_WRITE_POST, (whServerDmaFlags){0}); - } + /* SHA224 shares SHA256's internal 32-byte digest state */ + memcpy(sha224->digest, req.resumeState.hash, WC_SHA256_DIGEST_SIZE); + sha224->loLen = req.resumeState.loLen; + sha224->hiLen = req.resumeState.hiLen; + sha224->buffLen = 0; - if (ret == WH_ERROR_ACCESS) { - res.dmaAddrStatus.badAddr = req.output; - } + if (ret == 0 && req.inSz > 0) { + ret = wc_Sha224Update(sha224, inlineData, req.inSz); } - else if (ret == WH_ERROR_OK) { - /* Update requested, update the SHA224 operation, wrapping client - * address accesses with the associated DMA address processing */ - void* inAddr; + + if (ret == 0 && req.input.sz > 0) { ret = wh_Server_DmaProcessClientAddress( ctx, req.input.addr, &inAddr, req.input.sz, WH_DMA_OPER_CLIENT_READ_PRE, (whServerDmaFlags){0}); - - /* Update the SHA224 operation */ if (ret == WH_ERROR_OK) { - WH_DEBUG_SERVER_VERBOSE(" wc_Sha224Update: inAddr=%p, sz=%llu\n", inAddr, - (long long unsigned int)req.input.sz); - ret = wc_Sha224Update(sha224, inAddr, req.input.sz); + preOk = 1; + ret = wc_Sha224Update(sha224, inAddr, req.input.sz); } - - if (ret == WH_ERROR_OK) { - ret = wh_Server_DmaProcessClientAddress( - ctx, req.input.addr, &inAddr, req.input.sz, - WH_DMA_OPER_CLIENT_READ_POST, (whServerDmaFlags){0}); - } - if (ret == WH_ERROR_ACCESS) { res.dmaAddrStatus.badAddr = req.input; } } + /* Pair every successful PRE with a POST so DMA callbacks can release any + * resources they acquired, even if the Update failed. */ + if (preOk) { + (void)wh_Server_DmaProcessClientAddress( + ctx, req.input.addr, &inAddr, req.input.sz, + WH_DMA_OPER_CLIENT_READ_POST, (whServerDmaFlags){0}); + } - if (ret == WH_ERROR_OK) { - /* Reset the devId in the local context to ensure it isn't copied back - * to client memory */ - sha224->devId = clientDevId; - /* Copy SHA224 context back into client memory */ - ret = whServerDma_CopyToClient(ctx, req.state.addr, sha224, - req.state.sz, (whServerDmaFlags){0}); - if (ret != WH_ERROR_OK) { - res.dmaAddrStatus.badAddr = req.state; + if (ret == 0) { + if (req.isLastBlock) { + ret = wc_Sha224Final(sha224, res.hash); + } + else { + if (sha224->buffLen != 0) { + ret = WH_ERROR_ABORTED; + } + else { + memcpy(res.hash, sha224->digest, WC_SHA256_DIGEST_SIZE); + res.loLen = sha224->loLen; + res.hiLen = sha224->hiLen; + } } } - /* Translate the response */ (void)wh_MessageCrypto_TranslateSha2DmaResponse( magic, &res, (whMessageCrypto_Sha2DmaResponse*)cryptoDataOut); *outSize = sizeof(res); - /* return value populates rc in response message */ return ret; } #endif /* WOLFSSL_SHA224 */ @@ -5009,123 +5027,97 @@ static int _HandleSha384Dma(whServerContext* ctx, uint16_t magic, int devId, uint16_t* outSize) { (void)seq; - int ret = 0; - whMessageCrypto_Sha2DmaRequest req; - whMessageCrypto_Sha2DmaResponse res; - wc_Sha384 sha384[1]; - int clientDevId; - - if (inSize < sizeof(whMessageCrypto_Sha2DmaRequest)) { + int ret = 0; + int preOk = 0; + whMessageCrypto_Sha512DmaRequest req; + whMessageCrypto_Sha2DmaResponse res = {0}; + wc_Sha384 sha384[1]; + const uint8_t* inlineData; + void* inAddr = NULL; + + if (inSize < sizeof(whMessageCrypto_Sha512DmaRequest)) { return WH_ERROR_BADARGS; } - /* Translate the request */ - ret = wh_MessageCrypto_TranslateSha2DmaRequest( - magic, (whMessageCrypto_Sha2DmaRequest*)cryptoDataIn, &req); + ret = wh_MessageCrypto_TranslateSha512DmaRequest( + magic, (const whMessageCrypto_Sha512DmaRequest*)cryptoDataIn, &req); if (ret != WH_ERROR_OK) { return ret; } - /* Ensure state sizes are the same */ - if (req.state.sz != sizeof(*sha384)) { - res.dmaAddrStatus.badAddr = req.state; - ret = WH_ERROR_BADARGS; + if ((uint32_t)req.inSz > + (uint32_t)(inSize - sizeof(whMessageCrypto_Sha512DmaRequest))) { + return WH_ERROR_BADARGS; } - - if (ret == WH_ERROR_OK) { - /* Copy the SHA384 context from client address space */ - ret = whServerDma_CopyFromClient(ctx, sha384, req.state.addr, - req.state.sz, (whServerDmaFlags){0}); - if (ret != WH_ERROR_OK) { - res.dmaAddrStatus.badAddr = req.state; - } - else { - /* Validate buffLen from untrusted context */ - if (sha384->buffLen >= WC_SHA384_BLOCK_SIZE) { - ret = WH_ERROR_BADARGS; - } - else { - /* Save the client devId to be restored later, when the context - * is copied back into client memory. */ - clientDevId = sha384->devId; - /* overwrite the devId to that of the server for local crypto */ - sha384->devId = devId; - } - } + /* Non-final: inline and DMA input must be multiples of block size */ + if (!req.isLastBlock && ((req.inSz % WC_SHA384_BLOCK_SIZE) != 0 || + (req.input.sz % WC_SHA384_BLOCK_SIZE) != 0)) { + return WH_ERROR_BADARGS; + } + /* Final: inline data must be less than one block, no DMA input */ + if (req.isLastBlock && + (req.inSz >= WC_SHA384_BLOCK_SIZE || req.input.sz != 0)) { + return WH_ERROR_BADARGS; } - /* TODO: perhaps we should sequentially update and finalize (need individual - * flags as 0x0 could be a valid address?) just to future-proof, even though - * sha384 cryptoCb doesn't currently have a one-shot*/ - - /* If finalize requested, finalize the SHA384 operation, wrapping client - * address accesses with the associated DMA address processing */ - if (ret == WH_ERROR_OK && req.finalize) { - void* outAddr; - ret = wh_Server_DmaProcessClientAddress( - ctx, req.output.addr, &outAddr, req.output.sz, - WH_DMA_OPER_CLIENT_WRITE_PRE, (whServerDmaFlags){0}); + inlineData = + (const uint8_t*)cryptoDataIn + sizeof(whMessageCrypto_Sha512DmaRequest); - /* Finalize the SHA384 operation */ - if (ret == WH_ERROR_OK) { - WH_DEBUG_SERVER_VERBOSE(" wc_Sha384Final: outAddr=%p\n", outAddr); - ret = wc_Sha384Final(sha384, outAddr); - } + ret = wc_InitSha384_ex(sha384, NULL, devId); + if (ret != 0) { + return ret; + } - if (ret == WH_ERROR_OK) { - ret = wh_Server_DmaProcessClientAddress( - ctx, req.output.addr, &outAddr, req.output.sz, - WH_DMA_OPER_CLIENT_WRITE_POST, (whServerDmaFlags){0}); - } + /* SHA384 shares SHA512's internal 64-byte digest state */ + memcpy(sha384->digest, req.resumeState.hash, WC_SHA512_DIGEST_SIZE); + sha384->loLen = req.resumeState.loLen; + sha384->hiLen = req.resumeState.hiLen; + sha384->buffLen = 0; - if (ret == WH_ERROR_ACCESS) { - res.dmaAddrStatus.badAddr = req.output; - } + if (ret == 0 && req.inSz > 0) { + ret = wc_Sha384Update(sha384, inlineData, req.inSz); } - else if (ret == WH_ERROR_OK) { - /* Update requested, update the SHA384 operation, wrapping client - * address accesses with the associated DMA address processing */ - void* inAddr; + + if (ret == 0 && req.input.sz > 0) { ret = wh_Server_DmaProcessClientAddress( ctx, req.input.addr, &inAddr, req.input.sz, WH_DMA_OPER_CLIENT_READ_PRE, (whServerDmaFlags){0}); - - /* Update the SHA384 operation */ - if (ret == WH_ERROR_OK) { - WH_DEBUG_SERVER_VERBOSE(" wc_Sha384Update: inAddr=%p, sz=%llu\n", inAddr, - (long long unsigned int)req.input.sz); - ret = wc_Sha384Update(sha384, inAddr, req.input.sz); - } - if (ret == WH_ERROR_OK) { - ret = wh_Server_DmaProcessClientAddress( - ctx, req.input.addr, &inAddr, req.input.sz, - WH_DMA_OPER_CLIENT_READ_POST, (whServerDmaFlags){0}); + preOk = 1; + ret = wc_Sha384Update(sha384, inAddr, req.input.sz); } - if (ret == WH_ERROR_ACCESS) { res.dmaAddrStatus.badAddr = req.input; } } + /* Pair every successful PRE with a POST so DMA callbacks can release any + * resources they acquired, even if the Update failed. */ + if (preOk) { + (void)wh_Server_DmaProcessClientAddress( + ctx, req.input.addr, &inAddr, req.input.sz, + WH_DMA_OPER_CLIENT_READ_POST, (whServerDmaFlags){0}); + } - if (ret == WH_ERROR_OK) { - /* Reset the devId in the local context to ensure it isn't copied back - * to client memory */ - sha384->devId = clientDevId; - /* Copy SHA384 context back into client memory */ - ret = whServerDma_CopyToClient(ctx, req.state.addr, sha384, - req.state.sz, (whServerDmaFlags){0}); - if (ret != WH_ERROR_OK) { - res.dmaAddrStatus.badAddr = req.state; + if (ret == 0) { + if (req.isLastBlock) { + ret = wc_Sha384Final(sha384, res.hash); + } + else { + if (sha384->buffLen != 0) { + ret = WH_ERROR_ABORTED; + } + else { + memcpy(res.hash, sha384->digest, WC_SHA512_DIGEST_SIZE); + res.loLen = sha384->loLen; + res.hiLen = sha384->hiLen; + } } } - /* Translate the response */ (void)wh_MessageCrypto_TranslateSha2DmaResponse( magic, &res, (whMessageCrypto_Sha2DmaResponse*)cryptoDataOut); *outSize = sizeof(res); - /* return value populates rc in response message */ return ret; } #endif /* WOLFSSL_SHA384 */ @@ -5137,142 +5129,128 @@ static int _HandleSha512Dma(whServerContext* ctx, uint16_t magic, int devId, uint16_t* outSize) { (void)seq; - int ret = 0; - whMessageCrypto_Sha2DmaRequest req; - whMessageCrypto_Sha2DmaResponse res; - wc_Sha512 sha512[1]; - int clientDevId; - int hashType = WC_HASH_TYPE_SHA512; - - if (inSize < sizeof(whMessageCrypto_Sha2DmaRequest)) { + int ret = 0; + int preOk = 0; + whMessageCrypto_Sha512DmaRequest req; + whMessageCrypto_Sha2DmaResponse res = {0}; + wc_Sha512 sha512[1]; + const uint8_t* inlineData; + void* inAddr = NULL; + int hashType; + + if (inSize < sizeof(whMessageCrypto_Sha512DmaRequest)) { return WH_ERROR_BADARGS; } - /* Translate the request */ - ret = wh_MessageCrypto_TranslateSha2DmaRequest( - magic, (whMessageCrypto_Sha2DmaRequest*)cryptoDataIn, &req); + ret = wh_MessageCrypto_TranslateSha512DmaRequest( + magic, (const whMessageCrypto_Sha512DmaRequest*)cryptoDataIn, &req); if (ret != WH_ERROR_OK) { return ret; } - /* Ensure state sizes are the same */ - if (req.state.sz != sizeof(*sha512)) { - res.dmaAddrStatus.badAddr = req.state; - ret = WH_ERROR_BADARGS; - } - if (ret == WH_ERROR_OK) { - /* Copy the SHA512 context from client address space */ - ret = whServerDma_CopyFromClient(ctx, sha512, req.state.addr, - req.state.sz, (whServerDmaFlags){0}); - if (ret != WH_ERROR_OK) { - res.dmaAddrStatus.badAddr = req.state; - } - else { - /* Validate buffLen from untrusted context */ - if (sha512->buffLen >= WC_SHA512_BLOCK_SIZE) { - ret = WH_ERROR_BADARGS; - } - else { - /* Save the client devId to be restored later, when the context - * is copied back into client memory. */ - clientDevId = sha512->devId; - /* overwrite the devId to that of the server for local crypto */ - sha512->devId = devId; - /* retrieve hash Type to handle 512, 512-224, or 512-256 */ - hashType = sha512->hashType; - /* Validate hashType from untrusted context */ - if (hashType != WC_HASH_TYPE_SHA512 && - hashType != WC_HASH_TYPE_SHA512_224 && - hashType != WC_HASH_TYPE_SHA512_256) { - ret = WH_ERROR_BADARGS; - } - } - } + if ((uint32_t)req.inSz > + (uint32_t)(inSize - sizeof(whMessageCrypto_Sha512DmaRequest))) { + return WH_ERROR_BADARGS; + } + /* Non-final: inline and DMA input must be multiples of block size */ + if (!req.isLastBlock && ((req.inSz % WC_SHA512_BLOCK_SIZE) != 0 || + (req.input.sz % WC_SHA512_BLOCK_SIZE) != 0)) { + return WH_ERROR_BADARGS; + } + /* Final: inline data must be less than one block, no DMA input */ + if (req.isLastBlock && + (req.inSz >= WC_SHA512_BLOCK_SIZE || req.input.sz != 0)) { + return WH_ERROR_BADARGS; } - /* TODO: perhaps we should sequentially update and finalize (need individual - * flags as 0x0 could be a valid address?) just to future-proof, even though - * sha512 cryptoCb doesn't currently have a one-shot*/ - - /* If finalize requested, finalize the SHA512 operation, wrapping client - * address accesses with the associated DMA address processing */ - if (ret == WH_ERROR_OK && req.finalize) { - void* outAddr; - ret = wh_Server_DmaProcessClientAddress( - ctx, req.output.addr, &outAddr, req.output.sz, - WH_DMA_OPER_CLIENT_WRITE_PRE, (whServerDmaFlags){0}); + inlineData = + (const uint8_t*)cryptoDataIn + sizeof(whMessageCrypto_Sha512DmaRequest); + hashType = req.resumeState.hashType; - /* Finalize the SHA512 operation */ - if (ret == WH_ERROR_OK) { - WH_DEBUG_SERVER_VERBOSE(" wc_Sha512Final: outAddr=%p\n", outAddr); - WH_DEBUG_SERVER_VERBOSE(" hashTpe: %d\n", hashType); - switch (hashType) { - case WC_HASH_TYPE_SHA512_224: - ret = wc_Sha512_224Final(sha512, outAddr); - break; - case WC_HASH_TYPE_SHA512_256: - ret = wc_Sha512_256Final(sha512, outAddr); - break; - default: - ret = wc_Sha512Final(sha512, outAddr); - break; - } - } + switch (hashType) { +#ifndef WOLFSSL_NOSHA512_224 + case WC_HASH_TYPE_SHA512_224: + ret = wc_InitSha512_224_ex(sha512, NULL, devId); + break; +#endif +#ifndef WOLFSSL_NOSHA512_256 + case WC_HASH_TYPE_SHA512_256: + ret = wc_InitSha512_256_ex(sha512, NULL, devId); + break; +#endif + default: + ret = wc_InitSha512_ex(sha512, NULL, devId); + break; + } + if (ret != 0) { + return ret; + } - if (ret == WH_ERROR_OK) { - ret = wh_Server_DmaProcessClientAddress( - ctx, req.output.addr, &outAddr, req.output.sz, - WH_DMA_OPER_CLIENT_WRITE_POST, (whServerDmaFlags){0}); - } + memcpy(sha512->digest, req.resumeState.hash, WC_SHA512_DIGEST_SIZE); + sha512->loLen = req.resumeState.loLen; + sha512->hiLen = req.resumeState.hiLen; + sha512->buffLen = 0; + sha512->hashType = hashType; - if (ret == WH_ERROR_ACCESS) { - res.dmaAddrStatus.badAddr = req.output; - } + if (ret == 0 && req.inSz > 0) { + ret = wc_Sha512Update(sha512, inlineData, req.inSz); } - else if (ret == WH_ERROR_OK) { - /* Update requested, update the SHA512 operation, wrapping client - * address accesses with the associated DMA address processing */ - void* inAddr; + + if (ret == 0 && req.input.sz > 0) { ret = wh_Server_DmaProcessClientAddress( ctx, req.input.addr, &inAddr, req.input.sz, WH_DMA_OPER_CLIENT_READ_PRE, (whServerDmaFlags){0}); - - /* Update the SHA512 operation */ if (ret == WH_ERROR_OK) { - WH_DEBUG_SERVER_VERBOSE(" wc_Sha512Update: inAddr=%p, sz=%llu\n", inAddr, - (long long unsigned int)req.input.sz); - ret = wc_Sha512Update(sha512, inAddr, req.input.sz); + preOk = 1; + ret = wc_Sha512Update(sha512, inAddr, req.input.sz); } - - if (ret == WH_ERROR_OK) { - ret = wh_Server_DmaProcessClientAddress( - ctx, req.input.addr, &inAddr, req.input.sz, - WH_DMA_OPER_CLIENT_READ_POST, (whServerDmaFlags){0}); - } - if (ret == WH_ERROR_ACCESS) { res.dmaAddrStatus.badAddr = req.input; } } + /* Pair every successful PRE with a POST so DMA callbacks can release any + * resources they acquired, even if the Update failed. */ + if (preOk) { + (void)wh_Server_DmaProcessClientAddress( + ctx, req.input.addr, &inAddr, req.input.sz, + WH_DMA_OPER_CLIENT_READ_POST, (whServerDmaFlags){0}); + } - if (ret == WH_ERROR_OK) { - /* Reset the devId in the local context to ensure it isn't copied back - * to client memory */ - sha512->devId = clientDevId; - /* Copy SHA512 context back into client memory */ - ret = whServerDma_CopyToClient(ctx, req.state.addr, sha512, - req.state.sz, (whServerDmaFlags){0}); - if (ret != WH_ERROR_OK) { - res.dmaAddrStatus.badAddr = req.state; + if (ret == 0) { + if (req.isLastBlock) { + switch (hashType) { +#ifndef WOLFSSL_NOSHA512_224 + case WC_HASH_TYPE_SHA512_224: + ret = wc_Sha512_224Final(sha512, res.hash); + break; +#endif +#ifndef WOLFSSL_NOSHA512_256 + case WC_HASH_TYPE_SHA512_256: + ret = wc_Sha512_256Final(sha512, res.hash); + break; +#endif + default: + ret = wc_Sha512Final(sha512, res.hash); + break; + } + } + else { + if (sha512->buffLen != 0) { + ret = WH_ERROR_ABORTED; + } + else { + memcpy(res.hash, sha512->digest, WC_SHA512_DIGEST_SIZE); + res.loLen = sha512->loLen; + res.hiLen = sha512->hiLen; + res.hashType = sha512->hashType; + } } } - /* Translate the response */ (void)wh_MessageCrypto_TranslateSha2DmaResponse( magic, &res, (whMessageCrypto_Sha2DmaResponse*)cryptoDataOut); *outSize = sizeof(res); - /* return value populates rc in response message */ return ret; } #endif /* WOLFSSL_SHA512 */ diff --git a/test/wh_test_check_struct_padding.c b/test/wh_test_check_struct_padding.c index 067e82da8..dfc6d4913 100644 --- a/test/wh_test_check_struct_padding.c +++ b/test/wh_test_check_struct_padding.c @@ -123,7 +123,8 @@ whMessageCrypto_HkdfResponse hkdfRes; /* DMA crypto messages */ #if defined(WOLFHSM_CFG_DMA) -whMessageCrypto_Sha2DmaRequest hashSha2DmaReq; +whMessageCrypto_Sha256DmaRequest hashSha256DmaReq; +whMessageCrypto_Sha512DmaRequest hashSha512DmaReq; whMessageCrypto_Sha2DmaResponse hashSha2DmaRes; whMessageCrypto_MlDsaKeyGenDmaRequest pqMldsaKeygenDmaReq; whMessageCrypto_MlDsaKeyGenDmaResponse pqMldsaKeygenDmaRes; diff --git a/test/wh_test_crypto.c b/test/wh_test_crypto.c index 0217912fe..a951bcf70 100644 --- a/test/wh_test_crypto.c +++ b/test/wh_test_crypto.c @@ -43,6 +43,7 @@ #include "wolfhsm/wh_comm.h" #include "wolfhsm/wh_message.h" +#include "wolfhsm/wh_message_crypto.h" #ifdef WOLFHSM_CFG_ENABLE_CLIENT #include "wolfhsm/wh_client.h" @@ -1588,6 +1589,403 @@ static int whTest_CryptoSha256(whClientContext* ctx, int devId, WC_RNG* rng) } return ret; } + +/* Hash a buffer with a pure-software SHA256 (no devId) so we can compare. */ +static int whTest_Sha256Reference(const uint8_t* in, uint32_t inLen, + uint8_t out[WC_SHA256_DIGEST_SIZE]) +{ + wc_Sha256 sw[1]; + int ret = wc_InitSha256_ex(sw, NULL, INVALID_DEVID); + if (ret == 0) { + ret = wc_Sha256Update(sw, in, inLen); + } + if (ret == 0) { + ret = wc_Sha256Final(sw, out); + } + (void)wc_Sha256Free(sw); + return ret; +} + +/* Drive the new multi-block wire format through the blocking wrapper. Tests: + * - large multi-request input, + * - exact per-call inline capacity boundary, + * - one-byte-over-capacity boundary (forces a tail in the client buffer), + * - non-aligned chunked update sequence. + * + * Buffer is sized to comfortably exceed the per-call inline capacity at any + * reasonable comm-buffer size. We use a static buffer to keep stack pressure + * low under ASAN. */ +static uint8_t + whTest_Sha256BigBuf[2 * + (WH_MESSAGE_CRYPTO_SHA256_MAX_INLINE_UPDATE_SZ + 64u)]; + +static int whTest_CryptoSha256LargeInput(whClientContext* ctx, int devId, + WC_RNG* rng) +{ + int ret = WH_ERROR_OK; + wc_Sha256 sha256[1]; + uint8_t out[WC_SHA256_DIGEST_SIZE]; + uint8_t ref[WC_SHA256_DIGEST_SIZE]; + uint8_t* buf = whTest_Sha256BigBuf; + uint32_t BUFSZ = (uint32_t)sizeof(whTest_Sha256BigBuf); + uint32_t i; + + (void)ctx; + (void)rng; + + for (i = 0; i < BUFSZ; i++) { + buf[i] = (uint8_t)(i & 0xff); + } + + /* Test 1: large single-update */ + ret = wc_InitSha256_ex(sha256, NULL, devId); + if (ret == 0) { + ret = wc_Sha256Update(sha256, buf, BUFSZ); + } + if (ret == 0) { + ret = wc_Sha256Final(sha256, out); + } + (void)wc_Sha256Free(sha256); + if (ret == 0) { + ret = whTest_Sha256Reference(buf, BUFSZ, ref); + } + if (ret == 0 && memcmp(out, ref, WC_SHA256_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT("SHA256 large input mismatch\n"); + ret = -1; + } + + /* Test 2: exactly the per-call inline capacity in one shot */ + if (ret == 0) { + const uint32_t cap = WH_MESSAGE_CRYPTO_SHA256_MAX_INLINE_UPDATE_SZ; + ret = wc_InitSha256_ex(sha256, NULL, devId); + if (ret == 0) { + ret = wc_Sha256Update(sha256, buf, cap); + } + if (ret == 0) { + ret = wc_Sha256Final(sha256, out); + } + (void)wc_Sha256Free(sha256); + if (ret == 0) { + ret = whTest_Sha256Reference(buf, cap, ref); + } + if (ret == 0 && memcmp(out, ref, WC_SHA256_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT("SHA256 capacity-boundary mismatch\n"); + ret = -1; + } + } + + /* Test 3: capacity + 1 byte (one full request, then a tail buffered) */ + if (ret == 0) { + const uint32_t cap1 = + WH_MESSAGE_CRYPTO_SHA256_MAX_INLINE_UPDATE_SZ + 1u; + ret = wc_InitSha256_ex(sha256, NULL, devId); + if (ret == 0) { + ret = wc_Sha256Update(sha256, buf, cap1); + } + if (ret == 0) { + ret = wc_Sha256Final(sha256, out); + } + (void)wc_Sha256Free(sha256); + if (ret == 0) { + ret = whTest_Sha256Reference(buf, cap1, ref); + } + if (ret == 0 && memcmp(out, ref, WC_SHA256_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT("SHA256 capacity+1 mismatch\n"); + ret = -1; + } + } + + /* Test 4: non-aligned chunk stress test */ + if (ret == 0) { + const uint32_t chunks[] = {13, 17, 1280, 41, 1}; + const size_t nChunks = sizeof(chunks) / sizeof(chunks[0]); + uint32_t total = 0; + size_t k; + for (k = 0; k < nChunks; k++) { + total += chunks[k]; + } + if (total > BUFSZ) { + WH_ERROR_PRINT("test buffer too small for chunked stress test\n"); + ret = -1; + } + if (ret == 0) { + uint32_t off = 0; + ret = wc_InitSha256_ex(sha256, NULL, devId); + for (k = 0; ret == 0 && k < nChunks; k++) { + ret = wc_Sha256Update(sha256, buf + off, chunks[k]); + off += chunks[k]; + } + if (ret == 0) { + ret = wc_Sha256Final(sha256, out); + } + (void)wc_Sha256Free(sha256); + if (ret == 0) { + ret = whTest_Sha256Reference(buf, total, ref); + } + if (ret == 0 && memcmp(out, ref, WC_SHA256_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT("SHA256 chunked stress mismatch\n"); + ret = -1; + } + } + } + + if (ret == 0) { + WH_TEST_PRINT("SHA256 LARGE-INPUT DEVID=0x%X SUCCESS\n", devId); + } + return ret; +} + +/* Direct exercise of the new async non-DMA SHA256 primitives. */ +static int whTest_CryptoSha256Async(whClientContext* ctx, int devId, + WC_RNG* rng) +{ + int ret = WH_ERROR_OK; + wc_Sha256 sha256[1]; + uint8_t out[WC_SHA256_DIGEST_SIZE]; + uint8_t ref[WC_SHA256_DIGEST_SIZE]; + /* Use the same large static buffer as the LargeInput test. */ + uint8_t* buf = whTest_Sha256BigBuf; + uint32_t BUFSZ = (uint32_t)sizeof(whTest_Sha256BigBuf); + uint32_t i; + bool sent; + + (void)rng; + + for (i = 0; i < BUFSZ; i++) { + buf[i] = (uint8_t)((i * 31u + 7u) & 0xff); + } + + /* Case A: basic UpdateRequest -> UpdateResponse -> Final */ + ret = wc_InitSha256_ex(sha256, NULL, devId); + if (ret == 0) { + sent = false; + ret = wh_Client_Sha256UpdateRequest(ctx, sha256, buf, 256, &sent); + } + if (ret == 0 && sent) { + do { + ret = wh_Client_Sha256UpdateResponse(ctx, sha256); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0) { + ret = wh_Client_Sha256FinalRequest(ctx, sha256); + } + if (ret == 0) { + do { + ret = wh_Client_Sha256FinalResponse(ctx, sha256, out); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0) { + ret = whTest_Sha256Reference(buf, 256, ref); + } + if (ret == 0 && memcmp(out, ref, WC_SHA256_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT("Async SHA256 case A mismatch\n"); + ret = -1; + } + + /* Case B: pure-buffer-fill update (sent must be false), then finalize */ + if (ret == 0) { + (void)wc_Sha256Free(sha256); + ret = wc_InitSha256_ex(sha256, NULL, devId); + } + if (ret == 0) { + sent = true; /* expect to be cleared to false */ + ret = wh_Client_Sha256UpdateRequest(ctx, sha256, buf, 10, &sent); + if (ret == 0 && sent != false) { + WH_ERROR_PRINT( + "Async SHA256: expected sent==false on small update\n"); + ret = -1; + } + } + if (ret == 0) { + ret = wh_Client_Sha256FinalRequest(ctx, sha256); + } + if (ret == 0) { + do { + ret = wh_Client_Sha256FinalResponse(ctx, sha256, out); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0) { + ret = whTest_Sha256Reference(buf, 10, ref); + } + if (ret == 0 && memcmp(out, ref, WC_SHA256_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT("Async SHA256 case B mismatch\n"); + ret = -1; + } + + /* Case C: multi-round async updates that span more than the per-call + * inline capacity (forces multiple Request/Response pairs). */ + if (ret == 0) { + uint32_t consumed = 0; + (void)wc_Sha256Free(sha256); + ret = wc_InitSha256_ex(sha256, NULL, devId); + while (ret == 0 && consumed < BUFSZ) { + uint32_t chunk = 700; /* arbitrary, less than max inline */ + if (consumed + chunk > BUFSZ) { + chunk = BUFSZ - consumed; + } + sent = false; + ret = wh_Client_Sha256UpdateRequest(ctx, sha256, buf + consumed, + chunk, &sent); + if (ret == 0 && sent) { + do { + ret = wh_Client_Sha256UpdateResponse(ctx, sha256); + } while (ret == WH_ERROR_NOTREADY); + } + consumed += chunk; + } + if (ret == 0) { + ret = wh_Client_Sha256FinalRequest(ctx, sha256); + } + if (ret == 0) { + do { + ret = wh_Client_Sha256FinalResponse(ctx, sha256, out); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0) { + ret = whTest_Sha256Reference(buf, BUFSZ, ref); + } + if (ret == 0 && memcmp(out, ref, WC_SHA256_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT("Async SHA256 case C mismatch\n"); + ret = -1; + } + } + + /* Case D: oversized-input rejection. UpdateRequest with inLen > capacity + * must return BADARGS without mutating sha. */ + if (ret == 0) { + uint8_t savedDigest[WC_SHA256_DIGEST_SIZE]; + word32 savedBuffLen; + uint32_t cap; + int rc; + (void)wc_Sha256Free(sha256); + ret = wc_InitSha256_ex(sha256, NULL, devId); + if (ret == 0) { + memcpy(savedDigest, sha256->digest, WC_SHA256_DIGEST_SIZE); + savedBuffLen = sha256->buffLen; + cap = WH_MESSAGE_CRYPTO_SHA256_MAX_INLINE_UPDATE_SZ + + (uint32_t)(WC_SHA256_BLOCK_SIZE - 1u - sha256->buffLen); + sent = true; + rc = wh_Client_Sha256UpdateRequest(ctx, sha256, buf, cap + 1u, + &sent); + if (rc != WH_ERROR_BADARGS) { + WH_ERROR_PRINT("Async SHA256: expected BADARGS, got %d\n", rc); + ret = -1; + } + else if (sent != false) { + WH_ERROR_PRINT( + "Async SHA256: sent should remain false on err\n"); + ret = -1; + } + else if (sha256->buffLen != savedBuffLen || + memcmp(sha256->digest, savedDigest, + WC_SHA256_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT( + "Async SHA256: state mutated on rejected call\n"); + ret = -1; + } + } + (void)wc_Sha256Free(sha256); + } + + if (ret == 0) { + WH_TEST_PRINT("SHA256 ASYNC DEVID=0x%X SUCCESS\n", devId); + } + return ret; +} + +#ifdef WOLFHSM_CFG_DMA +/* Direct exercise of the new async DMA SHA256 primitives. */ +static int whTest_CryptoSha256DmaAsync(whClientContext* ctx, int devId, + WC_RNG* rng) +{ + int ret = WH_ERROR_OK; + wc_Sha256 sha256[1]; + uint8_t out[WC_SHA256_DIGEST_SIZE]; + uint8_t ref[WC_SHA256_DIGEST_SIZE]; + /* DMA bypasses the comm buffer, so any size goes; reuse the shared + * static buffer to keep stack pressure low. */ + uint8_t* buf = whTest_Sha256BigBuf; + uint32_t BUFSZ = (uint32_t)sizeof(whTest_Sha256BigBuf); + uint32_t i; + + (void)rng; + + for (i = 0; i < BUFSZ; i++) { + buf[i] = (uint8_t)((i * 17u + 3u) & 0xff); + } + + /* Case A: single large DMA Update + Final */ + ret = wc_InitSha256_ex(sha256, NULL, devId); + if (ret == 0) { + bool sent = false; + ret = wh_Client_Sha256DmaUpdateRequest(ctx, sha256, buf, BUFSZ, &sent); + if (ret == 0 && sent) { + do { + ret = wh_Client_Sha256DmaUpdateResponse(ctx, sha256); + } while (ret == WH_ERROR_NOTREADY); + } + } + if (ret == 0) { + ret = wh_Client_Sha256DmaFinalRequest(ctx, sha256); + } + if (ret == 0) { + do { + ret = wh_Client_Sha256DmaFinalResponse(ctx, sha256, out); + } while (ret == WH_ERROR_NOTREADY); + } + (void)wc_Sha256Free(sha256); + if (ret == 0) { + ret = whTest_Sha256Reference(buf, BUFSZ, ref); + } + if (ret == 0 && memcmp(out, ref, WC_SHA256_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT("Async DMA SHA256 case A mismatch\n"); + ret = -1; + } + + /* Case B: multiple DMA Update round-trips, then Final */ + if (ret == 0) { + uint32_t consumed = 0; + ret = wc_InitSha256_ex(sha256, NULL, devId); + while (ret == 0 && consumed < BUFSZ) { + uint32_t chunk = 1024; + bool sent = false; + if (consumed + chunk > BUFSZ) { + chunk = BUFSZ - consumed; + } + ret = wh_Client_Sha256DmaUpdateRequest(ctx, sha256, buf + consumed, + chunk, &sent); + if (ret == 0 && sent) { + do { + ret = wh_Client_Sha256DmaUpdateResponse(ctx, sha256); + } while (ret == WH_ERROR_NOTREADY); + } + consumed += chunk; + } + if (ret == 0) { + ret = wh_Client_Sha256DmaFinalRequest(ctx, sha256); + } + if (ret == 0) { + do { + ret = wh_Client_Sha256DmaFinalResponse(ctx, sha256, out); + } while (ret == WH_ERROR_NOTREADY); + } + (void)wc_Sha256Free(sha256); + if (ret == 0) { + ret = whTest_Sha256Reference(buf, BUFSZ, ref); + } + if (ret == 0 && memcmp(out, ref, WC_SHA256_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT("Async DMA SHA256 case B mismatch\n"); + ret = -1; + } + } + + if (ret == 0) { + WH_TEST_PRINT("SHA256 DMA ASYNC DEVID=0x%X SUCCESS\n", devId); + } + return ret; +} +#endif /* WOLFHSM_CFG_DMA */ + #endif /* !NO_SHA256 */ #ifdef WOLFSSL_SHA224 @@ -1657,53 +2055,452 @@ static int whTest_CryptoSha224(whClientContext* ctx, int devId, WC_RNG* rng) if (ret != 0) { WH_ERROR_PRINT("Failed to wc_Sha224Update (first) %d\n", ret); } - else { - /* Update with a full block, will trigger block to be sent to - * server and one additional byte to be buffered */ - ret = wc_Sha224Update(sha224, (const byte*)inMulti + 1, - WC_SHA224_BLOCK_SIZE); - if (ret != 0) { - WH_ERROR_PRINT("Failed to wc_Sha224Update (mid) %d\n", ret); - } - else { - /* Update with the remaining data, should not trigger server - * transaction */ - ret = wc_Sha224Update( - sha224, (const byte*)inMulti + 1 + WC_SHA224_BLOCK_SIZE, - strlen(inMulti) - 1 - WC_SHA224_BLOCK_SIZE); - if (ret != 0) { - WH_ERROR_PRINT("Failed to wc_Sha224Update (last) %d\n", - ret); - } - else { - /* Finalize should trigger a server transaction on the - * remaining partial buffer */ - ret = wc_Sha224Final(sha224, out); - if (ret != 0) { - WH_ERROR_PRINT("Failed to wc_Sha224Final %d\n", - ret); - } - else { - /* Compare the computed hash with the expected - * output */ - if (memcmp(out, expectedOutMulti, - WC_SHA224_DIGEST_SIZE) != 0) { - WH_ERROR_PRINT("SHA224 hash does not match the " - "expected output.\n"); - ret = -1; - } - } - } + else { + /* Update with a full block, will trigger block to be sent to + * server and one additional byte to be buffered */ + ret = wc_Sha224Update(sha224, (const byte*)inMulti + 1, + WC_SHA224_BLOCK_SIZE); + if (ret != 0) { + WH_ERROR_PRINT("Failed to wc_Sha224Update (mid) %d\n", ret); + } + else { + /* Update with the remaining data, should not trigger server + * transaction */ + ret = wc_Sha224Update( + sha224, (const byte*)inMulti + 1 + WC_SHA224_BLOCK_SIZE, + strlen(inMulti) - 1 - WC_SHA224_BLOCK_SIZE); + if (ret != 0) { + WH_ERROR_PRINT("Failed to wc_Sha224Update (last) %d\n", + ret); + } + else { + /* Finalize should trigger a server transaction on the + * remaining partial buffer */ + ret = wc_Sha224Final(sha224, out); + if (ret != 0) { + WH_ERROR_PRINT("Failed to wc_Sha224Final %d\n", + ret); + } + else { + /* Compare the computed hash with the expected + * output */ + if (memcmp(out, expectedOutMulti, + WC_SHA224_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT("SHA224 hash does not match the " + "expected output.\n"); + ret = -1; + } + } + } + } + } + (void)wc_Sha224Free(sha224); + } + } + if (ret == 0) { + WH_TEST_PRINT("SHA224 DEVID=0x%X SUCCESS\n", devId); + } + return ret; +} + +/* Hash a buffer with a pure-software SHA224 (no devId) so we can compare. */ +static int whTest_Sha224Reference(const uint8_t* in, uint32_t inLen, + uint8_t out[WC_SHA224_DIGEST_SIZE]) +{ + wc_Sha224 sw[1]; + int ret = wc_InitSha224_ex(sw, NULL, INVALID_DEVID); + if (ret == 0) { + ret = wc_Sha224Update(sw, in, inLen); + } + if (ret == 0) { + ret = wc_Sha224Final(sw, out); + } + (void)wc_Sha224Free(sw); + return ret; +} + +/* Drive the new multi-block wire format through the blocking wrapper. Tests: + * - large multi-request input, + * - exact per-call inline capacity boundary, + * - one-byte-over-capacity boundary (forces a tail in the client buffer), + * - non-aligned chunked update sequence. + * + * Buffer is sized to comfortably exceed the per-call inline capacity at any + * reasonable comm-buffer size. We use a static buffer to keep stack pressure + * low under ASAN. */ +static uint8_t + whTest_Sha224BigBuf[2 * + (WH_MESSAGE_CRYPTO_SHA224_MAX_INLINE_UPDATE_SZ + 64u)]; + +static int whTest_CryptoSha224LargeInput(whClientContext* ctx, int devId, + WC_RNG* rng) +{ + int ret = WH_ERROR_OK; + wc_Sha224 sha224[1]; + uint8_t out[WC_SHA224_DIGEST_SIZE]; + uint8_t ref[WC_SHA224_DIGEST_SIZE]; + uint8_t* buf = whTest_Sha224BigBuf; + uint32_t BUFSZ = (uint32_t)sizeof(whTest_Sha224BigBuf); + uint32_t i; + + (void)ctx; + (void)rng; + + for (i = 0; i < BUFSZ; i++) { + buf[i] = (uint8_t)(i & 0xff); + } + + /* Test 1: large single-update */ + ret = wc_InitSha224_ex(sha224, NULL, devId); + if (ret == 0) { + ret = wc_Sha224Update(sha224, buf, BUFSZ); + } + if (ret == 0) { + ret = wc_Sha224Final(sha224, out); + } + (void)wc_Sha224Free(sha224); + if (ret == 0) { + ret = whTest_Sha224Reference(buf, BUFSZ, ref); + } + if (ret == 0 && memcmp(out, ref, WC_SHA224_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT("SHA224 large input mismatch\n"); + ret = -1; + } + + /* Test 2: exactly the per-call inline capacity in one shot */ + if (ret == 0) { + const uint32_t cap = WH_MESSAGE_CRYPTO_SHA224_MAX_INLINE_UPDATE_SZ; + ret = wc_InitSha224_ex(sha224, NULL, devId); + if (ret == 0) { + ret = wc_Sha224Update(sha224, buf, cap); + } + if (ret == 0) { + ret = wc_Sha224Final(sha224, out); + } + (void)wc_Sha224Free(sha224); + if (ret == 0) { + ret = whTest_Sha224Reference(buf, cap, ref); + } + if (ret == 0 && memcmp(out, ref, WC_SHA224_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT("SHA224 capacity-boundary mismatch\n"); + ret = -1; + } + } + + /* Test 3: capacity + 1 byte (one full request, then a tail buffered) */ + if (ret == 0) { + const uint32_t cap1 = + WH_MESSAGE_CRYPTO_SHA224_MAX_INLINE_UPDATE_SZ + 1u; + ret = wc_InitSha224_ex(sha224, NULL, devId); + if (ret == 0) { + ret = wc_Sha224Update(sha224, buf, cap1); + } + if (ret == 0) { + ret = wc_Sha224Final(sha224, out); + } + (void)wc_Sha224Free(sha224); + if (ret == 0) { + ret = whTest_Sha224Reference(buf, cap1, ref); + } + if (ret == 0 && memcmp(out, ref, WC_SHA224_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT("SHA224 capacity+1 mismatch\n"); + ret = -1; + } + } + + /* Test 4: non-aligned chunk stress test */ + if (ret == 0) { + const uint32_t chunks[] = {13, 17, 1280, 41, 1}; + const size_t nChunks = sizeof(chunks) / sizeof(chunks[0]); + uint32_t total = 0; + size_t k; + for (k = 0; k < nChunks; k++) { + total += chunks[k]; + } + if (total > BUFSZ) { + WH_ERROR_PRINT("test buffer too small for chunked stress test\n"); + ret = -1; + } + if (ret == 0) { + uint32_t off = 0; + ret = wc_InitSha224_ex(sha224, NULL, devId); + for (k = 0; ret == 0 && k < nChunks; k++) { + ret = wc_Sha224Update(sha224, buf + off, chunks[k]); + off += chunks[k]; + } + if (ret == 0) { + ret = wc_Sha224Final(sha224, out); + } + (void)wc_Sha224Free(sha224); + if (ret == 0) { + ret = whTest_Sha224Reference(buf, total, ref); + } + if (ret == 0 && memcmp(out, ref, WC_SHA224_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT("SHA224 chunked stress mismatch\n"); + ret = -1; + } + } + } + + if (ret == 0) { + WH_TEST_PRINT("SHA224 LARGE-INPUT DEVID=0x%X SUCCESS\n", devId); + } + return ret; +} + +/* Direct exercise of the new async non-DMA SHA224 primitives. */ +static int whTest_CryptoSha224Async(whClientContext* ctx, int devId, + WC_RNG* rng) +{ + int ret = WH_ERROR_OK; + wc_Sha224 sha224[1]; + uint8_t out[WC_SHA224_DIGEST_SIZE]; + uint8_t ref[WC_SHA224_DIGEST_SIZE]; + /* Use the same large static buffer as the LargeInput test. */ + uint8_t* buf = whTest_Sha224BigBuf; + uint32_t BUFSZ = (uint32_t)sizeof(whTest_Sha224BigBuf); + uint32_t i; + bool sent; + + (void)rng; + + for (i = 0; i < BUFSZ; i++) { + buf[i] = (uint8_t)((i * 31u + 7u) & 0xff); + } + + /* Case A: basic UpdateRequest -> UpdateResponse -> Final */ + ret = wc_InitSha224_ex(sha224, NULL, devId); + if (ret == 0) { + sent = false; + ret = wh_Client_Sha224UpdateRequest(ctx, sha224, buf, 256, &sent); + } + if (ret == 0 && sent) { + do { + ret = wh_Client_Sha224UpdateResponse(ctx, sha224); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0) { + ret = wh_Client_Sha224FinalRequest(ctx, sha224); + } + if (ret == 0) { + do { + ret = wh_Client_Sha224FinalResponse(ctx, sha224, out); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0) { + ret = whTest_Sha224Reference(buf, 256, ref); + } + if (ret == 0 && memcmp(out, ref, WC_SHA224_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT("Async SHA224 case A mismatch\n"); + ret = -1; + } + + /* Case B: pure-buffer-fill update (sent must be false), then finalize */ + if (ret == 0) { + (void)wc_Sha224Free(sha224); + ret = wc_InitSha224_ex(sha224, NULL, devId); + } + if (ret == 0) { + sent = true; /* expect to be cleared to false */ + ret = wh_Client_Sha224UpdateRequest(ctx, sha224, buf, 10, &sent); + if (ret == 0 && sent != false) { + WH_ERROR_PRINT( + "Async SHA224: expected sent==false on small update\n"); + ret = -1; + } + } + if (ret == 0) { + ret = wh_Client_Sha224FinalRequest(ctx, sha224); + } + if (ret == 0) { + do { + ret = wh_Client_Sha224FinalResponse(ctx, sha224, out); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0) { + ret = whTest_Sha224Reference(buf, 10, ref); + } + if (ret == 0 && memcmp(out, ref, WC_SHA224_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT("Async SHA224 case B mismatch\n"); + ret = -1; + } + + /* Case C: multi-round async updates that span more than the per-call + * inline capacity (forces multiple Request/Response pairs). */ + if (ret == 0) { + uint32_t consumed = 0; + (void)wc_Sha224Free(sha224); + ret = wc_InitSha224_ex(sha224, NULL, devId); + while (ret == 0 && consumed < BUFSZ) { + uint32_t chunk = 700; /* arbitrary, less than max inline */ + if (consumed + chunk > BUFSZ) { + chunk = BUFSZ - consumed; + } + sent = false; + ret = wh_Client_Sha224UpdateRequest(ctx, sha224, buf + consumed, + chunk, &sent); + if (ret == 0 && sent) { + do { + ret = wh_Client_Sha224UpdateResponse(ctx, sha224); + } while (ret == WH_ERROR_NOTREADY); + } + consumed += chunk; + } + if (ret == 0) { + ret = wh_Client_Sha224FinalRequest(ctx, sha224); + } + if (ret == 0) { + do { + ret = wh_Client_Sha224FinalResponse(ctx, sha224, out); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0) { + ret = whTest_Sha224Reference(buf, BUFSZ, ref); + } + if (ret == 0 && memcmp(out, ref, WC_SHA224_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT("Async SHA224 case C mismatch\n"); + ret = -1; + } + } + + /* Case D: oversized-input rejection. UpdateRequest with inLen > capacity + * must return BADARGS without mutating sha. */ + if (ret == 0) { + uint8_t savedDigest[WC_SHA256_DIGEST_SIZE]; + word32 savedBuffLen; + uint32_t cap; + int rc; + (void)wc_Sha224Free(sha224); + ret = wc_InitSha224_ex(sha224, NULL, devId); + if (ret == 0) { + memcpy(savedDigest, sha224->digest, WC_SHA256_DIGEST_SIZE); + savedBuffLen = sha224->buffLen; + cap = WH_MESSAGE_CRYPTO_SHA224_MAX_INLINE_UPDATE_SZ + + (uint32_t)(WC_SHA224_BLOCK_SIZE - 1u - sha224->buffLen); + sent = true; + rc = wh_Client_Sha224UpdateRequest(ctx, sha224, buf, cap + 1u, + &sent); + if (rc != WH_ERROR_BADARGS) { + WH_ERROR_PRINT("Async SHA224: expected BADARGS, got %d\n", rc); + ret = -1; + } + else if (sent != false) { + WH_ERROR_PRINT( + "Async SHA224: sent should remain false on err\n"); + ret = -1; + } + else if (sha224->buffLen != savedBuffLen || + memcmp(sha224->digest, savedDigest, + WC_SHA256_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT( + "Async SHA224: state mutated on rejected call\n"); + ret = -1; + } + } + (void)wc_Sha224Free(sha224); + } + + if (ret == 0) { + WH_TEST_PRINT("SHA224 ASYNC DEVID=0x%X SUCCESS\n", devId); + } + return ret; +} + +#ifdef WOLFHSM_CFG_DMA +/* Direct exercise of the new async DMA SHA224 primitives. */ +static int whTest_CryptoSha224DmaAsync(whClientContext* ctx, int devId, + WC_RNG* rng) +{ + int ret = WH_ERROR_OK; + wc_Sha224 sha224[1]; + uint8_t out[WC_SHA224_DIGEST_SIZE]; + uint8_t ref[WC_SHA224_DIGEST_SIZE]; + /* DMA bypasses the comm buffer, so any size goes; reuse the shared + * static buffer to keep stack pressure low. */ + uint8_t* buf = whTest_Sha224BigBuf; + uint32_t BUFSZ = (uint32_t)sizeof(whTest_Sha224BigBuf); + uint32_t i; + + (void)rng; + + for (i = 0; i < BUFSZ; i++) { + buf[i] = (uint8_t)((i * 17u + 3u) & 0xff); + } + + /* Case A: single large DMA Update + Final */ + ret = wc_InitSha224_ex(sha224, NULL, devId); + if (ret == 0) { + bool sent = false; + ret = wh_Client_Sha224DmaUpdateRequest(ctx, sha224, buf, BUFSZ, &sent); + if (ret == 0 && sent) { + do { + ret = wh_Client_Sha224DmaUpdateResponse(ctx, sha224); + } while (ret == WH_ERROR_NOTREADY); + } + } + if (ret == 0) { + ret = wh_Client_Sha224DmaFinalRequest(ctx, sha224); + } + if (ret == 0) { + do { + ret = wh_Client_Sha224DmaFinalResponse(ctx, sha224, out); + } while (ret == WH_ERROR_NOTREADY); + } + (void)wc_Sha224Free(sha224); + if (ret == 0) { + ret = whTest_Sha224Reference(buf, BUFSZ, ref); + } + if (ret == 0 && memcmp(out, ref, WC_SHA224_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT("Async DMA SHA224 case A mismatch\n"); + ret = -1; + } + + /* Case B: multiple DMA Update round-trips, then Final */ + if (ret == 0) { + uint32_t consumed = 0; + ret = wc_InitSha224_ex(sha224, NULL, devId); + while (ret == 0 && consumed < BUFSZ) { + uint32_t chunk = 1024; + if (consumed + chunk > BUFSZ) { + chunk = BUFSZ - consumed; + } + { + bool sent = false; + ret = wh_Client_Sha224DmaUpdateRequest( + ctx, sha224, buf + consumed, chunk, &sent); + if (ret == 0 && sent) { + do { + ret = wh_Client_Sha224DmaUpdateResponse(ctx, sha224); + } while (ret == WH_ERROR_NOTREADY); } } - (void)wc_Sha224Free(sha224); + consumed += chunk; + } + if (ret == 0) { + ret = wh_Client_Sha224DmaFinalRequest(ctx, sha224); + } + if (ret == 0) { + do { + ret = wh_Client_Sha224DmaFinalResponse(ctx, sha224, out); + } while (ret == WH_ERROR_NOTREADY); + } + (void)wc_Sha224Free(sha224); + if (ret == 0) { + ret = whTest_Sha224Reference(buf, BUFSZ, ref); + } + if (ret == 0 && memcmp(out, ref, WC_SHA224_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT("Async DMA SHA224 case B mismatch\n"); + ret = -1; } } + if (ret == 0) { - WH_TEST_PRINT("SHA224 DEVID=0x%X SUCCESS\n", devId); + WH_TEST_PRINT("SHA224 DMA ASYNC DEVID=0x%X SUCCESS\n", devId); } return ret; } +#endif /* WOLFHSM_CFG_DMA */ + #endif /* WOLFSSL_SHA224 */ #ifdef WOLFSSL_SHA384 @@ -1817,14 +2614,413 @@ static int whTest_CryptoSha384(whClientContext* ctx, int devId, WC_RNG* rng) } } } - (void)wc_Sha384Free(sha384); + (void)wc_Sha384Free(sha384); + } + } + if (ret == 0) { + WH_TEST_PRINT("SHA384 DEVID=0x%X SUCCESS\n", devId); + } + return ret; +} + +/* Hash a buffer with a pure-software SHA384 (no devId) so we can compare. */ +static int whTest_Sha384Reference(const uint8_t* in, uint32_t inLen, + uint8_t out[WC_SHA384_DIGEST_SIZE]) +{ + wc_Sha384 sw[1]; + int ret = wc_InitSha384_ex(sw, NULL, INVALID_DEVID); + if (ret == 0) { + ret = wc_Sha384Update(sw, in, inLen); + } + if (ret == 0) { + ret = wc_Sha384Final(sw, out); + } + (void)wc_Sha384Free(sw); + return ret; +} + +/* Drive the new multi-block wire format through the blocking wrapper. Tests: + * - large multi-request input, + * - exact per-call inline capacity boundary, + * - one-byte-over-capacity boundary (forces a tail in the client buffer), + * - non-aligned chunked update sequence. + * + * Buffer is sized to comfortably exceed the per-call inline capacity at any + * reasonable comm-buffer size. We use a static buffer to keep stack pressure + * low under ASAN. */ +static uint8_t + whTest_Sha384BigBuf[2 * + (WH_MESSAGE_CRYPTO_SHA384_MAX_INLINE_UPDATE_SZ + 128u)]; + +static int whTest_CryptoSha384LargeInput(whClientContext* ctx, int devId, + WC_RNG* rng) +{ + int ret = WH_ERROR_OK; + wc_Sha384 sha384[1]; + uint8_t out[WC_SHA384_DIGEST_SIZE]; + uint8_t ref[WC_SHA384_DIGEST_SIZE]; + uint8_t* buf = whTest_Sha384BigBuf; + uint32_t BUFSZ = (uint32_t)sizeof(whTest_Sha384BigBuf); + uint32_t i; + + (void)ctx; + (void)rng; + + for (i = 0; i < BUFSZ; i++) { + buf[i] = (uint8_t)(i & 0xff); + } + + /* Test 1: large single-update */ + ret = wc_InitSha384_ex(sha384, NULL, devId); + if (ret == 0) { + ret = wc_Sha384Update(sha384, buf, BUFSZ); + } + if (ret == 0) { + ret = wc_Sha384Final(sha384, out); + } + (void)wc_Sha384Free(sha384); + if (ret == 0) { + ret = whTest_Sha384Reference(buf, BUFSZ, ref); + } + if (ret == 0 && memcmp(out, ref, WC_SHA384_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT("SHA384 large input mismatch\n"); + ret = -1; + } + + /* Test 2: exactly the per-call inline capacity in one shot */ + if (ret == 0) { + const uint32_t cap = WH_MESSAGE_CRYPTO_SHA384_MAX_INLINE_UPDATE_SZ; + ret = wc_InitSha384_ex(sha384, NULL, devId); + if (ret == 0) { + ret = wc_Sha384Update(sha384, buf, cap); + } + if (ret == 0) { + ret = wc_Sha384Final(sha384, out); + } + (void)wc_Sha384Free(sha384); + if (ret == 0) { + ret = whTest_Sha384Reference(buf, cap, ref); + } + if (ret == 0 && memcmp(out, ref, WC_SHA384_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT("SHA384 capacity-boundary mismatch\n"); + ret = -1; + } + } + + /* Test 3: capacity + 1 byte (one full request, then a tail buffered) */ + if (ret == 0) { + const uint32_t cap1 = + WH_MESSAGE_CRYPTO_SHA384_MAX_INLINE_UPDATE_SZ + 1u; + ret = wc_InitSha384_ex(sha384, NULL, devId); + if (ret == 0) { + ret = wc_Sha384Update(sha384, buf, cap1); + } + if (ret == 0) { + ret = wc_Sha384Final(sha384, out); + } + (void)wc_Sha384Free(sha384); + if (ret == 0) { + ret = whTest_Sha384Reference(buf, cap1, ref); + } + if (ret == 0 && memcmp(out, ref, WC_SHA384_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT("SHA384 capacity+1 mismatch\n"); + ret = -1; + } + } + + /* Test 4: non-aligned chunk stress test */ + if (ret == 0) { + const uint32_t chunks[] = {13, 17, 1280, 41, 1}; + const size_t nChunks = sizeof(chunks) / sizeof(chunks[0]); + uint32_t total = 0; + size_t k; + for (k = 0; k < nChunks; k++) { + total += chunks[k]; + } + if (total > BUFSZ) { + WH_ERROR_PRINT("test buffer too small for chunked stress test\n"); + ret = -1; + } + if (ret == 0) { + uint32_t off = 0; + ret = wc_InitSha384_ex(sha384, NULL, devId); + for (k = 0; ret == 0 && k < nChunks; k++) { + ret = wc_Sha384Update(sha384, buf + off, chunks[k]); + off += chunks[k]; + } + if (ret == 0) { + ret = wc_Sha384Final(sha384, out); + } + (void)wc_Sha384Free(sha384); + if (ret == 0) { + ret = whTest_Sha384Reference(buf, total, ref); + } + if (ret == 0 && memcmp(out, ref, WC_SHA384_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT("SHA384 chunked stress mismatch\n"); + ret = -1; + } + } + } + + if (ret == 0) { + WH_TEST_PRINT("SHA384 LARGE-INPUT DEVID=0x%X SUCCESS\n", devId); + } + return ret; +} + +/* Direct exercise of the new async non-DMA SHA384 primitives. */ +static int whTest_CryptoSha384Async(whClientContext* ctx, int devId, + WC_RNG* rng) +{ + int ret = WH_ERROR_OK; + wc_Sha384 sha384[1]; + uint8_t out[WC_SHA384_DIGEST_SIZE]; + uint8_t ref[WC_SHA384_DIGEST_SIZE]; + /* Use the same large static buffer as the LargeInput test. */ + uint8_t* buf = whTest_Sha384BigBuf; + uint32_t BUFSZ = (uint32_t)sizeof(whTest_Sha384BigBuf); + uint32_t i; + bool sent; + + (void)rng; + + for (i = 0; i < BUFSZ; i++) { + buf[i] = (uint8_t)((i * 31u + 7u) & 0xff); + } + + /* Case A: basic UpdateRequest -> UpdateResponse -> Final */ + ret = wc_InitSha384_ex(sha384, NULL, devId); + if (ret == 0) { + sent = false; + ret = wh_Client_Sha384UpdateRequest(ctx, sha384, buf, 256, &sent); + } + if (ret == 0 && sent) { + do { + ret = wh_Client_Sha384UpdateResponse(ctx, sha384); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0) { + ret = wh_Client_Sha384FinalRequest(ctx, sha384); + } + if (ret == 0) { + do { + ret = wh_Client_Sha384FinalResponse(ctx, sha384, out); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0) { + ret = whTest_Sha384Reference(buf, 256, ref); + } + if (ret == 0 && memcmp(out, ref, WC_SHA384_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT("Async SHA384 case A mismatch\n"); + ret = -1; + } + + /* Case B: pure-buffer-fill update (sent must be false), then finalize */ + if (ret == 0) { + (void)wc_Sha384Free(sha384); + ret = wc_InitSha384_ex(sha384, NULL, devId); + } + if (ret == 0) { + sent = true; /* expect to be cleared to false */ + ret = wh_Client_Sha384UpdateRequest(ctx, sha384, buf, 10, &sent); + if (ret == 0 && sent != false) { + WH_ERROR_PRINT( + "Async SHA384: expected sent==false on small update\n"); + ret = -1; + } + } + if (ret == 0) { + ret = wh_Client_Sha384FinalRequest(ctx, sha384); + } + if (ret == 0) { + do { + ret = wh_Client_Sha384FinalResponse(ctx, sha384, out); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0) { + ret = whTest_Sha384Reference(buf, 10, ref); + } + if (ret == 0 && memcmp(out, ref, WC_SHA384_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT("Async SHA384 case B mismatch\n"); + ret = -1; + } + + /* Case C: multi-round async updates that span more than the per-call + * inline capacity (forces multiple Request/Response pairs). */ + if (ret == 0) { + uint32_t consumed = 0; + (void)wc_Sha384Free(sha384); + ret = wc_InitSha384_ex(sha384, NULL, devId); + while (ret == 0 && consumed < BUFSZ) { + uint32_t chunk = 1400; /* arbitrary, less than max inline */ + if (consumed + chunk > BUFSZ) { + chunk = BUFSZ - consumed; + } + sent = false; + ret = wh_Client_Sha384UpdateRequest(ctx, sha384, buf + consumed, + chunk, &sent); + if (ret == 0 && sent) { + do { + ret = wh_Client_Sha384UpdateResponse(ctx, sha384); + } while (ret == WH_ERROR_NOTREADY); + } + consumed += chunk; + } + if (ret == 0) { + ret = wh_Client_Sha384FinalRequest(ctx, sha384); + } + if (ret == 0) { + do { + ret = wh_Client_Sha384FinalResponse(ctx, sha384, out); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0) { + ret = whTest_Sha384Reference(buf, BUFSZ, ref); + } + if (ret == 0 && memcmp(out, ref, WC_SHA384_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT("Async SHA384 case C mismatch\n"); + ret = -1; + } + } + + /* Case D: oversized-input rejection. UpdateRequest with inLen > capacity + * must return BADARGS without mutating sha. */ + if (ret == 0) { + uint8_t savedDigest[WC_SHA512_DIGEST_SIZE]; + word32 savedBuffLen; + uint32_t cap; + int rc; + (void)wc_Sha384Free(sha384); + ret = wc_InitSha384_ex(sha384, NULL, devId); + if (ret == 0) { + memcpy(savedDigest, sha384->digest, WC_SHA512_DIGEST_SIZE); + savedBuffLen = sha384->buffLen; + cap = WH_MESSAGE_CRYPTO_SHA384_MAX_INLINE_UPDATE_SZ + + (uint32_t)(WC_SHA384_BLOCK_SIZE - 1u - sha384->buffLen); + sent = true; + rc = wh_Client_Sha384UpdateRequest(ctx, sha384, buf, cap + 1u, + &sent); + if (rc != WH_ERROR_BADARGS) { + WH_ERROR_PRINT("Async SHA384: expected BADARGS, got %d\n", rc); + ret = -1; + } + else if (sent != false) { + WH_ERROR_PRINT( + "Async SHA384: sent should remain false on err\n"); + ret = -1; + } + else if (sha384->buffLen != savedBuffLen || + memcmp(sha384->digest, savedDigest, + WC_SHA512_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT( + "Async SHA384: state mutated on rejected call\n"); + ret = -1; + } + } + (void)wc_Sha384Free(sha384); + } + + if (ret == 0) { + WH_TEST_PRINT("SHA384 ASYNC DEVID=0x%X SUCCESS\n", devId); + } + return ret; +} + +#ifdef WOLFHSM_CFG_DMA +/* Direct exercise of the new async DMA SHA384 primitives. */ +static int whTest_CryptoSha384DmaAsync(whClientContext* ctx, int devId, + WC_RNG* rng) +{ + int ret = WH_ERROR_OK; + wc_Sha384 sha384[1]; + uint8_t out[WC_SHA384_DIGEST_SIZE]; + uint8_t ref[WC_SHA384_DIGEST_SIZE]; + /* DMA bypasses the comm buffer, so any size goes; reuse the shared + * static buffer to keep stack pressure low. */ + uint8_t* buf = whTest_Sha384BigBuf; + uint32_t BUFSZ = (uint32_t)sizeof(whTest_Sha384BigBuf); + uint32_t i; + + (void)rng; + + for (i = 0; i < BUFSZ; i++) { + buf[i] = (uint8_t)((i * 17u + 3u) & 0xff); + } + + /* Case A: single large DMA Update + Final */ + ret = wc_InitSha384_ex(sha384, NULL, devId); + if (ret == 0) { + bool sent = false; + ret = wh_Client_Sha384DmaUpdateRequest(ctx, sha384, buf, BUFSZ, &sent); + if (ret == 0 && sent) { + do { + ret = wh_Client_Sha384DmaUpdateResponse(ctx, sha384); + } while (ret == WH_ERROR_NOTREADY); + } + } + if (ret == 0) { + ret = wh_Client_Sha384DmaFinalRequest(ctx, sha384); + } + if (ret == 0) { + do { + ret = wh_Client_Sha384DmaFinalResponse(ctx, sha384, out); + } while (ret == WH_ERROR_NOTREADY); + } + (void)wc_Sha384Free(sha384); + if (ret == 0) { + ret = whTest_Sha384Reference(buf, BUFSZ, ref); + } + if (ret == 0 && memcmp(out, ref, WC_SHA384_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT("Async DMA SHA384 case A mismatch\n"); + ret = -1; + } + + /* Case B: multiple DMA Update round-trips, then Final */ + if (ret == 0) { + uint32_t consumed = 0; + ret = wc_InitSha384_ex(sha384, NULL, devId); + while (ret == 0 && consumed < BUFSZ) { + uint32_t chunk = 1024; + if (consumed + chunk > BUFSZ) { + chunk = BUFSZ - consumed; + } + { + bool sent = false; + ret = wh_Client_Sha384DmaUpdateRequest( + ctx, sha384, buf + consumed, chunk, &sent); + if (ret == 0 && sent) { + do { + ret = wh_Client_Sha384DmaUpdateResponse(ctx, sha384); + } while (ret == WH_ERROR_NOTREADY); + } + } + consumed += chunk; + } + if (ret == 0) { + ret = wh_Client_Sha384DmaFinalRequest(ctx, sha384); + } + if (ret == 0) { + do { + ret = wh_Client_Sha384DmaFinalResponse(ctx, sha384, out); + } while (ret == WH_ERROR_NOTREADY); + } + (void)wc_Sha384Free(sha384); + if (ret == 0) { + ret = whTest_Sha384Reference(buf, BUFSZ, ref); + } + if (ret == 0 && memcmp(out, ref, WC_SHA384_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT("Async DMA SHA384 case B mismatch\n"); + ret = -1; } } + if (ret == 0) { - WH_TEST_PRINT("SHA384 DEVID=0x%X SUCCESS\n", devId); + WH_TEST_PRINT("SHA384 DMA ASYNC DEVID=0x%X SUCCESS\n", devId); } return ret; } +#endif /* WOLFHSM_CFG_DMA */ + #endif /* WOLFSSL_SHA384 */ #ifdef WOLFSSL_SHA512 @@ -1950,6 +3146,405 @@ static int whTest_CryptoSha512(whClientContext* ctx, int devId, WC_RNG* rng) } return ret; } + +/* Hash a buffer with a pure-software SHA512 (no devId) so we can compare. */ +static int whTest_Sha512Reference(const uint8_t* in, uint32_t inLen, + uint8_t out[WC_SHA512_DIGEST_SIZE]) +{ + wc_Sha512 sw[1]; + int ret = wc_InitSha512_ex(sw, NULL, INVALID_DEVID); + if (ret == 0) { + ret = wc_Sha512Update(sw, in, inLen); + } + if (ret == 0) { + ret = wc_Sha512Final(sw, out); + } + (void)wc_Sha512Free(sw); + return ret; +} + +/* Drive the new multi-block wire format through the blocking wrapper. Tests: + * - large multi-request input, + * - exact per-call inline capacity boundary, + * - one-byte-over-capacity boundary (forces a tail in the client buffer), + * - non-aligned chunked update sequence. + * + * Buffer is sized to comfortably exceed the per-call inline capacity at any + * reasonable comm-buffer size. We use a static buffer to keep stack pressure + * low under ASAN. */ +static uint8_t + whTest_Sha512BigBuf[2 * + (WH_MESSAGE_CRYPTO_SHA512_MAX_INLINE_UPDATE_SZ + 128u)]; + +static int whTest_CryptoSha512LargeInput(whClientContext* ctx, int devId, + WC_RNG* rng) +{ + int ret = WH_ERROR_OK; + wc_Sha512 sha512[1]; + uint8_t out[WC_SHA512_DIGEST_SIZE]; + uint8_t ref[WC_SHA512_DIGEST_SIZE]; + uint8_t* buf = whTest_Sha512BigBuf; + uint32_t BUFSZ = (uint32_t)sizeof(whTest_Sha512BigBuf); + uint32_t i; + + (void)ctx; + (void)rng; + + for (i = 0; i < BUFSZ; i++) { + buf[i] = (uint8_t)(i & 0xff); + } + + /* Test 1: large single-update */ + ret = wc_InitSha512_ex(sha512, NULL, devId); + if (ret == 0) { + ret = wc_Sha512Update(sha512, buf, BUFSZ); + } + if (ret == 0) { + ret = wc_Sha512Final(sha512, out); + } + (void)wc_Sha512Free(sha512); + if (ret == 0) { + ret = whTest_Sha512Reference(buf, BUFSZ, ref); + } + if (ret == 0 && memcmp(out, ref, WC_SHA512_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT("SHA512 large input mismatch\n"); + ret = -1; + } + + /* Test 2: exactly the per-call inline capacity in one shot */ + if (ret == 0) { + const uint32_t cap = WH_MESSAGE_CRYPTO_SHA512_MAX_INLINE_UPDATE_SZ; + ret = wc_InitSha512_ex(sha512, NULL, devId); + if (ret == 0) { + ret = wc_Sha512Update(sha512, buf, cap); + } + if (ret == 0) { + ret = wc_Sha512Final(sha512, out); + } + (void)wc_Sha512Free(sha512); + if (ret == 0) { + ret = whTest_Sha512Reference(buf, cap, ref); + } + if (ret == 0 && memcmp(out, ref, WC_SHA512_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT("SHA512 capacity-boundary mismatch\n"); + ret = -1; + } + } + + /* Test 3: capacity + 1 byte (one full request, then a tail buffered) */ + if (ret == 0) { + const uint32_t cap1 = + WH_MESSAGE_CRYPTO_SHA512_MAX_INLINE_UPDATE_SZ + 1u; + ret = wc_InitSha512_ex(sha512, NULL, devId); + if (ret == 0) { + ret = wc_Sha512Update(sha512, buf, cap1); + } + if (ret == 0) { + ret = wc_Sha512Final(sha512, out); + } + (void)wc_Sha512Free(sha512); + if (ret == 0) { + ret = whTest_Sha512Reference(buf, cap1, ref); + } + if (ret == 0 && memcmp(out, ref, WC_SHA512_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT("SHA512 capacity+1 mismatch\n"); + ret = -1; + } + } + + /* Test 4: non-aligned chunk stress test */ + if (ret == 0) { + const uint32_t chunks[] = {13, 17, 1280, 41, 1}; + const size_t nChunks = sizeof(chunks) / sizeof(chunks[0]); + uint32_t total = 0; + size_t k; + for (k = 0; k < nChunks; k++) { + total += chunks[k]; + } + if (total > BUFSZ) { + WH_ERROR_PRINT("test buffer too small for chunked stress test\n"); + ret = -1; + } + if (ret == 0) { + uint32_t off = 0; + ret = wc_InitSha512_ex(sha512, NULL, devId); + for (k = 0; ret == 0 && k < nChunks; k++) { + ret = wc_Sha512Update(sha512, buf + off, chunks[k]); + off += chunks[k]; + } + if (ret == 0) { + ret = wc_Sha512Final(sha512, out); + } + (void)wc_Sha512Free(sha512); + if (ret == 0) { + ret = whTest_Sha512Reference(buf, total, ref); + } + if (ret == 0 && memcmp(out, ref, WC_SHA512_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT("SHA512 chunked stress mismatch\n"); + ret = -1; + } + } + } + + if (ret == 0) { + WH_TEST_PRINT("SHA512 LARGE-INPUT DEVID=0x%X SUCCESS\n", devId); + } + return ret; +} + +/* Direct exercise of the new async non-DMA SHA512 primitives. */ +static int whTest_CryptoSha512Async(whClientContext* ctx, int devId, + WC_RNG* rng) +{ + int ret = WH_ERROR_OK; + wc_Sha512 sha512[1]; + uint8_t out[WC_SHA512_DIGEST_SIZE]; + uint8_t ref[WC_SHA512_DIGEST_SIZE]; + /* Use the same large static buffer as the LargeInput test. */ + uint8_t* buf = whTest_Sha512BigBuf; + uint32_t BUFSZ = (uint32_t)sizeof(whTest_Sha512BigBuf); + uint32_t i; + bool sent; + + (void)rng; + + for (i = 0; i < BUFSZ; i++) { + buf[i] = (uint8_t)((i * 31u + 7u) & 0xff); + } + + /* Case A: basic UpdateRequest -> UpdateResponse -> Final */ + ret = wc_InitSha512_ex(sha512, NULL, devId); + if (ret == 0) { + sent = false; + ret = wh_Client_Sha512UpdateRequest(ctx, sha512, buf, 256, &sent); + } + if (ret == 0 && sent) { + do { + ret = wh_Client_Sha512UpdateResponse(ctx, sha512); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0) { + ret = wh_Client_Sha512FinalRequest(ctx, sha512); + } + if (ret == 0) { + do { + ret = wh_Client_Sha512FinalResponse(ctx, sha512, out); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0) { + ret = whTest_Sha512Reference(buf, 256, ref); + } + if (ret == 0 && memcmp(out, ref, WC_SHA512_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT("Async SHA512 case A mismatch\n"); + ret = -1; + } + + /* Case B: pure-buffer-fill update (sent must be false), then finalize */ + if (ret == 0) { + (void)wc_Sha512Free(sha512); + ret = wc_InitSha512_ex(sha512, NULL, devId); + } + if (ret == 0) { + sent = true; /* expect to be cleared to false */ + ret = wh_Client_Sha512UpdateRequest(ctx, sha512, buf, 10, &sent); + if (ret == 0 && sent != false) { + WH_ERROR_PRINT( + "Async SHA512: expected sent==false on small update\n"); + ret = -1; + } + } + if (ret == 0) { + ret = wh_Client_Sha512FinalRequest(ctx, sha512); + } + if (ret == 0) { + do { + ret = wh_Client_Sha512FinalResponse(ctx, sha512, out); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0) { + ret = whTest_Sha512Reference(buf, 10, ref); + } + if (ret == 0 && memcmp(out, ref, WC_SHA512_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT("Async SHA512 case B mismatch\n"); + ret = -1; + } + + /* Case C: multi-round async updates that span more than the per-call + * inline capacity (forces multiple Request/Response pairs). */ + if (ret == 0) { + uint32_t consumed = 0; + (void)wc_Sha512Free(sha512); + ret = wc_InitSha512_ex(sha512, NULL, devId); + while (ret == 0 && consumed < BUFSZ) { + uint32_t chunk = 1400; /* arbitrary, less than max inline */ + if (consumed + chunk > BUFSZ) { + chunk = BUFSZ - consumed; + } + sent = false; + ret = wh_Client_Sha512UpdateRequest(ctx, sha512, buf + consumed, + chunk, &sent); + if (ret == 0 && sent) { + do { + ret = wh_Client_Sha512UpdateResponse(ctx, sha512); + } while (ret == WH_ERROR_NOTREADY); + } + consumed += chunk; + } + if (ret == 0) { + ret = wh_Client_Sha512FinalRequest(ctx, sha512); + } + if (ret == 0) { + do { + ret = wh_Client_Sha512FinalResponse(ctx, sha512, out); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0) { + ret = whTest_Sha512Reference(buf, BUFSZ, ref); + } + if (ret == 0 && memcmp(out, ref, WC_SHA512_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT("Async SHA512 case C mismatch\n"); + ret = -1; + } + } + + /* Case D: oversized-input rejection. UpdateRequest with inLen > capacity + * must return BADARGS without mutating sha. */ + if (ret == 0) { + uint8_t savedDigest[WC_SHA512_DIGEST_SIZE]; + word32 savedBuffLen; + uint32_t cap; + int rc; + (void)wc_Sha512Free(sha512); + ret = wc_InitSha512_ex(sha512, NULL, devId); + if (ret == 0) { + memcpy(savedDigest, sha512->digest, WC_SHA512_DIGEST_SIZE); + savedBuffLen = sha512->buffLen; + cap = WH_MESSAGE_CRYPTO_SHA512_MAX_INLINE_UPDATE_SZ + + (uint32_t)(WC_SHA512_BLOCK_SIZE - 1u - sha512->buffLen); + sent = true; + rc = wh_Client_Sha512UpdateRequest(ctx, sha512, buf, cap + 1u, + &sent); + if (rc != WH_ERROR_BADARGS) { + WH_ERROR_PRINT("Async SHA512: expected BADARGS, got %d\n", rc); + ret = -1; + } + else if (sent != false) { + WH_ERROR_PRINT( + "Async SHA512: sent should remain false on err\n"); + ret = -1; + } + else if (sha512->buffLen != savedBuffLen || + memcmp(sha512->digest, savedDigest, + WC_SHA512_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT( + "Async SHA512: state mutated on rejected call\n"); + ret = -1; + } + } + (void)wc_Sha512Free(sha512); + } + + if (ret == 0) { + WH_TEST_PRINT("SHA512 ASYNC DEVID=0x%X SUCCESS\n", devId); + } + return ret; +} + +#ifdef WOLFHSM_CFG_DMA +/* Direct exercise of the new async DMA SHA512 primitives. */ +static int whTest_CryptoSha512DmaAsync(whClientContext* ctx, int devId, + WC_RNG* rng) +{ + int ret = WH_ERROR_OK; + wc_Sha512 sha512[1]; + uint8_t out[WC_SHA512_DIGEST_SIZE]; + uint8_t ref[WC_SHA512_DIGEST_SIZE]; + /* DMA bypasses the comm buffer, so any size goes; reuse the shared + * static buffer to keep stack pressure low. */ + uint8_t* buf = whTest_Sha512BigBuf; + uint32_t BUFSZ = (uint32_t)sizeof(whTest_Sha512BigBuf); + uint32_t i; + + (void)rng; + + for (i = 0; i < BUFSZ; i++) { + buf[i] = (uint8_t)((i * 17u + 3u) & 0xff); + } + + /* Case A: single large DMA Update + Final */ + ret = wc_InitSha512_ex(sha512, NULL, devId); + if (ret == 0) { + bool sent = false; + ret = wh_Client_Sha512DmaUpdateRequest(ctx, sha512, buf, BUFSZ, &sent); + if (ret == 0 && sent) { + do { + ret = wh_Client_Sha512DmaUpdateResponse(ctx, sha512); + } while (ret == WH_ERROR_NOTREADY); + } + } + if (ret == 0) { + ret = wh_Client_Sha512DmaFinalRequest(ctx, sha512); + } + if (ret == 0) { + do { + ret = wh_Client_Sha512DmaFinalResponse(ctx, sha512, out); + } while (ret == WH_ERROR_NOTREADY); + } + (void)wc_Sha512Free(sha512); + if (ret == 0) { + ret = whTest_Sha512Reference(buf, BUFSZ, ref); + } + if (ret == 0 && memcmp(out, ref, WC_SHA512_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT("Async DMA SHA512 case A mismatch\n"); + ret = -1; + } + + /* Case B: multiple DMA Update round-trips, then Final */ + if (ret == 0) { + uint32_t consumed = 0; + ret = wc_InitSha512_ex(sha512, NULL, devId); + while (ret == 0 && consumed < BUFSZ) { + uint32_t chunk = 1024; + if (consumed + chunk > BUFSZ) { + chunk = BUFSZ - consumed; + } + { + bool sent = false; + ret = wh_Client_Sha512DmaUpdateRequest( + ctx, sha512, buf + consumed, chunk, &sent); + if (ret == 0 && sent) { + do { + ret = wh_Client_Sha512DmaUpdateResponse(ctx, sha512); + } while (ret == WH_ERROR_NOTREADY); + } + } + consumed += chunk; + } + if (ret == 0) { + ret = wh_Client_Sha512DmaFinalRequest(ctx, sha512); + } + if (ret == 0) { + do { + ret = wh_Client_Sha512DmaFinalResponse(ctx, sha512, out); + } while (ret == WH_ERROR_NOTREADY); + } + (void)wc_Sha512Free(sha512); + if (ret == 0) { + ret = whTest_Sha512Reference(buf, BUFSZ, ref); + } + if (ret == 0 && memcmp(out, ref, WC_SHA512_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT("Async DMA SHA512 case B mismatch\n"); + ret = -1; + } + } + + if (ret == 0) { + WH_TEST_PRINT("SHA512 DMA ASYNC DEVID=0x%X SUCCESS\n", devId); + } + return ret; +} +#endif /* WOLFHSM_CFG_DMA */ + #endif /* WOLFSSL_SHA512 */ #ifdef HAVE_HKDF @@ -5881,40 +7476,88 @@ int whTest_CryptoClientConfig(whClientConfig* config) i = 0; while ((ret == WH_ERROR_OK) && (i < WH_NUM_DEVIDS)) { ret = whTest_CryptoSha256(client, WH_DEV_IDS_ARRAY[i], rng); + if (ret == WH_ERROR_OK) { + ret = + whTest_CryptoSha256LargeInput(client, WH_DEV_IDS_ARRAY[i], rng); + } + if (ret == WH_ERROR_OK) { + ret = whTest_CryptoSha256Async(client, WH_DEV_IDS_ARRAY[i], rng); + } if (ret == WH_ERROR_OK) { i++; } } +#ifdef WOLFHSM_CFG_DMA + if (ret == WH_ERROR_OK) { + ret = whTest_CryptoSha256DmaAsync(client, WH_DEV_ID_DMA, rng); + } +#endif /* WOLFHSM_CFG_DMA */ #endif /* !NO_SHA256 */ #ifdef WOLFSSL_SHA224 i = 0; while ((ret == WH_ERROR_OK) && (i < WH_NUM_DEVIDS)) { ret = whTest_CryptoSha224(client, WH_DEV_IDS_ARRAY[i], rng); + if (ret == WH_ERROR_OK) { + ret = + whTest_CryptoSha224LargeInput(client, WH_DEV_IDS_ARRAY[i], rng); + } + if (ret == WH_ERROR_OK) { + ret = whTest_CryptoSha224Async(client, WH_DEV_IDS_ARRAY[i], rng); + } if (ret == WH_ERROR_OK) { i++; } } +#ifdef WOLFHSM_CFG_DMA + if (ret == WH_ERROR_OK) { + ret = whTest_CryptoSha224DmaAsync(client, WH_DEV_ID_DMA, rng); + } +#endif /* WOLFHSM_CFG_DMA */ #endif /* WOLFSSL_SHA224 */ #ifdef WOLFSSL_SHA384 i = 0; while ((ret == WH_ERROR_OK) && (i < WH_NUM_DEVIDS)) { ret = whTest_CryptoSha384(client, WH_DEV_IDS_ARRAY[i], rng); + if (ret == WH_ERROR_OK) { + ret = + whTest_CryptoSha384LargeInput(client, WH_DEV_IDS_ARRAY[i], rng); + } + if (ret == WH_ERROR_OK) { + ret = whTest_CryptoSha384Async(client, WH_DEV_IDS_ARRAY[i], rng); + } if (ret == WH_ERROR_OK) { i++; } } +#ifdef WOLFHSM_CFG_DMA + if (ret == WH_ERROR_OK) { + ret = whTest_CryptoSha384DmaAsync(client, WH_DEV_ID_DMA, rng); + } +#endif /* WOLFHSM_CFG_DMA */ #endif /* WOLFSSL_SHA384 */ #ifdef WOLFSSL_SHA512 i = 0; while ((ret == WH_ERROR_OK) && (i < WH_NUM_DEVIDS)) { ret = whTest_CryptoSha512(client, WH_DEV_IDS_ARRAY[i], rng); + if (ret == WH_ERROR_OK) { + ret = + whTest_CryptoSha512LargeInput(client, WH_DEV_IDS_ARRAY[i], rng); + } + if (ret == WH_ERROR_OK) { + ret = whTest_CryptoSha512Async(client, WH_DEV_IDS_ARRAY[i], rng); + } if (ret == WH_ERROR_OK) { i++; } } +#ifdef WOLFHSM_CFG_DMA + if (ret == WH_ERROR_OK) { + ret = whTest_CryptoSha512DmaAsync(client, WH_DEV_ID_DMA, rng); + } +#endif /* WOLFHSM_CFG_DMA */ #endif /* WOLFSSL_SHA512 */ #ifdef HAVE_HKDF diff --git a/wolfhsm/wh_client.h b/wolfhsm/wh_client.h index 5fcac92fd..cf4fba3a4 100644 --- a/wolfhsm/wh_client.h +++ b/wolfhsm/wh_client.h @@ -97,10 +97,30 @@ typedef struct { const whDmaAddrAllowList* dmaAddrAllowList; /* allowed addresses */ } whClientDmaConfig; +/* Per-operation async DMA context: stores translated input DMA address + * that must survive across the Request/Response boundary for POST cleanup. + * State is now passed inline (not via DMA), so only input tracking is needed. + * ioAddr: translated DMA address for input POST + * clientAddr: original client address for POST + * ioSz: DMA'd size for POST */ +typedef struct { + uintptr_t ioAddr; + uintptr_t clientAddr; + uint64_t ioSz; +} whClientDmaAsyncSha; + +/* Async DMA context union. Only one DMA request can be in flight at a time + * per client context, so a single union suffices. Each Response function + * knows which member to access based on its own operation type. */ +typedef union { + whClientDmaAsyncSha sha; +} whClientDmaAsyncCtx; + typedef struct { whClientDmaClientMemCb cb; const whDmaAddrAllowList* dmaAddrAllowList; /* allowed addresses */ void* heap; /* heap hint for using static memory (or other allocator) */ + whClientDmaAsyncCtx asyncCtx; } whClientDmaContext; #endif /* WOLFHSM_CFG_DMA */ diff --git a/wolfhsm/wh_client_crypto.h b/wolfhsm/wh_client_crypto.h index b09514a28..e61fb220d 100644 --- a/wolfhsm/wh_client_crypto.h +++ b/wolfhsm/wh_client_crypto.h @@ -30,6 +30,7 @@ /* System libraries */ #include +#include /* Common WolfHSM types and defines shared with the server */ #include "wolfhsm/wh_common.h" @@ -909,6 +910,63 @@ int wh_Client_Sha256(whClientContext* ctx, wc_Sha256* sha, const uint8_t* in, uint32_t inLen, uint8_t* out); +/** + * @brief Async request half of a non-DMA SHA-256 Update. + * + * Serializes and sends an Update request carrying as many full blocks as + * fit in the comm buffer (up to WH_MESSAGE_CRYPTO_SHA256_MAX_INLINE_UPDATE_SZ + * bytes), absorbing any leading bytes already buffered in sha->buffer. Any + * tail (<64 bytes) remaining after this call is stored in sha->buffer for the + * next call. Does NOT wait for a reply. + * + * Contract: at most one outstanding async request may be in flight per + * whClientContext (response matching uses ctx->last_req_kind/last_req_id). + * If *requestSent is true, the caller MUST call wh_Client_Sha256UpdateResponse + * before issuing any other async Request on the same ctx, including a Request + * using a different wc_Sha256 instance or a different algorithm. + * + * @param[in] ctx Client context. + * @param[in,out] sha SHA-256 context (buffer/buffLen updated on success). + * @param[in] in Input data (may be NULL only if inLen == 0). + * @param[in] inLen Input length. Must not exceed the per-call capacity + * (max inline + remaining buffer slack); use the + * blocking wrapper for arbitrary lengths. + * @param[out] requestSent Set to true if a server request was sent and a + * matching Response call is required; false if the + * input was fully absorbed into sha->buffer and no + * round-trip was issued. + * @return WH_ERROR_OK on success, WH_ERROR_BADARGS if inLen exceeds the + * per-call capacity (sha is left unchanged in that case). + */ +int wh_Client_Sha256UpdateRequest(whClientContext* ctx, wc_Sha256* sha, + const uint8_t* in, uint32_t inLen, + bool* requestSent); + +/** + * @brief Async response half of a non-DMA SHA-256 Update. + * + * Single-shot RecvResponse; returns WH_ERROR_NOTREADY if the server has not + * yet replied. On success, updates sha->digest/hiLen/loLen from the reply. + * MUST only be called if the matching Request returned requestSent == true. + */ +int wh_Client_Sha256UpdateResponse(whClientContext* ctx, wc_Sha256* sha); + +/** + * @brief Async request half of a non-DMA SHA-256 Final. + * + * Sends the current sha->buffer (0..63 bytes) as the last block. + */ +int wh_Client_Sha256FinalRequest(whClientContext* ctx, wc_Sha256* sha); + +/** + * @brief Async response half of a non-DMA SHA-256 Final. + * + * Single-shot RecvResponse. Copies final digest into out, then resets sha + * state via wc_InitSha256_ex (preserving devId). + */ +int wh_Client_Sha256FinalResponse(whClientContext* ctx, wc_Sha256* sha, + uint8_t* out); + /** * @brief Performs a SHA-256 hash operation on the input data using DMA. * @@ -925,6 +983,44 @@ int wh_Client_Sha256(whClientContext* ctx, wc_Sha256* sha, const uint8_t* in, int wh_Client_Sha256Dma(whClientContext* ctx, wc_Sha256* sha, const uint8_t* in, uint32_t inLen, uint8_t* out); +#ifdef WOLFHSM_CFG_DMA +/** + * @brief Async request half of a DMA SHA-256 Update. + * + * Buffers partial blocks on the client. Sends whole blocks via DMA to the + * server, with any assembled first block (from the partial buffer) as inline + * trailing data. Sets *requestSent to indicate whether a message was sent + * (false when all input was absorbed into the partial-block buffer). + */ +int wh_Client_Sha256DmaUpdateRequest(whClientContext* ctx, wc_Sha256* sha, + const uint8_t* in, uint32_t inLen, + bool* requestSent); + +/** + * @brief Async response half of a DMA SHA-256 Update. + * + * Receives the server response and restores the updated SHA state from the + * inline response. Runs POST DMA cleanup for the input buffer. + */ +int wh_Client_Sha256DmaUpdateResponse(whClientContext* ctx, wc_Sha256* sha); + +/** + * @brief Async request half of a DMA SHA-256 Final. + * + * Sends the partial-block tail as inline data with the resume state. No DMA + * addresses are used (the final hash is returned inline in the response). + */ +int wh_Client_Sha256DmaFinalRequest(whClientContext* ctx, wc_Sha256* sha); + +/** + * @brief Async response half of a DMA SHA-256 Final. + * + * Receives the final hash from the inline response and copies it to out. + */ +int wh_Client_Sha256DmaFinalResponse(whClientContext* ctx, wc_Sha256* sha, + uint8_t* out); +#endif /* WOLFHSM_CFG_DMA */ + #endif /* !NO_SHA256 */ #if defined(WOLFSSL_SHA224) @@ -943,6 +1039,64 @@ int wh_Client_Sha256Dma(whClientContext* ctx, wc_Sha256* sha, const uint8_t* in, */ int wh_Client_Sha224(whClientContext* ctx, wc_Sha224* sha, const uint8_t* in, uint32_t inLen, uint8_t* out); + +/** + * @brief Async request half of a non-DMA SHA-224 Update. + * + * Serializes and sends an Update request carrying as many full blocks as + * fit in the comm buffer (up to WH_MESSAGE_CRYPTO_SHA224_MAX_INLINE_UPDATE_SZ + * bytes), absorbing any leading bytes already buffered in sha->buffer. Any + * tail (<64 bytes) remaining after this call is stored in sha->buffer for the + * next call. Does NOT wait for a reply. + * + * Contract: at most one outstanding async request may be in flight per + * whClientContext (response matching uses ctx->last_req_kind/last_req_id). + * If *requestSent is true, the caller MUST call wh_Client_Sha224UpdateResponse + * before issuing any other async Request on the same ctx, including a Request + * using a different wc_Sha224 instance or a different algorithm. + * + * @param[in] ctx Client context. + * @param[in,out] sha SHA-224 context (buffer/buffLen updated on success). + * @param[in] in Input data (may be NULL only if inLen == 0). + * @param[in] inLen Input length. Must not exceed the per-call capacity + * (max inline + remaining buffer slack); use the + * blocking wrapper for arbitrary lengths. + * @param[out] requestSent Set to true if a server request was sent and a + * matching Response call is required; false if the + * input was fully absorbed into sha->buffer and no + * round-trip was issued. + * @return WH_ERROR_OK on success, WH_ERROR_BADARGS if inLen exceeds the + * per-call capacity (sha is left unchanged in that case). + */ +int wh_Client_Sha224UpdateRequest(whClientContext* ctx, wc_Sha224* sha, + const uint8_t* in, uint32_t inLen, + bool* requestSent); + +/** + * @brief Async response half of a non-DMA SHA-224 Update. + * + * Single-shot RecvResponse; returns WH_ERROR_NOTREADY if the server has not + * yet replied. On success, updates sha->digest/hiLen/loLen from the reply. + * MUST only be called if the matching Request returned requestSent == true. + */ +int wh_Client_Sha224UpdateResponse(whClientContext* ctx, wc_Sha224* sha); + +/** + * @brief Async request half of a non-DMA SHA-224 Final. + * + * Sends the current sha->buffer (0..63 bytes) as the last block. + */ +int wh_Client_Sha224FinalRequest(whClientContext* ctx, wc_Sha224* sha); + +/** + * @brief Async response half of a non-DMA SHA-224 Final. + * + * Single-shot RecvResponse. Copies final digest into out, then resets sha + * state via wc_InitSha224_ex (preserving devId). + */ +int wh_Client_Sha224FinalResponse(whClientContext* ctx, wc_Sha224* sha, + uint8_t* out); + /** * @brief Performs a SHA-224 hash operation on the input data using DMA. * @@ -958,6 +1112,17 @@ int wh_Client_Sha224(whClientContext* ctx, wc_Sha224* sha, const uint8_t* in, */ int wh_Client_Sha224Dma(whClientContext* ctx, wc_Sha224* sha, const uint8_t* in, uint32_t inLen, uint8_t* out); + +#ifdef WOLFHSM_CFG_DMA +int wh_Client_Sha224DmaUpdateRequest(whClientContext* ctx, wc_Sha224* sha, + const uint8_t* in, uint32_t inLen, + bool* requestSent); +int wh_Client_Sha224DmaUpdateResponse(whClientContext* ctx, wc_Sha224* sha); +int wh_Client_Sha224DmaFinalRequest(whClientContext* ctx, wc_Sha224* sha); +int wh_Client_Sha224DmaFinalResponse(whClientContext* ctx, wc_Sha224* sha, + uint8_t* out); +#endif /* WOLFHSM_CFG_DMA */ + #endif /* WOLFSSL_SHA224 */ #if defined(WOLFSSL_SHA384) @@ -976,6 +1141,64 @@ int wh_Client_Sha224Dma(whClientContext* ctx, wc_Sha224* sha, const uint8_t* in, */ int wh_Client_Sha384(whClientContext* ctx, wc_Sha384* sha, const uint8_t* in, uint32_t inLen, uint8_t* out); + +/** + * @brief Async request half of a non-DMA SHA-384 Update. + * + * Serializes and sends an Update request carrying as many full blocks as + * fit in the comm buffer (up to WH_MESSAGE_CRYPTO_SHA384_MAX_INLINE_UPDATE_SZ + * bytes), absorbing any leading bytes already buffered in sha->buffer. Any + * tail (<128 bytes) remaining after this call is stored in sha->buffer for + * the next call. Does NOT wait for a reply. + * + * Contract: at most one outstanding async request may be in flight per + * whClientContext (response matching uses ctx->last_req_kind/last_req_id). + * If *requestSent is true, the caller MUST call wh_Client_Sha384UpdateResponse + * before issuing any other async Request on the same ctx, including a Request + * using a different wc_Sha384 instance or a different algorithm. + * + * @param[in] ctx Client context. + * @param[in,out] sha SHA-384 context (buffer/buffLen updated on success). + * @param[in] in Input data (may be NULL only if inLen == 0). + * @param[in] inLen Input length. Must not exceed the per-call capacity + * (max inline + remaining buffer slack); use the + * blocking wrapper for arbitrary lengths. + * @param[out] requestSent Set to true if a server request was sent and a + * matching Response call is required; false if the + * input was fully absorbed into sha->buffer and no + * round-trip was issued. + * @return WH_ERROR_OK on success, WH_ERROR_BADARGS if inLen exceeds the + * per-call capacity (sha is left unchanged in that case). + */ +int wh_Client_Sha384UpdateRequest(whClientContext* ctx, wc_Sha384* sha, + const uint8_t* in, uint32_t inLen, + bool* requestSent); + +/** + * @brief Async response half of a non-DMA SHA-384 Update. + * + * Single-shot RecvResponse; returns WH_ERROR_NOTREADY if the server has not + * yet replied. On success, updates sha->digest/hiLen/loLen from the reply. + * MUST only be called if the matching Request returned requestSent == true. + */ +int wh_Client_Sha384UpdateResponse(whClientContext* ctx, wc_Sha384* sha); + +/** + * @brief Async request half of a non-DMA SHA-384 Final. + * + * Sends the current sha->buffer (0..127 bytes) as the last block. + */ +int wh_Client_Sha384FinalRequest(whClientContext* ctx, wc_Sha384* sha); + +/** + * @brief Async response half of a non-DMA SHA-384 Final. + * + * Single-shot RecvResponse. Copies final digest into out, then resets sha + * state via wc_InitSha384_ex (preserving devId). + */ +int wh_Client_Sha384FinalResponse(whClientContext* ctx, wc_Sha384* sha, + uint8_t* out); + /** * @brief Performs a SHA-384 hash operation on the input data using DMA. * @@ -992,6 +1215,16 @@ int wh_Client_Sha384(whClientContext* ctx, wc_Sha384* sha, const uint8_t* in, int wh_Client_Sha384Dma(whClientContext* ctx, wc_Sha384* sha, const uint8_t* in, uint32_t inLen, uint8_t* out); +#ifdef WOLFHSM_CFG_DMA +int wh_Client_Sha384DmaUpdateRequest(whClientContext* ctx, wc_Sha384* sha, + const uint8_t* in, uint32_t inLen, + bool* requestSent); +int wh_Client_Sha384DmaUpdateResponse(whClientContext* ctx, wc_Sha384* sha); +int wh_Client_Sha384DmaFinalRequest(whClientContext* ctx, wc_Sha384* sha); +int wh_Client_Sha384DmaFinalResponse(whClientContext* ctx, wc_Sha384* sha, + uint8_t* out); +#endif /* WOLFHSM_CFG_DMA */ + #endif /* WOLFSSL_SHA384 */ #if defined(WOLFSSL_SHA512) @@ -1010,6 +1243,64 @@ int wh_Client_Sha384Dma(whClientContext* ctx, wc_Sha384* sha, const uint8_t* in, */ int wh_Client_Sha512(whClientContext* ctx, wc_Sha512* sha, const uint8_t* in, uint32_t inLen, uint8_t* out); + +/** + * @brief Async request half of a non-DMA SHA-512 Update. + * + * Serializes and sends an Update request carrying as many full blocks as + * fit in the comm buffer (up to WH_MESSAGE_CRYPTO_SHA512_MAX_INLINE_UPDATE_SZ + * bytes), absorbing any leading bytes already buffered in sha->buffer. Any + * tail (<128 bytes) remaining after this call is stored in sha->buffer for + * the next call. Does NOT wait for a reply. + * + * Contract: at most one outstanding async request may be in flight per + * whClientContext (response matching uses ctx->last_req_kind/last_req_id). + * If *requestSent is true, the caller MUST call wh_Client_Sha512UpdateResponse + * before issuing any other async Request on the same ctx, including a Request + * using a different wc_Sha512 instance or a different algorithm. + * + * @param[in] ctx Client context. + * @param[in,out] sha SHA-512 context (buffer/buffLen updated on success). + * @param[in] in Input data (may be NULL only if inLen == 0). + * @param[in] inLen Input length. Must not exceed the per-call capacity + * (max inline + remaining buffer slack); use the + * blocking wrapper for arbitrary lengths. + * @param[out] requestSent Set to true if a server request was sent and a + * matching Response call is required; false if the + * input was fully absorbed into sha->buffer and no + * round-trip was issued. + * @return WH_ERROR_OK on success, WH_ERROR_BADARGS if inLen exceeds the + * per-call capacity (sha is left unchanged in that case). + */ +int wh_Client_Sha512UpdateRequest(whClientContext* ctx, wc_Sha512* sha, + const uint8_t* in, uint32_t inLen, + bool* requestSent); + +/** + * @brief Async response half of a non-DMA SHA-512 Update. + * + * Single-shot RecvResponse; returns WH_ERROR_NOTREADY if the server has not + * yet replied. On success, updates sha->digest/hiLen/loLen from the reply. + * MUST only be called if the matching Request returned requestSent == true. + */ +int wh_Client_Sha512UpdateResponse(whClientContext* ctx, wc_Sha512* sha); + +/** + * @brief Async request half of a non-DMA SHA-512 Final. + * + * Sends the current sha->buffer (0..127 bytes) as the last block. + */ +int wh_Client_Sha512FinalRequest(whClientContext* ctx, wc_Sha512* sha); + +/** + * @brief Async response half of a non-DMA SHA-512 Final. + * + * Single-shot RecvResponse. Copies final digest into out, then resets sha + * state via wc_InitSha512_ex (preserving devId and hashType). + */ +int wh_Client_Sha512FinalResponse(whClientContext* ctx, wc_Sha512* sha, + uint8_t* out); + /** * @brief Performs a SHA-512 hash operation on the input data using DMA. * @@ -1025,6 +1316,17 @@ int wh_Client_Sha512(whClientContext* ctx, wc_Sha512* sha, const uint8_t* in, */ int wh_Client_Sha512Dma(whClientContext* ctx, wc_Sha512* sha, const uint8_t* in, uint32_t inLen, uint8_t* out); + +#ifdef WOLFHSM_CFG_DMA +int wh_Client_Sha512DmaUpdateRequest(whClientContext* ctx, wc_Sha512* sha, + const uint8_t* in, uint32_t inLen, + bool* requestSent); +int wh_Client_Sha512DmaUpdateResponse(whClientContext* ctx, wc_Sha512* sha); +int wh_Client_Sha512DmaFinalRequest(whClientContext* ctx, wc_Sha512* sha); +int wh_Client_Sha512DmaFinalResponse(whClientContext* ctx, wc_Sha512* sha, + uint8_t* out); +#endif /* WOLFHSM_CFG_DMA */ + #endif /* WOLFSSL_SHA512 */ #ifdef HAVE_DILITHIUM diff --git a/wolfhsm/wh_message_crypto.h b/wolfhsm/wh_message_crypto.h index 6f33d89cf..34f783ae3 100644 --- a/wolfhsm/wh_message_crypto.h +++ b/wolfhsm/wh_message_crypto.h @@ -740,48 +740,104 @@ int wh_MessageCrypto_TranslateEd25519VerifyResponse( * SHA */ -/* SHA256 and SHA224 Request */ +/* SHA256/SHA224 Request (variable-length input data follows the struct). + * + * Wire layout in the comm buffer: + * whMessageCrypto_GenericRequestHeader + * whMessageCrypto_Sha256Request + * uint8_t in[inSz] + * + * Non-final updates: inSz must be a multiple of WC_SHA256_BLOCK_SIZE (or + * WC_SHA224_BLOCK_SIZE for SHA224, which is the same value). + * Final: 0..(BLOCK_SIZE - 1). The client buffers any partial-block tail + * locally and only sends the final tail with isLastBlock=1. + */ typedef struct { struct { uint32_t hiLen; uint32_t loLen; /* intermediate hash value */ - uint8_t hash[32]; /* TODO (BRN) WC_SHA256_DIGEST_SIZE */ + uint8_t hash[32]; /* WC_SHA256_DIGEST_SIZE */ } resumeState; - /* Flag indicating to the server that this is the last block and it should - * finalize the hash. If set, inBlock may be only partially full*/ + /* 1 = last block; server finalizes after consuming inSz bytes. + * 0 = non-final update; inSz MUST be a multiple of the block size. */ uint32_t isLastBlock; - /* Length of the last input block of data. Only valid if isLastBlock=1 */ - uint32_t lastBlockLen; - /* Full sha256 input block to hash */ - uint8_t inBlock[64]; /* TODO (BRN) WC_SHA256_BLOCK_SIZE */ - uint8_t WH_PAD[4]; + /* Number of input bytes trailing this struct. */ + uint32_t inSz; } whMessageCrypto_Sha256Request; +/* Maximum number of input bytes that can be carried inline (after the generic + * crypto request header and the Sha256Request struct) in a single comm-buffer + * message, rounded down to a multiple of WC_SHA256_BLOCK_SIZE so non-final + * updates always carry whole blocks. */ +#define WH_MESSAGE_CRYPTO_SHA256_MAX_INLINE_UPDATE_SZ \ + (((WOLFHSM_CFG_COMM_DATA_LEN - \ + (uint32_t)sizeof(whMessageCrypto_GenericRequestHeader) - \ + (uint32_t)sizeof(whMessageCrypto_Sha256Request)) / \ + 64u) * \ + 64u) + +WH_UTILS_STATIC_ASSERT(WH_MESSAGE_CRYPTO_SHA256_MAX_INLINE_UPDATE_SZ >= 64u, + "Comm buffer too small to fit a SHA256 block"); + +/* SHA224 shares the SHA256 wire format and block size (64), so the same + * per-call inline capacity applies. Exposed as a separate macro so SHA224 + * call sites stay self-documenting. */ +#define WH_MESSAGE_CRYPTO_SHA224_MAX_INLINE_UPDATE_SZ \ + WH_MESSAGE_CRYPTO_SHA256_MAX_INLINE_UPDATE_SZ + int wh_MessageCrypto_TranslateSha256Request( uint16_t magic, const whMessageCrypto_Sha256Request* src, whMessageCrypto_Sha256Request* dest); -/* SHA512 and SHA384 Request */ +/* SHA512/SHA384 Request (variable-length input data follows the struct). + * + * Wire layout in the comm buffer: + * whMessageCrypto_GenericRequestHeader + * whMessageCrypto_Sha512Request + * uint8_t in[inSz] + * + * Non-final updates: inSz must be a multiple of WC_SHA512_BLOCK_SIZE (or + * WC_SHA384_BLOCK_SIZE for SHA384, which is the same value). + * Final: 0..(BLOCK_SIZE - 1). The client buffers any partial-block tail + * locally and only sends the final tail with isLastBlock=1. + */ typedef struct { struct { uint32_t hiLen; uint32_t loLen; /* intermediate hash value */ - uint8_t hash[64]; /* TODO (HM) WC_SHA512_DIGEST_SIZE */ + uint8_t hash[64]; /* WC_SHA512_DIGEST_SIZE */ uint32_t hashType; } resumeState; - /* Flag indicating to the server that this is the last block and it should - * finalize the hash. If set, inBlock may be only partially full*/ + /* 1 = last block; server finalizes after consuming inSz bytes. + * 0 = non-final update; inSz MUST be a multiple of the block size. */ uint32_t isLastBlock; - /* Length of the last input block of data. Only valid if isLastBlock=1 */ - uint32_t lastBlockLen; - /* Full sha512 input block to hash */ - uint8_t inBlock[128]; /* TODO (HM) WC_SHA512_BLOCK_SIZE 128*/ - uint8_t WH_PAD[4]; + /* Number of input bytes trailing this struct. */ + uint32_t inSz; } whMessageCrypto_Sha512Request; +/* Maximum number of input bytes that can be carried inline (after the generic + * crypto request header and the Sha512Request struct) in a single comm-buffer + * message, rounded down to a multiple of WC_SHA512_BLOCK_SIZE so non-final + * updates always carry whole blocks. */ +#define WH_MESSAGE_CRYPTO_SHA512_MAX_INLINE_UPDATE_SZ \ + (((WOLFHSM_CFG_COMM_DATA_LEN - \ + (uint32_t)sizeof(whMessageCrypto_GenericRequestHeader) - \ + (uint32_t)sizeof(whMessageCrypto_Sha512Request)) / \ + 128u) * \ + 128u) + +WH_UTILS_STATIC_ASSERT(WH_MESSAGE_CRYPTO_SHA512_MAX_INLINE_UPDATE_SZ >= 128u, + "Comm buffer too small to fit a SHA512 block"); + +/* SHA384 shares the SHA512 wire format and block size (128), so the same + * per-call inline capacity applies. Exposed as a separate macro so SHA384 + * call sites stay self-documenting. */ +#define WH_MESSAGE_CRYPTO_SHA384_MAX_INLINE_UPDATE_SZ \ + WH_MESSAGE_CRYPTO_SHA512_MAX_INLINE_UPDATE_SZ + /* SHA2 Response */ typedef struct { /* Resulting hash value */ @@ -966,26 +1022,61 @@ typedef struct { } whMessageCrypto_DmaAddrStatus; -/* SHA2 DMA Request */ +/* SHA256/SHA224 DMA Request - state is passed inline (not via DMA) for + * cross-architecture safety. Only input data goes via DMA. + * + * Wire layout in the comm buffer: + * whMessageCrypto_GenericRequestHeader + * whMessageCrypto_Sha256DmaRequest + * uint8_t in[inSz] (inline trailing data: assembled first block from + * partial buffer, or partial tail on Final) + * + * Non-final: DMA input must be whole blocks. inSz is 0 or BLOCK_SIZE + * (assembled first block from client partial-block buffer). + * Final: inSz = buffLen (0..BLOCK_SIZE-1), no DMA input. */ +typedef struct { + struct { + uint32_t hiLen; + uint32_t loLen; + uint8_t hash[32]; /* WC_SHA256_DIGEST_SIZE */ + } resumeState; + whMessageCrypto_DmaBuffer input; /* DMA whole blocks (Update only) */ + uint32_t isLastBlock; + uint32_t inSz; /* inline trailing data size */ +} whMessageCrypto_Sha256DmaRequest; + +/* SHA512/SHA384 DMA Request */ typedef struct { - /* Since client addresses are subject to DMA checking, we can't use them to - * determine the requested operation (update/final). Therefore we need to - * indicate to the server which SHA224 operation to perform */ - uint64_t finalize; + struct { + uint32_t hiLen; + uint32_t loLen; + uint8_t hash[64]; /* WC_SHA512_DIGEST_SIZE */ + uint32_t hashType; + uint8_t WH_PAD[4]; + } resumeState; whMessageCrypto_DmaBuffer input; - whMessageCrypto_DmaBuffer state; - whMessageCrypto_DmaBuffer output; -} whMessageCrypto_Sha2DmaRequest; + uint32_t isLastBlock; + uint32_t inSz; +} whMessageCrypto_Sha512DmaRequest; -/* SHA224 DMA Response */ +/* SHA2 DMA Response - carries updated state or final hash inline */ typedef struct { + uint32_t hiLen; + uint32_t loLen; + uint8_t hash[64]; /* big enough for all SHA2 variants */ + uint32_t hashType; whMessageCrypto_DmaAddrStatus dmaAddrStatus; + uint8_t WH_PAD[4]; } whMessageCrypto_Sha2DmaResponse; /* SHA2 DMA translation functions */ -int wh_MessageCrypto_TranslateSha2DmaRequest( - uint16_t magic, const whMessageCrypto_Sha2DmaRequest* src, - whMessageCrypto_Sha2DmaRequest* dest); +int wh_MessageCrypto_TranslateSha256DmaRequest( + uint16_t magic, const whMessageCrypto_Sha256DmaRequest* src, + whMessageCrypto_Sha256DmaRequest* dest); + +int wh_MessageCrypto_TranslateSha512DmaRequest( + uint16_t magic, const whMessageCrypto_Sha512DmaRequest* src, + whMessageCrypto_Sha512DmaRequest* dest); int wh_MessageCrypto_TranslateSha2DmaResponse( uint16_t magic, const whMessageCrypto_Sha2DmaResponse* src, From 7ef52c5a0d6427344e5f1fc928cfb9a99ecd6dd9 Mon Sep 17 00:00:00 2001 From: Brett Nicholas <7547222+bigbrett@users.noreply.github.com> Date: Tue, 14 Apr 2026 13:36:46 -0600 Subject: [PATCH 2/3] fix gcov option for gcov bug --- .github/workflows/code-coverage.yml | 2 +- test/Makefile | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml index 3b7e10ab3..efb2043b0 100644 --- a/.github/workflows/code-coverage.yml +++ b/.github/workflows/code-coverage.yml @@ -44,7 +44,7 @@ jobs: run: | echo "=== Coverage Summary ===" cd test - gcovr Build --root .. --filter '\.\./src/.*' --filter '\.\./wolfhsm/.*' --print-summary + gcovr Build --root .. --gcov-ignore-parse-errors=negative_hits.warn_once_per_file --filter '\.\./src/.*' --filter '\.\./wolfhsm/.*' --print-summary # Upload coverage report as artifact - name: Upload coverage report diff --git a/test/Makefile b/test/Makefile index f457dcbf4..06ebb59c4 100644 --- a/test/Makefile +++ b/test/Makefile @@ -332,6 +332,7 @@ coverage: mkdir -p ../coverage && gcovr Build \ --root .. \ --gcov-executable gcov \ + --gcov-ignore-parse-errors=negative_hits.warn_once_per_file \ --filter '\.\./src/.*' \ --filter '\.\./wolfhsm/.*' \ --html-details ../coverage/index.html \ From 2244bc017af4222af9b2ab28bd9cb7785a546639 Mon Sep 17 00:00:00 2001 From: Brett Nicholas <7547222+bigbrett@users.noreply.github.com> Date: Thu, 16 Apr 2026 12:28:16 -0600 Subject: [PATCH 3/3] add defensive buf len check --- src/wh_client_crypto.c | 56 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/src/wh_client_crypto.c b/src/wh_client_crypto.c index 1f6c5d4d9..3cf6c1f3d 100644 --- a/src/wh_client_crypto.c +++ b/src/wh_client_crypto.c @@ -4328,6 +4328,10 @@ int wh_Client_Sha256UpdateRequest(whClientContext* ctx, wc_Sha256* sha, } *requestSent = false; + if (sha->buffLen >= WC_SHA256_BLOCK_SIZE) { + return WH_ERROR_BADARGS; + } + capacity = _Sha256UpdatePerCallCapacity(sha); if (inLen > capacity) { return WH_ERROR_BADARGS; @@ -4456,6 +4460,9 @@ int wh_Client_Sha256FinalRequest(whClientContext* ctx, wc_Sha256* sha) if (ctx == NULL || sha == NULL) { return WH_ERROR_BADARGS; } + if (sha->buffLen >= WC_SHA256_BLOCK_SIZE) { + return WH_ERROR_BADARGS; + } dataPtr = wh_CommClient_GetDataPtr(ctx->comm); if (dataPtr == NULL) { @@ -4588,6 +4595,10 @@ int wh_Client_Sha256DmaUpdateRequest(whClientContext* ctx, wc_Sha256* sha, } *requestSent = false; + if (sha->buffLen >= WC_SHA256_BLOCK_SIZE) { + return WH_ERROR_BADARGS; + } + if (inLen == 0) { return WH_ERROR_OK; } @@ -4741,6 +4752,9 @@ int wh_Client_Sha256DmaFinalRequest(whClientContext* ctx, wc_Sha256* sha) if (ctx == NULL || sha == NULL) { return WH_ERROR_BADARGS; } + if (sha->buffLen >= WC_SHA256_BLOCK_SIZE) { + return WH_ERROR_BADARGS; + } dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm); if (dataPtr == NULL) { @@ -4870,6 +4884,10 @@ int wh_Client_Sha224UpdateRequest(whClientContext* ctx, wc_Sha224* sha, } *requestSent = false; + if (sha->buffLen >= WC_SHA224_BLOCK_SIZE) { + return WH_ERROR_BADARGS; + } + capacity = _Sha224UpdatePerCallCapacity(sha); if (inLen > capacity) { return WH_ERROR_BADARGS; @@ -5001,6 +5019,9 @@ int wh_Client_Sha224FinalRequest(whClientContext* ctx, wc_Sha224* sha) if (ctx == NULL || sha == NULL) { return WH_ERROR_BADARGS; } + if (sha->buffLen >= WC_SHA224_BLOCK_SIZE) { + return WH_ERROR_BADARGS; + } dataPtr = wh_CommClient_GetDataPtr(ctx->comm); if (dataPtr == NULL) { @@ -5133,6 +5154,10 @@ int wh_Client_Sha224DmaUpdateRequest(whClientContext* ctx, wc_Sha224* sha, } *requestSent = false; + if (sha->buffLen >= WC_SHA224_BLOCK_SIZE) { + return WH_ERROR_BADARGS; + } + if (inLen == 0) { return WH_ERROR_OK; } @@ -5273,6 +5298,9 @@ int wh_Client_Sha224DmaFinalRequest(whClientContext* ctx, wc_Sha224* sha) if (ctx == NULL || sha == NULL) { return WH_ERROR_BADARGS; } + if (sha->buffLen >= WC_SHA224_BLOCK_SIZE) { + return WH_ERROR_BADARGS; + } dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm); if (dataPtr == NULL) { @@ -5398,6 +5426,10 @@ int wh_Client_Sha384UpdateRequest(whClientContext* ctx, wc_Sha384* sha, } *requestSent = false; + if (sha->buffLen >= WC_SHA384_BLOCK_SIZE) { + return WH_ERROR_BADARGS; + } + capacity = _Sha384UpdatePerCallCapacity(sha); if (inLen > capacity) { return WH_ERROR_BADARGS; @@ -5530,6 +5562,9 @@ int wh_Client_Sha384FinalRequest(whClientContext* ctx, wc_Sha384* sha) if (ctx == NULL || sha == NULL) { return WH_ERROR_BADARGS; } + if (sha->buffLen >= WC_SHA384_BLOCK_SIZE) { + return WH_ERROR_BADARGS; + } dataPtr = wh_CommClient_GetDataPtr(ctx->comm); if (dataPtr == NULL) { @@ -5663,6 +5698,10 @@ int wh_Client_Sha384DmaUpdateRequest(whClientContext* ctx, wc_Sha384* sha, } *requestSent = false; + if (sha->buffLen >= WC_SHA384_BLOCK_SIZE) { + return WH_ERROR_BADARGS; + } + if (inLen == 0) { return WH_ERROR_OK; } @@ -5804,6 +5843,9 @@ int wh_Client_Sha384DmaFinalRequest(whClientContext* ctx, wc_Sha384* sha) if (ctx == NULL || sha == NULL) { return WH_ERROR_BADARGS; } + if (sha->buffLen >= WC_SHA384_BLOCK_SIZE) { + return WH_ERROR_BADARGS; + } dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm); if (dataPtr == NULL) { @@ -5931,6 +5973,10 @@ int wh_Client_Sha512UpdateRequest(whClientContext* ctx, wc_Sha512* sha, } *requestSent = false; + if (sha->buffLen >= WC_SHA512_BLOCK_SIZE) { + return WH_ERROR_BADARGS; + } + capacity = _Sha512UpdatePerCallCapacity(sha); if (inLen > capacity) { return WH_ERROR_BADARGS; @@ -6060,6 +6106,9 @@ int wh_Client_Sha512FinalRequest(whClientContext* ctx, wc_Sha512* sha) if (ctx == NULL || sha == NULL) { return WH_ERROR_BADARGS; } + if (sha->buffLen >= WC_SHA512_BLOCK_SIZE) { + return WH_ERROR_BADARGS; + } dataPtr = wh_CommClient_GetDataPtr(ctx->comm); if (dataPtr == NULL) { @@ -6212,6 +6261,10 @@ int wh_Client_Sha512DmaUpdateRequest(whClientContext* ctx, wc_Sha512* sha, } *requestSent = false; + if (sha->buffLen >= WC_SHA512_BLOCK_SIZE) { + return WH_ERROR_BADARGS; + } + if (inLen == 0) { return WH_ERROR_OK; } @@ -6352,6 +6405,9 @@ int wh_Client_Sha512DmaFinalRequest(whClientContext* ctx, wc_Sha512* sha) if (ctx == NULL || sha == NULL) { return WH_ERROR_BADARGS; } + if (sha->buffLen >= WC_SHA512_BLOCK_SIZE) { + return WH_ERROR_BADARGS; + } dataPtr = (uint8_t*)wh_CommClient_GetDataPtr(ctx->comm); if (dataPtr == NULL) {