CMAC refactor + deprecate cancellation#279
Conversation
There was a problem hiding this comment.
Pull request overview
This PR refactors the CMAC implementation to use a stateless server pattern with client-held state, removes the unused cancellation API, and extends CMAC test coverage (including DMA paths) to follow wolfCrypt’s NIST test vectors and cached-key flows.
Changes:
- Redesigns CMAC request/response (including DMA) to carry only non-sensitive intermediate state, with AES subkeys re-derived on each server call using stack-allocated
Cmaccontexts. - Removes the cancellation API and all associated client/server/test wiring, simplifying the transport and error model.
- Updates the CMAC client and server crypto handlers plus tests to support cached keys (both RAM and NVM) for one-shot and incremental CMAC generate/verify in both non-DMA and DMA paths.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
wolfhsm/wh_settings.h |
Removes the WOLFHSM_CFG_CANCEL_API configuration option from the public settings comments to match the removal of the cancellation feature. |
wolfhsm/wh_server.h |
Drops the per-algorithm algoCtx union (including server-side Cmac) and the cancelSeq field from whServerContext, aligning with stateless CMAC and no cancellation. |
wolfhsm/wh_message_crypto.h |
Introduces whMessageCrypto_CmacState and updates CMAC and CMAC DMA request/response structs to embed non-sensitive CMAC state, keeping request/response sizes aligned and documenting the new wire format. |
wolfhsm/wh_message.h |
Removes the WH_MESSAGE_GROUP_CANCEL message group since cancel messages are no longer supported. |
wolfhsm/wh_error.h |
Deletes WH_ERROR_CANCEL and WH_ERROR_CANCEL_LATE error codes to reflect that cancellation is no longer part of the API. |
wolfhsm/wh_client_crypto.h |
Removes the declaration and documentation of wh_Client_CmacCancelableResponse, consistent with dropping cancelable CMAC handling. |
wolfhsm/wh_client.h |
Strips the client-side cancel callback type, associated fields in whClientContext, config wiring, and public cancel APIs (EnableCancel, DisableCancel, Cancel*). |
test/wh_test_crypto.c |
Replaces the previous single CMAC KAT and cancellation tests with a suite of NIST SP 800-38B-based CMAC tests (128/192/256-bit keys; oneshot and incremental; cached and committed keys) and removes all cancel-related test harness plumbing. |
test/config/wolfhsm_cfg.h |
Stops enabling WOLFHSM_CFG_CANCEL_API in the POSIX test harness, fully disabling cancellation in test builds as well. |
src/wh_server_crypto.c |
Reimplements _HandleCmac and _HandleCmacDma to use stack-local Cmac contexts, reconstruct keys from inline bytes or keystore IDs, restore/persist only non-sensitive CMAC state, validate lengths, and remove the cancellation helper logic. |
src/wh_server.c |
Removes server-side canceled sequence getters/setters and the special-case response rewriting for canceled operations. |
src/wh_message_crypto.c |
Adds wh_MessageCrypto_TranslateCmacState and updates CMAC and CMAC DMA request/response translators to include the portable CMAC state plus the new DMA buffer layout, preserving endian correctness. |
src/wh_client_crypto.c |
Updates wh_Client_Cmac and wh_Client_CmacDma to pack/unpack the CMAC state in messages, reuse stashed raw keys for non-HSM incremental calls, enforce message size limits, and drop the cancelable-response path. |
src/wh_client.c |
Removes all client-side cancellation functions and associated context initialization, completing the cancellation API deprecation. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- refactor DMA CMAC to use client-supplied state vs state by reference - refactor to use stack allocated CMAC context - expand CMAC tests to mimic wolfCrypt tests but with cached keys - housekeeping, error checking
854738a to
2dedb29
Compare
AlexLanzano
left a comment
There was a problem hiding this comment.
Looks good overall! Just had a suggestion about how to handle deprecated APIs.
Also, will this change affect any of our current ports if we update wolfHSM?
|
@AlexLanzano Yes it will affect ports if they had |
billphipps
left a comment
There was a problem hiding this comment.
I shed a single tear. :)
A handful of minor nits. This is an excellent course correction! Thank you!
- Reserve removed error codes (wh_error.h): WH_ERROR_RESERVED{1,2}
- Reserve removed message group (wh_message.h): WH_MESSAGE_GROUP_RESERVED
- Rename CMAC → CmacAes (wh_message_crypto.h, all consumers): All structs and translation functions renamed to indicate AES-specific
- Remove `type` field from request structs and translation; use WC_CMAC_AES literal on server
- Remove switch(req.type) in server handlers; guard with #if defined(WOLFSSL_CMAC) && !defined(NO_AES) && defined(WOLFSSL_AES_DIRECT) instead
- Extract _CmacResolveKey() static helper for shared key resolution (inline key or keystore + usage policy + size validation)
- Extract wh_Crypto_CmacAesSaveStateToMsg() / RestoreStateFromMsg() to deduplicate state pack/unpack across client + server + DMA
- BAD_FUNC_ARG → WH_ERROR_BADARGS for bufferSz validation (now inside RestoreStateFromMsg)
- Don't read req.* after response may overlap — use WC_CMAC_AES literal instead of req.type
- #define/#undef → enum for CMAC_MLEN_* test constants
|
@billphipps addressed all your comments |
Overview
Implementation Description
Refactors the server to be stateless*. The server no longer holds onto a CMAC session. Instead, the client carries all non-sensitive intermediate state and re-sends it with each request. The server re-derives the sensitive material (AES subkeys) from the key on every call, restores the portable state, processes new data, and returns updated state.
There are two locations for CMAC state:
Client-side: The wolfCrypt
Cmacstruct, which the caller owns. The client library uses specific fields within this struct to persist state between server round-trips:cmac->buffer[16]— partial block buffer (unprocessed leftover bytes)cmac->digest[16]— running CBC-MAC intermediate digestcmac->bufferSz— how many bytes are in the partial block buffercmac->totalSz— total bytes processed so farcmac->aes.devKey/cmac->aes.keylen— stashed copy of the raw key (for non-HSM keys)cmac->devCtx— the keyId (for HSM-stored keys)Server-side: The server is stateless with respect to CMAC. It does not persist any CMAC context between requests. Every request is self-contained. The client sends enough state for the server to reconstruct a working CMAC context, process the data, and return updated state.
What Gets Transmitted?
A message layer state struct (
whMessageCrypto_CmacState) carries only the non-sensitive fields:buffer,digest,bufferSz, andtotalSz. Critically, the AES subkeys (k1/k2) are never transmitted. The server re-derives them from the key on every request by callingwc_InitCmac_ex(). While I thought about only re-deriving on the finalize, there are other Init operations that must be invoked on every update, and it made the code more complicated. I figured the single AES operation and some bit shifting is fast enough that it could be done every operation.When Is The State Updated?
Cmacfields into the request'sresumeState.resumeStateback into its localCmacstruct.cmac->aes.devKeyso subsequent update/final calls can re-send the key without the caller needing to provide it again.DMA
The DMA path works exactly as the non-DMA path, except the input data is passed by reference. CMAC state and the result are still passed through the transport. The server handling performs the necessary address translations.