Skip to content

Conversation

@bigbrett
Copy link
Contributor

@bigbrett bigbrett commented Feb 3, 2026

Overview

  • Refactors CMAC to use client-held state instead of persisting state on the server
  • Switch to stack-allocated CMAC contexts on the server
  • Removes the cancellation API (unused complexity tied to the old state model)
  • Updates non-DMA oneshot CMAC generate to support cached keys
  • Expands CMAC tests to mirror wolfCrypt test patterns using cached keys

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:

  1. Client-side: The wolfCrypt Cmac struct, 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 digest
    • cmac->bufferSz — how many bytes are in the partial block buffer
    • cmac->totalSz — total bytes processed so far
    • cmac->aes.devKey / cmac->aes.keylen — stashed copy of the raw key (for non-HSM keys)
    • cmac->devCtx — the keyId (for HSM-stored keys)
  2. 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, and totalSz. Critically, the AES subkeys (k1/k2) are never transmitted. The server re-derives them from the key on every request by calling wc_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?

  • Before each request: Client packs its local Cmac fields into the request's resumeState.
  • After each response: Client unpacks the server's returned resumeState back into its local Cmac struct.
  • Key stashing: After the first successful call with a raw key, the client copies the key bytes into cmac->aes.devKey so 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.

@bigbrett bigbrett changed the title CMAC refactor to use client-held state CMAC refactor to use client-held state and deprecate cancellation Feb 3, 2026
@bigbrett bigbrett requested a review from Copilot February 3, 2026 20:18
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR 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 Cmac contexts.
  • 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.

@bigbrett bigbrett changed the title CMAC refactor to use client-held state and deprecate cancellation CMAC refactor + deprecate cancellation Feb 3, 2026
- 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
@bigbrett bigbrett force-pushed the cmac-refactor-client-state branch from 854738a to 2dedb29 Compare February 3, 2026 22:47
@bigbrett bigbrett marked this pull request as ready for review February 3, 2026 23:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants