From e8caa92e97f8998692d9fd921bdd31cf4b1feeed Mon Sep 17 00:00:00 2001 From: Tobias Frauenschlaeger Date: Tue, 1 Sep 2026 13:07:42 +0200 Subject: [PATCH 1/2] client: report WH_ERROR_REQUEST_SIZE when AES will not fit the comm buffer wh_Client_AesGcmRequest and the CBC, CTR and ECB request builders return WH_ERROR_BADARGS when the request exceeds WOLFHSM_CFG_COMM_DATA_LEN. The arguments are valid; the transport is just too small for them, and a caller cannot tell the two apart. On a TC4Dx demo with COMM_DATA_LEN 8192, a TLS record above about 8.1 KiB surfaced as a decrypt failure and bad_record_mac. Return a distinct WH_ERROR_REQUEST_SIZE instead. The check sits ahead of every memcpy into the comm buffer and ahead of the send, so the error has no side effects. It stays a hard error rather than CRYPTOCB_UNAVAILABLE: the caller asked for the HSM through its devId, and a supported algorithm must not silently run in software because of a transport limit. Large payloads belong on the DMA path or need a larger comm buffer. whTest_CryptoAesCommBuffer drives an oversized request through each mode's wolfCrypt entry point and requires exactly WH_ERROR_REQUEST_SIZE. Reverting any one builder to WH_ERROR_BADARGS fails the test for that mode, as does mapping the CBC result to CRYPTOCB_UNAVAILABLE. --- src/wh_client_crypto.c | 8 +-- test/wh_test_crypto.c | 121 +++++++++++++++++++++++++++++++++++++++++ wolfhsm/wh_error.h | 3 + 3 files changed, 128 insertions(+), 4 deletions(-) diff --git a/src/wh_client_crypto.c b/src/wh_client_crypto.c index 893b1c10b..68ea03673 100644 --- a/src/wh_client_crypto.c +++ b/src/wh_client_crypto.c @@ -484,7 +484,7 @@ int wh_Client_AesCtrRequest(whClientContext* ctx, Aes* aes, int enc, len + key_len + AES_IV_SIZE + AES_BLOCK_SIZE; if (req_len > WOLFHSM_CFG_COMM_DATA_LEN) { - return WH_ERROR_BADARGS; + return WH_ERROR_REQUEST_SIZE; } req->enc = enc; @@ -826,7 +826,7 @@ int wh_Client_AesEcbRequest(whClientContext* ctx, Aes* aes, int enc, len + key_len; if (req_len > WOLFHSM_CFG_COMM_DATA_LEN) { - return WH_ERROR_BADARGS; + return WH_ERROR_REQUEST_SIZE; } req->enc = enc; @@ -1159,7 +1159,7 @@ int wh_Client_AesCbcRequest(whClientContext* ctx, Aes* aes, int enc, len + key_len + iv_len; if (req_len > WOLFHSM_CFG_COMM_DATA_LEN) { - return WH_ERROR_BADARGS; + return WH_ERROR_REQUEST_SIZE; } req->enc = enc; @@ -1507,7 +1507,7 @@ int wh_Client_AesGcmRequest(whClientContext* ctx, Aes* aes, int enc, len + key_len + iv_len + authin_len + ((enc == 0) ? tag_len : 0); if (req_len > WOLFHSM_CFG_COMM_DATA_LEN) { - return WH_ERROR_BADARGS; + return WH_ERROR_REQUEST_SIZE; } req->enc = enc; diff --git a/test/wh_test_crypto.c b/test/wh_test_crypto.c index c24040ddb..e0f4a988a 100644 --- a/test/wh_test_crypto.c +++ b/test/wh_test_crypto.c @@ -10003,6 +10003,119 @@ static int whTestCrypto_Aes(whClientContext* ctx, int devId, WC_RNG* rng) return ret; } +#if !defined(NO_AES) && \ + (defined(HAVE_AES_CBC) || defined(WOLFSSL_AES_COUNTER) || \ + defined(HAVE_AES_ECB) || defined(HAVE_AESGCM)) +/* One full comm buffer of plaintext cannot fit alongside the request header */ +#define WH_TEST_AES_OVERSZ \ + ((WOLFHSM_CFG_COMM_DATA_LEN / AES_BLOCK_SIZE) * AES_BLOCK_SIZE) +static uint8_t whTest_AesOverszIn[WH_TEST_AES_OVERSZ]; +static uint8_t whTest_AesOverszOut[WH_TEST_AES_OVERSZ]; + +static int whTest_CryptoAesCommBuffer(int devId, WC_RNG* rng) +{ + int ret; + int overRet; + Aes aes[1]; + uint8_t key[AES_128_KEY_SIZE]; + uint8_t iv[AES_BLOCK_SIZE]; +#ifdef HAVE_AESGCM + uint8_t tag[AES_BLOCK_SIZE]; +#endif + + memset(whTest_AesOverszIn, 0xA5, sizeof(whTest_AesOverszIn)); + + ret = wc_RNG_GenerateBlock(rng, key, sizeof(key)); + if (ret == 0) { + ret = wc_RNG_GenerateBlock(rng, iv, sizeof(iv)); + } + if (ret != 0) { + WH_ERROR_PRINT("Failed to wc_RNG_GenerateBlock %d\n", ret); + return ret; + } + +#ifdef HAVE_AES_CBC + ret = wc_AesInit(aes, NULL, devId); + if (ret == 0) { + ret = wc_AesSetKey(aes, key, sizeof(key), iv, AES_ENCRYPTION); + if (ret == 0) { + overRet = wc_AesCbcEncrypt(aes, whTest_AesOverszOut, + whTest_AesOverszIn, + sizeof(whTest_AesOverszIn)); + if (overRet != WH_ERROR_REQUEST_SIZE) { + WH_ERROR_PRINT("Oversized AES-CBC returned %d\n", overRet); + ret = -1; + } + } + (void)wc_AesFree(aes); + } +#endif /* HAVE_AES_CBC */ + +#ifdef WOLFSSL_AES_COUNTER + if (ret == 0) { + ret = wc_AesInit(aes, NULL, devId); + if (ret == 0) { + ret = wc_AesSetKey(aes, key, sizeof(key), iv, AES_ENCRYPTION); + if (ret == 0) { + overRet = wc_AesCtrEncrypt(aes, whTest_AesOverszOut, + whTest_AesOverszIn, + sizeof(whTest_AesOverszIn)); + if (overRet != WH_ERROR_REQUEST_SIZE) { + WH_ERROR_PRINT("Oversized AES-CTR returned %d\n", overRet); + ret = -1; + } + } + (void)wc_AesFree(aes); + } + } +#endif /* WOLFSSL_AES_COUNTER */ + +#ifdef HAVE_AES_ECB + if (ret == 0) { + ret = wc_AesInit(aes, NULL, devId); + if (ret == 0) { + ret = wc_AesSetKey(aes, key, sizeof(key), NULL, AES_ENCRYPTION); + if (ret == 0) { + overRet = wc_AesEcbEncrypt(aes, whTest_AesOverszOut, + whTest_AesOverszIn, + sizeof(whTest_AesOverszIn)); + if (overRet != WH_ERROR_REQUEST_SIZE) { + WH_ERROR_PRINT("Oversized AES-ECB returned %d\n", overRet); + ret = -1; + } + } + (void)wc_AesFree(aes); + } + } +#endif /* HAVE_AES_ECB */ + +#ifdef HAVE_AESGCM + if (ret == 0) { + ret = wc_AesInit(aes, NULL, devId); + if (ret == 0) { + ret = wc_AesGcmSetKey(aes, key, sizeof(key)); + if (ret == 0) { + overRet = wc_AesGcmEncrypt( + aes, whTest_AesOverszOut, whTest_AesOverszIn, + sizeof(whTest_AesOverszIn), iv, sizeof(iv), tag, + sizeof(tag), iv, sizeof(iv)); + if (overRet != WH_ERROR_REQUEST_SIZE) { + WH_ERROR_PRINT("Oversized AES-GCM returned %d\n", overRet); + ret = -1; + } + } + (void)wc_AesFree(aes); + } + } +#endif /* HAVE_AESGCM */ + + if (ret == 0) { + WH_TEST_PRINT("AES COMM BUFFER DEVID=0x%X SUCCESS\n", devId); + } + return ret; +} +#endif /* !NO_AES && HAVE_AES_CBC */ + /* Direct exercise of the native async AES primitives * (wh_Client_AesXxxRequest / wh_Client_AesXxxResponse). * Covers each mode's round-trip, state continuity, and argument rejection. */ @@ -17903,6 +18016,14 @@ int whTest_CryptoClientConfig(whClientConfig* config) i++; } } +#if defined(HAVE_AES_CBC) || defined(WOLFSSL_AES_COUNTER) || \ + defined(HAVE_AES_ECB) || defined(HAVE_AESGCM) + if (ret == WH_ERROR_OK) { + /* The comm buffer bounds the non-DMA path only */ + (void)wh_Client_SetDmaMode(client, 0); + ret = whTest_CryptoAesCommBuffer(WH_CLIENT_DEVID(client), rng); + } +#endif #ifdef WOLFHSM_CFG_DMA /* Dedicated async DMA tests drive the wh_Client_*Dma APIs directly; prefer * DMA so any wolfCrypt-routed operations also take the DMA path. */ diff --git a/wolfhsm/wh_error.h b/wolfhsm/wh_error.h index 5ca7096c7..e1dcce0e5 100644 --- a/wolfhsm/wh_error.h +++ b/wolfhsm/wh_error.h @@ -51,6 +51,9 @@ enum WH_ERROR_ENUM { the comm layer. Unlike NOTREADY, retrying the same call will not clear this -- drain the pending response or call wh_CommClient_AbortPending before issuing a new request. */ + WH_ERROR_REQUEST_SIZE = + -2012, /* Request does not fit WOLFHSM_CFG_COMM_DATA_LEN. No side + effects: nothing was sent and no local state changed. */ /* NVM and keystore specific status returns */ WH_ERROR_LOCKED = -2100, /* Unlock and retry if necessary */ From f2efbaba73d304109ae8fc94da8dcfa8bd582b97 Mon Sep 17 00:00:00 2001 From: Tobias Frauenschlaeger Date: Wed, 2 Sep 2026 17:23:08 +0200 Subject: [PATCH 2/2] Carry a small AES-GCM AAD in the request instead of over DMA The DMA AES-GCM request already sends the IV, the auth tag and the key as trailing data inside the packet, but passed the AAD as a DmaBuffer - an address the server has to translate, map and release for what is usually a handful of bytes. Send it inline when it is small enough, and keep the DMA path for anything larger. This is not only about saving a round of address handling. The AAD is frequently built somewhere the server cannot reach at all: wolfSSL assembles the TLS 1.3 additional data in a stack local, and on targets where task stacks are outside the address range the server can address, that disqualified the entire request - payload buffers included - and dropped the whole operation back to software. Measured on an AURIX TC4Dx with the CSS engine behind wolfHSM, that was three quarters of all crypto: the port's own self-test dispatch goes from 172 DMA / 523 comm-buffer to 692 / 3, and TLS record decryption stops falling back to software entirely. A request with aad.addr == 0 and aad.sz > 0 carries the AAD immediately after the key. The server accounts for it in the expected request size, reads it from the packet, and skips both the address translation and the release. WOLFHSM_CFG_DMA_INLINE_AAD_MAX_SIZE bounds how much the request will carry, defaulting to 128 bytes - ample for a TLS 1.2 or 1.3 record header and for the AUTOSAR and CAN headers that motivate this, while leaving the rest of WOLFHSM_CFG_COMM_DATA_LEN alone. Anything larger goes over DMA exactly as before, as does everything when the knob is set to 0. It is a client-side policy only: the server reads whatever the request carries and never consults the value, so a client and a server built with different settings still interoperate. The server cannot lean on that knob, and must not lean on aad.sz either. It arrives as a client-supplied uint64_t, and the expected-size check is an exact equality, so an unbounded AAD term in that sum could be chosen to wrap it back onto the received size - passing validation with an arbitrary ivSz and handing GHASH a length that walks gigabytes past the message. Bound it before the sum is formed: an inline AAD cannot exceed the request carrying it, and no AAD may exceed what wc_AesGcmEncrypt can hash. The size check, the DMA translation and the wc_AesGcm call then all use that one validated length rather than validating one and truncating another. The client always builds a self-consistent frame, so that bound can only be reached by a hand-built packet. whTest_CryptoReqSize drives the handler directly with five: an inline AAD the message is too short to hold, one declared larger than the whole message, one whose low 32 bits match the frame exactly, one sized so the 64-bit sum wraps back onto the received size, and a correctly framed control that must not be rejected. The third is what pins the bound rather than the pre-existing equality check: truncated to uint32_t it matches the frame, so only a test made before that cast rejects it. Both sides locate that AAD from the key size the CLIENT put on the wire, not from the resolved key length. They differ for an HSM-side key: the wire carries keySz 0 while the server replaces keyLen with the keystore key's own length, and computing the offset from that reads the AAD past where the client wrote it - encrypt then tags over the wrong bytes and only the decrypt fails, as AES_GCM_AUTH_E. Mixed versions fail closed rather than silently. An old server computes the expected request size without the inline AAD, so the length check rejects the request with WH_ERROR_BADARGS; an old client always sends a real address and is unaffected. Only the inline case is new on the wire, and only in the client-to-new-server direction. Inlining also takes the AAD out of the DMA callbacks and the client-side allowlist, so wh_Client_AesGcmDmaRequest and wh_Client_AesGcmDmaResponse now document that the AAD is address-translated and POST-cleaned only when it is large enough to travel over DMA. The DMA form of the AAD had no coverage before - every AAD in the suite fits the comm buffer, so all of them would now take the inline path and the DMA branch would never run again. The AES-GCM DMA async round-trip therefore runs twice, at the inline cap and one byte past it. Setting the knob to 0 leaves only the DMA form, and the test says so in its output rather than reporting a pass that covered half of what it claims. A round-trip alone would not have been enough. Encrypt and decrypt carry the same AAD, so an AAD the server reads at the wrong offset or length still verifies against itself and the test passes. Each leg therefore compares its ciphertext and tag against a software reference computed on INVALID_DEVID. An off-by-one in the DMA AAD length fails the tag comparison on the above-cap leg; without it the suite stays green. --- docs/src/9-Configuration.md | 1 + src/wh_client_crypto.c | 51 ++++-- src/wh_server_crypto.c | 57 +++++-- test/wh_test.c | 4 + test/wh_test_crypto.c | 123 +++++++++----- test/wh_test_crypto_reqsize.c | 295 ++++++++++++++++++++++++++++++++++ test/wh_test_crypto_reqsize.h | 24 +++ wolfhsm/wh_client_crypto.h | 18 ++- wolfhsm/wh_message_crypto.h | 1 + wolfhsm/wh_settings.h | 18 +++ 10 files changed, 515 insertions(+), 77 deletions(-) create mode 100644 test/wh_test_crypto_reqsize.c create mode 100644 test/wh_test_crypto_reqsize.h diff --git a/docs/src/9-Configuration.md b/docs/src/9-Configuration.md index 01760f2ac..b111805f5 100644 --- a/docs/src/9-Configuration.md +++ b/docs/src/9-Configuration.md @@ -134,6 +134,7 @@ These macros gate and tune DMA-mode crypto and large-buffer operations. | `WOLFHSM_CFG_DMAADDR_COUNT` | `10` | Number of entries in the DMA address allowlist used by the server to validate client-supplied DMA buffers. | | `WOLFHSM_CFG_DMA_PTR_SIZE` | Compiler-detected (`__SIZEOF_POINTER__`) | Override the assumed DMA pointer size, in bytes (must be `4` or `8`). Auto-detection works for GCC/Clang and IAR; define this explicitly for any toolchain that does not provide `__SIZEOF_POINTER__`. | | `WOLFHSM_CFG_DMA_ALT_PTR_SIZE` | Undefined | If defined, allows the DMA pointer size to differ from the native CPU pointer size (e.g. a 32-bit-pointer server reachable from a 64-bit-pointer client). When undefined, wh_settings.h refuses to build with a mismatched `WOLFHSM_CFG_DMA_PTR_SIZE`. | +| `WOLFHSM_CFG_DMA_INLINE_AAD_MAX_SIZE` | `128` | Largest additional authenticated data, in bytes, that a DMA AEAD request carries inside the message instead of over DMA, letting the AAD live in memory the server cannot address. An AAD above this size is passed as a DMA buffer; `0` sends every AAD over DMA. A client-side policy: the server reads whatever the request carries and never consults this value, so client and server may be built with different ones. Inline AAD does require a server new enough to understand the encoding; an older server rejects such a request with `WH_ERROR_BADARGS`. This is an upper bound only - the AAD shares the message with the request header, IV, tag and key, so a value close to `WOLFHSM_CFG_COMM_DATA_LEN` can never be reached. | | `WOLFHSM_CFG_DMA_CUSTOM_CLIENT_COPY` | Undefined | If defined, expose hooks that let the integrator override the client-to-server and server-to-client memory copy used during DMA requests. Useful when DMA buffers live in shared memory that requires custom invalidation or cache maintenance beyond the standard `XCACHE*` macros. | ## Authentication diff --git a/src/wh_client_crypto.c b/src/wh_client_crypto.c index 68ea03673..ecdea4a41 100644 --- a/src/wh_client_crypto.c +++ b/src/wh_client_crypto.c @@ -1635,15 +1635,17 @@ int wh_Client_AesGcmDmaRequest(whClientContext* ctx, Aes* aes, int enc, const uint8_t* authin, uint32_t authin_len, const uint8_t* dec_tag, uint32_t tag_len) { - int ret = WH_ERROR_OK; - whMessageCrypto_AesGcmDmaRequest* req = NULL; - uint8_t* dataPtr = NULL; - uintptr_t inAddr = 0; - uintptr_t outAddr = 0; - uintptr_t aadAddr = 0; - bool inAcq = false; - bool outAcq = false; - bool aadAcq = false; + int ret = WH_ERROR_OK; + whMessageCrypto_AesGcmDmaRequest* req = NULL; + uint8_t* dataPtr = NULL; + uintptr_t inAddr = 0; + uintptr_t outAddr = 0; + uintptr_t aadAddr = 0; + bool inAcq = false; + bool outAcq = false; + bool aadAcq = false; + bool aadInline = false; + uint8_t* req_aad; uint8_t* req_iv; uint8_t* req_tag; uint8_t* req_key; @@ -1687,6 +1689,16 @@ int wh_Client_AesGcmDmaRequest(whClientContext* ctx, Aes* aes, int enc, req_len += req->keySz; } + /* DMA would demand the AAD live in memory the server can address, which a + * TLS record header on the caller's stack often does not. */ + req_aad = req_key + req->keySz; + if ((authin != NULL) && (authin_len > 0) && + (authin_len <= WOLFHSM_CFG_DMA_INLINE_AAD_MAX_SIZE) && + ((req_len + authin_len) <= WOLFHSM_CFG_COMM_DATA_LEN)) { + aadInline = true; + req_len += authin_len; + } + if (req_len > WOLFHSM_CFG_COMM_DATA_LEN) { return WH_ERROR_BADARGS; } @@ -1700,6 +1712,9 @@ int wh_Client_AesGcmDmaRequest(whClientContext* ctx, Aes* aes, int enc, if (req->keySz > 0) { memcpy(req_key, (const uint8_t*)(aes->devKey), req->keySz); } + if (aadInline) { + memcpy(req_aad, authin, authin_len); + } if (in != NULL && len > 0) { req->input.sz = len; @@ -1725,12 +1740,18 @@ int wh_Client_AesGcmDmaRequest(whClientContext* ctx, Aes* aes, int enc, if (ret == WH_ERROR_OK && authin != NULL && authin_len > 0) { req->aad.sz = authin_len; - ret = wh_Client_DmaProcessClientAddress( - ctx, (uintptr_t)authin, (void**)&aadAddr, req->aad.sz, - WH_DMA_OPER_CLIENT_READ_PRE, (whDmaFlags){0}); - if (ret == WH_ERROR_OK) { - aadAcq = true; - req->aad.addr = aadAddr; + if (aadInline) { + /* addr 0 with a non-zero size means "follows the key inline". */ + req->aad.addr = 0; + } + else { + ret = wh_Client_DmaProcessClientAddress( + ctx, (uintptr_t)authin, (void**)&aadAddr, req->aad.sz, + WH_DMA_OPER_CLIENT_READ_PRE, (whDmaFlags){0}); + if (ret == WH_ERROR_OK) { + aadAcq = true; + req->aad.addr = aadAddr; + } } } diff --git a/src/wh_server_crypto.c b/src/wh_server_crypto.c index 23e3b703c..721ea4a03 100644 --- a/src/wh_server_crypto.c +++ b/src/wh_server_crypto.c @@ -4258,10 +4258,11 @@ static int _HandleAesGcmDma(whServerContext* ctx, uint16_t magic, int devId, whMessageCrypto_AesGcmDmaResponse res; Aes aes[1] = {0}; - void* inAddr = NULL; - void* outAddr = NULL; - void* aadAddr = NULL; - word32 outSz = 0; + void* inAddr = NULL; + void* outAddr = NULL; + void* aadAddr = NULL; + const uint8_t* aadPtr = NULL; + word32 outSz = 0; whKeyId keyId; uint8_t cachedKey[AES_256_KEY_SIZE]; @@ -4282,12 +4283,25 @@ static int _HandleAesGcmDma(whServerContext* ctx, uint16_t magic, int devId, uint32_t enc = req.enc; uint32_t keyLen = req.keySz; + uint32_t reqKeySz = req.keySz; uint32_t len = req.input.sz; uint32_t ivLen = req.ivSz; uint32_t tagLen = req.authTagSz; - uint64_t needed_size = (uint64_t)sizeof(whMessageCrypto_AesGcmDmaRequest) + - (uint64_t)keyLen + (uint64_t)ivLen + - (uint64_t)(enc != 0 ? 0 : tagLen); + uint32_t aadLen = 0; + int aadInline = ((req.aad.sz > 0) && (req.aad.addr == 0)); + uint64_t needed_size = 0; + + /* addr 0 with a non-zero size means the AAD follows the key inline; bound + * that length here, before it can wrap the sum below. */ + if (req.aad.sz > (aadInline ? (uint64_t)inSize : (uint64_t)0xFFFFFFFFu)) { + return WH_ERROR_BADARGS; + } + aadLen = (uint32_t)req.aad.sz; + + needed_size = (uint64_t)sizeof(whMessageCrypto_AesGcmDmaRequest) + + (uint64_t)keyLen + (uint64_t)ivLen + + (uint64_t)(enc != 0 ? 0 : tagLen) + + (uint64_t)(aadInline ? aadLen : 0); if (needed_size != inSize) { return WH_ERROR_BADARGS; } @@ -4352,13 +4366,22 @@ static int _HandleAesGcmDma(whServerContext* ctx, uint16_t magic, int devId, } /* Handle AAD */ - if (ret == WH_ERROR_OK && req.aad.sz > 0) { - /* Process client address for AAD */ - ret = wh_Server_DmaProcessClientAddress( - ctx, req.aad.addr, &aadAddr, req.aad.sz, - WH_DMA_OPER_CLIENT_READ_PRE, (whServerDmaFlags){0}); - if (ret != WH_ERROR_OK) { - res.dmaAddrStatus.badAddr = req.aad; + if (ret == WH_ERROR_OK && aadLen > 0) { + if (aadInline) { + /* The wire keySz, not keyLen: a keystore key contributes no bytes + * here, but keyLen has already been replaced by its length. */ + aadPtr = (const uint8_t*)(iv + ivLen + (enc != 0 ? 0 : tagLen) + + reqKeySz); + } + else { + /* Process client address for AAD */ + ret = wh_Server_DmaProcessClientAddress( + ctx, req.aad.addr, &aadAddr, aadLen, + WH_DMA_OPER_CLIENT_READ_PRE, (whServerDmaFlags){0}); + if (ret != WH_ERROR_OK) { + res.dmaAddrStatus.badAddr = req.aad; + } + aadPtr = (const uint8_t*)aadAddr; } } @@ -4386,13 +4409,13 @@ static int _HandleAesGcmDma(whServerContext* ctx, uint16_t magic, int devId, ret = wc_AesGcmEncrypt( aes, (byte*)outAddr, (byte*)inAddr, (word32)len, (byte*)iv, (word32)ivLen, (byte*)out_tag, (word32)tagLen, - (byte*)aadAddr, (word32)req.aad.sz); + (byte*)aadPtr, aadLen); } else { ret = wc_AesGcmDecrypt( aes, (byte*)outAddr, (byte*)inAddr, (word32)len, (byte*)iv, (word32)ivLen, (byte*)tag, (word32)tagLen, - (byte*)aadAddr, (word32)req.aad.sz); + (byte*)aadPtr, aadLen); } if (ret == WH_ERROR_OK) { outSz = len; @@ -4422,7 +4445,7 @@ static int _HandleAesGcmDma(whServerContext* ctx, uint16_t magic, int devId, } if (aadAddr != NULL) { if (wh_Server_DmaProcessClientAddress( - ctx, req.aad.addr, &aadAddr, req.aad.sz, + ctx, req.aad.addr, &aadAddr, aadLen, WH_DMA_OPER_CLIENT_READ_POST, (whServerDmaFlags){0}) != WH_ERROR_OK) { WH_DEBUG_SERVER_VERBOSE( diff --git a/test/wh_test.c b/test/wh_test.c index 2d3bc9925..68bab52f0 100644 --- a/test/wh_test.c +++ b/test/wh_test.c @@ -46,6 +46,7 @@ #include "wh_test_timeout.h" #include "wh_test_dma.h" #include "wh_test_keystore_reqsize.h" +#include "wh_test_crypto_reqsize.h" #ifdef WOLFHSM_CFG_ENABLE_AUTHENTICATION #include "wh_test_auth.h" #endif /* WOLFHSM_CFG_ENABLE_AUTHENTICATION */ @@ -99,6 +100,9 @@ int whTest_Unit(void) #if defined(WOLFHSM_CFG_ENABLE_SERVER) && !defined(WOLFHSM_CFG_NO_CRYPTO) /* Keystore req_size validation */ WH_TEST_ASSERT(0 == whTest_KeystoreReqSize()); + + /* Crypto request framing validation */ + WH_TEST_ASSERT(0 == whTest_CryptoReqSize()); #endif /* WOLFHSM_CFG_ENABLE_SERVER && !WOLFHSM_CFG_NO_CRYPTO */ /* Comm tests */ diff --git a/test/wh_test_crypto.c b/test/wh_test_crypto.c index e0f4a988a..2d7498e63 100644 --- a/test/wh_test_crypto.c +++ b/test/wh_test_crypto.c @@ -11399,51 +11399,98 @@ static int whTest_CryptoAesDmaAsync(whClientContext* ctx, int devId, #endif /* HAVE_AES_ECB */ #ifdef HAVE_AESGCM - /* GCM DMA: round-trip with AAD via DMA */ + /* GCM DMA: round-trip at the inline-AAD cap and one byte past it, so both + * forms of the AAD are exercised unless the cap is configured to 0. */ +#if WOLFHSM_CFG_DMA_INLINE_AAD_MAX_SIZE == 0 + WH_TEST_PRINT("AES GCM DMA ASYNC: inline AAD disabled by config, only the " + "DMA form of the AAD is covered\n"); +#endif if (ret == 0) { - uint8_t authin[32]; - uint8_t enc_tag[AES_BLOCK_SIZE]; - uint8_t dec_tag[AES_BLOCK_SIZE]; + Aes swAes[1]; + uint8_t authin[WOLFHSM_CFG_DMA_INLINE_AAD_MAX_SIZE + 1]; + uint8_t enc_tag[AES_BLOCK_SIZE]; + uint8_t dec_tag[AES_BLOCK_SIZE]; + uint8_t refCipher[WH_TEST_AES_ASYNC_DMA_BUFSZ]; + uint8_t refTag[AES_BLOCK_SIZE]; + uint32_t aadSz[2] = {WOLFHSM_CFG_DMA_INLINE_AAD_MAX_SIZE, + WOLFHSM_CFG_DMA_INLINE_AAD_MAX_SIZE + 1}; + size_t i; memset(authin, 0x5A, sizeof(authin)); - memset(enc_tag, 0, sizeof(enc_tag)); - ret = wc_AesInit(aes, NULL, devId); - if (ret == 0) { - ret = wc_AesGcmSetKey(aes, key, sizeof(key)); - } - if (ret == 0) { - ret = wh_Client_AesGcmDmaRequest( - ctx, aes, 1, plainIn, sizeof(plainIn), cipher, iv, - AES_BLOCK_SIZE, authin, sizeof(authin), NULL, sizeof(enc_tag)); - } - if (ret == 0) { - do { - ret = wh_Client_AesGcmDmaResponse(ctx, aes, enc_tag, - sizeof(enc_tag)); - } while (ret == WH_ERROR_NOTREADY); - } + for (i = 0; i < 2 && ret == 0; i++) { + memset(enc_tag, 0, sizeof(enc_tag)); + memset(refCipher, 0, sizeof(refCipher)); + memset(refTag, 0, sizeof(refTag)); - if (ret == 0) { - memcpy(dec_tag, enc_tag, sizeof(dec_tag)); - ret = wh_Client_AesGcmDmaRequest( - ctx, aes, 0, cipher, sizeof(cipher), plainOut, iv, - AES_BLOCK_SIZE, authin, sizeof(authin), dec_tag, - sizeof(dec_tag)); - } - if (ret == 0) { - do { - ret = wh_Client_AesGcmDmaResponse(ctx, aes, NULL, 0); - } while (ret == WH_ERROR_NOTREADY); - } - if (ret == 0 && memcmp(plainIn, plainOut, sizeof(plainIn)) != 0) { - WH_ERROR_PRINT("AES-GCM DMA async round-trip mismatch\n"); - ret = -1; + /* Encrypt and decrypt carry the same AAD, so only a reference tag + * catches an AAD the server read at the wrong offset or length. */ + ret = wc_AesInit(swAes, NULL, INVALID_DEVID); + if (ret == 0) { + ret = wc_AesGcmSetKey(swAes, key, sizeof(key)); + if (ret == 0) { + ret = wc_AesGcmEncrypt(swAes, refCipher, plainIn, + sizeof(plainIn), iv, AES_BLOCK_SIZE, + refTag, sizeof(refTag), authin, + aadSz[i]); + } + (void)wc_AesFree(swAes); + } + if (ret != 0) { + WH_ERROR_PRINT("AES-GCM DMA async reference failed %d\n", ret); + break; + } + + ret = wc_AesInit(aes, NULL, devId); + if (ret == 0) { + ret = wc_AesGcmSetKey(aes, key, sizeof(key)); + } + if (ret == 0) { + ret = wh_Client_AesGcmDmaRequest( + ctx, aes, 1, plainIn, sizeof(plainIn), cipher, iv, + AES_BLOCK_SIZE, authin, aadSz[i], NULL, sizeof(enc_tag)); + } + if (ret == 0) { + do { + ret = wh_Client_AesGcmDmaResponse(ctx, aes, enc_tag, + sizeof(enc_tag)); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0 && memcmp(cipher, refCipher, sizeof(plainIn)) != 0) { + WH_ERROR_PRINT("AES-GCM DMA async cipher mismatch, aadSz=%u\n", + (unsigned int)aadSz[i]); + ret = -1; + } + if (ret == 0 && memcmp(enc_tag, refTag, sizeof(refTag)) != 0) { + WH_ERROR_PRINT("AES-GCM DMA async tag mismatch, aadSz=%u\n", + (unsigned int)aadSz[i]); + ret = -1; + } + + if (ret == 0) { + memcpy(dec_tag, enc_tag, sizeof(dec_tag)); + ret = wh_Client_AesGcmDmaRequest( + ctx, aes, 0, cipher, sizeof(cipher), plainOut, iv, + AES_BLOCK_SIZE, authin, aadSz[i], dec_tag, + sizeof(dec_tag)); + } + if (ret == 0) { + do { + ret = wh_Client_AesGcmDmaResponse(ctx, aes, NULL, 0); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0 && memcmp(plainIn, plainOut, sizeof(plainIn)) != 0) { + WH_ERROR_PRINT("AES-GCM DMA async round-trip mismatch, " + "aadSz=%u\n", + (unsigned int)aadSz[i]); + ret = -1; + } + (void)wc_AesFree(aes); + memset(cipher, 0, sizeof(cipher)); + memset(plainOut, 0, sizeof(plainOut)); } - (void)wc_AesFree(aes); - memset(cipher, 0, sizeof(cipher)); - memset(plainOut, 0, sizeof(plainOut)); } + if (ret == 0) { WH_TEST_PRINT("AES GCM DMA ASYNC DEVID=0x%X SUCCESS\n", devId); } diff --git a/test/wh_test_crypto_reqsize.c b/test/wh_test_crypto_reqsize.c new file mode 100644 index 000000000..18a894d0f --- /dev/null +++ b/test/wh_test_crypto_reqsize.c @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2025 wolfSSL Inc. + * + * This file is part of wolfHSM. + * + * wolfHSM is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfHSM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with wolfHSM. If not, see . + */ +/* + * test/wh_test_crypto_reqsize.c + * + * Unit tests to verify _HandleAesGcmDma validates the declared inline-AAD + * length against the received message, preventing reads past the packet. + * + * The client always builds a self-consistent frame, so these cases can only + * be produced by driving wh_Server_HandleCryptoDmaRequest directly with a + * hand-built packet. + */ + +#include "wolfhsm/wh_settings.h" + +#include +#include + +#include "wolfhsm/wh_error.h" +#include "wolfhsm/wh_comm.h" +#include "wolfhsm/wh_message.h" +#include "wolfhsm/wh_server.h" +#include "wolfhsm/wh_transport_mem.h" +#include "wolfhsm/wh_nvm.h" +#include "wolfhsm/wh_nvm_flash.h" +#include "wolfhsm/wh_flash_ramsim.h" + +#if !defined(WOLFHSM_CFG_NO_CRYPTO) +#include "wolfssl/wolfcrypt/settings.h" +#include "wolfssl/wolfcrypt/types.h" +#include "wolfssl/wolfcrypt/aes.h" +#include "wolfhsm/wh_message_crypto.h" +#include "wolfhsm/wh_server_crypto.h" +#endif + +#include "wh_test_common.h" + +#if defined(WOLFHSM_CFG_ENABLE_SERVER) && !defined(WOLFHSM_CFG_NO_CRYPTO) && \ + defined(WOLFHSM_CFG_DMA) && defined(HAVE_AESGCM) + +#define BUFFER_SIZE 4096 +#define FLASH_RAM_SIZE (1024 * 1024) +#define FLASH_SECTOR_SIZE (128 * 1024) +#define FLASH_PAGE_SIZE 8 + +typedef struct { + whServerContext server[1]; + whNvmContext nvm[1]; + whServerCryptoContext crypto[1]; + + uint8_t reqBuf[BUFFER_SIZE]; + uint8_t respBuf[BUFFER_SIZE]; + whTransportMemConfig tmcf[1]; + whTransportServerCb tscb[1]; + whTransportMemServerContext tmsc[1]; + whCommServerConfig cs_conf[1]; + + whFlashRamsimCtx fc[1]; + whFlashRamsimCfg fc_conf[1]; + whFlashCb fcb[1]; + whNvmFlashConfig nf_conf[1]; + whNvmFlashContext nfc[1]; + whNvmCb nfcb[1]; + whNvmConfig n_conf[1]; + whServerConfig s_conf[1]; +} TestCtx; + +/* Flash memory is static to avoid 1MB on the stack */ +static uint8_t _flashMemory[FLASH_RAM_SIZE]; + +static int _SetupServer(TestCtx* ctx) +{ + memset(ctx, 0, sizeof(*ctx)); + memset(_flashMemory, 0, sizeof(_flashMemory)); + + ctx->tmcf[0] = (whTransportMemConfig){ + .req = (whTransportMemCsr*)ctx->reqBuf, + .req_size = sizeof(ctx->reqBuf), + .resp = (whTransportMemCsr*)ctx->respBuf, + .resp_size = sizeof(ctx->respBuf), + }; + ctx->tscb[0] = (whTransportServerCb)WH_TRANSPORT_MEM_SERVER_CB; + ctx->cs_conf[0] = (whCommServerConfig){ + .transport_cb = ctx->tscb, + .transport_context = (void*)ctx->tmsc, + .transport_config = (void*)ctx->tmcf, + .server_id = 125, + }; + + ctx->fc_conf[0] = (whFlashRamsimCfg){ + .size = FLASH_RAM_SIZE, + .sectorSize = FLASH_SECTOR_SIZE, + .pageSize = FLASH_PAGE_SIZE, + .erasedByte = ~(uint8_t)0, + .memory = _flashMemory, + }; + ctx->fcb[0] = (whFlashCb)WH_FLASH_RAMSIM_CB; + ctx->nf_conf[0] = (whNvmFlashConfig){ + .cb = ctx->fcb, + .context = ctx->fc, + .config = ctx->fc_conf, + }; + ctx->nfcb[0] = (whNvmCb)WH_NVM_FLASH_CB; + ctx->n_conf[0] = (whNvmConfig){ + .cb = ctx->nfcb, + .context = ctx->nfc, + .config = ctx->nf_conf, + }; + + ctx->s_conf[0] = (whServerConfig){ + .comm_config = ctx->cs_conf, + .nvm = ctx->nvm, + .crypto = ctx->crypto, + }; + + WH_TEST_RETURN_ON_FAIL(wh_Nvm_Init(ctx->nvm, ctx->n_conf)); + WH_TEST_RETURN_ON_FAIL(wolfCrypt_Init()); + WH_TEST_RETURN_ON_FAIL( + wc_InitRng_ex(ctx->crypto->rng, NULL, INVALID_DEVID)); + WH_TEST_RETURN_ON_FAIL(wh_Server_Init(ctx->server, ctx->s_conf)); + WH_TEST_RETURN_ON_FAIL( + wh_Server_SetConnected(ctx->server, WH_COMM_CONNECTED)); + return 0; +} + +static void _CleanupServer(TestCtx* ctx) +{ + (void)wh_Server_Cleanup(ctx->server); + (void)wh_Nvm_Cleanup(ctx->nvm); + (void)wc_FreeRng(ctx->crypto->rng); + (void)wolfCrypt_Cleanup(); +} + +#define WH_TEST_GCM_KEY_SZ AES_128_KEY_SIZE +#define WH_TEST_GCM_IV_SZ 12 +#define WH_TEST_GCM_TAG_SZ AES_BLOCK_SIZE +#define WH_TEST_GCM_AAD_SZ 16 + +/* Lay out a well-formed encrypt request carrying aadBytes of inline AAD after + * the key, and return the size of the frame actually written. */ +static uint16_t _BuildInlineAadRequest(uint8_t* req_packet, uint32_t aadBytes) +{ + whMessageCrypto_GenericRequestHeader* hdr = + (whMessageCrypto_GenericRequestHeader*)req_packet; + whMessageCrypto_AesGcmDmaRequest* req = + (whMessageCrypto_AesGcmDmaRequest*)(req_packet + sizeof(*hdr)); + + memset(req_packet, 0, WOLFHSM_CFG_COMM_DATA_LEN); + + hdr->algoType = WC_CIPHER_AES_GCM; + hdr->algoSubType = WH_MESSAGE_CRYPTO_ALGO_SUBTYPE_NONE; + hdr->affinity = 0; + + req->enc = 1; + req->keySz = WH_TEST_GCM_KEY_SZ; + req->ivSz = WH_TEST_GCM_IV_SZ; + req->authTagSz = WH_TEST_GCM_TAG_SZ; + req->input.sz = 0; + req->input.addr = 0; + req->output.sz = 0; + req->output.addr = 0; + /* addr 0 with a non-zero size is the inline-AAD encoding */ + req->aad.addr = 0; + req->aad.sz = aadBytes; + + return (uint16_t)(sizeof(*hdr) + sizeof(*req) + WH_TEST_GCM_KEY_SZ + + WH_TEST_GCM_IV_SZ + aadBytes); +} + +/* Overwrite the declared sizes so the header disagrees with the frame. */ +static void _DeclareSizes(uint8_t* req_packet, uint32_t ivSz, uint64_t aadSz) +{ + whMessageCrypto_AesGcmDmaRequest* req = + (whMessageCrypto_AesGcmDmaRequest*)(req_packet + + sizeof(whMessageCrypto_GenericRequestHeader)); + + req->ivSz = ivSz; + req->aad.sz = aadSz; +} + +static int _Dispatch(TestCtx* ctx, uint8_t* req_packet, uint16_t req_size, + uint8_t* resp_packet) +{ + uint16_t resp_size = 0; + + return wh_Server_HandleCryptoDmaRequest( + ctx->server, WH_COMM_MAGIC_NATIVE, WC_ALGO_TYPE_CIPHER, 0, req_size, + req_packet, &resp_size, resp_packet); +} + +static int wh_Crypto_TestAesGcmDmaAadFraming(void) +{ + TestCtx ctx[1]; + uint8_t req_packet[WOLFHSM_CFG_COMM_DATA_LEN]; + uint8_t resp_packet[WOLFHSM_CFG_COMM_DATA_LEN]; + uint16_t req_size; + uint64_t craft; + int ret; + + WH_TEST_RETURN_ON_FAIL(_SetupServer(ctx)); + + /* Test 1: the frame declares an inline AAD, but req_size stops short of + * the AAD bytes it claims to carry. */ + req_size = _BuildInlineAadRequest(req_packet, WH_TEST_GCM_AAD_SZ); + ret = _Dispatch(ctx, req_packet, (uint16_t)(req_size - WH_TEST_GCM_AAD_SZ), + resp_packet); + if (ret != WH_ERROR_BADARGS) { + WH_ERROR_PRINT("Short inline-AAD frame not rejected: %d\n", ret); + _CleanupServer(ctx); + return -1; + } + + /* Test 2: a declared inline AAD larger than the message carrying it. */ + req_size = _BuildInlineAadRequest(req_packet, WH_TEST_GCM_AAD_SZ); + _DeclareSizes(req_packet, WH_TEST_GCM_IV_SZ, (uint64_t)req_size + 1); + ret = _Dispatch(ctx, req_packet, req_size, resp_packet); + if (ret != WH_ERROR_BADARGS) { + WH_ERROR_PRINT("Oversized inline-AAD length not rejected: %d\n", ret); + _CleanupServer(ctx); + return -1; + } + + /* Test 3: a declared length whose low 32 bits match the frame exactly, so + * only a check made before the truncation to uint32_t rejects it. */ + req_size = _BuildInlineAadRequest(req_packet, WH_TEST_GCM_AAD_SZ); + _DeclareSizes(req_packet, WH_TEST_GCM_IV_SZ, + (uint64_t)0x100000000ull + WH_TEST_GCM_AAD_SZ); + ret = _Dispatch(ctx, req_packet, req_size, resp_packet); + if (ret != WH_ERROR_BADARGS) { + WH_ERROR_PRINT("Truncating inline-AAD length not rejected: %d\n", ret); + _CleanupServer(ctx); + return -1; + } + + /* Test 4: the length the unbounded 64-bit sum would have wrapped back onto + * req_size, paired with an ivSz the equality check would then not bound. */ + req_size = _BuildInlineAadRequest(req_packet, WH_TEST_GCM_AAD_SZ); + craft = (uint64_t)(req_size - + sizeof(whMessageCrypto_GenericRequestHeader)) - + ((uint64_t)sizeof(whMessageCrypto_AesGcmDmaRequest) + + (uint64_t)WH_TEST_GCM_KEY_SZ + (uint64_t)0x40000000u); + _DeclareSizes(req_packet, 0x40000000u, craft); + ret = _Dispatch(ctx, req_packet, req_size, resp_packet); + if (ret != WH_ERROR_BADARGS) { + WH_ERROR_PRINT("Wrapped inline-AAD length not rejected: %d\n", ret); + _CleanupServer(ctx); + return -1; + } + + /* Test 5: the correctly framed encoding must clear the framing check + * rather than being rejected as malformed. */ + req_size = _BuildInlineAadRequest(req_packet, WH_TEST_GCM_AAD_SZ); + ret = _Dispatch(ctx, req_packet, req_size, resp_packet); + if (ret == WH_ERROR_BADARGS) { + WH_ERROR_PRINT("Well-formed inline-AAD frame rejected\n"); + _CleanupServer(ctx); + return -1; + } + + WH_TEST_PRINT("AES-GCM DMA inline-AAD framing tests: ALL PASSED\n"); + + _CleanupServer(ctx); + return 0; +} + +int whTest_CryptoReqSize(void) +{ + WH_TEST_PRINT("Testing AES-GCM DMA request framing validation...\n"); + return wh_Crypto_TestAesGcmDmaAadFraming(); +} + +#else /* server && !no-crypto && DMA && AESGCM */ + +int whTest_CryptoReqSize(void) +{ + return 0; +} + +#endif diff --git a/test/wh_test_crypto_reqsize.h b/test/wh_test_crypto_reqsize.h new file mode 100644 index 000000000..70416532e --- /dev/null +++ b/test/wh_test_crypto_reqsize.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2025 wolfSSL Inc. + * + * This file is part of wolfHSM. + * + * wolfHSM is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfHSM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with wolfHSM. If not, see . + */ +#ifndef WH_TEST_CRYPTO_REQSIZE_H_ +#define WH_TEST_CRYPTO_REQSIZE_H_ + +int whTest_CryptoReqSize(void); + +#endif /* WH_TEST_CRYPTO_REQSIZE_H_ */ diff --git a/wolfhsm/wh_client_crypto.h b/wolfhsm/wh_client_crypto.h index 9a0e921c6..eeeba1b3d 100644 --- a/wolfhsm/wh_client_crypto.h +++ b/wolfhsm/wh_client_crypto.h @@ -2087,11 +2087,14 @@ int wh_Client_AesGcmResponse(whClientContext* ctx, Aes* aes, uint8_t* out, /** * @brief Send an AES-GCM DMA request to the server (non-blocking) * - * Performs PRE address translation for the input, output, and AAD buffers, - * stashes the translated addresses in ctx->dma.asyncCtx.aes for POST - * cleanup, and sends the DMA request to the server. Does NOT wait for a - * reply. The IV, auth tag (for decrypt), and key are passed inline. Caller - * must keep in, out, and authin valid until the matching + * Performs PRE address translation for the input and output buffers, stashes + * the translated addresses in ctx->dma.asyncCtx.aes for POST cleanup, and + * sends the DMA request to the server. Does NOT wait for a reply. The IV, + * auth tag (for decrypt), and key are passed inline. An AAD of at most + * WOLFHSM_CFG_DMA_INLINE_AAD_MAX_SIZE bytes is copied inline as well, and is + * therefore neither address-translated nor passed to the DMA callbacks; a + * larger AAD is translated and cleaned up like the other buffers. Caller must + * keep in, out, and authin valid until the matching * wh_Client_AesGcmDmaResponse completes. * * Contract: at most one outstanding async request may be in flight per @@ -2115,8 +2118,9 @@ int wh_Client_AesGcmDmaRequest(whClientContext* ctx, Aes* aes, int enc, * Single-shot RecvResponse; returns WH_ERROR_NOTREADY if the server has not * yet replied. The output data is written by the server directly to the * client buffer passed to wh_Client_AesGcmDmaRequest; for encrypt the auth - * tag is returned inline and copied into enc_tag. POST DMA cleanup for - * input, output, and AAD buffers is performed on every non-NOTREADY return. + * tag is returned inline and copied into enc_tag. POST DMA cleanup is + * performed on every non-NOTREADY return for the input and output buffers, + * and for the AAD buffer when it was passed over DMA rather than inline. * * @param[in] ctx Pointer to the client context * @param[in] aes Pointer to the AES structure diff --git a/wolfhsm/wh_message_crypto.h b/wolfhsm/wh_message_crypto.h index 809513bc9..1e73a0d5c 100644 --- a/wolfhsm/wh_message_crypto.h +++ b/wolfhsm/wh_message_crypto.h @@ -1582,6 +1582,7 @@ typedef struct { * uint8_t iv[ivSz] * uint8_t authTag[authTagSz] * uint8_t key[keySz] + * uint8_t aad[aad.sz] only when aad.addr == 0 */ } whMessageCrypto_AesGcmDmaRequest; diff --git a/wolfhsm/wh_settings.h b/wolfhsm/wh_settings.h index f9974d905..fcdd45b5d 100644 --- a/wolfhsm/wh_settings.h +++ b/wolfhsm/wh_settings.h @@ -136,6 +136,14 @@ * operation in DMA requests. * Default: Not defined * + * WOLFHSM_CFG_DMA_INLINE_AAD_MAX_SIZE - Largest AAD, in bytes, that a DMA + * AEAD request carries inside the message instead of over DMA. An upper bound + * only, since the AAD shares the message with the request header, IV, tag and + * key. Client-side policy: the server reads whatever the request carries and + * never consults this value, though inline AAD does require a server new + * enough to understand it. 0 sends every AAD over DMA. + * Default: 128 + * * WOLFHSM_CFG_CERT_MAX_VERIFY_ROOTS - Maximum number of trusted root NVM IDs * accepted in a single wh_Server_CertVerifyMultiRoot request. Bounded so the * non-DMA wire request fits within WOLFHSM_CFG_COMM_DATA_LEN alongside the @@ -711,6 +719,16 @@ #endif #endif +/* Largest AAD a DMA AEAD request carries inline rather than over DMA. An upper + * bound only: the AAD shares the message with the header, IV, tag and key. */ +#ifndef WOLFHSM_CFG_DMA_INLINE_AAD_MAX_SIZE +#define WOLFHSM_CFG_DMA_INLINE_AAD_MAX_SIZE 128 +#endif + +#if WOLFHSM_CFG_DMA_INLINE_AAD_MAX_SIZE > WOLFHSM_CFG_COMM_DATA_LEN +#error "WOLFHSM_CFG_DMA_INLINE_AAD_MAX_SIZE exceeds WOLFHSM_CFG_COMM_DATA_LEN" +#endif + #endif /* WOLFHSM_CFG_DMA */ #endif /* !WOLFHSM_WH_SETTINGS_H_ */