From 206ad058b9a05f96546e18d89e816a4403edf790 Mon Sep 17 00:00:00 2001 From: Brett Nicholas <7547222+bigbrett@users.noreply.github.com> Date: Mon, 27 Apr 2026 16:06:51 -0600 Subject: [PATCH 1/2] add aes async crypto --- docs/draft/async-crypto.md | 91 ++- src/wh_client_crypto.c | 1502 ++++++++++++++++++++---------------- test/wh_test_crypto.c | 644 ++++++++++++++++ wolfhsm/wh_client.h | 20 + wolfhsm/wh_client_crypto.h | 310 +++++++- 5 files changed, 1892 insertions(+), 675 deletions(-) diff --git a/docs/draft/async-crypto.md b/docs/draft/async-crypto.md index 0059b4b3..73406d37 100644 --- a/docs/draft/async-crypto.md +++ b/docs/draft/async-crypto.md @@ -416,8 +416,6 @@ int wh_Client_Sha256Dma(whClientContext* ctx, wc_Sha256* sha, const uint8_t* in, | **`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 | - - ## RNG: Single-Shot with Caller-Driven Chunking The RNG generate operation is the second algorithm to receive the async @@ -540,6 +538,85 @@ int wh_Client_RngGenerate(whClientContext* ctx, uint8_t* out, uint32_t size); int wh_Client_RngGenerateDma(whClientContext* ctx, uint8_t* out, uint32_t size); ``` +## AES: One-Shot with DMA Support + +AES modes (CBC, CTR, ECB, GCM) are all **one-shot** operations — every call +consumes a fixed buffer of input and returns a fixed buffer of output. There +is no streaming state to accumulate across multiple Request/Response pairs +the way SHA does, which makes the async split significantly simpler than +SHA: + +- **No partial-block buffering** on the client. The entire plaintext or + ciphertext is handed to one Request and the full result comes back in + one Response. +- **No `requestSent` flag.** Each call sends exactly one request and + expects exactly one response. If the request's serialised size would + exceed `WOLFHSM_CFG_COMM_DATA_LEN`, the inline Request returns + `WH_ERROR_BADARGS` up front; DMA variants bypass the cap for payload + data. +- **No snapshot/rollback.** There is no local buffer to corrupt: the key + lives on `aes->devKey` (or as a cached keyId), the IV on `aes->reg`, and + these are read-only until the Response arrives. + +### Mutable state: IV and counter + +For **CBC** and **CTR**, the Response updates mutable state on the `Aes` +struct so subsequent calls chain correctly: + +- **CBC** — `aes->reg` is updated with the last ciphertext block. For + decryption, the Request captures the last ciphertext block from the + input buffer into `aes->reg` *before* sending, so in-place (input + pointer == output pointer) operation still produces the right chaining + state after the Response overwrites the plaintext. +- **CTR** — `aes->reg`, `aes->tmp`, and `aes->left` are updated from the + Response so the counter advances correctly for subsequent calls. CTR + is symmetric: callers should use `AES_ENCRYPTION` for the key schedule + and pass `enc = 1` in both directions. + +**ECB** and **GCM** carry no inter-call state on the `Aes` struct. For +GCM, the IV, AAD, and (on decrypt) the expected tag are passed as explicit +arguments on each call. + +### DMA variant contract + +The DMA pairs follow the same pattern as SHA DMA: + +1. **Fail-fast** on `wh_CommClient_IsRequestPending()` before acquiring + any DMA mapping, so a Request cannot be issued while another call is + still outstanding and cannot leak a translated address if + `wh_Client_SendRequest` later rejects the call. +2. **PRE-translate** input, output, and (for GCM) AAD buffers. Non-DMA + payload fields (key material, IV, auth tag) stay inline in the + request message. +3. **Stash** the translated addresses in `ctx->dma.asyncCtx.aes` so the + matching Response can issue POST cleanup. +4. **POST cleanup** runs on every non-`WH_ERROR_NOTREADY` return from the + Response, so the caller's buffers are safe to read regardless of + success or error. +5. The caller must keep the input, output, and AAD buffers valid until + the Response returns something other than `WH_ERROR_NOTREADY`. + +### API Reference + +Inline (non-DMA) pairs: + +- `wh_Client_AesCbcRequest` / `wh_Client_AesCbcResponse` +- `wh_Client_AesCtrRequest` / `wh_Client_AesCtrResponse` +- `wh_Client_AesEcbRequest` / `wh_Client_AesEcbResponse` +- `wh_Client_AesGcmRequest` / `wh_Client_AesGcmResponse` + +DMA pairs (require `WOLFHSM_CFG_DMA`): + +- `wh_Client_AesCbcDmaRequest` / `wh_Client_AesCbcDmaResponse` +- `wh_Client_AesCtrDmaRequest` / `wh_Client_AesCtrDmaResponse` +- `wh_Client_AesEcbDmaRequest` / `wh_Client_AesEcbDmaResponse` +- `wh_Client_AesGcmDmaRequest` / `wh_Client_AesGcmDmaResponse` + +The existing blocking wrappers (`wh_Client_AesCbc`, `wh_Client_AesCtr`, +`wh_Client_AesEcb`, `wh_Client_AesGcm`, and their `*Dma` variants) are now +thin shells that call the new async primitives in a poll loop, so blocking +and async paths share identical wire behaviour. + ## Roadmap: Remaining Algorithms The async split pattern will be applied algorithm by algorithm to all crypto @@ -555,15 +632,15 @@ the full set of operations and their planned async status. | SHA-384 | Update/Final Request/Response | Shares SHA-512 wire format | | SHA-512 | Update/Final Request/Response | Non-DMA and DMA variants | | RNG Generate | `wh_Client_RngGenerate{Request,Response}` and DMA variants | Single-shot per call; non-DMA callers chunk against `WH_MESSAGE_CRYPTO_RNG_MAX_INLINE_SZ`, DMA has no per-call cap | +| AES-CBC | `wh_Client_AesCbc{,Dma}{Request,Response}` | Non-DMA and DMA variants | +| AES-CTR | `wh_Client_AesCtr{,Dma}{Request,Response}` | Non-DMA and DMA variants | +| AES-ECB | `wh_Client_AesEcb{,Dma}{Request,Response}` | Non-DMA and DMA variants | +| AES-GCM | `wh_Client_AesGcm{,Dma}{Request,Response}` | Non-DMA and DMA variants; AAD supports DMA | **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 | @@ -572,7 +649,7 @@ the full set of operations and their planned async status. | 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 | +| CMAC | `wh_Client_Cmac{Request,Response}` | Medium | Streaming (Init/Update/Final), so follows SHA-style pattern rather than the one-shot AES 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 | diff --git a/src/wh_client_crypto.c b/src/wh_client_crypto.c index 96ec5747..f26be1af 100644 --- a/src/wh_client_crypto.c +++ b/src/wh_client_crypto.c @@ -411,518 +411,664 @@ int wh_Client_RngGenerateDma(whClientContext* ctx, uint8_t* out, uint32_t size) #ifndef NO_AES #ifdef WOLFSSL_AES_COUNTER -int wh_Client_AesCtr(whClientContext* ctx, Aes* aes, int enc, const uint8_t* in, - uint32_t len, uint8_t* out) +int wh_Client_AesCtrRequest(whClientContext* ctx, Aes* aes, int enc, + const uint8_t* in, uint32_t len) { - int ret = WH_ERROR_OK; - whMessageCrypto_AesCtrRequest* req; - whMessageCrypto_AesCtrResponse* res; - uint8_t* dataPtr; + whMessageCrypto_AesCtrRequest* req; + uint8_t* dataPtr; + uint8_t* req_in; + uint8_t* req_key; + uint8_t* req_iv; + uint8_t* req_tmp; + uint32_t req_len; + uint32_t key_len; + whKeyId key_id; - if ((ctx == NULL) || (aes == NULL) || (in == NULL) || (out == NULL)) { + if ((ctx == NULL) || (aes == NULL) || ((in == NULL) && (len > 0))) { return WH_ERROR_BADARGS; } - uint32_t key_len = aes->keylen; - const uint8_t* key = (const uint8_t*)(aes->devKey); - whKeyId key_id = WH_DEVCTX_TO_KEYID(aes->devCtx); - uint8_t* iv = (uint8_t*)aes->reg; - uint32_t iv_len = AES_IV_SIZE; - uint32_t left = aes->left; - uint8_t* tmp = (uint8_t*)aes->tmp; + /* Fail-fast on occupied transport to prevent modification to local state */ + if (wh_CommClient_IsRequestPending(ctx->comm) == 1) { + return WH_ERROR_REQUEST_PENDING; + } - uint16_t group = WH_MESSAGE_GROUP_CRYPTO; - uint16_t action = WC_ALGO_TYPE_CIPHER; - uint16_t type = WC_CIPHER_AES_CTR; + key_len = aes->keylen; + key_id = WH_DEVCTX_TO_KEYID(aes->devCtx); - /* 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_AesCtrRequest*)_createCryptoRequest( dataPtr, WC_CIPHER_AES_CTR, ctx->cryptoAffinity); - uint8_t* req_in = (uint8_t*)(req + 1); - uint8_t* req_key = req_in + len; - uint8_t* req_iv = req_key + key_len; - uint8_t* req_tmp = req_iv + iv_len; - uint32_t req_len = sizeof(whMessageCrypto_GenericRequestHeader) + - sizeof(*req) + len + key_len + iv_len + - AES_BLOCK_SIZE; - WH_DEBUG_CLIENT_VERBOSE("enc:%d keylen:%d ivsz:%d insz:%d reqsz:%u " - "left:%d\n", - enc, key_len, iv_len, len, req_len, left); + req_in = (uint8_t*)(req + 1); + req_key = req_in + len; + req_iv = req_key + key_len; + req_tmp = req_iv + AES_IV_SIZE; + req_len = sizeof(whMessageCrypto_GenericRequestHeader) + sizeof(*req) + + len + key_len + AES_IV_SIZE + AES_BLOCK_SIZE; + if (req_len > WOLFHSM_CFG_COMM_DATA_LEN) { return WH_ERROR_BADARGS; } - /* setup request packet */ req->enc = enc; req->keyLen = key_len; req->sz = len; req->keyId = key_id; - req->left = left; + req->left = aes->left; if ((in != NULL) && (len > 0)) { memcpy(req_in, in, len); } - if ((key != NULL) && (key_len > 0)) { - memcpy(req_key, key, key_len); + if (key_len > 0) { + memcpy(req_key, (const uint8_t*)(aes->devKey), key_len); } - if ((iv != NULL) && (iv_len > 0)) { - memcpy(req_iv, iv, iv_len); + memcpy(req_iv, (uint8_t*)aes->reg, AES_IV_SIZE); + memcpy(req_tmp, (uint8_t*)aes->tmp, AES_BLOCK_SIZE); + + return wh_Client_SendRequest(ctx, WH_MESSAGE_GROUP_CRYPTO, + WC_ALGO_TYPE_CIPHER, req_len, dataPtr); +} + +int wh_Client_AesCtrResponse(whClientContext* ctx, Aes* aes, uint8_t* out, + uint32_t* out_size) +{ + int ret; + uint8_t* dataPtr; + uint16_t group = 0; + uint16_t action = 0; + uint16_t res_len = 0; + whMessageCrypto_AesCtrResponse* res; + + if ((ctx == NULL) || (aes == NULL) || (out == NULL)) { + return WH_ERROR_BADARGS; } - if (tmp != NULL) { - memcpy(req_tmp, tmp, AES_BLOCK_SIZE); + + dataPtr = wh_CommClient_GetDataPtr(ctx->comm); + if (dataPtr == NULL) { + return WH_ERROR_BADARGS; } - WH_DEBUG_VERBOSE_HEXDUMP("[client] in: \n", req_in, len); - WH_DEBUG_VERBOSE_HEXDUMP("[client] key: \n", req_key, key_len); - WH_DEBUG_VERBOSE_HEXDUMP("[client] iv: \n", req_iv, iv_len); - WH_DEBUG_VERBOSE_HEXDUMP("[client] tmp: \n", req_tmp, AES_BLOCK_SIZE); - WH_DEBUG_VERBOSE_HEXDUMP("[client] req packet: \n", (uint8_t*)req, req_len); - ret = wh_Client_SendRequest(ctx, group, action, req_len, dataPtr); - /* read response */ + ret = wh_Client_RecvResponse(ctx, &group, &action, &res_len, dataPtr); if (ret == WH_ERROR_OK) { - /* Response packet */ - uint16_t res_len = 0; - do { - ret = - wh_Client_RecvResponse(ctx, &group, &action, &res_len, dataPtr); - } while (ret == WH_ERROR_NOTREADY); + ret = _getCryptoResponse(dataPtr, WC_CIPHER_AES_CTR, (uint8_t**)&res); if (ret == WH_ERROR_OK) { - ret = _getCryptoResponse(dataPtr, type, (uint8_t**)&res); - if (ret == WH_ERROR_OK) { - /* Response packet */ - uint8_t* res_out = (uint8_t*)(res + 1); - /* tmp buffer follows after the output data */ - uint8_t* res_reg = res_out + res->sz; - uint8_t* res_tmp = res_reg + AES_BLOCK_SIZE; - - WH_DEBUG_CLIENT_VERBOSE("out size:%d res_len:%d\n", res->sz, res_len); - WH_DEBUG_VERBOSE_HEXDUMP("[client] res_out: \n", res_out, res->sz); - WH_DEBUG_VERBOSE_HEXDUMP("[client] res_tmp: \n", res_tmp, - AES_BLOCK_SIZE); - /* copy the response res_out */ - memcpy(out, res_out, res->sz); - /* Update the CTR state */ - aes->left = res->left; - /* Update the iv data */ - memcpy(iv, res_reg, AES_BLOCK_SIZE); - /* Update the tmp data */ - memcpy(tmp, res_tmp, AES_BLOCK_SIZE); + uint8_t* res_out = (uint8_t*)(res + 1); + uint8_t* res_reg = res_out + res->sz; + uint8_t* res_tmp = res_reg + AES_BLOCK_SIZE; + + memcpy(out, res_out, res->sz); + aes->left = res->left; + memcpy((uint8_t*)aes->reg, res_reg, AES_BLOCK_SIZE); + memcpy((uint8_t*)aes->tmp, res_tmp, AES_BLOCK_SIZE); + if (out_size != NULL) { + *out_size = res->sz; } } } return ret; } +int wh_Client_AesCtr(whClientContext* ctx, Aes* aes, int enc, const uint8_t* in, + uint32_t len, uint8_t* out) +{ + int ret; + + if ((ctx == NULL) || (aes == NULL) || (in == NULL) || (out == NULL)) { + return WH_ERROR_BADARGS; + } + + ret = wh_Client_AesCtrRequest(ctx, aes, enc, in, len); + if (ret == WH_ERROR_OK) { + do { + ret = wh_Client_AesCtrResponse(ctx, aes, out, NULL); + } while (ret == WH_ERROR_NOTREADY); + } + return ret; +} + #ifdef WOLFHSM_CFG_DMA -int wh_Client_AesCtrDma(whClientContext* ctx, Aes* aes, int enc, - const uint8_t* in, uint32_t len, uint8_t* out) +int wh_Client_AesCtrDmaRequest(whClientContext* ctx, Aes* aes, int enc, + const uint8_t* in, uint32_t len, uint8_t* out) { int ret = WH_ERROR_OK; whMessageCrypto_AesCtrDmaRequest* req = NULL; uint8_t* dataPtr = NULL; uintptr_t inAddr = 0; uintptr_t outAddr = 0; + bool inAcq = false; + bool outAcq = false; + uint8_t* req_iv; + uint8_t* req_tmp; + uint8_t* req_key; + uint32_t req_len; - uint16_t group = WH_MESSAGE_GROUP_CRYPTO_DMA; - uint16_t action = WC_ALGO_TYPE_CIPHER; - uint16_t type = WC_CIPHER_AES_CTR; - - const uint8_t* key = NULL; - uint8_t* iv = (uint8_t*)aes->reg; - uint8_t* tmp = (uint8_t*)aes->tmp; - - if (ctx == NULL || aes == NULL || in == NULL || out == NULL ) { + if ((ctx == NULL) || (aes == NULL) || ((in == NULL) && (len > 0)) || + ((out == NULL) && (len > 0))) { return WH_ERROR_BADARGS; } - /* Get data buffer */ + /* Fail-fast on occupied transport before acquiring DMA mappings */ + if (wh_CommClient_IsRequestPending(ctx->comm) == 1) { + return WH_ERROR_REQUEST_PENDING; + } + dataPtr = wh_CommClient_GetDataPtr(ctx->comm); if (dataPtr == NULL) { return WH_ERROR_BADARGS; } - /* Setup generic header and get pointer to request data */ req = (whMessageCrypto_AesCtrDmaRequest*)_createCryptoRequest( dataPtr, WC_CIPHER_AES_CTR, ctx->cryptoAffinity); - uint8_t* req_iv = (uint8_t*)req + sizeof(whMessageCrypto_AesCtrDmaRequest); - uint8_t* req_tmp = req_iv + AES_IV_SIZE; - uint8_t* req_key = req_tmp + AES_BLOCK_SIZE; - uint32_t req_len = sizeof(whMessageCrypto_GenericRequestHeader) + - sizeof(*req) + AES_IV_SIZE + AES_BLOCK_SIZE; + req_iv = (uint8_t*)req + sizeof(whMessageCrypto_AesCtrDmaRequest); + req_tmp = req_iv + AES_IV_SIZE; + req_key = req_tmp + AES_BLOCK_SIZE; + req_len = sizeof(whMessageCrypto_GenericRequestHeader) + sizeof(*req) + + AES_IV_SIZE + AES_BLOCK_SIZE; - /* Setup request packet */ memset(req, 0, sizeof(*req)); req->enc = enc; req->left = aes->left; req->keyId = WH_DEVCTX_TO_KEYID(aes->devCtx); if (req->keyId != WH_KEYID_ERASED) { - /* Using keyId-based key, server will load it from keystore */ - key = NULL; req->keySz = 0; } else { - /* Using direct key */ - key = (const uint8_t*)(aes->devKey); req->keySz = aes->keylen; - req_len += req->keySz; + req_len += req->keySz; } - /* Copy request data not handled by DMA */ - memcpy(req_iv, iv, AES_IV_SIZE); - memcpy(req_tmp, tmp, AES_BLOCK_SIZE); - if (key != NULL && req->keySz > 0) { - memcpy(req_key, key, req->keySz); + if (req_len > WOLFHSM_CFG_COMM_DATA_LEN) { + return WH_ERROR_BADARGS; + } + + memcpy(req_iv, (uint8_t*)aes->reg, AES_IV_SIZE); + memcpy(req_tmp, (uint8_t*)aes->tmp, AES_BLOCK_SIZE); + if (req->keySz > 0) { + memcpy(req_key, (const uint8_t*)(aes->devKey), req->keySz); } - if (ret == WH_ERROR_OK && in != NULL) { + if (in != NULL && len > 0) { req->input.sz = len; 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) { + inAcq = true; req->input.addr = inAddr; } } - if (ret == WH_ERROR_OK && out != NULL) { + if (ret == WH_ERROR_OK && out != NULL && len > 0) { req->output.sz = len; 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) { + outAcq = true; req->output.addr = outAddr; } } - WH_DEBUG_VERBOSE_HEXDUMP("[client] in: \n", in, len); - WH_DEBUG_VERBOSE_HEXDUMP("[client] key: \n", req_key, req->keySz); - WH_DEBUG_VERBOSE_HEXDUMP("[client] iv: \n", req_iv, AES_IV_SIZE); - WH_DEBUG_VERBOSE_HEXDUMP("[client] tmp: \n", req_tmp, AES_BLOCK_SIZE); - - /* Send request and receive response */ - WH_DEBUG_VERBOSE_HEXDUMP("[client] AESCTR DMA req packet: \n", - dataPtr, req_len); if (ret == WH_ERROR_OK) { - ret = wh_Client_SendRequest(ctx, group, action, req_len, dataPtr); + /* Stash addresses for POST cleanup in matching Response */ + ctx->dma.asyncCtx.aes.inAddr = inAddr; + ctx->dma.asyncCtx.aes.inClientAddr = (uintptr_t)in; + ctx->dma.asyncCtx.aes.inSz = inAcq ? len : 0; + ctx->dma.asyncCtx.aes.outAddr = outAddr; + ctx->dma.asyncCtx.aes.outClientAddr = (uintptr_t)out; + ctx->dma.asyncCtx.aes.outSz = outAcq ? len : 0; + ctx->dma.asyncCtx.aes.aadSz = 0; + + ret = wh_Client_SendRequest(ctx, WH_MESSAGE_GROUP_CRYPTO_DMA, + WC_ALGO_TYPE_CIPHER, req_len, dataPtr); } - if (ret == WH_ERROR_OK) { - uint16_t resLen = 0; - do { - ret = - wh_Client_RecvResponse(ctx, &group, &action, &resLen, dataPtr); - } while (ret == WH_ERROR_NOTREADY); - if (ret == WH_ERROR_OK) { - /* Get response */ - whMessageCrypto_AesCtrDmaResponse* res; - ret = _getCryptoResponse(dataPtr, type, (uint8_t**)&res); - /* wolfCrypt allows positive error codes on success in some - * scenarios */ - if (ret >= WH_ERROR_OK) { - uint8_t* res_iv = (uint8_t*)res + - sizeof(whMessageCrypto_AesCtrDmaResponse); - uint8_t* res_tmp = res_iv + AES_IV_SIZE; - WH_DEBUG_VERBOSE_HEXDUMP("[client] res_iv: \n", res_iv, AES_IV_SIZE); - WH_DEBUG_VERBOSE_HEXDUMP("[client] res_tmp: \n", res_tmp, - AES_BLOCK_SIZE); - - aes->left = res->left; - memcpy(iv, res_iv, AES_IV_SIZE); - memcpy(tmp, res_tmp, AES_BLOCK_SIZE); - ret = WH_ERROR_OK; /* Success */ - } + if (ret != WH_ERROR_OK) { + if (inAcq) { + (void)wh_Client_DmaProcessClientAddress( + ctx, (uintptr_t)in, (void**)&inAddr, len, + WH_DMA_OPER_CLIENT_READ_POST, (whDmaFlags){0}); + } + if (outAcq) { + (void)wh_Client_DmaProcessClientAddress( + ctx, (uintptr_t)out, (void**)&outAddr, len, + WH_DMA_OPER_CLIENT_WRITE_POST, (whDmaFlags){0}); + } + memset(&ctx->dma.asyncCtx.aes, 0, sizeof(ctx->dma.asyncCtx.aes)); + } + return ret; +} + +int wh_Client_AesCtrDmaResponse(whClientContext* ctx, Aes* aes) +{ + int ret; + uint8_t* dataPtr; + uint16_t res_len = 0; + whMessageCrypto_AesCtrDmaResponse* res; + + if ((ctx == NULL) || (aes == NULL)) { + return WH_ERROR_BADARGS; + } + + dataPtr = wh_CommClient_GetDataPtr(ctx->comm); + if (dataPtr == NULL) { + return WH_ERROR_BADARGS; + } + + ret = wh_Client_RecvResponse(ctx, NULL, NULL, &res_len, dataPtr); + if (ret == WH_ERROR_NOTREADY) { + return ret; + } + + if (ret == WH_ERROR_OK) { + ret = _getCryptoResponse(dataPtr, WC_CIPHER_AES_CTR, (uint8_t**)&res); + if (ret >= WH_ERROR_OK) { + uint8_t* res_iv = + (uint8_t*)res + sizeof(whMessageCrypto_AesCtrDmaResponse); + uint8_t* res_tmp = res_iv + AES_IV_SIZE; + aes->left = res->left; + memcpy((uint8_t*)aes->reg, res_iv, AES_IV_SIZE); + memcpy((uint8_t*)aes->tmp, res_tmp, AES_BLOCK_SIZE); + ret = WH_ERROR_OK; } } - /* post address translation callbacks (for cleanup) */ - if (in != NULL) { + /* POST cleanup on every non-NOTREADY return so caller buffers are safe */ + if (ctx->dma.asyncCtx.aes.inSz > 0) { + uintptr_t addr = ctx->dma.asyncCtx.aes.inAddr; (void)wh_Client_DmaProcessClientAddress( - ctx, (uintptr_t)in, (void**)&inAddr, len, - WH_DMA_OPER_CLIENT_READ_POST, (whDmaFlags){0}); + ctx, ctx->dma.asyncCtx.aes.inClientAddr, (void**)&addr, + ctx->dma.asyncCtx.aes.inSz, WH_DMA_OPER_CLIENT_READ_POST, + (whDmaFlags){0}); + ctx->dma.asyncCtx.aes.inSz = 0; } - if (out != NULL) { + if (ctx->dma.asyncCtx.aes.outSz > 0) { + uintptr_t addr = ctx->dma.asyncCtx.aes.outAddr; (void)wh_Client_DmaProcessClientAddress( - ctx, (uintptr_t)out, (void**)&outAddr, len, - WH_DMA_OPER_CLIENT_WRITE_POST, (whDmaFlags){0}); + ctx, ctx->dma.asyncCtx.aes.outClientAddr, (void**)&addr, + ctx->dma.asyncCtx.aes.outSz, WH_DMA_OPER_CLIENT_WRITE_POST, + (whDmaFlags){0}); + ctx->dma.asyncCtx.aes.outSz = 0; + } + return ret; +} + +int wh_Client_AesCtrDma(whClientContext* ctx, Aes* aes, int enc, + const uint8_t* in, uint32_t len, uint8_t* out) +{ + int ret; + + if ((ctx == NULL) || (aes == NULL) || (in == NULL) || (out == NULL)) { + return WH_ERROR_BADARGS; } + ret = wh_Client_AesCtrDmaRequest(ctx, aes, enc, in, len, out); + if (ret == WH_ERROR_OK) { + do { + ret = wh_Client_AesCtrDmaResponse(ctx, aes); + } while (ret == WH_ERROR_NOTREADY); + } return ret; } #endif /* WOLFHSM_CFG_DMA */ #endif /* WOLFSSL_AES_COUNTER */ #ifdef HAVE_AES_ECB -int wh_Client_AesEcb(whClientContext* ctx, Aes* aes, int enc, const uint8_t* in, - uint32_t len, uint8_t* out) +int wh_Client_AesEcbRequest(whClientContext* ctx, Aes* aes, int enc, + const uint8_t* in, uint32_t len) { - int ret = WH_ERROR_OK; - uint16_t blocks = len / AES_BLOCK_SIZE; - whMessageCrypto_AesEcbRequest* req; - whMessageCrypto_AesEcbResponse* res; - uint8_t* dataPtr; + whMessageCrypto_AesEcbRequest* req; + uint8_t* dataPtr; + uint8_t* req_in; + uint8_t* req_key; + uint32_t req_len; + uint32_t key_len; + whKeyId key_id; + uint16_t blocks = len / AES_BLOCK_SIZE; + if ((ctx == NULL) || (aes == NULL) || ((len % AES_BLOCK_SIZE) != 0) || + ((in == NULL) && (len > 0))) { + return WH_ERROR_BADARGS; + } if (blocks == 0) { - /* Nothing to do. */ return WH_ERROR_OK; } - if ((ctx == NULL) || (aes == NULL) || ((len % AES_BLOCK_SIZE) != 0) || - (in == NULL) || (out == NULL)) { - return WH_ERROR_BADARGS; + /* Fail-fast on occupied transport to prevent modification to local state */ + if (wh_CommClient_IsRequestPending(ctx->comm) == 1) { + return WH_ERROR_REQUEST_PENDING; } - uint32_t key_len = aes->keylen; - const uint8_t* key = (const uint8_t*)(aes->devKey); - whKeyId key_id = WH_DEVCTX_TO_KEYID(aes->devCtx); - - uint16_t group = WH_MESSAGE_GROUP_CRYPTO; - uint16_t action = WC_ALGO_TYPE_CIPHER; - uint16_t type = WC_CIPHER_AES_ECB; + key_len = aes->keylen; + key_id = WH_DEVCTX_TO_KEYID(aes->devCtx); - /* 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_AesEcbRequest*)_createCryptoRequest( dataPtr, WC_CIPHER_AES_ECB, ctx->cryptoAffinity); - uint8_t* req_in = (uint8_t*)(req + 1); - uint8_t* req_key = req_in + len; - uint32_t req_len = sizeof(whMessageCrypto_GenericRequestHeader) + - sizeof(*req) + len + key_len; - - - WH_DEBUG_CLIENT_VERBOSE("enc:%d keylen:%d insz:%d reqsz:%u blocks:%u \n", - enc, (int)key_len, (int)len, (unsigned int)req_len, - (unsigned int)blocks); + req_in = (uint8_t*)(req + 1); + req_key = req_in + len; + req_len = sizeof(whMessageCrypto_GenericRequestHeader) + sizeof(*req) + + len + key_len; if (req_len > WOLFHSM_CFG_COMM_DATA_LEN) { return WH_ERROR_BADARGS; } - /* setup request packet */ req->enc = enc; req->keyLen = key_len; req->sz = len; req->keyId = key_id; + if ((in != NULL) && (len > 0)) { memcpy(req_in, in, len); } - if ((key != NULL) && (key_len > 0)) { - memcpy(req_key, key, key_len); + if (key_len > 0) { + memcpy(req_key, (const uint8_t*)(aes->devKey), key_len); } - WH_DEBUG_VERBOSE_HEXDUMP("[client] in: \n", req_in, len); - WH_DEBUG_VERBOSE_HEXDUMP("[client] key: \n", req_key, key_len); - WH_DEBUG_VERBOSE_HEXDUMP("[client] req packet: \n", (uint8_t*)req, req_len); - ret = wh_Client_SendRequest(ctx, group, action, req_len, dataPtr); - /* read response */ - if (ret == WH_ERROR_OK) { - /* Response packet */ - uint16_t res_len = 0; - do { - ret = - wh_Client_RecvResponse(ctx, &group, &action, &res_len, dataPtr); - } while (ret == WH_ERROR_NOTREADY); - if (ret == WH_ERROR_OK) { - ret = _getCryptoResponse(dataPtr, type, (uint8_t**)&res); - if (ret == WH_ERROR_OK) { - /* Response packet */ - uint8_t* res_out = (uint8_t*)(res + 1); - WH_DEBUG_CLIENT_VERBOSE("out size:%d res_len:%d\n", (int)res->sz, - (int)res_len); - WH_DEBUG_VERBOSE_HEXDUMP("[client] res_out: \n", out, res->sz); - /* copy the response res_out */ - memcpy(out, res_out, res->sz); - } - } - } - return ret; + return wh_Client_SendRequest(ctx, WH_MESSAGE_GROUP_CRYPTO, + WC_ALGO_TYPE_CIPHER, req_len, dataPtr); } -#ifdef WOLFHSM_CFG_DMA -int wh_Client_AesEcbDma(whClientContext* ctx, Aes* aes, int enc, - const uint8_t* in, uint32_t len, uint8_t* out) +int wh_Client_AesEcbResponse(whClientContext* ctx, Aes* aes, uint8_t* out, + uint32_t* out_size) { - int ret = WH_ERROR_OK; - whMessageCrypto_AesEcbDmaRequest* req = NULL; - uint8_t* dataPtr = NULL; - uintptr_t inAddr = 0; - uintptr_t outAddr = 0; - - uint16_t group = WH_MESSAGE_GROUP_CRYPTO_DMA; - uint16_t action = WC_ALGO_TYPE_CIPHER; - uint16_t type = WC_CIPHER_AES_ECB; - - const uint8_t* key = NULL; + int ret; + uint8_t* dataPtr; + uint16_t group = 0; + uint16_t action = 0; + uint16_t res_len = 0; + whMessageCrypto_AesEcbResponse* res; - if (ctx == NULL || aes == NULL || in == NULL || out == NULL) { - return WH_ERROR_BADARGS; - } + (void)aes; - if ((len % AES_BLOCK_SIZE) != 0) { + if ((ctx == NULL) || (out == NULL)) { return WH_ERROR_BADARGS; } - /* 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_AesEcbDmaRequest*)_createCryptoRequest( - dataPtr, WC_CIPHER_AES_ECB, ctx->cryptoAffinity); - uint8_t* req_key = (uint8_t*)req + sizeof(whMessageCrypto_AesEcbDmaRequest); - uint32_t req_len = sizeof(whMessageCrypto_GenericRequestHeader) + - sizeof(*req); + ret = wh_Client_RecvResponse(ctx, &group, &action, &res_len, dataPtr); + if (ret == WH_ERROR_OK) { + ret = _getCryptoResponse(dataPtr, WC_CIPHER_AES_ECB, (uint8_t**)&res); + if (ret == WH_ERROR_OK) { + uint8_t* res_out = (uint8_t*)(res + 1); + memcpy(out, res_out, res->sz); + if (out_size != NULL) { + *out_size = res->sz; + } + } + } + return ret; +} + +int wh_Client_AesEcb(whClientContext* ctx, Aes* aes, int enc, const uint8_t* in, + uint32_t len, uint8_t* out) +{ + int ret; + + if ((ctx == NULL) || (aes == NULL) || ((len % AES_BLOCK_SIZE) != 0) || + (in == NULL) || (out == NULL)) { + return WH_ERROR_BADARGS; + } + if (len == 0) { + return WH_ERROR_OK; + } + + ret = wh_Client_AesEcbRequest(ctx, aes, enc, in, len); + if (ret == WH_ERROR_OK) { + do { + ret = wh_Client_AesEcbResponse(ctx, aes, out, NULL); + } while (ret == WH_ERROR_NOTREADY); + } + return ret; +} + +#ifdef WOLFHSM_CFG_DMA +int wh_Client_AesEcbDmaRequest(whClientContext* ctx, Aes* aes, int enc, + const uint8_t* in, uint32_t len, uint8_t* out) +{ + int ret = WH_ERROR_OK; + whMessageCrypto_AesEcbDmaRequest* req = NULL; + uint8_t* dataPtr = NULL; + uintptr_t inAddr = 0; + uintptr_t outAddr = 0; + bool inAcq = false; + bool outAcq = false; + uint8_t* req_key; + uint32_t req_len; + + if ((ctx == NULL) || (aes == NULL) || ((in == NULL) && (len > 0)) || + ((out == NULL) && (len > 0)) || ((len % AES_BLOCK_SIZE) != 0)) { + return WH_ERROR_BADARGS; + } + + if (wh_CommClient_IsRequestPending(ctx->comm) == 1) { + return WH_ERROR_REQUEST_PENDING; + } + + dataPtr = wh_CommClient_GetDataPtr(ctx->comm); + if (dataPtr == NULL) { + return WH_ERROR_BADARGS; + } + + req = (whMessageCrypto_AesEcbDmaRequest*)_createCryptoRequest( + dataPtr, WC_CIPHER_AES_ECB, ctx->cryptoAffinity); + req_key = (uint8_t*)req + sizeof(whMessageCrypto_AesEcbDmaRequest); + req_len = sizeof(whMessageCrypto_GenericRequestHeader) + sizeof(*req); - /* Setup request packet */ memset(req, 0, sizeof(*req)); - req->enc = enc; + req->enc = enc; req->keyId = WH_DEVCTX_TO_KEYID(aes->devCtx); if (req->keyId != WH_KEYID_ERASED) { - /* Using keyId-based key, server will load it from keystore */ - key = NULL; req->keySz = 0; } else { - /* Using direct key */ - key = (const uint8_t*)(aes->devKey); req->keySz = aes->keylen; - req_len += req->keySz; - memcpy(req_key, key, req->keySz); + req_len += req->keySz; + } + + if (req_len > WOLFHSM_CFG_COMM_DATA_LEN) { + return WH_ERROR_BADARGS; } - if (ret == WH_ERROR_OK && in != NULL) { + if (req->keySz > 0) { + memcpy(req_key, (const uint8_t*)(aes->devKey), req->keySz); + } + + if (in != NULL && len > 0) { req->input.sz = len; 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) { + inAcq = true; req->input.addr = inAddr; } } - if (ret == WH_ERROR_OK && out != NULL) { + if (ret == WH_ERROR_OK && out != NULL && len > 0) { req->output.sz = len; 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) { + outAcq = true; req->output.addr = outAddr; } } - WH_DEBUG_VERBOSE_HEXDUMP("[client] in: \n", in, len); - WH_DEBUG_VERBOSE_HEXDUMP("[client] key: \n", req_key, req->keySz); - - /* Send request and receive response */ - WH_DEBUG_VERBOSE_HEXDUMP("[client] AESECB DMA req packet: \n", - dataPtr, req_len); if (ret == WH_ERROR_OK) { - ret = wh_Client_SendRequest(ctx, group, action, req_len, dataPtr); + ctx->dma.asyncCtx.aes.inAddr = inAddr; + ctx->dma.asyncCtx.aes.inClientAddr = (uintptr_t)in; + ctx->dma.asyncCtx.aes.inSz = inAcq ? len : 0; + ctx->dma.asyncCtx.aes.outAddr = outAddr; + ctx->dma.asyncCtx.aes.outClientAddr = (uintptr_t)out; + ctx->dma.asyncCtx.aes.outSz = outAcq ? len : 0; + ctx->dma.asyncCtx.aes.aadSz = 0; + + ret = wh_Client_SendRequest(ctx, WH_MESSAGE_GROUP_CRYPTO_DMA, + WC_ALGO_TYPE_CIPHER, req_len, dataPtr); } - if (ret == WH_ERROR_OK) { - uint16_t resLen = 0; - do { - ret = - wh_Client_RecvResponse(ctx, &group, &action, &resLen, dataPtr); - } while (ret == WH_ERROR_NOTREADY); - if (ret == WH_ERROR_OK) { - /* Get response */ - whMessageCrypto_AesEcbDmaResponse* res; - ret = _getCryptoResponse(dataPtr, type, (uint8_t**)&res); - /* wolfCrypt allows positive error codes on success in some - * scenarios */ - if (ret >= 0) { - /* For DMA operations, data is already in client memory, - * no need to copy it back */ - ret = WH_ERROR_OK; /* Success */ - } + if (ret != WH_ERROR_OK) { + if (inAcq) { + (void)wh_Client_DmaProcessClientAddress( + ctx, (uintptr_t)in, (void**)&inAddr, len, + WH_DMA_OPER_CLIENT_READ_POST, (whDmaFlags){0}); } + if (outAcq) { + (void)wh_Client_DmaProcessClientAddress( + ctx, (uintptr_t)out, (void**)&outAddr, len, + WH_DMA_OPER_CLIENT_WRITE_POST, (whDmaFlags){0}); + } + memset(&ctx->dma.asyncCtx.aes, 0, sizeof(ctx->dma.asyncCtx.aes)); } + return ret; +} - /* post address translation callbacks (for cleanup) */ - if (in != NULL) { +int wh_Client_AesEcbDmaResponse(whClientContext* ctx, Aes* aes) +{ + int ret; + uint8_t* dataPtr; + uint16_t res_len = 0; + whMessageCrypto_AesEcbDmaResponse* res; + + (void)aes; + + if (ctx == NULL) { + return WH_ERROR_BADARGS; + } + + dataPtr = wh_CommClient_GetDataPtr(ctx->comm); + if (dataPtr == NULL) { + return WH_ERROR_BADARGS; + } + + ret = wh_Client_RecvResponse(ctx, NULL, NULL, &res_len, dataPtr); + if (ret == WH_ERROR_NOTREADY) { + return ret; + } + + if (ret == WH_ERROR_OK) { + ret = _getCryptoResponse(dataPtr, WC_CIPHER_AES_ECB, (uint8_t**)&res); + if (ret >= 0) { + ret = WH_ERROR_OK; + } + } + + if (ctx->dma.asyncCtx.aes.inSz > 0) { + uintptr_t addr = ctx->dma.asyncCtx.aes.inAddr; (void)wh_Client_DmaProcessClientAddress( - ctx, (uintptr_t)in, (void**)&inAddr, len, - WH_DMA_OPER_CLIENT_READ_POST, (whDmaFlags){0}); + ctx, ctx->dma.asyncCtx.aes.inClientAddr, (void**)&addr, + ctx->dma.asyncCtx.aes.inSz, WH_DMA_OPER_CLIENT_READ_POST, + (whDmaFlags){0}); + ctx->dma.asyncCtx.aes.inSz = 0; } - if (out != NULL) { + if (ctx->dma.asyncCtx.aes.outSz > 0) { + uintptr_t addr = ctx->dma.asyncCtx.aes.outAddr; (void)wh_Client_DmaProcessClientAddress( - ctx, (uintptr_t)out, (void**)&outAddr, len, - WH_DMA_OPER_CLIENT_WRITE_POST, (whDmaFlags){0}); + ctx, ctx->dma.asyncCtx.aes.outClientAddr, (void**)&addr, + ctx->dma.asyncCtx.aes.outSz, WH_DMA_OPER_CLIENT_WRITE_POST, + (whDmaFlags){0}); + ctx->dma.asyncCtx.aes.outSz = 0; } + return ret; +} +int wh_Client_AesEcbDma(whClientContext* ctx, Aes* aes, int enc, + const uint8_t* in, uint32_t len, uint8_t* out) +{ + int ret; + + if ((ctx == NULL) || (aes == NULL) || (in == NULL) || (out == NULL) || + ((len % AES_BLOCK_SIZE) != 0)) { + return WH_ERROR_BADARGS; + } + if (len == 0) { + return WH_ERROR_OK; + } + + ret = wh_Client_AesEcbDmaRequest(ctx, aes, enc, in, len, out); + if (ret == WH_ERROR_OK) { + do { + ret = wh_Client_AesEcbDmaResponse(ctx, aes); + } while (ret == WH_ERROR_NOTREADY); + } return ret; } #endif /* WOLFHSM_CFG_DMA */ #endif /* HAVE_AES_ECB */ #ifdef HAVE_AES_CBC -int wh_Client_AesCbc(whClientContext* ctx, Aes* aes, int enc, const uint8_t* in, - uint32_t len, uint8_t* out) +int wh_Client_AesCbcRequest(whClientContext* ctx, Aes* aes, int enc, + const uint8_t* in, uint32_t len) { - int ret = WH_ERROR_OK; - uint16_t blocks = len / AES_BLOCK_SIZE; - whMessageCrypto_AesCbcRequest* req; - whMessageCrypto_AesCbcResponse* res; - uint8_t* dataPtr; + whMessageCrypto_AesCbcRequest* req; + uint8_t* dataPtr; + uint16_t blocks; + uint32_t key_len; + const uint8_t* key; + whKeyId key_id; + uint8_t* iv; + uint32_t iv_len; + uint8_t* req_in; + uint8_t* req_key; + uint8_t* req_iv; + uint32_t req_len; + + if ((ctx == NULL) || (aes == NULL) || ((len % AES_BLOCK_SIZE) != 0) || + (in == NULL)) { + return WH_ERROR_BADARGS; + } + + blocks = len / AES_BLOCK_SIZE; if (blocks == 0) { - /* Nothing to do. */ return WH_ERROR_OK; } - if ((ctx == NULL) || (aes == NULL) || ((len % AES_BLOCK_SIZE) != 0) || - (in == NULL) || (out == NULL)) { - return WH_ERROR_BADARGS; + /* Fail-fast on occupied transport to prevent modification to local state */ + if (wh_CommClient_IsRequestPending(ctx->comm) == 1) { + return WH_ERROR_REQUEST_PENDING; } - uint32_t key_len = aes->keylen; - const uint8_t* key = (const uint8_t*)(aes->devKey); - whKeyId key_id = WH_DEVCTX_TO_KEYID(aes->devCtx); - uint8_t* iv = (uint8_t*)aes->reg; - uint32_t iv_len = AES_IV_SIZE; - - uint16_t group = WH_MESSAGE_GROUP_CRYPTO; - uint16_t action = WC_ALGO_TYPE_CIPHER; - uint16_t type = WC_CIPHER_AES_CBC; + key_len = aes->keylen; + key = (const uint8_t*)(aes->devKey); + key_id = WH_DEVCTX_TO_KEYID(aes->devCtx); + iv = (uint8_t*)aes->reg; + iv_len = AES_IV_SIZE; - /* 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_AesCbcRequest*)_createCryptoRequest( dataPtr, WC_CIPHER_AES_CBC, ctx->cryptoAffinity); - uint8_t* req_in = (uint8_t*)(req + 1); - uint8_t* req_key = req_in + len; - uint8_t* req_iv = req_key + key_len; - uint32_t req_len = sizeof(whMessageCrypto_GenericRequestHeader) + - sizeof(*req) + len + key_len + iv_len; - - WH_DEBUG_CLIENT_VERBOSE("enc:%d keylen:%d ivsz:%d insz:%d reqsz:%u " - "blocks:%u\n", - enc, (int)key_len, (int)iv_len, (int)len, - (unsigned int)req_len, (unsigned int)blocks); + req_in = (uint8_t*)(req + 1); + req_key = req_in + len; + req_iv = req_key + key_len; + req_len = sizeof(whMessageCrypto_GenericRequestHeader) + sizeof(*req) + + len + key_len + iv_len; if (req_len > WOLFHSM_CFG_COMM_DATA_LEN) { return WH_ERROR_BADARGS; } - /* setup request packet */ req->enc = enc; req->keyLen = key_len; req->sz = len; @@ -937,256 +1083,353 @@ int wh_Client_AesCbc(whClientContext* ctx, Aes* aes, int enc, const uint8_t* in, memcpy(req_iv, iv, iv_len); } - WH_DEBUG_VERBOSE_HEXDUMP("[client] in: \n", req_in, len); - WH_DEBUG_VERBOSE_HEXDUMP("[client] key: \n", req_key, key_len); - WH_DEBUG_VERBOSE_HEXDUMP("[client] iv: \n", req_iv, iv_len); - WH_DEBUG_VERBOSE_HEXDUMP("[client] req packet: \n", (uint8_t*)req, req_len); - ret = wh_Client_SendRequest(ctx, group, action, req_len, dataPtr); - /* read response */ + return wh_Client_SendRequest(ctx, WH_MESSAGE_GROUP_CRYPTO, + WC_ALGO_TYPE_CIPHER, req_len, dataPtr); +} + +int wh_Client_AesCbcResponse(whClientContext* ctx, Aes* aes, uint8_t* out, + uint32_t* out_size) +{ + int ret; + uint8_t* dataPtr; + uint16_t group = 0; + uint16_t action = 0; + uint16_t res_len = 0; + whMessageCrypto_AesCbcResponse* res; + + if ((ctx == NULL) || (aes == 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, &res_len, dataPtr); if (ret == WH_ERROR_OK) { - /* Response packet */ - uint16_t res_len = 0; - do { - ret = - wh_Client_RecvResponse(ctx, &group, &action, &res_len, dataPtr); - } while (ret == WH_ERROR_NOTREADY); + ret = _getCryptoResponse(dataPtr, WC_CIPHER_AES_CBC, (uint8_t**)&res); if (ret == WH_ERROR_OK) { - ret = _getCryptoResponse(dataPtr, type, (uint8_t**)&res); - if (ret == WH_ERROR_OK) { - /* Response packet */ - uint8_t* res_out = (uint8_t*)(res + 1); - uint8_t* res_iv = res_out + res->sz; - WH_DEBUG_CLIENT_VERBOSE("out size:%d res_len:%d\n", (int)res->sz, - (int)res_len); - WH_DEBUG_VERBOSE_HEXDUMP("[client] res_out: \n", out, res->sz); - WH_DEBUG_VERBOSE_HEXDUMP("[client] res_iv: \n", res_iv, AES_IV_SIZE); - /* copy the response res_out */ - memcpy(out, res_out, res->sz); - /* Update the CBC state with the last cipher text block */ - memcpy(iv, res_iv, AES_IV_SIZE); + uint8_t* res_out = (uint8_t*)(res + 1); + uint8_t* res_iv = res_out + res->sz; + memcpy(out, res_out, res->sz); + /* Update the IV from the server response for CBC chaining. + * The server provides the updated IV after the output data. */ + memcpy((uint8_t*)aes->reg, res_iv, AES_IV_SIZE); + if (out_size != NULL) { + *out_size = res->sz; } } } + + return ret; +} + +int wh_Client_AesCbc(whClientContext* ctx, Aes* aes, int enc, const uint8_t* in, + uint32_t len, uint8_t* out) +{ + int ret; + + if ((ctx == NULL) || (aes == NULL) || ((len % AES_BLOCK_SIZE) != 0) || + (in == NULL) || (out == NULL)) { + return WH_ERROR_BADARGS; + } + if (len == 0) { + return WH_ERROR_OK; + } + + ret = wh_Client_AesCbcRequest(ctx, aes, enc, in, len); + if (ret == WH_ERROR_OK) { + do { + ret = wh_Client_AesCbcResponse(ctx, aes, out, NULL); + } while (ret == WH_ERROR_NOTREADY); + } return ret; } #ifdef WOLFHSM_CFG_DMA -int wh_Client_AesCbcDma(whClientContext* ctx, Aes* aes, int enc, - const uint8_t* in, uint32_t len, uint8_t* out) +int wh_Client_AesCbcDmaRequest(whClientContext* ctx, Aes* aes, int enc, + const uint8_t* in, uint32_t len, uint8_t* out) { int ret = WH_ERROR_OK; whMessageCrypto_AesCbcDmaRequest* req = NULL; uint8_t* dataPtr = NULL; uintptr_t inAddr = 0; uintptr_t outAddr = 0; + bool inAcq = false; + bool outAcq = false; + uint8_t* req_iv; + uint8_t* req_key; + uint32_t req_len; + uint8_t* iv; - uint16_t group = WH_MESSAGE_GROUP_CRYPTO_DMA; - uint16_t action = WC_ALGO_TYPE_CIPHER; - uint16_t type = WC_CIPHER_AES_CBC; - - const uint8_t* key = NULL; - uint8_t* iv = (uint8_t*)aes->reg; - - if (ctx == NULL || aes == NULL || in == NULL || out == NULL) { + if ((ctx == NULL) || (aes == NULL) || ((in == NULL) && (len > 0)) || + ((out == NULL) && (len > 0)) || ((len % AES_BLOCK_SIZE) != 0)) { return WH_ERROR_BADARGS; } - if ((len % AES_BLOCK_SIZE) != 0) { - return WH_ERROR_BADARGS; + if (wh_CommClient_IsRequestPending(ctx->comm) == 1) { + return WH_ERROR_REQUEST_PENDING; } - /* Get data buffer */ + iv = (uint8_t*)aes->reg; dataPtr = wh_CommClient_GetDataPtr(ctx->comm); if (dataPtr == NULL) { return WH_ERROR_BADARGS; } - /* Setup generic header and get pointer to request data */ req = (whMessageCrypto_AesCbcDmaRequest*)_createCryptoRequest( dataPtr, WC_CIPHER_AES_CBC, ctx->cryptoAffinity); - uint8_t* req_iv = (uint8_t*)req + sizeof(whMessageCrypto_AesCbcDmaRequest); - uint8_t* req_key = req_iv + AES_IV_SIZE; - uint32_t req_len = sizeof(whMessageCrypto_GenericRequestHeader) + - sizeof(*req) + AES_IV_SIZE; + req_iv = (uint8_t*)req + sizeof(whMessageCrypto_AesCbcDmaRequest); + req_key = req_iv + AES_IV_SIZE; + req_len = sizeof(whMessageCrypto_GenericRequestHeader) + sizeof(*req) + + AES_IV_SIZE; - /* Setup request packet */ memset(req, 0, sizeof(*req)); - req->enc = enc; + req->enc = enc; req->keyId = WH_DEVCTX_TO_KEYID(aes->devCtx); if (req->keyId != WH_KEYID_ERASED) { - /* Using keyId-based key, server will load it from keystore */ - key = NULL; req->keySz = 0; } else { - /* Using direct key */ - key = (const uint8_t*)(aes->devKey); req->keySz = aes->keylen; - req_len += req->keySz; + req_len += req->keySz; + } + + if (req_len > WOLFHSM_CFG_COMM_DATA_LEN) { + return WH_ERROR_BADARGS; } - /* Copy request data not handled by DMA */ memcpy(req_iv, iv, AES_IV_SIZE); - if (key != NULL && req->keySz > 0) { - memcpy(req_key, key, req->keySz); + if (req->keySz > 0) { + memcpy(req_key, (const uint8_t*)(aes->devKey), req->keySz); } - if (ret == WH_ERROR_OK && in != NULL) { + if (in != NULL && len > 0) { req->input.sz = len; 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) { + inAcq = true; req->input.addr = inAddr; } } - if (ret == WH_ERROR_OK && out != NULL) { + if (ret == WH_ERROR_OK && out != NULL && len > 0) { req->output.sz = len; 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) { + outAcq = true; req->output.addr = outAddr; } } - WH_DEBUG_VERBOSE_HEXDUMP("[client] key: \n", req_key, req->keySz); - WH_DEBUG_VERBOSE_HEXDUMP("[client] iv: \n", req_iv, AES_IV_SIZE); - - /* Send request and receive response */ - WH_DEBUG_VERBOSE_HEXDUMP("[client] AESCBC DMA req packet: \n", - dataPtr, req_len); if (ret == WH_ERROR_OK) { - ret = wh_Client_SendRequest(ctx, group, action, req_len, dataPtr); + ctx->dma.asyncCtx.aes.inAddr = inAddr; + ctx->dma.asyncCtx.aes.inClientAddr = (uintptr_t)in; + ctx->dma.asyncCtx.aes.inSz = inAcq ? len : 0; + ctx->dma.asyncCtx.aes.outAddr = outAddr; + ctx->dma.asyncCtx.aes.outClientAddr = (uintptr_t)out; + ctx->dma.asyncCtx.aes.outSz = outAcq ? len : 0; + ctx->dma.asyncCtx.aes.aadSz = 0; + + ret = wh_Client_SendRequest(ctx, WH_MESSAGE_GROUP_CRYPTO_DMA, + WC_ALGO_TYPE_CIPHER, req_len, dataPtr); } - if (ret == WH_ERROR_OK) { - uint16_t resLen = 0; - do { - ret = - wh_Client_RecvResponse(ctx, &group, &action, &resLen, dataPtr); - } while (ret == WH_ERROR_NOTREADY); - if (ret == WH_ERROR_OK) { - /* Get response */ - whMessageCrypto_AesCbcDmaResponse* res; - ret = _getCryptoResponse(dataPtr, type, (uint8_t**)&res); - /* wolfCrypt allows positive error codes on success in some - * scenarios */ - if (ret >= WH_ERROR_OK) { - uint8_t* res_iv = (uint8_t*)res + - sizeof(whMessageCrypto_AesCbcDmaResponse); - WH_DEBUG_VERBOSE_HEXDUMP("[client] res_iv: \n", res_iv, - AES_IV_SIZE); - memcpy(iv, res_iv, AES_IV_SIZE); - ret = WH_ERROR_OK; /* Success */ - } + if (ret != WH_ERROR_OK) { + if (inAcq) { + (void)wh_Client_DmaProcessClientAddress( + ctx, (uintptr_t)in, (void**)&inAddr, len, + WH_DMA_OPER_CLIENT_READ_POST, (whDmaFlags){0}); + } + if (outAcq) { + (void)wh_Client_DmaProcessClientAddress( + ctx, (uintptr_t)out, (void**)&outAddr, len, + WH_DMA_OPER_CLIENT_WRITE_POST, (whDmaFlags){0}); + } + memset(&ctx->dma.asyncCtx.aes, 0, sizeof(ctx->dma.asyncCtx.aes)); + } + return ret; +} + +int wh_Client_AesCbcDmaResponse(whClientContext* ctx, Aes* aes) +{ + int ret; + uint8_t* dataPtr; + uint16_t res_len = 0; + whMessageCrypto_AesCbcDmaResponse* res; + + if ((ctx == NULL) || (aes == NULL)) { + return WH_ERROR_BADARGS; + } + + dataPtr = wh_CommClient_GetDataPtr(ctx->comm); + if (dataPtr == NULL) { + return WH_ERROR_BADARGS; + } + + ret = wh_Client_RecvResponse(ctx, NULL, NULL, &res_len, dataPtr); + if (ret == WH_ERROR_NOTREADY) { + return ret; + } + + if (ret == WH_ERROR_OK) { + ret = _getCryptoResponse(dataPtr, WC_CIPHER_AES_CBC, (uint8_t**)&res); + if (ret >= WH_ERROR_OK) { + uint8_t* res_iv = + (uint8_t*)res + sizeof(whMessageCrypto_AesCbcDmaResponse); + memcpy((uint8_t*)aes->reg, res_iv, AES_IV_SIZE); + ret = WH_ERROR_OK; } } - /* post address translation callbacks (for cleanup) */ - if (in != NULL) { + if (ctx->dma.asyncCtx.aes.inSz > 0) { + uintptr_t addr = ctx->dma.asyncCtx.aes.inAddr; (void)wh_Client_DmaProcessClientAddress( - ctx, (uintptr_t)in, (void**)&inAddr, len, - WH_DMA_OPER_CLIENT_READ_POST, (whDmaFlags){0}); + ctx, ctx->dma.asyncCtx.aes.inClientAddr, (void**)&addr, + ctx->dma.asyncCtx.aes.inSz, WH_DMA_OPER_CLIENT_READ_POST, + (whDmaFlags){0}); + ctx->dma.asyncCtx.aes.inSz = 0; } - if (out != NULL) { + if (ctx->dma.asyncCtx.aes.outSz > 0) { + uintptr_t addr = ctx->dma.asyncCtx.aes.outAddr; (void)wh_Client_DmaProcessClientAddress( - ctx, (uintptr_t)out, (void**)&outAddr, len, - WH_DMA_OPER_CLIENT_WRITE_POST, (whDmaFlags){0}); + ctx, ctx->dma.asyncCtx.aes.outClientAddr, (void**)&addr, + ctx->dma.asyncCtx.aes.outSz, WH_DMA_OPER_CLIENT_WRITE_POST, + (whDmaFlags){0}); + ctx->dma.asyncCtx.aes.outSz = 0; + } + return ret; +} + +int wh_Client_AesCbcDma(whClientContext* ctx, Aes* aes, int enc, + const uint8_t* in, uint32_t len, uint8_t* out) +{ + int ret; + + if ((ctx == NULL) || (aes == NULL) || (in == NULL) || (out == NULL) || + ((len % AES_BLOCK_SIZE) != 0)) { + return WH_ERROR_BADARGS; + } + if (len == 0) { + return WH_ERROR_OK; } + ret = wh_Client_AesCbcDmaRequest(ctx, aes, enc, in, len, out); + if (ret == WH_ERROR_OK) { + do { + ret = wh_Client_AesCbcDmaResponse(ctx, aes); + } while (ret == WH_ERROR_NOTREADY); + } return ret; } #endif /* WOLFHSM_CFG_DMA */ +#endif /* HAVE_AES_CBC */ -int wh_Client_AesCbcRequest(whClientContext* ctx, Aes* aes, int enc, - const uint8_t* in, uint32_t len) +#ifdef HAVE_AESGCM +int wh_Client_AesGcmRequest(whClientContext* ctx, Aes* aes, int enc, + const uint8_t* in, uint32_t len, const uint8_t* iv, + uint32_t iv_len, const uint8_t* authin, + uint32_t authin_len, const uint8_t* dec_tag, + uint32_t tag_len) { - whMessageCrypto_AesCbcRequest* req; + whMessageCrypto_AesGcmRequest* req; uint8_t* dataPtr; - uint16_t blocks; - uint32_t key_len; - const uint8_t* key; - whKeyId key_id; - uint8_t* iv; - uint32_t iv_len; uint8_t* req_in; uint8_t* req_key; uint8_t* req_iv; + uint8_t* req_authin; + uint8_t* req_tag; uint32_t req_len; + uint32_t key_len; + whKeyId key_id; - if ((ctx == NULL) || (aes == NULL) || ((len % AES_BLOCK_SIZE) != 0) || - (in == NULL)) { + if ((ctx == NULL) || (aes == NULL) || ((in == NULL) && (len > 0)) || + ((iv == NULL) && (iv_len > 0)) || + ((authin == NULL) && (authin_len > 0)) || + ((enc == 0) && (dec_tag == NULL))) { return WH_ERROR_BADARGS; } - blocks = len / AES_BLOCK_SIZE; - - if (blocks == 0) { - return WH_ERROR_OK; + /* Fail-fast on occupied transport to prevent modification to local state */ + if (wh_CommClient_IsRequestPending(ctx->comm) == 1) { + return WH_ERROR_REQUEST_PENDING; } key_len = aes->keylen; - key = (const uint8_t*)(aes->devKey); key_id = WH_DEVCTX_TO_KEYID(aes->devCtx); - iv = (uint8_t*)aes->reg; - iv_len = AES_IV_SIZE; dataPtr = wh_CommClient_GetDataPtr(ctx->comm); if (dataPtr == NULL) { return WH_ERROR_BADARGS; } - req = (whMessageCrypto_AesCbcRequest*)_createCryptoRequest( - dataPtr, WC_CIPHER_AES_CBC, ctx->cryptoAffinity); - req_in = (uint8_t*)(req + 1); - req_key = req_in + len; - req_iv = req_key + key_len; - req_len = sizeof(whMessageCrypto_GenericRequestHeader) + - sizeof(*req) + len + key_len + iv_len; + req = (whMessageCrypto_AesGcmRequest*)_createCryptoRequest( + dataPtr, WC_CIPHER_AES_GCM, ctx->cryptoAffinity); + + req_in = (uint8_t*)(req + 1); + req_key = req_in + len; + req_iv = req_key + key_len; + req_authin = req_iv + iv_len; + req_tag = req_authin + authin_len; + + req_len = sizeof(whMessageCrypto_GenericRequestHeader) + sizeof(*req) + + len + key_len + iv_len + authin_len + ((enc == 0) ? tag_len : 0); if (req_len > WOLFHSM_CFG_COMM_DATA_LEN) { return WH_ERROR_BADARGS; } - req->enc = enc; - req->keyLen = key_len; - req->sz = len; - req->keyId = key_id; - if ((in != NULL) && (len > 0)) { + req->enc = enc; + req->keyLen = key_len; + req->sz = len; + req->ivSz = iv_len; + req->authInSz = authin_len; + req->authTagSz = tag_len; + req->keyId = key_id; + + if (in != NULL && len > 0) { memcpy(req_in, in, len); } - if ((key != NULL) && (key_len > 0)) { - memcpy(req_key, key, key_len); + if (key_len > 0) { + memcpy(req_key, (const uint8_t*)(aes->devKey), key_len); } - if ((iv != NULL) && (iv_len > 0)) { + if (iv != NULL && iv_len > 0) { memcpy(req_iv, iv, iv_len); } - - /* For decryption, update the IV with the last ciphertext block before - * sending, since the input buffer may be reused for output (in-place) */ - if (enc == 0) { - uint32_t last_offset = (blocks - 1) * AES_BLOCK_SIZE; - memcpy(iv, in + last_offset, iv_len); + if (authin != NULL && authin_len > 0) { + memcpy(req_authin, authin, authin_len); + } + if (enc == 0 && dec_tag != NULL && tag_len > 0) { + memcpy(req_tag, dec_tag, tag_len); } return wh_Client_SendRequest(ctx, WH_MESSAGE_GROUP_CRYPTO, WC_ALGO_TYPE_CIPHER, req_len, dataPtr); } -int wh_Client_AesCbcResponse(whClientContext* ctx, Aes* aes, uint8_t* out, - uint32_t* out_size) +int wh_Client_AesGcmResponse(whClientContext* ctx, Aes* aes, uint8_t* out, + uint32_t out_capacity, uint32_t* out_size, + uint8_t* enc_tag, uint32_t tag_len) { int ret; uint8_t* dataPtr; uint16_t group = 0; uint16_t action = 0; uint16_t res_len = 0; - whMessageCrypto_AesCbcResponse* res; + whMessageCrypto_AesGcmResponse* res; - if ((ctx == NULL) || (aes == NULL) || (out == NULL)) { + (void)aes; + + /* out may be NULL (e.g. GMAC: tag-only, no plaintext). If provided the + * response payload is copied to it after validating that the server's + * reported size fits within out_capacity. */ + if (ctx == NULL || (out == NULL && out_capacity > 0)) { return WH_ERROR_BADARGS; } @@ -1197,339 +1440,270 @@ int wh_Client_AesCbcResponse(whClientContext* ctx, Aes* aes, uint8_t* out, ret = wh_Client_RecvResponse(ctx, &group, &action, &res_len, dataPtr); if (ret == WH_ERROR_OK) { - ret = _getCryptoResponse(dataPtr, WC_CIPHER_AES_CBC, (uint8_t**)&res); - if (ret == WH_ERROR_OK) { + ret = _getCryptoResponse(dataPtr, WC_CIPHER_AES_GCM, (uint8_t**)&res); + if (ret >= WH_ERROR_OK) { uint8_t* res_out = (uint8_t*)(res + 1); - uint8_t* res_iv = res_out + res->sz; - memcpy(out, res_out, res->sz); - /* Update the IV from the server response for CBC chaining. - * The server provides the updated IV after the output data. */ - memcpy((uint8_t*)aes->reg, res_iv, AES_IV_SIZE); + uint8_t* res_tag = res_out + res->sz; + + if (out != NULL && res->sz > 0) { + if (res->sz > out_capacity) { + return WH_ERROR_ABORTED; + } + memcpy(out, res_out, res->sz); + } if (out_size != NULL) { *out_size = res->sz; } + if (enc_tag != NULL && res->authTagSz > 0 && + res->authTagSz <= tag_len) { + memcpy(enc_tag, res_tag, res->authTagSz); + } + ret = WH_ERROR_OK; } } - return ret; } -#endif /* HAVE_AES_CBC */ -#ifdef HAVE_AESGCM int wh_Client_AesGcm(whClientContext* ctx, Aes* aes, int enc, const uint8_t* in, uint32_t len, const uint8_t* iv, uint32_t iv_len, const uint8_t* authin, uint32_t authin_len, const uint8_t* dec_tag, uint8_t* enc_tag, uint32_t tag_len, uint8_t* out) { - int ret = WH_ERROR_OK; - - if ((ctx == NULL) || (aes == NULL) || ((in == NULL) && (len > 0)) || - ((iv == NULL) && (iv_len > 0)) || - ((authin == NULL) && (authin_len > 0)) || - ((enc == 0) && (dec_tag == NULL))) { - return WH_ERROR_BADARGS; - } - - uint16_t group = WH_MESSAGE_GROUP_CRYPTO; - uint16_t action = WC_ALGO_TYPE_CIPHER; - uint16_t type = WC_CIPHER_AES_GCM; - - uint32_t key_len = aes->keylen; - const uint8_t* key = (const uint8_t*)(aes->devKey); - whKeyId key_id = WH_DEVCTX_TO_KEYID(aes->devCtx); - - - /* Get data buffer */ - uint8_t* dataPtr = wh_CommClient_GetDataPtr(ctx->comm); - if (dataPtr == NULL) { - return WH_ERROR_BADARGS; - } - - /* Setup generic header and get pointer to request data */ - whMessageCrypto_AesGcmRequest* req = - (whMessageCrypto_AesGcmRequest*)_createCryptoRequest( - dataPtr, WC_CIPHER_AES_GCM, ctx->cryptoAffinity); - - uint8_t* req_in = (uint8_t*)(req + 1); - uint8_t* req_key = req_in + len; - uint8_t* req_iv = req_key + key_len; - uint8_t* req_authin = req_iv + iv_len; - uint8_t* req_tag = req_authin + authin_len; - - uint32_t req_len = sizeof(whMessageCrypto_GenericRequestHeader) + - sizeof(*req) + len + key_len + iv_len + authin_len + - ((enc == 0) ? tag_len : 0); - - WH_DEBUG_CLIENT_VERBOSE("AESGCM: enc:%d keylen:%d ivsz:%d insz:%d authinsz:%d " - "authtagsz:%d reqsz:%u\n", - enc, (int)key_len, (int)iv_len, (int)len, (int)authin_len, - (int)tag_len, (unsigned int)req_len); - WH_DEBUG_CLIENT_VERBOSE("AESGCM: req:%p in:%p key:%p iv:%p authin:%p tag:%p\n", req, - req_in, req_key, req_iv, req_authin, req_tag); - if (req_len > WOLFHSM_CFG_COMM_DATA_LEN) { - return WH_ERROR_BADARGS; - } - - /* setup request packet */ - req->enc = enc; - req->keyLen = key_len; - req->sz = len; - req->ivSz = iv_len; - req->authInSz = authin_len; - req->authTagSz = tag_len; - req->keyId = key_id; - - if (in != NULL && len > 0) { - memcpy(req_in, in, len); - } - if (key != NULL && key_len > 0) { - memcpy(req_key, key, key_len); - } - if (iv != NULL && iv_len > 0) { - memcpy(req_iv, iv, iv_len); - } - if (authin != NULL && authin_len > 0) { - memcpy(req_authin, authin, authin_len); - } - - WH_DEBUG_VERBOSE_HEXDUMP("[client] in: \n", req_in, len); - WH_DEBUG_VERBOSE_HEXDUMP("[client] key: \n", req_key, key_len); - WH_DEBUG_VERBOSE_HEXDUMP("[client] iv: \n", req_iv, iv_len); - WH_DEBUG_VERBOSE_HEXDUMP("[client] authin: \n", req_authin, authin_len); - - /* set auth tag by direction */ - if (enc == 0 && dec_tag != NULL && tag_len > 0) { - memcpy(req_tag, dec_tag, tag_len); - WH_DEBUG_VERBOSE_HEXDUMP("[client] dec tag: \n", req_tag, tag_len); - } - - WH_DEBUG_VERBOSE_HEXDUMP("[client] AESGCM req packet: \n", dataPtr, req_len); + int ret; - /* Send request and receive response */ - ret = wh_Client_SendRequest(ctx, group, action, req_len, dataPtr); + ret = wh_Client_AesGcmRequest(ctx, aes, enc, in, len, iv, iv_len, authin, + authin_len, dec_tag, tag_len); if (ret == WH_ERROR_OK) { - uint16_t res_len = 0; do { - ret = - wh_Client_RecvResponse(ctx, &group, &action, &res_len, dataPtr); + ret = wh_Client_AesGcmResponse(ctx, aes, out, len, NULL, enc_tag, + tag_len); } while (ret == WH_ERROR_NOTREADY); - - if (ret == WH_ERROR_OK) { - /* Get response */ - whMessageCrypto_AesGcmResponse* res; - ret = _getCryptoResponse(dataPtr, type, (uint8_t**)&res); - /* wolfCrypt allows positive error codes on success in some - * scenarios */ - if (ret >= WH_ERROR_OK) { - /* The encrypted/decrypted data follows directly after the - * response struct */ - uint8_t* res_out = (uint8_t*)(res + 1); - /* The auth tag follows after the output data */ - uint8_t* res_tag = res_out + res->sz; - - WH_DEBUG_CLIENT_VERBOSE("out size:%d datasz:%d tag_len:%d\n", - (int)res->sz, (int)res_len, (int)res->authTagSz); - WH_DEBUG_VERBOSE_HEXDUMP("[client] res_out: \n", res_out, res->sz); - if (enc != 0 && res->authTagSz > 0) { - WH_DEBUG_VERBOSE_HEXDUMP("[client] res_tag: \n", res_tag, - res->authTagSz); - } - /* copy the response result if present */ - if (out != NULL && res->sz == len) { - memcpy(out, res_out, res->sz); - } - - /* write the authTag if applicable */ - if (enc != 0 && enc_tag != NULL && res->authTagSz > 0 && - res->authTagSz <= tag_len) { - memcpy(enc_tag, res_tag, res->authTagSz); - WH_DEBUG_CLIENT_VERBOSE("res tag_len:%d exp tag_len:%u", - (int)res->authTagSz, (unsigned int)tag_len); - WH_DEBUG_VERBOSE_HEXDUMP("[client] enc authtag: ", enc_tag, - res->authTagSz); - } - } - } } - return ret; } #ifdef WOLFHSM_CFG_DMA -int wh_Client_AesGcmDma(whClientContext* ctx, Aes* aes, int enc, - const uint8_t* in, uint32_t len, const uint8_t* iv, - uint32_t iv_len, const uint8_t* authin, - uint32_t authin_len, const uint8_t* dec_tag, - uint8_t* enc_tag, uint32_t tag_len, uint8_t* out) +int wh_Client_AesGcmDmaRequest(whClientContext* ctx, Aes* aes, int enc, + const uint8_t* in, uint32_t len, uint8_t* out, + const uint8_t* iv, uint32_t iv_len, + 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; - - uint16_t group = WH_MESSAGE_GROUP_CRYPTO_DMA; - uint16_t action = WC_ALGO_TYPE_CIPHER; - uint16_t type = WC_CIPHER_AES_GCM; - - const uint8_t* key = NULL; - - if (ctx == NULL || aes == NULL) { - return WH_ERROR_BADARGS; - } - - if ((in == NULL) && (len > 0)) { - return WH_ERROR_BADARGS; - } - - if ((iv == NULL) && (iv_len > 0)) { - return WH_ERROR_BADARGS; - } + 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; + uint8_t* req_iv; + uint8_t* req_tag; + uint8_t* req_key; + uint32_t req_len; - if ((authin == NULL) && (authin_len > 0)) { + if ((ctx == NULL) || (aes == NULL) || ((in == NULL) && (len > 0)) || + ((out == NULL) && (len > 0)) || ((iv == NULL) && (iv_len > 0)) || + ((authin == NULL) && (authin_len > 0)) || + ((enc == 0) && (dec_tag == NULL))) { return WH_ERROR_BADARGS; } - if ((enc == 0) && (dec_tag == NULL)) { - return WH_ERROR_BADARGS; + if (wh_CommClient_IsRequestPending(ctx->comm) == 1) { + return WH_ERROR_REQUEST_PENDING; } - /* 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_AesGcmDmaRequest*)_createCryptoRequest( dataPtr, WC_CIPHER_AES_GCM, ctx->cryptoAffinity); - uint8_t* req_iv = (uint8_t*)req + sizeof(whMessageCrypto_AesGcmDmaRequest); - uint8_t* req_tag = req_iv + iv_len; - uint8_t* req_key = req_tag + (enc != 0 ? 0 : tag_len); - uint32_t req_len = sizeof(whMessageCrypto_GenericRequestHeader) + - sizeof(*req) + iv_len + (enc != 0 ? 0 : tag_len); + req_iv = (uint8_t*)req + sizeof(whMessageCrypto_AesGcmDmaRequest); + req_tag = req_iv + iv_len; + req_key = req_tag + (enc != 0 ? 0 : tag_len); + req_len = sizeof(whMessageCrypto_GenericRequestHeader) + sizeof(*req) + + iv_len + (enc != 0 ? 0 : tag_len); - /* Setup request packet */ memset(req, 0, sizeof(*req)); - req->enc = enc; - req->ivSz = iv_len; + req->enc = enc; + req->ivSz = iv_len; req->authTagSz = tag_len; req->keyId = WH_DEVCTX_TO_KEYID(aes->devCtx); if (req->keyId != WH_KEYID_ERASED) { - /* Using keyId-based key, server will load it from keystore */ - key = NULL; req->keySz = 0; } else { - /* Using direct key */ - key = (const uint8_t*)(aes->devKey); req->keySz = aes->keylen; - req_len += req->keySz; + req_len += req->keySz; } if (req_len > WOLFHSM_CFG_COMM_DATA_LEN) { return WH_ERROR_BADARGS; } - /* Copy request data not handled by DMA */ - memcpy(req_iv, iv, iv_len); + if (iv_len > 0) { + memcpy(req_iv, iv, iv_len); + } if (enc == 0 && tag_len > 0) { memcpy(req_tag, dec_tag, tag_len); - WH_DEBUG_VERBOSE_HEXDUMP("[client] dec tag: \n", dec_tag, tag_len); } - if (key != NULL && req->keySz > 0) { - memcpy(req_key, key, req->keySz); + if (req->keySz > 0) { + memcpy(req_key, (const uint8_t*)(aes->devKey), req->keySz); } - if (ret == WH_ERROR_OK && in != NULL) { + if (in != NULL && len > 0) { req->input.sz = len; 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) { + inAcq = true; req->input.addr = inAddr; } } - if (ret == WH_ERROR_OK && out != NULL) { + if (ret == WH_ERROR_OK && out != NULL && len > 0) { req->output.sz = len; 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) { + outAcq = true; req->output.addr = outAddr; } } - if (ret == WH_ERROR_OK && authin != NULL) { + 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; } } - WH_DEBUG_VERBOSE_HEXDUMP("[client] key: \n", req_key, req->keySz); - WH_DEBUG_VERBOSE_HEXDUMP("[client] iv: \n", req_iv, iv_len); - WH_DEBUG_VERBOSE_HEXDUMP("[client] authin: \n", authin, authin_len); - - /* Send request and receive response */ - WH_DEBUG_VERBOSE_HEXDUMP("[client] AESGCM DMA req packet: \n", dataPtr, req_len); if (ret == WH_ERROR_OK) { - ret = wh_Client_SendRequest(ctx, group, action, req_len, dataPtr); + ctx->dma.asyncCtx.aes.inAddr = inAddr; + ctx->dma.asyncCtx.aes.inClientAddr = (uintptr_t)in; + ctx->dma.asyncCtx.aes.inSz = inAcq ? len : 0; + ctx->dma.asyncCtx.aes.outAddr = outAddr; + ctx->dma.asyncCtx.aes.outClientAddr = (uintptr_t)out; + ctx->dma.asyncCtx.aes.outSz = outAcq ? len : 0; + ctx->dma.asyncCtx.aes.aadAddr = aadAddr; + ctx->dma.asyncCtx.aes.aadClientAddr = (uintptr_t)authin; + ctx->dma.asyncCtx.aes.aadSz = aadAcq ? authin_len : 0; + + ret = wh_Client_SendRequest(ctx, WH_MESSAGE_GROUP_CRYPTO_DMA, + WC_ALGO_TYPE_CIPHER, req_len, dataPtr); } - if (ret == WH_ERROR_OK) { - uint16_t resLen = 0; - do { - ret = - wh_Client_RecvResponse(ctx, &group, &action, &resLen, dataPtr); - } while (ret == WH_ERROR_NOTREADY); - if (ret == WH_ERROR_OK) { - /* Get response */ - whMessageCrypto_AesGcmDmaResponse* res; - ret = _getCryptoResponse(dataPtr, type, (uint8_t**)&res); - /* wolfCrypt allows positive error codes on success in some - * scenarios */ - if (ret >= WH_ERROR_OK) { - /* Write the authTag is applicable */ - if (enc != 0 && enc_tag != NULL && res->authTagSz > 0 && - res->authTagSz <= tag_len) { - uint8_t* res_tag = (uint8_t*)res + - sizeof(whMessageCrypto_AesGcmDmaResponse); - memcpy(enc_tag, res_tag, res->authTagSz); - WH_DEBUG_CLIENT_VERBOSE("res tag_len:%d exp tag_len:%u", - (int)res->authTagSz, (unsigned int)tag_len); - WH_DEBUG_VERBOSE_HEXDUMP("[client] enc authtag: ", enc_tag, - res->authTagSz); - } - ret = WH_ERROR_OK; /* Success */ + if (ret != WH_ERROR_OK) { + if (inAcq) { + (void)wh_Client_DmaProcessClientAddress( + ctx, (uintptr_t)in, (void**)&inAddr, len, + WH_DMA_OPER_CLIENT_READ_POST, (whDmaFlags){0}); + } + if (outAcq) { + (void)wh_Client_DmaProcessClientAddress( + ctx, (uintptr_t)out, (void**)&outAddr, len, + WH_DMA_OPER_CLIENT_WRITE_POST, (whDmaFlags){0}); + } + if (aadAcq) { + (void)wh_Client_DmaProcessClientAddress( + ctx, (uintptr_t)authin, (void**)&aadAddr, authin_len, + WH_DMA_OPER_CLIENT_READ_POST, (whDmaFlags){0}); + } + memset(&ctx->dma.asyncCtx.aes, 0, sizeof(ctx->dma.asyncCtx.aes)); + } + return ret; +} + +int wh_Client_AesGcmDmaResponse(whClientContext* ctx, Aes* aes, + uint8_t* enc_tag, uint32_t tag_len) +{ + int ret; + uint8_t* dataPtr; + uint16_t res_len = 0; + whMessageCrypto_AesGcmDmaResponse* res; + + (void)aes; + + if (ctx == NULL) { + return WH_ERROR_BADARGS; + } + + dataPtr = wh_CommClient_GetDataPtr(ctx->comm); + if (dataPtr == NULL) { + return WH_ERROR_BADARGS; + } + + ret = wh_Client_RecvResponse(ctx, NULL, NULL, &res_len, dataPtr); + if (ret == WH_ERROR_NOTREADY) { + return ret; + } + + if (ret == WH_ERROR_OK) { + ret = _getCryptoResponse(dataPtr, WC_CIPHER_AES_GCM, (uint8_t**)&res); + if (ret >= WH_ERROR_OK) { + if (enc_tag != NULL && res->authTagSz > 0 && + res->authTagSz <= tag_len) { + uint8_t* res_tag = + (uint8_t*)res + sizeof(whMessageCrypto_AesGcmDmaResponse); + memcpy(enc_tag, res_tag, res->authTagSz); } + ret = WH_ERROR_OK; } } - /* post address translation callbacks (for cleanup) */ - if (in != NULL) { + if (ctx->dma.asyncCtx.aes.inSz > 0) { + uintptr_t addr = ctx->dma.asyncCtx.aes.inAddr; (void)wh_Client_DmaProcessClientAddress( - ctx, (uintptr_t)in, (void**)&inAddr, len, - WH_DMA_OPER_CLIENT_READ_POST, (whDmaFlags){0}); + ctx, ctx->dma.asyncCtx.aes.inClientAddr, (void**)&addr, + ctx->dma.asyncCtx.aes.inSz, WH_DMA_OPER_CLIENT_READ_POST, + (whDmaFlags){0}); + ctx->dma.asyncCtx.aes.inSz = 0; } - if (out != NULL) { + if (ctx->dma.asyncCtx.aes.outSz > 0) { + uintptr_t addr = ctx->dma.asyncCtx.aes.outAddr; (void)wh_Client_DmaProcessClientAddress( - ctx, (uintptr_t)out, (void**)&outAddr, len, - WH_DMA_OPER_CLIENT_WRITE_POST, (whDmaFlags){0}); + ctx, ctx->dma.asyncCtx.aes.outClientAddr, (void**)&addr, + ctx->dma.asyncCtx.aes.outSz, WH_DMA_OPER_CLIENT_WRITE_POST, + (whDmaFlags){0}); + ctx->dma.asyncCtx.aes.outSz = 0; } - if (authin != NULL) { + if (ctx->dma.asyncCtx.aes.aadSz > 0) { + uintptr_t addr = ctx->dma.asyncCtx.aes.aadAddr; (void)wh_Client_DmaProcessClientAddress( - ctx, (uintptr_t)authin, (void**)&aadAddr, authin_len, - WH_DMA_OPER_CLIENT_READ_POST, (whDmaFlags){0}); + ctx, ctx->dma.asyncCtx.aes.aadClientAddr, (void**)&addr, + ctx->dma.asyncCtx.aes.aadSz, WH_DMA_OPER_CLIENT_READ_POST, + (whDmaFlags){0}); + ctx->dma.asyncCtx.aes.aadSz = 0; } + return ret; +} + +int wh_Client_AesGcmDma(whClientContext* ctx, Aes* aes, int enc, + const uint8_t* in, uint32_t len, const uint8_t* iv, + uint32_t iv_len, const uint8_t* authin, + uint32_t authin_len, const uint8_t* dec_tag, + uint8_t* enc_tag, uint32_t tag_len, uint8_t* out) +{ + int ret; + ret = wh_Client_AesGcmDmaRequest(ctx, aes, enc, in, len, out, iv, iv_len, + authin, authin_len, dec_tag, tag_len); + if (ret == WH_ERROR_OK) { + do { + ret = wh_Client_AesGcmDmaResponse(ctx, aes, enc_tag, tag_len); + } while (ret == WH_ERROR_NOTREADY); + } return ret; } #endif /* WOLFHSM_CFG_DMA */ diff --git a/test/wh_test_crypto.c b/test/wh_test_crypto.c index d33fa192..b0ebb626 100644 --- a/test/wh_test_crypto.c +++ b/test/wh_test_crypto.c @@ -6631,6 +6631,642 @@ static int whTestCrypto_Aes(whClientContext* ctx, int devId, WC_RNG* rng) #endif /* HAVE_AES_GCM */ return ret; } + +/* 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. */ +static int whTest_CryptoAesAsync(whClientContext* ctx, int devId, WC_RNG* rng) +{ +#define WH_TEST_AES_ASYNC_KEYSZ 16 +#define WH_TEST_AES_ASYNC_BUFSZ 64 + int ret = 0; + Aes aes[1]; + uint8_t key[WH_TEST_AES_ASYNC_KEYSZ]; + uint8_t iv[AES_BLOCK_SIZE]; + uint8_t plainIn[WH_TEST_AES_ASYNC_BUFSZ]; + uint8_t cipher[WH_TEST_AES_ASYNC_BUFSZ]; + uint8_t plainOut[WH_TEST_AES_ASYNC_BUFSZ]; + + memset(plainIn, 0xAA, sizeof(plainIn)); + memset(cipher, 0, sizeof(cipher)); + memset(plainOut, 0, sizeof(plainOut)); + + if (wc_RNG_GenerateBlock(rng, key, sizeof(key)) != 0 || + wc_RNG_GenerateBlock(rng, iv, sizeof(iv)) != 0) { + WH_ERROR_PRINT("AES async: failed to generate key/iv\n"); + return -1; + } + +#ifdef HAVE_AES_CBC + /* CBC: round-trip via async Request/Response pair */ + 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) { + ret = + wh_Client_AesCbcRequest(ctx, aes, 1, plainIn, sizeof(plainIn)); + } + if (ret == 0) { + do { + ret = wh_Client_AesCbcResponse(ctx, aes, cipher, NULL); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0) { + ret = wc_AesSetKey(aes, key, sizeof(key), iv, AES_DECRYPTION); + } + if (ret == 0) { + ret = wh_Client_AesCbcRequest(ctx, aes, 0, cipher, sizeof(cipher)); + } + if (ret == 0) { + do { + ret = wh_Client_AesCbcResponse(ctx, aes, plainOut, NULL); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0 && memcmp(plainIn, plainOut, sizeof(plainIn)) != 0) { + WH_ERROR_PRINT("AES-CBC async round-trip mismatch\n"); + ret = -1; + } + (void)wc_AesFree(aes); + memset(cipher, 0, sizeof(cipher)); + memset(plainOut, 0, sizeof(plainOut)); + } + + /* CBC: argument validation */ + if (ret == 0 && wh_Client_AesCbcRequest(NULL, aes, 1, plainIn, 16) != + WH_ERROR_BADARGS) { + WH_ERROR_PRINT("AES-CBC async: NULL ctx should be BADARGS\n"); + ret = -1; + } + if (ret == 0 && wh_Client_AesCbcRequest(ctx, NULL, 1, plainIn, 16) != + WH_ERROR_BADARGS) { + WH_ERROR_PRINT("AES-CBC async: NULL aes should be BADARGS\n"); + ret = -1; + } + if (ret == 0 && + wh_Client_AesCbcResponse(ctx, aes, NULL, NULL) != WH_ERROR_BADARGS) { + WH_ERROR_PRINT("AES-CBC async: NULL out should be BADARGS\n"); + ret = -1; + } + /* CBC: non-block-aligned length rejected */ + if (ret == 0) { + int tmp; + ret = wc_AesInit(aes, NULL, devId); + if (ret == 0) { + ret = wc_AesSetKey(aes, key, sizeof(key), iv, AES_ENCRYPTION); + } + if (ret == 0) { + tmp = wh_Client_AesCbcRequest(ctx, aes, 1, plainIn, 15); + if (tmp != WH_ERROR_BADARGS) { + WH_ERROR_PRINT( + "AES-CBC async: non-block-aligned should be BADARGS, " + "got %d\n", + tmp); + ret = -1; + } + } + (void)wc_AesFree(aes); + } + if (ret == 0) { + WH_TEST_PRINT("AES CBC ASYNC DEVID=0x%X SUCCESS\n", devId); + } +#endif /* HAVE_AES_CBC */ + +#ifdef WOLFSSL_AES_COUNTER + /* CTR: round-trip via async Request/Response pair. CTR is symmetric, so + * wolfCrypt's wc_AesCtrEncrypt is used for both directions — the enc + * flag and key schedule are always ENCRYPTION even when the caller's + * intent is to decrypt. */ + if (ret == 0) { + ret = wc_AesInit(aes, NULL, devId); + if (ret == 0) { + ret = wc_AesSetKeyDirect(aes, key, sizeof(key), iv, AES_ENCRYPTION); + } + if (ret == 0) { + ret = + wh_Client_AesCtrRequest(ctx, aes, 1, plainIn, sizeof(plainIn)); + } + if (ret == 0) { + do { + ret = wh_Client_AesCtrResponse(ctx, aes, cipher, NULL); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0) { + ret = wc_AesSetKeyDirect(aes, key, sizeof(key), iv, AES_ENCRYPTION); + } + if (ret == 0) { + ret = wh_Client_AesCtrRequest(ctx, aes, 1, cipher, sizeof(cipher)); + } + if (ret == 0) { + do { + ret = wh_Client_AesCtrResponse(ctx, aes, plainOut, NULL); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0 && memcmp(plainIn, plainOut, sizeof(plainIn)) != 0) { + WH_ERROR_PRINT("AES-CTR async round-trip mismatch\n"); + ret = -1; + } + (void)wc_AesFree(aes); + memset(cipher, 0, sizeof(cipher)); + memset(plainOut, 0, sizeof(plainOut)); + } + + /* CTR: state continuity — two halves via async must equal single-shot */ + if (ret == 0) { + uint8_t refCipher[WH_TEST_AES_ASYNC_BUFSZ]; + uint8_t chunkCipher[WH_TEST_AES_ASYNC_BUFSZ]; + Aes aesRef[1]; + ret = wc_AesInit(aesRef, NULL, devId); + if (ret == 0) { + ret = wc_AesSetKeyDirect(aesRef, key, sizeof(key), iv, + AES_ENCRYPTION); + } + if (ret == 0) { + ret = wh_Client_AesCtr(ctx, aesRef, 1, plainIn, sizeof(plainIn), + refCipher); + } + (void)wc_AesFree(aesRef); + + if (ret == 0) { + ret = wc_AesInit(aes, NULL, devId); + } + if (ret == 0) { + ret = wc_AesSetKeyDirect(aes, key, sizeof(key), iv, AES_ENCRYPTION); + } + if (ret == 0) { + ret = wh_Client_AesCtrRequest(ctx, aes, 1, plainIn, + sizeof(plainIn) / 2); + } + if (ret == 0) { + do { + ret = wh_Client_AesCtrResponse(ctx, aes, chunkCipher, NULL); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0) { + ret = wh_Client_AesCtrRequest(ctx, aes, 1, + plainIn + sizeof(plainIn) / 2, + sizeof(plainIn) / 2); + } + if (ret == 0) { + do { + ret = wh_Client_AesCtrResponse( + ctx, aes, chunkCipher + sizeof(plainIn) / 2, NULL); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0 && + memcmp(refCipher, chunkCipher, sizeof(refCipher)) != 0) { + WH_ERROR_PRINT( + "AES-CTR async: split state did not match single-shot\n"); + ret = -1; + } + (void)wc_AesFree(aes); + } + if (ret == 0) { + WH_TEST_PRINT("AES CTR ASYNC DEVID=0x%X SUCCESS\n", devId); + } +#endif /* WOLFSSL_AES_COUNTER */ + +#ifdef HAVE_AES_ECB + /* ECB: round-trip via async Request/Response pair */ + 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) { + ret = + wh_Client_AesEcbRequest(ctx, aes, 1, plainIn, sizeof(plainIn)); + } + if (ret == 0) { + do { + ret = wh_Client_AesEcbResponse(ctx, aes, cipher, NULL); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0) { + ret = wc_AesSetKey(aes, key, sizeof(key), iv, AES_DECRYPTION); + } + if (ret == 0) { + ret = wh_Client_AesEcbRequest(ctx, aes, 0, cipher, sizeof(cipher)); + } + if (ret == 0) { + do { + ret = wh_Client_AesEcbResponse(ctx, aes, plainOut, NULL); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0 && memcmp(plainIn, plainOut, sizeof(plainIn)) != 0) { + WH_ERROR_PRINT("AES-ECB async round-trip mismatch\n"); + ret = -1; + } + (void)wc_AesFree(aes); + memset(cipher, 0, sizeof(cipher)); + memset(plainOut, 0, sizeof(plainOut)); + } + + /* ECB: non-block-aligned length rejected */ + if (ret == 0) { + int tmp; + ret = wc_AesInit(aes, NULL, devId); + if (ret == 0) { + ret = wc_AesSetKey(aes, key, sizeof(key), iv, AES_ENCRYPTION); + } + if (ret == 0) { + tmp = wh_Client_AesEcbRequest(ctx, aes, 1, plainIn, 15); + if (tmp != WH_ERROR_BADARGS) { + WH_ERROR_PRINT( + "AES-ECB async: non-block-aligned should be BADARGS\n"); + ret = -1; + } + } + (void)wc_AesFree(aes); + } + if (ret == 0) { + WH_TEST_PRINT("AES ECB ASYNC DEVID=0x%X SUCCESS\n", devId); + } +#endif /* HAVE_AES_ECB */ + +#ifdef HAVE_AESGCM + /* GCM: round-trip via async Request/Response pair */ + if (ret == 0) { + uint8_t authin[16]; + uint8_t enc_tag[AES_BLOCK_SIZE]; + uint8_t dec_tag[AES_BLOCK_SIZE]; + + 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_AesGcmRequest( + ctx, aes, 1, plainIn, sizeof(plainIn), iv, AES_BLOCK_SIZE, + authin, sizeof(authin), NULL, sizeof(enc_tag)); + } + if (ret == 0) { + do { + ret = wh_Client_AesGcmResponse(ctx, aes, cipher, sizeof(cipher), + NULL, enc_tag, sizeof(enc_tag)); + } while (ret == WH_ERROR_NOTREADY); + } + + /* Decrypt with correct tag succeeds */ + if (ret == 0) { + memcpy(dec_tag, enc_tag, sizeof(dec_tag)); + ret = wh_Client_AesGcmRequest( + ctx, aes, 0, cipher, sizeof(cipher), iv, AES_BLOCK_SIZE, authin, + sizeof(authin), dec_tag, sizeof(dec_tag)); + } + if (ret == 0) { + do { + ret = wh_Client_AesGcmResponse(ctx, aes, plainOut, + sizeof(plainOut), NULL, NULL, 0); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0 && memcmp(plainIn, plainOut, sizeof(plainIn)) != 0) { + WH_ERROR_PRINT("AES-GCM async round-trip mismatch\n"); + ret = -1; + } + + /* Decrypt with corrupted tag must fail */ + if (ret == 0) { + int tmp; + dec_tag[0] ^= 0x01; + tmp = wh_Client_AesGcmRequest( + ctx, aes, 0, cipher, sizeof(cipher), iv, AES_BLOCK_SIZE, authin, + sizeof(authin), dec_tag, sizeof(dec_tag)); + if (tmp == WH_ERROR_OK) { + do { + tmp = wh_Client_AesGcmResponse( + ctx, aes, plainOut, sizeof(plainOut), NULL, NULL, 0); + } while (tmp == WH_ERROR_NOTREADY); + } + if (tmp == 0) { + WH_ERROR_PRINT( + "AES-GCM async: decrypt with bad tag unexpectedly OK\n"); + ret = -1; + } + } + (void)wc_AesFree(aes); + memset(cipher, 0, sizeof(cipher)); + memset(plainOut, 0, sizeof(plainOut)); + } + if (ret == 0) { + WH_TEST_PRINT("AES GCM ASYNC DEVID=0x%X SUCCESS\n", devId); + } +#endif /* HAVE_AESGCM */ + +#ifdef HAVE_AES_CBC + /* CBC: stacked async Request must return REQUEST_PENDING, and the second + * Request must not mutate aes->reg before fail-fast. */ + if (ret == 0) { + uint8_t ivBefore[AES_BLOCK_SIZE]; + uint8_t ivAfter[AES_BLOCK_SIZE]; + int tmp; + ret = wc_AesInit(aes, NULL, devId); + if (ret == 0) { + ret = wc_AesSetKey(aes, key, sizeof(key), iv, AES_ENCRYPTION); + } + /* Issue the first Request but do NOT consume the Response. The + * transport stays "request pending" until the matching Response is + * read. */ + if (ret == 0) { + ret = + wh_Client_AesCbcRequest(ctx, aes, 1, plainIn, sizeof(plainIn)); + } + if (ret == 0) { + /* Snapshot aes->reg before attempting a stacked decrypt Request */ + memcpy(ivBefore, (uint8_t*)aes->reg, sizeof(ivBefore)); + tmp = + wh_Client_AesCbcRequest(ctx, aes, 0, plainIn, sizeof(plainIn)); + if (tmp != WH_ERROR_REQUEST_PENDING) { + WH_ERROR_PRINT("AES-CBC async: stacked Request expected " + "REQUEST_PENDING, got %d\n", + tmp); + ret = -1; + } + memcpy(ivAfter, (uint8_t*)aes->reg, sizeof(ivAfter)); + if (ret == 0 && memcmp(ivBefore, ivAfter, sizeof(ivBefore)) != 0) { + WH_ERROR_PRINT( + "AES-CBC async: stacked Request mutated aes->reg\n"); + ret = -1; + } + } + /* Drain the outstanding response to leave the transport idle */ + if (ret == 0 || ret == -1) { + int drainRet; + do { + drainRet = wh_Client_AesCbcResponse(ctx, aes, cipher, NULL); + } while (drainRet == WH_ERROR_NOTREADY); + if (ret == 0 && drainRet != WH_ERROR_OK) { + WH_ERROR_PRINT( + "AES-CBC async: failed to drain pending response: %d\n", + drainRet); + ret = -1; + } + } + (void)wc_AesFree(aes); + memset(cipher, 0, sizeof(cipher)); + } + if (ret == 0) { + WH_TEST_PRINT("AES CBC ASYNC FAILURE-PATH DEVID=0x%X SUCCESS\n", devId); + } +#endif /* HAVE_AES_CBC */ + +#ifdef HAVE_AESGCM + /* GCM: out_capacity smaller than the server's reported payload must be + * rejected by Response without overflowing out. */ + if (ret == 0) { + uint8_t authin[8]; + uint8_t enc_tag[AES_BLOCK_SIZE]; + uint8_t tinyOut[8]; /* deliberately smaller than plainIn */ + int tmp; + + memset(authin, 0x37, 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_AesGcmRequest( + ctx, aes, 1, plainIn, sizeof(plainIn), iv, AES_BLOCK_SIZE, + authin, sizeof(authin), NULL, sizeof(enc_tag)); + } + if (ret == 0) { + do { + tmp = + wh_Client_AesGcmResponse(ctx, aes, tinyOut, sizeof(tinyOut), + NULL, enc_tag, sizeof(enc_tag)); + } while (tmp == WH_ERROR_NOTREADY); + if (tmp != WH_ERROR_ABORTED) { + WH_ERROR_PRINT( + "AES-GCM async: undersized out_capacity expected " + "ABORTED, got %d\n", + tmp); + ret = -1; + } + } + (void)wc_AesFree(aes); + memset(cipher, 0, sizeof(cipher)); + } + if (ret == 0) { + WH_TEST_PRINT("AES GCM ASYNC OUT-CAPACITY DEVID=0x%X SUCCESS\n", devId); + } +#endif /* HAVE_AESGCM */ + + return ret; +#undef WH_TEST_AES_ASYNC_KEYSZ +#undef WH_TEST_AES_ASYNC_BUFSZ +} + +#ifdef WOLFHSM_CFG_DMA +/* Direct exercise of the native async DMA AES primitives. */ +static int whTest_CryptoAesDmaAsync(whClientContext* ctx, int devId, + WC_RNG* rng) +{ +#define WH_TEST_AES_ASYNC_DMA_KEYSZ 32 +#define WH_TEST_AES_ASYNC_DMA_BUFSZ 128 + int ret = 0; + Aes aes[1]; + uint8_t key[WH_TEST_AES_ASYNC_DMA_KEYSZ]; + uint8_t iv[AES_BLOCK_SIZE]; + uint8_t plainIn[WH_TEST_AES_ASYNC_DMA_BUFSZ]; + uint8_t cipher[WH_TEST_AES_ASYNC_DMA_BUFSZ]; + uint8_t plainOut[WH_TEST_AES_ASYNC_DMA_BUFSZ]; + + memset(plainIn, 0xBB, sizeof(plainIn)); + memset(cipher, 0, sizeof(cipher)); + memset(plainOut, 0, sizeof(plainOut)); + + if (wc_RNG_GenerateBlock(rng, key, sizeof(key)) != 0 || + wc_RNG_GenerateBlock(rng, iv, sizeof(iv)) != 0) { + WH_ERROR_PRINT("AES DMA async: failed to generate key/iv\n"); + return -1; + } + +#ifdef HAVE_AES_CBC + /* CBC DMA: round-trip via async DMA Request/Response pair */ + 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) { + ret = wh_Client_AesCbcDmaRequest(ctx, aes, 1, plainIn, + sizeof(plainIn), cipher); + } + if (ret == 0) { + do { + ret = wh_Client_AesCbcDmaResponse(ctx, aes); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0) { + ret = wc_AesSetKey(aes, key, sizeof(key), iv, AES_DECRYPTION); + } + if (ret == 0) { + ret = wh_Client_AesCbcDmaRequest(ctx, aes, 0, cipher, + sizeof(cipher), plainOut); + } + if (ret == 0) { + do { + ret = wh_Client_AesCbcDmaResponse(ctx, aes); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0 && memcmp(plainIn, plainOut, sizeof(plainIn)) != 0) { + WH_ERROR_PRINT("AES-CBC DMA async round-trip mismatch\n"); + ret = -1; + } + (void)wc_AesFree(aes); + memset(cipher, 0, sizeof(cipher)); + memset(plainOut, 0, sizeof(plainOut)); + } + if (ret == 0) { + WH_TEST_PRINT("AES CBC DMA ASYNC DEVID=0x%X SUCCESS\n", devId); + } +#endif /* HAVE_AES_CBC */ + +#ifdef WOLFSSL_AES_COUNTER + /* CTR DMA: round-trip. CTR is symmetric (see non-DMA test comment). */ + if (ret == 0) { + ret = wc_AesInit(aes, NULL, devId); + if (ret == 0) { + ret = wc_AesSetKeyDirect(aes, key, sizeof(key), iv, AES_ENCRYPTION); + } + if (ret == 0) { + ret = wh_Client_AesCtrDmaRequest(ctx, aes, 1, plainIn, + sizeof(plainIn), cipher); + } + if (ret == 0) { + do { + ret = wh_Client_AesCtrDmaResponse(ctx, aes); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0) { + ret = wc_AesSetKeyDirect(aes, key, sizeof(key), iv, AES_ENCRYPTION); + } + if (ret == 0) { + ret = wh_Client_AesCtrDmaRequest(ctx, aes, 1, cipher, + sizeof(cipher), plainOut); + } + if (ret == 0) { + do { + ret = wh_Client_AesCtrDmaResponse(ctx, aes); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0 && memcmp(plainIn, plainOut, sizeof(plainIn)) != 0) { + WH_ERROR_PRINT("AES-CTR DMA async round-trip mismatch\n"); + ret = -1; + } + (void)wc_AesFree(aes); + memset(cipher, 0, sizeof(cipher)); + memset(plainOut, 0, sizeof(plainOut)); + } + if (ret == 0) { + WH_TEST_PRINT("AES CTR DMA ASYNC DEVID=0x%X SUCCESS\n", devId); + } +#endif /* WOLFSSL_AES_COUNTER */ + +#ifdef HAVE_AES_ECB + /* ECB DMA: round-trip */ + 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) { + ret = wh_Client_AesEcbDmaRequest(ctx, aes, 1, plainIn, + sizeof(plainIn), cipher); + } + if (ret == 0) { + do { + ret = wh_Client_AesEcbDmaResponse(ctx, aes); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0) { + ret = wc_AesSetKey(aes, key, sizeof(key), iv, AES_DECRYPTION); + } + if (ret == 0) { + ret = wh_Client_AesEcbDmaRequest(ctx, aes, 0, cipher, + sizeof(cipher), plainOut); + } + if (ret == 0) { + do { + ret = wh_Client_AesEcbDmaResponse(ctx, aes); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0 && memcmp(plainIn, plainOut, sizeof(plainIn)) != 0) { + WH_ERROR_PRINT("AES-ECB DMA async round-trip mismatch\n"); + ret = -1; + } + (void)wc_AesFree(aes); + memset(cipher, 0, sizeof(cipher)); + memset(plainOut, 0, sizeof(plainOut)); + } + if (ret == 0) { + WH_TEST_PRINT("AES ECB DMA ASYNC DEVID=0x%X SUCCESS\n", devId); + } +#endif /* HAVE_AES_ECB */ + +#ifdef HAVE_AESGCM + /* GCM DMA: round-trip with AAD via DMA */ + if (ret == 0) { + uint8_t authin[32]; + uint8_t enc_tag[AES_BLOCK_SIZE]; + uint8_t dec_tag[AES_BLOCK_SIZE]; + + 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); + } + + 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; + } + (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); + } +#endif /* HAVE_AESGCM */ + + return ret; +#undef WH_TEST_AES_ASYNC_DMA_KEYSZ +#undef WH_TEST_AES_ASYNC_DMA_BUFSZ +} +#endif /* WOLFHSM_CFG_DMA */ + #endif /* !NO_AES */ #if defined(WOLFSSL_CMAC) && !defined(NO_AES) && defined(WOLFSSL_AES_DIRECT) @@ -8604,10 +9240,18 @@ int whTest_CryptoClientConfig(whClientConfig* config) i = 0; while ((ret == WH_ERROR_OK) && (i < WH_NUM_DEVIDS)) { ret = whTestCrypto_Aes(client, WH_DEV_IDS_ARRAY[i], rng); + if (ret == WH_ERROR_OK) { + ret = whTest_CryptoAesAsync(client, WH_DEV_IDS_ARRAY[i], rng); + } if (ret == WH_ERROR_OK) { i++; } } +#ifdef WOLFHSM_CFG_DMA + if (ret == WH_ERROR_OK) { + ret = whTest_CryptoAesDmaAsync(client, WH_DEV_ID_DMA, rng); + } +#endif /* WOLFHSM_CFG_DMA */ #endif /* !NO_AES */ #if defined(WOLFSSL_CMAC) && !defined(NO_AES) && defined(WOLFSSL_AES_DIRECT) diff --git a/wolfhsm/wh_client.h b/wolfhsm/wh_client.h index a71a788c..6cd30b23 100644 --- a/wolfhsm/wh_client.h +++ b/wolfhsm/wh_client.h @@ -119,12 +119,32 @@ typedef struct { uint64_t outSz; } whClientDmaAsyncRng; +/* Per-operation async DMA context for AES modes (ECB/CBC/CTR/GCM): stores + * translated DMA addresses for the input, output, and optional AAD buffers + * so the matching *Response can run POST cleanup. + * + * For non-GCM modes only input and output are used; aadSz stays 0 and aad + * cleanup is skipped. A sz of 0 means "no mapping held" and the Response + * will skip POST cleanup for that buffer. */ +typedef struct { + uintptr_t inAddr; /* translated input DMA address (READ) */ + uintptr_t inClientAddr; + uint64_t inSz; + uintptr_t outAddr; /* translated output DMA address (WRITE) */ + uintptr_t outClientAddr; + uint64_t outSz; + uintptr_t aadAddr; /* GCM AAD DMA address (READ) */ + uintptr_t aadClientAddr; + uint64_t aadSz; +} whClientDmaAsyncAes; + /* 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; whClientDmaAsyncRng rng; + whClientDmaAsyncAes aes; } whClientDmaAsyncCtx; typedef struct { diff --git a/wolfhsm/wh_client_crypto.h b/wolfhsm/wh_client_crypto.h index 044fb8c0..d51f95aa 100644 --- a/wolfhsm/wh_client_crypto.h +++ b/wolfhsm/wh_client_crypto.h @@ -1079,6 +1079,91 @@ int wh_Client_AesCtr(whClientContext* ctx, Aes* aes, int enc, const uint8_t* in, */ int wh_Client_AesCtrDma(whClientContext* ctx, Aes* aes, int enc, const uint8_t* in, uint32_t len, uint8_t* out); + +/** + * @brief Send an AES-CTR encrypt/decrypt request to the server (non-blocking) + * + * Sends a single AES-CTR request to the server. The key material is read from + * the Aes struct (set via wc_AesSetKey or wh_Client_AesSetKeyId). The counter + * state (IV register and partial-block remainder) is carried on the Aes + * struct across the Request/Response boundary; callers must not mutate the + * Aes struct between the two halves. Use wh_Client_AesCtrResponse to + * retrieve the result. + * + * Contract: at most one outstanding async request may be in flight per + * whClientContext. The caller MUST call wh_Client_AesCtrResponse before + * issuing any other async Request on the same ctx. + * + * @param[in] ctx Pointer to the client context + * @param[in] aes Pointer to the AES structure with key and counter state + * @param[in] enc 1 for encrypt, 0 for decrypt (ignored by CTR, present for API + * symmetry with the other modes) + * @param[in] in Pointer to the input data (may be NULL only if len == 0) + * @param[in] len Length of the input data in bytes + * @return int Returns 0 on success or a negative error code on failure. + */ +int wh_Client_AesCtrRequest(whClientContext* ctx, Aes* aes, int enc, + const uint8_t* in, uint32_t len); + +/** + * @brief Receive the server's AES-CTR response (non-blocking) + * + * Retrieves the result of a prior wh_Client_AesCtrRequest call. The counter + * state (IV register and partial-block remainder) in the Aes struct is + * updated from the server response so subsequent CTR calls continue from the + * correct counter. Returns WH_ERROR_NOTREADY if the response is not yet + * available. + * + * @param[in] ctx Pointer to the client context + * @param[in,out] aes Pointer to the AES structure (counter state updated) + * @param[out] out Pointer to where the output data is placed. Must + * not be NULL. + * @param[out] out_size Set to the number of bytes produced. May be NULL. + * @return int Returns 0 on success, WH_ERROR_NOTREADY if the response is not + * yet available, or a negative error code on failure. + */ +int wh_Client_AesCtrResponse(whClientContext* ctx, Aes* aes, uint8_t* out, + uint32_t* out_size); + +/** + * @brief Send an AES-CTR encrypt/decrypt DMA request to the server + * (non-blocking) + * + * 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. Caller + * must keep in and out valid until the matching wh_Client_AesCtrDmaResponse + * completes. + * + * Contract: at most one outstanding async request may be in flight per + * whClientContext. The caller MUST call wh_Client_AesCtrDmaResponse before + * issuing any other async Request on the same ctx. + * + * @param[in] ctx Pointer to the client context + * @param[in] aes Pointer to the AES structure with key and counter state + * @param[in] enc 1 for encrypt, 0 for decrypt + * @param[in] in Pointer to the input data (may be NULL only if len == 0) + * @param[in] len Length of the input data in bytes + * @param[out] out Pointer to the output buffer + * @return int Returns 0 on success, WH_ERROR_REQUEST_PENDING if the + * transport is still busy with a prior request, or a negative + * error code on failure. On failure any acquired DMA mapping is + * released before returning. + */ +int wh_Client_AesCtrDmaRequest(whClientContext* ctx, Aes* aes, int enc, + const uint8_t* in, uint32_t len, uint8_t* out); + +/** + * @brief Receive the server's AES-CTR DMA response (non-blocking) + * + * 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_AesCtrDmaRequest. POST DMA cleanup for + * both input and output buffers is performed on every non-NOTREADY return + * so the client buffer is safe to read regardless of error. The counter + * state on the Aes struct is updated on success. + */ +int wh_Client_AesCtrDmaResponse(whClientContext* ctx, Aes* aes); #endif /* WOLFSSL_AES_COUNTER */ #ifdef HAVE_AES_ECB @@ -1116,6 +1201,76 @@ int wh_Client_AesEcb(whClientContext* ctx, Aes* aes, int enc, const uint8_t* in, */ int wh_Client_AesEcbDma(whClientContext* ctx, Aes* aes, int enc, const uint8_t* in, uint32_t len, uint8_t* out); + +/** + * @brief Send an AES-ECB encrypt/decrypt request to the server (non-blocking) + * + * Sends a single AES-ECB request to the server. The key material is read + * from the Aes struct (set via wc_AesSetKey or wh_Client_AesSetKeyId). Use + * wh_Client_AesEcbResponse to retrieve the result. + * + * Contract: at most one outstanding async request may be in flight per + * whClientContext. The caller MUST call wh_Client_AesEcbResponse before + * issuing any other async Request on the same ctx. As a special case, + * len == 0 is a no-op: this function returns WH_ERROR_OK without sending, + * and the caller MUST NOT call wh_Client_AesEcbResponse afterwards (no + * response will arrive). + * + * @param[in] ctx Pointer to the client context + * @param[in] aes Pointer to the AES structure with key state + * @param[in] enc 1 for encrypt, 0 for decrypt + * @param[in] in Pointer to the input data (must be block-aligned) + * @param[in] len Length of the input data in bytes (must be a multiple of + * AES_BLOCK_SIZE) + * @return int Returns 0 on success or a negative error code on failure. + */ +int wh_Client_AesEcbRequest(whClientContext* ctx, Aes* aes, int enc, + const uint8_t* in, uint32_t len); + +/** + * @brief Receive the server's AES-ECB response (non-blocking) + * + * Retrieves the result of a prior wh_Client_AesEcbRequest call. Returns + * WH_ERROR_NOTREADY if the response is not yet available. + * + * @param[in] ctx Pointer to the client context + * @param[in] aes Pointer to the AES structure + * @param[out] out Pointer to where the output data is placed. Must not + * be NULL. + * @param[out] out_size Set to the number of bytes produced. May be NULL. + * @return int Returns 0 on success, WH_ERROR_NOTREADY if the response is + * not yet available, or a negative error code on failure. + */ +int wh_Client_AesEcbResponse(whClientContext* ctx, Aes* aes, uint8_t* out, + uint32_t* out_size); + +/** + * @brief Send an AES-ECB DMA request to the server (non-blocking) + * + * 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. Caller + * must keep in and out valid until the matching wh_Client_AesEcbDmaResponse + * completes. + * + * @return int Returns 0 on success, WH_ERROR_REQUEST_PENDING if the + * transport is still busy with a prior request, or a negative + * error code on failure. On failure any acquired DMA mapping is + * released before returning. + */ +int wh_Client_AesEcbDmaRequest(whClientContext* ctx, Aes* aes, int enc, + const uint8_t* in, uint32_t len, uint8_t* out); + +/** + * @brief Receive the server's AES-ECB DMA response (non-blocking) + * + * 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_AesEcbDmaRequest. POST DMA cleanup for + * both input and output buffers is performed on every non-NOTREADY return + * so the client buffer is safe to read regardless of error. + */ +int wh_Client_AesEcbDmaResponse(whClientContext* ctx, Aes* aes); #endif /* HAVE_AES_ECB */ #ifdef HAVE_AES_CBC @@ -1160,10 +1315,16 @@ int wh_Client_AesCbcDma(whClientContext* ctx, Aes* aes, int enc, * @brief Send an AES-CBC encrypt/decrypt request to the server (non-blocking) * * Sends a single AES-CBC request to the server. The key material is read from - * the Aes struct (set via wc_AesSetKey or wh_Client_AesSetKeyId). For - * decryption, the IV is updated with the last input ciphertext block before - * sending to support in-place operation. Use wh_Client_AesCbcResponse to - * retrieve the result. + * the Aes struct (set via wc_AesSetKey or wh_Client_AesSetKeyId). The IV state + * on the Aes struct is updated only after the matching wh_Client_AesCbcResponse + * succeeds; a failed Request leaves aes->reg unchanged so callers can retry. + * + * Contract: at most one outstanding async request may be in flight per + * whClientContext. The caller MUST call wh_Client_AesCbcResponse before + * issuing any other async Request on the same ctx. As a special case, + * len == 0 is a no-op: this function returns WH_ERROR_OK without sending, + * and the caller MUST NOT call wh_Client_AesCbcResponse afterwards (no + * response will arrive). * * @param[in] ctx Pointer to the client context * @param[in] aes Pointer to the AES structure with key and IV state @@ -1193,6 +1354,40 @@ int wh_Client_AesCbcRequest(whClientContext* ctx, Aes* aes, int enc, */ int wh_Client_AesCbcResponse(whClientContext* ctx, Aes* aes, uint8_t* out, uint32_t* out_size); + +/** + * @brief Send an AES-CBC DMA request to the server (non-blocking) + * + * 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. Caller + * must keep in and out valid until the matching wh_Client_AesCbcDmaResponse + * completes. + * + * Contract: at most one outstanding async request may be in flight per + * whClientContext. The caller MUST call wh_Client_AesCbcDmaResponse before + * issuing any other async Request on the same ctx. + * + * @return int Returns 0 on success, WH_ERROR_REQUEST_PENDING if the + * transport is still busy with a prior request, or a negative + * error code on failure. On failure any acquired DMA mapping is + * released before returning. + */ +int wh_Client_AesCbcDmaRequest(whClientContext* ctx, Aes* aes, int enc, + const uint8_t* in, uint32_t len, uint8_t* out); + +/** + * @brief Receive the server's AES-CBC DMA response (non-blocking) + * + * 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_AesCbcDmaRequest; the updated IV is + * returned inline and copied back onto the Aes struct for CBC chaining. + * POST DMA cleanup for both input and output buffers is performed on every + * non-NOTREADY return so the client buffer is safe to read regardless of + * error. + */ +int wh_Client_AesCbcDmaResponse(whClientContext* ctx, Aes* aes); #endif /* HAVE_AES_CBC */ #ifdef HAVE_AESGCM @@ -1252,6 +1447,113 @@ int wh_Client_AesGcmDma(whClientContext* ctx, Aes* aes, int enc, uint32_t iv_len, const uint8_t* authin, uint32_t authin_len, const uint8_t* dec_tag, uint8_t* enc_tag, uint32_t tag_len, uint8_t* out); + +/** + * @brief Send an AES-GCM encrypt/decrypt request to the server (non-blocking) + * + * Sends a single AES-GCM request to the server. The key material is read + * from the Aes struct (set via wc_AesSetKey or wh_Client_AesSetKeyId). The + * ciphertext, AAD, and decrypt tag (if any) are inlined in the request. Use + * wh_Client_AesGcmResponse to retrieve the output ciphertext/plaintext and + * (for encrypt) the auth tag. + * + * Contract: at most one outstanding async request may be in flight per + * whClientContext. The caller MUST call wh_Client_AesGcmResponse before + * issuing any other async Request on the same ctx. + * + * @param[in] ctx Pointer to the client context + * @param[in] aes Pointer to the AES structure with key state + * @param[in] enc 1 for encrypt, 0 for decrypt + * @param[in] in Pointer to the input data (may be NULL only if + * len == 0) + * @param[in] len Length of the input data in bytes + * @param[in] iv Pointer to the IV (may be NULL only if iv_len == 0) + * @param[in] iv_len Length of the IV in bytes + * @param[in] authin Pointer to the AAD (may be NULL only if + * authin_len == 0) + * @param[in] authin_len Length of the AAD in bytes + * @param[in] dec_tag For decrypt: pointer to the expected auth tag (NULL + * only if enc == 1). Ignored for encrypt. + * @param[in] tag_len Length of the auth tag in bytes + * @return int Returns 0 on success or a negative error code on failure. + */ +int wh_Client_AesGcmRequest(whClientContext* ctx, Aes* aes, int enc, + const uint8_t* in, uint32_t len, const uint8_t* iv, + uint32_t iv_len, const uint8_t* authin, + uint32_t authin_len, const uint8_t* dec_tag, + uint32_t tag_len); + +/** + * @brief Receive the server's AES-GCM response (non-blocking) + * + * Retrieves the result of a prior wh_Client_AesGcmRequest call. For encrypt, + * the auth tag is copied into enc_tag. Returns WH_ERROR_NOTREADY if the + * response is not yet available. For decrypt, a failing tag comparison is + * surfaced as a negative error from the server (AES_GCM_AUTH_E). + * + * @param[in] ctx Pointer to the client context + * @param[in] aes Pointer to the AES structure + * @param[out] out Pointer to where the output data is placed. May be + * NULL for GMAC (tag-only) operations, in which case + * out_capacity must be 0. + * @param[in] out_capacity Capacity of the out buffer in bytes. If the server + * reports a larger payload, the call returns + * WH_ERROR_ABORTED instead of writing past out. + * @param[out] out_size Set to the number of bytes produced. May be NULL. + * @param[out] enc_tag For encrypt: buffer to receive the auth tag. + * Ignored for decrypt (may be NULL). + * @param[in] tag_len Length of the enc_tag buffer in bytes. + * @return int Returns 0 on success, WH_ERROR_NOTREADY if the response is + * not yet available, WH_ERROR_ABORTED if the server's reported + * payload size exceeds out_capacity, or a negative error code on + * failure. + */ +int wh_Client_AesGcmResponse(whClientContext* ctx, Aes* aes, uint8_t* out, + uint32_t out_capacity, uint32_t* out_size, + uint8_t* enc_tag, uint32_t tag_len); + +/** + * @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 + * wh_Client_AesGcmDmaResponse completes. + * + * Contract: at most one outstanding async request may be in flight per + * whClientContext. The caller MUST call wh_Client_AesGcmDmaResponse before + * issuing any other async Request on the same ctx. + * + * @return int Returns 0 on success, WH_ERROR_REQUEST_PENDING if the + * transport is still busy with a prior request, or a negative + * error code on failure. On failure any acquired DMA mapping is + * released before returning. + */ +int wh_Client_AesGcmDmaRequest(whClientContext* ctx, Aes* aes, int enc, + const uint8_t* in, uint32_t len, uint8_t* out, + const uint8_t* iv, uint32_t iv_len, + const uint8_t* authin, uint32_t authin_len, + const uint8_t* dec_tag, uint32_t tag_len); + +/** + * @brief Receive the server's AES-GCM DMA response (non-blocking) + * + * 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. + * + * @param[in] ctx Pointer to the client context + * @param[in] aes Pointer to the AES structure + * @param[out] enc_tag For encrypt: buffer to receive the auth tag. Ignored + * for decrypt (may be NULL). + * @param[in] tag_len Length of the enc_tag buffer in bytes. + */ +int wh_Client_AesGcmDmaResponse(whClientContext* ctx, Aes* aes, + uint8_t* enc_tag, uint32_t tag_len); #endif /* HAVE_AESGCM */ #endif /* !NO_AES */ From 0d3f6c7f875912a67d88b032e736657b97ebe6e4 Mon Sep 17 00:00:00 2001 From: Brett Nicholas <7547222+bigbrett@users.noreply.github.com> Date: Mon, 4 May 2026 13:20:46 -0600 Subject: [PATCH 2/2] add KAT for AES modes --- test/wh_test_crypto.c | 956 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 956 insertions(+) diff --git a/test/wh_test_crypto.c b/test/wh_test_crypto.c index b0ebb626..2b96d23c 100644 --- a/test/wh_test_crypto.c +++ b/test/wh_test_crypto.c @@ -7062,6 +7062,490 @@ static int whTest_CryptoAesAsync(whClientContext* ctx, int devId, WC_RNG* rng) #undef WH_TEST_AES_ASYNC_BUFSZ } +/* Known-answer tests for the async AES primitives. Vectors are taken from + * wolfCrypt's test.c (NIST SP 800-38A for CBC/CTR/ECB; NIST SP 800-38D / + * GCM spec test cases for GCM) and are run through the async + * Request/Response APIs to verify async-ification did not change output. */ +static int whTest_CryptoAesAsyncKat(whClientContext* ctx, int devId) +{ + int ret = 0; + Aes aes[1]; + uint8_t outBuf[64]; + +#ifdef HAVE_AES_CBC + /* AES-CBC KATs from wolfCrypt test.c aes_cbc_test / aes192_test / + * aes256_test. Each vector encrypts one AES block, decrypts it back, + * and compares against the published ciphertext. */ + { + /* AES-128-CBC: from aes_cbc_test */ + const uint8_t k128[16] = { + '0','1','2','3','4','5','6','7', + '8','9','a','b','c','d','e','f' + }; + const uint8_t iv128[16] = { + '1','2','3','4','5','6','7','8', + '9','0','a','b','c','d','e','f' + }; + const uint8_t p128[16] = { + 0x6e,0x6f,0x77,0x20,0x69,0x73,0x20,0x74, + 0x68,0x65,0x20,0x74,0x69,0x6d,0x65,0x20 + }; + const uint8_t c128[16] = { + 0x95,0x94,0x92,0x57,0x5f,0x42,0x81,0x53, + 0x2c,0xcc,0x9d,0x46,0x77,0xa2,0x33,0xcb + }; + /* AES-192-CBC: NIST SP 800-38A F.2.3 */ + const uint8_t k192[24] = { + 0x8e,0x73,0xb0,0xf7,0xda,0x0e,0x64,0x52, + 0xc8,0x10,0xf3,0x2b,0x80,0x90,0x79,0xe5, + 0x62,0xf8,0xea,0xd2,0x52,0x2c,0x6b,0x7b + }; + const uint8_t iv_nist[16] = { + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F + }; + const uint8_t p_nist[16] = { + 0x6b,0xc1,0xbe,0xe2,0x2e,0x40,0x9f,0x96, + 0xe9,0x3d,0x7e,0x11,0x73,0x93,0x17,0x2a + }; + const uint8_t c192[16] = { + 0x4f,0x02,0x1d,0xb2,0x43,0xbc,0x63,0x3d, + 0x71,0x78,0x18,0x3a,0x9f,0xa0,0x71,0xe8 + }; + /* AES-256-CBC: NIST SP 800-38A F.2.5 */ + const uint8_t k256[32] = { + 0x60,0x3d,0xeb,0x10,0x15,0xca,0x71,0xbe, + 0x2b,0x73,0xae,0xf0,0x85,0x7d,0x77,0x81, + 0x1f,0x35,0x2c,0x07,0x3b,0x61,0x08,0xd7, + 0x2d,0x98,0x10,0xa3,0x09,0x14,0xdf,0xf4 + }; + const uint8_t c256[16] = { + 0xf5,0x8c,0x4c,0x04,0xd6,0xe5,0xf1,0xba, + 0x77,0x9e,0xab,0xfb,0x5f,0x7b,0xfb,0xd6 + }; + + struct { + const uint8_t* k; + int kSz; + const uint8_t* iv; + const uint8_t* p; + const uint8_t* c; + } v[] = { + {k128, 16, iv128, p128, c128}, + {k192, 24, iv_nist, p_nist, c192}, + {k256, 32, iv_nist, p_nist, c256}, + }; + size_t i; + for (i = 0; i < sizeof(v) / sizeof(v[0]) && ret == 0; i++) { + memset(outBuf, 0, sizeof(outBuf)); + ret = wc_AesInit(aes, NULL, devId); + if (ret == 0) { + ret = wc_AesSetKey(aes, v[i].k, v[i].kSz, v[i].iv, + AES_ENCRYPTION); + } + if (ret == 0) { + ret = wh_Client_AesCbcRequest(ctx, aes, 1, v[i].p, + AES_BLOCK_SIZE); + } + if (ret == 0) { + do { + ret = wh_Client_AesCbcResponse(ctx, aes, outBuf, NULL); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0 && memcmp(outBuf, v[i].c, AES_BLOCK_SIZE) != 0) { + WH_ERROR_PRINT("AES-%d-CBC async KAT enc mismatch\n", + v[i].kSz * 8); + ret = -1; + } + if (ret == 0) { + ret = wc_AesSetKey(aes, v[i].k, v[i].kSz, v[i].iv, + AES_DECRYPTION); + } + if (ret == 0) { + memset(outBuf, 0, sizeof(outBuf)); + ret = wh_Client_AesCbcRequest(ctx, aes, 0, v[i].c, + AES_BLOCK_SIZE); + } + if (ret == 0) { + do { + ret = wh_Client_AesCbcResponse(ctx, aes, outBuf, NULL); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0 && memcmp(outBuf, v[i].p, AES_BLOCK_SIZE) != 0) { + WH_ERROR_PRINT("AES-%d-CBC async KAT dec mismatch\n", + v[i].kSz * 8); + ret = -1; + } + (void)wc_AesFree(aes); + } + } + if (ret == 0) { + WH_TEST_PRINT("AES CBC ASYNC KAT DEVID=0x%X SUCCESS\n", devId); + } +#endif /* HAVE_AES_CBC */ + +#ifdef WOLFSSL_AES_COUNTER + /* AES-CTR KATs from wolfCrypt test.c aes_ctr_test (NIST SP 800-38A + * F.5). 64-byte plaintext, single ciphertext per key size. */ + { + const uint8_t ctrIv[16] = { + 0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7, + 0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff + }; + const uint8_t ctrPlain[64] = { + 0x6b,0xc1,0xbe,0xe2,0x2e,0x40,0x9f,0x96, + 0xe9,0x3d,0x7e,0x11,0x73,0x93,0x17,0x2a, + 0xae,0x2d,0x8a,0x57,0x1e,0x03,0xac,0x9c, + 0x9e,0xb7,0x6f,0xac,0x45,0xaf,0x8e,0x51, + 0x30,0xc8,0x1c,0x46,0xa3,0x5c,0xe4,0x11, + 0xe5,0xfb,0xc1,0x19,0x1a,0x0a,0x52,0xef, + 0xf6,0x9f,0x24,0x45,0xdf,0x4f,0x9b,0x17, + 0xad,0x2b,0x41,0x7b,0xe6,0x6c,0x37,0x10 + }; + const uint8_t ctr128Key[16] = { + 0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6, + 0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c + }; + const uint8_t ctr128Cipher[64] = { + 0x87,0x4d,0x61,0x91,0xb6,0x20,0xe3,0x26, + 0x1b,0xef,0x68,0x64,0x99,0x0d,0xb6,0xce, + 0x98,0x06,0xf6,0x6b,0x79,0x70,0xfd,0xff, + 0x86,0x17,0x18,0x7b,0xb9,0xff,0xfd,0xff, + 0x5a,0xe4,0xdf,0x3e,0xdb,0xd5,0xd3,0x5e, + 0x5b,0x4f,0x09,0x02,0x0d,0xb0,0x3e,0xab, + 0x1e,0x03,0x1d,0xda,0x2f,0xbe,0x03,0xd1, + 0x79,0x21,0x70,0xa0,0xf3,0x00,0x9c,0xee + }; + const uint8_t ctr192Key[24] = { + 0x8e,0x73,0xb0,0xf7,0xda,0x0e,0x64,0x52, + 0xc8,0x10,0xf3,0x2b,0x80,0x90,0x79,0xe5, + 0x62,0xf8,0xea,0xd2,0x52,0x2c,0x6b,0x7b + }; + const uint8_t ctr192Cipher[64] = { + 0x1a,0xbc,0x93,0x24,0x17,0x52,0x1c,0xa2, + 0x4f,0x2b,0x04,0x59,0xfe,0x7e,0x6e,0x0b, + 0x09,0x03,0x39,0xec,0x0a,0xa6,0xfa,0xef, + 0xd5,0xcc,0xc2,0xc6,0xf4,0xce,0x8e,0x94, + 0x1e,0x36,0xb2,0x6b,0xd1,0xeb,0xc6,0x70, + 0xd1,0xbd,0x1d,0x66,0x56,0x20,0xab,0xf7, + 0x4f,0x78,0xa7,0xf6,0xd2,0x98,0x09,0x58, + 0x5a,0x97,0xda,0xec,0x58,0xc6,0xb0,0x50 + }; + const uint8_t ctr256Key[32] = { + 0x60,0x3d,0xeb,0x10,0x15,0xca,0x71,0xbe, + 0x2b,0x73,0xae,0xf0,0x85,0x7d,0x77,0x81, + 0x1f,0x35,0x2c,0x07,0x3b,0x61,0x08,0xd7, + 0x2d,0x98,0x10,0xa3,0x09,0x14,0xdf,0xf4 + }; + const uint8_t ctr256Cipher[64] = { + 0x60,0x1e,0xc3,0x13,0x77,0x57,0x89,0xa5, + 0xb7,0xa7,0xf5,0x04,0xbb,0xf3,0xd2,0x28, + 0xf4,0x43,0xe3,0xca,0x4d,0x62,0xb5,0x9a, + 0xca,0x84,0xe9,0x90,0xca,0xca,0xf5,0xc5, + 0x2b,0x09,0x30,0xda,0xa2,0x3d,0xe9,0x4c, + 0xe8,0x70,0x17,0xba,0x2d,0x84,0x98,0x8d, + 0xdf,0xc9,0xc5,0x8d,0xb6,0x7a,0xad,0xa6, + 0x13,0xc2,0xdd,0x08,0x45,0x79,0x41,0xa6 + }; + + struct { + const uint8_t* k; + int kSz; + const uint8_t* c; + } v[] = { + {ctr128Key, 16, ctr128Cipher}, + {ctr192Key, 24, ctr192Cipher}, + {ctr256Key, 32, ctr256Cipher}, + }; + size_t i; + for (i = 0; i < sizeof(v) / sizeof(v[0]) && ret == 0; i++) { + memset(outBuf, 0, sizeof(outBuf)); + ret = wc_AesInit(aes, NULL, devId); + if (ret == 0) { + ret = wc_AesSetKeyDirect(aes, v[i].k, v[i].kSz, ctrIv, + AES_ENCRYPTION); + } + if (ret == 0) { + ret = wh_Client_AesCtrRequest(ctx, aes, 1, ctrPlain, + sizeof(ctrPlain)); + } + if (ret == 0) { + do { + ret = wh_Client_AesCtrResponse(ctx, aes, outBuf, NULL); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0 && + memcmp(outBuf, v[i].c, sizeof(ctrPlain)) != 0) { + WH_ERROR_PRINT("AES-%d-CTR async KAT enc mismatch\n", + v[i].kSz * 8); + ret = -1; + } + /* CTR is symmetric: applying the same op to ciphertext + * recovers plaintext */ + if (ret == 0) { + ret = wc_AesSetKeyDirect(aes, v[i].k, v[i].kSz, ctrIv, + AES_ENCRYPTION); + } + if (ret == 0) { + memset(outBuf, 0, sizeof(outBuf)); + ret = wh_Client_AesCtrRequest(ctx, aes, 1, v[i].c, + sizeof(ctrPlain)); + } + if (ret == 0) { + do { + ret = wh_Client_AesCtrResponse(ctx, aes, outBuf, NULL); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0 && + memcmp(outBuf, ctrPlain, sizeof(ctrPlain)) != 0) { + WH_ERROR_PRINT("AES-%d-CTR async KAT dec mismatch\n", + v[i].kSz * 8); + ret = -1; + } + (void)wc_AesFree(aes); + } + } + if (ret == 0) { + WH_TEST_PRINT("AES CTR ASYNC KAT DEVID=0x%X SUCCESS\n", devId); + } +#endif /* WOLFSSL_AES_COUNTER */ + +#ifdef HAVE_AES_ECB + /* AES-ECB KATs from wolfCrypt test.c aes_ecb_test. */ + { + const uint8_t ecbIv[16] = { + '1','2','3','4','5','6','7','8', + '9','0','a','b','c','d','e','f' + }; + const uint8_t ecbMsg[16] = { + 0x6e,0x6f,0x77,0x20,0x69,0x73,0x20,0x74, + 0x68,0x65,0x20,0x74,0x69,0x6d,0x65,0x20 + }; + const uint8_t ecbK128[16] = { + '0','1','2','3','4','5','6','7', + '8','9','a','b','c','d','e','f' + }; + const uint8_t ecbC128[16] = { + 0xd0,0xc9,0xd9,0xc9,0x40,0xe8,0x97,0xb6, + 0xc8,0x8c,0x33,0x3b,0xb5,0x8f,0x85,0xd1 + }; + const uint8_t ecbK192[24] = { + '0','1','2','3','4','5','6','7', + '8','9','a','b','c','d','e','f', + '0','1','2','3','4','5','6','7' + }; + const uint8_t ecbC192[16] = { + 0x06,0x57,0xee,0x78,0x3f,0x96,0x00,0xb1, + 0xec,0x76,0x94,0x30,0x29,0xbe,0x15,0xab + }; + const uint8_t ecbK256[32] = { + '0','1','2','3','4','5','6','7', + '8','9','a','b','c','d','e','f', + '0','1','2','3','4','5','6','7', + '8','9','a','b','c','d','e','f' + }; + const uint8_t ecbC256[16] = { + 0xcd,0xf2,0x81,0x3e,0x73,0x3e,0xf7,0x33, + 0x3d,0x18,0xfd,0x41,0x85,0x37,0x04,0x82 + }; + + struct { + const uint8_t* k; + int kSz; + const uint8_t* c; + } v[] = { + {ecbK128, 16, ecbC128}, + {ecbK192, 24, ecbC192}, + {ecbK256, 32, ecbC256}, + }; + size_t i; + for (i = 0; i < sizeof(v) / sizeof(v[0]) && ret == 0; i++) { + memset(outBuf, 0, sizeof(outBuf)); + ret = wc_AesInit(aes, NULL, devId); + if (ret == 0) { + ret = wc_AesSetKey(aes, v[i].k, v[i].kSz, ecbIv, + AES_ENCRYPTION); + } + if (ret == 0) { + ret = wh_Client_AesEcbRequest(ctx, aes, 1, ecbMsg, + AES_BLOCK_SIZE); + } + if (ret == 0) { + do { + ret = wh_Client_AesEcbResponse(ctx, aes, outBuf, NULL); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0 && memcmp(outBuf, v[i].c, AES_BLOCK_SIZE) != 0) { + WH_ERROR_PRINT("AES-%d-ECB async KAT enc mismatch\n", + v[i].kSz * 8); + ret = -1; + } + if (ret == 0) { + ret = wc_AesSetKey(aes, v[i].k, v[i].kSz, ecbIv, + AES_DECRYPTION); + } + if (ret == 0) { + memset(outBuf, 0, sizeof(outBuf)); + ret = wh_Client_AesEcbRequest(ctx, aes, 0, v[i].c, + AES_BLOCK_SIZE); + } + if (ret == 0) { + do { + ret = wh_Client_AesEcbResponse(ctx, aes, outBuf, NULL); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0 && memcmp(outBuf, ecbMsg, AES_BLOCK_SIZE) != 0) { + WH_ERROR_PRINT("AES-%d-ECB async KAT dec mismatch\n", + v[i].kSz * 8); + ret = -1; + } + (void)wc_AesFree(aes); + } + } + if (ret == 0) { + WH_TEST_PRINT("AES ECB ASYNC KAT DEVID=0x%X SUCCESS\n", devId); + } +#endif /* HAVE_AES_ECB */ + +#ifdef HAVE_AESGCM + /* AES-GCM KATs from wolfCrypt test.c aesgcm_test (GCM spec test + * cases). Verifies ciphertext, tag, and decrypt round-trip. */ + { + /* AES-128-GCM: k3/p3/c3_3/t3_3, IV iv1, no AAD */ + const uint8_t gcmIv[12] = { + 0xca,0xfe,0xba,0xbe,0xfa,0xce,0xdb,0xad, + 0xde,0xca,0xf8,0x88 + }; + const uint8_t gcm128Key[16] = { + 0xbb,0x01,0xd7,0x03,0x81,0x1c,0x10,0x1a, + 0x35,0xe0,0xff,0xd2,0x91,0xba,0xf2,0x4b + }; + const uint8_t gcm128Plain[16] = { + 0x57,0xce,0x45,0x1f,0xa5,0xe2,0x35,0xa5, + 0x8e,0x1a,0xa2,0x3b,0x77,0xcb,0xaf,0xe2 + }; + const uint8_t gcm128Cipher[16] = { + 0x79,0xa7,0x08,0xd4,0xad,0x1f,0x3b,0xac, + 0x70,0x16,0x64,0x40,0xde,0x03,0xed,0xea + }; + const uint8_t gcm128Tag[16] = { + 0x39,0xb1,0x1e,0x73,0x18,0xda,0x04,0x75, + 0xa1,0xed,0x52,0xb9,0x0d,0x5c,0xe7,0x28 + }; + /* AES-256-GCM: GCM Test Case 16 (k1/p/c1/t1) with AAD a */ + const uint8_t gcm256Key[32] = { + 0xfe,0xff,0xe9,0x92,0x86,0x65,0x73,0x1c, + 0x6d,0x6a,0x8f,0x94,0x67,0x30,0x83,0x08, + 0xfe,0xff,0xe9,0x92,0x86,0x65,0x73,0x1c, + 0x6d,0x6a,0x8f,0x94,0x67,0x30,0x83,0x08 + }; + const uint8_t gcm256Plain[60] = { + 0xd9,0x31,0x32,0x25,0xf8,0x84,0x06,0xe5, + 0xa5,0x59,0x09,0xc5,0xaf,0xf5,0x26,0x9a, + 0x86,0xa7,0xa9,0x53,0x15,0x34,0xf7,0xda, + 0x2e,0x4c,0x30,0x3d,0x8a,0x31,0x8a,0x72, + 0x1c,0x3c,0x0c,0x95,0x95,0x68,0x09,0x53, + 0x2f,0xcf,0x0e,0x24,0x49,0xa6,0xb5,0x25, + 0xb1,0x6a,0xed,0xf5,0xaa,0x0d,0xe6,0x57, + 0xba,0x63,0x7b,0x39 + }; + const uint8_t gcm256Aad[20] = { + 0xfe,0xed,0xfa,0xce,0xde,0xad,0xbe,0xef, + 0xfe,0xed,0xfa,0xce,0xde,0xad,0xbe,0xef, + 0xab,0xad,0xda,0xd2 + }; + const uint8_t gcm256Cipher[60] = { + 0x52,0x2d,0xc1,0xf0,0x99,0x56,0x7d,0x07, + 0xf4,0x7f,0x37,0xa3,0x2a,0x84,0x42,0x7d, + 0x64,0x3a,0x8c,0xdc,0xbf,0xe5,0xc0,0xc9, + 0x75,0x98,0xa2,0xbd,0x25,0x55,0xd1,0xaa, + 0x8c,0xb0,0x8e,0x48,0x59,0x0d,0xbb,0x3d, + 0xa7,0xb0,0x8b,0x10,0x56,0x82,0x88,0x38, + 0xc5,0xf6,0x1e,0x63,0x93,0xba,0x7a,0x0a, + 0xbc,0xc9,0xf6,0x62 + }; + const uint8_t gcm256Tag[16] = { + 0x76,0xfc,0x6e,0xce,0x0f,0x4e,0x17,0x68, + 0xcd,0xdf,0x88,0x53,0xbb,0x2d,0x55,0x1b + }; + + struct { + const uint8_t* k; + int kSz; + const uint8_t* p; + int pSz; + const uint8_t* aad; + int aadSz; + const uint8_t* c; + const uint8_t* t; + } v[] = { + {gcm128Key, 16, gcm128Plain, sizeof(gcm128Plain), + NULL, 0, gcm128Cipher, gcm128Tag}, + {gcm256Key, 32, gcm256Plain, sizeof(gcm256Plain), + gcm256Aad, sizeof(gcm256Aad), gcm256Cipher, gcm256Tag}, + }; + uint8_t cipherBuf[64]; + uint8_t plainBuf[64]; + uint8_t tagBuf[AES_BLOCK_SIZE]; + size_t i; + for (i = 0; i < sizeof(v) / sizeof(v[0]) && ret == 0; i++) { + memset(cipherBuf, 0, sizeof(cipherBuf)); + memset(plainBuf, 0, sizeof(plainBuf)); + memset(tagBuf, 0, sizeof(tagBuf)); + ret = wc_AesInit(aes, NULL, devId); + if (ret == 0) { + ret = wc_AesGcmSetKey(aes, v[i].k, v[i].kSz); + } + if (ret == 0) { + ret = wh_Client_AesGcmRequest( + ctx, aes, 1, v[i].p, v[i].pSz, gcmIv, sizeof(gcmIv), + v[i].aad, v[i].aadSz, NULL, sizeof(tagBuf)); + } + if (ret == 0) { + do { + ret = wh_Client_AesGcmResponse(ctx, aes, cipherBuf, + sizeof(cipherBuf), NULL, + tagBuf, sizeof(tagBuf)); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0 && memcmp(cipherBuf, v[i].c, v[i].pSz) != 0) { + WH_ERROR_PRINT("AES-%d-GCM async KAT cipher mismatch\n", + v[i].kSz * 8); + ret = -1; + } + if (ret == 0 && memcmp(tagBuf, v[i].t, sizeof(tagBuf)) != 0) { + WH_ERROR_PRINT("AES-%d-GCM async KAT tag mismatch\n", + v[i].kSz * 8); + ret = -1; + } + if (ret == 0) { + ret = wh_Client_AesGcmRequest( + ctx, aes, 0, v[i].c, v[i].pSz, gcmIv, sizeof(gcmIv), + v[i].aad, v[i].aadSz, v[i].t, sizeof(tagBuf)); + } + if (ret == 0) { + do { + ret = wh_Client_AesGcmResponse(ctx, aes, plainBuf, + sizeof(plainBuf), NULL, + NULL, 0); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0 && memcmp(plainBuf, v[i].p, v[i].pSz) != 0) { + WH_ERROR_PRINT("AES-%d-GCM async KAT plain mismatch\n", + v[i].kSz * 8); + ret = -1; + } + (void)wc_AesFree(aes); + } + } + if (ret == 0) { + WH_TEST_PRINT("AES GCM ASYNC KAT DEVID=0x%X SUCCESS\n", devId); + } +#endif /* HAVE_AESGCM */ + + return ret; +} + #ifdef WOLFHSM_CFG_DMA /* Direct exercise of the native async DMA AES primitives. */ static int whTest_CryptoAesDmaAsync(whClientContext* ctx, int devId, @@ -7265,6 +7749,472 @@ static int whTest_CryptoAesDmaAsync(whClientContext* ctx, int devId, #undef WH_TEST_AES_ASYNC_DMA_KEYSZ #undef WH_TEST_AES_ASYNC_DMA_BUFSZ } + +/* Known-answer tests for the DMA async AES primitives. Same vectors as + * whTest_CryptoAesAsyncKat run through the DMA Request/Response APIs. */ +static int whTest_CryptoAesDmaAsyncKat(whClientContext* ctx, int devId) +{ + int ret = 0; + Aes aes[1]; + uint8_t outBuf[64]; + +#ifdef HAVE_AES_CBC + { + const uint8_t k128[16] = { + '0','1','2','3','4','5','6','7', + '8','9','a','b','c','d','e','f' + }; + const uint8_t iv128[16] = { + '1','2','3','4','5','6','7','8', + '9','0','a','b','c','d','e','f' + }; + const uint8_t p128[16] = { + 0x6e,0x6f,0x77,0x20,0x69,0x73,0x20,0x74, + 0x68,0x65,0x20,0x74,0x69,0x6d,0x65,0x20 + }; + const uint8_t c128[16] = { + 0x95,0x94,0x92,0x57,0x5f,0x42,0x81,0x53, + 0x2c,0xcc,0x9d,0x46,0x77,0xa2,0x33,0xcb + }; + const uint8_t k192[24] = { + 0x8e,0x73,0xb0,0xf7,0xda,0x0e,0x64,0x52, + 0xc8,0x10,0xf3,0x2b,0x80,0x90,0x79,0xe5, + 0x62,0xf8,0xea,0xd2,0x52,0x2c,0x6b,0x7b + }; + const uint8_t iv_nist[16] = { + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F + }; + const uint8_t p_nist[16] = { + 0x6b,0xc1,0xbe,0xe2,0x2e,0x40,0x9f,0x96, + 0xe9,0x3d,0x7e,0x11,0x73,0x93,0x17,0x2a + }; + const uint8_t c192[16] = { + 0x4f,0x02,0x1d,0xb2,0x43,0xbc,0x63,0x3d, + 0x71,0x78,0x18,0x3a,0x9f,0xa0,0x71,0xe8 + }; + const uint8_t k256[32] = { + 0x60,0x3d,0xeb,0x10,0x15,0xca,0x71,0xbe, + 0x2b,0x73,0xae,0xf0,0x85,0x7d,0x77,0x81, + 0x1f,0x35,0x2c,0x07,0x3b,0x61,0x08,0xd7, + 0x2d,0x98,0x10,0xa3,0x09,0x14,0xdf,0xf4 + }; + const uint8_t c256[16] = { + 0xf5,0x8c,0x4c,0x04,0xd6,0xe5,0xf1,0xba, + 0x77,0x9e,0xab,0xfb,0x5f,0x7b,0xfb,0xd6 + }; + + struct { + const uint8_t* k; + int kSz; + const uint8_t* iv; + const uint8_t* p; + const uint8_t* c; + } v[] = { + {k128, 16, iv128, p128, c128}, + {k192, 24, iv_nist, p_nist, c192}, + {k256, 32, iv_nist, p_nist, c256}, + }; + size_t i; + for (i = 0; i < sizeof(v) / sizeof(v[0]) && ret == 0; i++) { + memset(outBuf, 0, sizeof(outBuf)); + ret = wc_AesInit(aes, NULL, devId); + if (ret == 0) { + ret = wc_AesSetKey(aes, v[i].k, v[i].kSz, v[i].iv, + AES_ENCRYPTION); + } + if (ret == 0) { + ret = wh_Client_AesCbcDmaRequest(ctx, aes, 1, v[i].p, + AES_BLOCK_SIZE, outBuf); + } + if (ret == 0) { + do { + ret = wh_Client_AesCbcDmaResponse(ctx, aes); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0 && memcmp(outBuf, v[i].c, AES_BLOCK_SIZE) != 0) { + WH_ERROR_PRINT("AES-%d-CBC DMA async KAT enc mismatch\n", + v[i].kSz * 8); + ret = -1; + } + if (ret == 0) { + ret = wc_AesSetKey(aes, v[i].k, v[i].kSz, v[i].iv, + AES_DECRYPTION); + } + if (ret == 0) { + memset(outBuf, 0, sizeof(outBuf)); + ret = wh_Client_AesCbcDmaRequest(ctx, aes, 0, v[i].c, + AES_BLOCK_SIZE, outBuf); + } + if (ret == 0) { + do { + ret = wh_Client_AesCbcDmaResponse(ctx, aes); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0 && memcmp(outBuf, v[i].p, AES_BLOCK_SIZE) != 0) { + WH_ERROR_PRINT("AES-%d-CBC DMA async KAT dec mismatch\n", + v[i].kSz * 8); + ret = -1; + } + (void)wc_AesFree(aes); + } + } + if (ret == 0) { + WH_TEST_PRINT("AES CBC DMA ASYNC KAT DEVID=0x%X SUCCESS\n", devId); + } +#endif /* HAVE_AES_CBC */ + +#ifdef WOLFSSL_AES_COUNTER + { + const uint8_t ctrIv[16] = { + 0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7, + 0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff + }; + const uint8_t ctrPlain[64] = { + 0x6b,0xc1,0xbe,0xe2,0x2e,0x40,0x9f,0x96, + 0xe9,0x3d,0x7e,0x11,0x73,0x93,0x17,0x2a, + 0xae,0x2d,0x8a,0x57,0x1e,0x03,0xac,0x9c, + 0x9e,0xb7,0x6f,0xac,0x45,0xaf,0x8e,0x51, + 0x30,0xc8,0x1c,0x46,0xa3,0x5c,0xe4,0x11, + 0xe5,0xfb,0xc1,0x19,0x1a,0x0a,0x52,0xef, + 0xf6,0x9f,0x24,0x45,0xdf,0x4f,0x9b,0x17, + 0xad,0x2b,0x41,0x7b,0xe6,0x6c,0x37,0x10 + }; + const uint8_t ctr128Key[16] = { + 0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6, + 0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c + }; + const uint8_t ctr128Cipher[64] = { + 0x87,0x4d,0x61,0x91,0xb6,0x20,0xe3,0x26, + 0x1b,0xef,0x68,0x64,0x99,0x0d,0xb6,0xce, + 0x98,0x06,0xf6,0x6b,0x79,0x70,0xfd,0xff, + 0x86,0x17,0x18,0x7b,0xb9,0xff,0xfd,0xff, + 0x5a,0xe4,0xdf,0x3e,0xdb,0xd5,0xd3,0x5e, + 0x5b,0x4f,0x09,0x02,0x0d,0xb0,0x3e,0xab, + 0x1e,0x03,0x1d,0xda,0x2f,0xbe,0x03,0xd1, + 0x79,0x21,0x70,0xa0,0xf3,0x00,0x9c,0xee + }; + const uint8_t ctr192Key[24] = { + 0x8e,0x73,0xb0,0xf7,0xda,0x0e,0x64,0x52, + 0xc8,0x10,0xf3,0x2b,0x80,0x90,0x79,0xe5, + 0x62,0xf8,0xea,0xd2,0x52,0x2c,0x6b,0x7b + }; + const uint8_t ctr192Cipher[64] = { + 0x1a,0xbc,0x93,0x24,0x17,0x52,0x1c,0xa2, + 0x4f,0x2b,0x04,0x59,0xfe,0x7e,0x6e,0x0b, + 0x09,0x03,0x39,0xec,0x0a,0xa6,0xfa,0xef, + 0xd5,0xcc,0xc2,0xc6,0xf4,0xce,0x8e,0x94, + 0x1e,0x36,0xb2,0x6b,0xd1,0xeb,0xc6,0x70, + 0xd1,0xbd,0x1d,0x66,0x56,0x20,0xab,0xf7, + 0x4f,0x78,0xa7,0xf6,0xd2,0x98,0x09,0x58, + 0x5a,0x97,0xda,0xec,0x58,0xc6,0xb0,0x50 + }; + const uint8_t ctr256Key[32] = { + 0x60,0x3d,0xeb,0x10,0x15,0xca,0x71,0xbe, + 0x2b,0x73,0xae,0xf0,0x85,0x7d,0x77,0x81, + 0x1f,0x35,0x2c,0x07,0x3b,0x61,0x08,0xd7, + 0x2d,0x98,0x10,0xa3,0x09,0x14,0xdf,0xf4 + }; + const uint8_t ctr256Cipher[64] = { + 0x60,0x1e,0xc3,0x13,0x77,0x57,0x89,0xa5, + 0xb7,0xa7,0xf5,0x04,0xbb,0xf3,0xd2,0x28, + 0xf4,0x43,0xe3,0xca,0x4d,0x62,0xb5,0x9a, + 0xca,0x84,0xe9,0x90,0xca,0xca,0xf5,0xc5, + 0x2b,0x09,0x30,0xda,0xa2,0x3d,0xe9,0x4c, + 0xe8,0x70,0x17,0xba,0x2d,0x84,0x98,0x8d, + 0xdf,0xc9,0xc5,0x8d,0xb6,0x7a,0xad,0xa6, + 0x13,0xc2,0xdd,0x08,0x45,0x79,0x41,0xa6 + }; + + struct { + const uint8_t* k; + int kSz; + const uint8_t* c; + } v[] = { + {ctr128Key, 16, ctr128Cipher}, + {ctr192Key, 24, ctr192Cipher}, + {ctr256Key, 32, ctr256Cipher}, + }; + size_t i; + for (i = 0; i < sizeof(v) / sizeof(v[0]) && ret == 0; i++) { + memset(outBuf, 0, sizeof(outBuf)); + ret = wc_AesInit(aes, NULL, devId); + if (ret == 0) { + ret = wc_AesSetKeyDirect(aes, v[i].k, v[i].kSz, ctrIv, + AES_ENCRYPTION); + } + if (ret == 0) { + ret = wh_Client_AesCtrDmaRequest(ctx, aes, 1, ctrPlain, + sizeof(ctrPlain), outBuf); + } + if (ret == 0) { + do { + ret = wh_Client_AesCtrDmaResponse(ctx, aes); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0 && + memcmp(outBuf, v[i].c, sizeof(ctrPlain)) != 0) { + WH_ERROR_PRINT("AES-%d-CTR DMA async KAT enc mismatch\n", + v[i].kSz * 8); + ret = -1; + } + if (ret == 0) { + ret = wc_AesSetKeyDirect(aes, v[i].k, v[i].kSz, ctrIv, + AES_ENCRYPTION); + } + if (ret == 0) { + memset(outBuf, 0, sizeof(outBuf)); + ret = wh_Client_AesCtrDmaRequest(ctx, aes, 1, v[i].c, + sizeof(ctrPlain), outBuf); + } + if (ret == 0) { + do { + ret = wh_Client_AesCtrDmaResponse(ctx, aes); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0 && + memcmp(outBuf, ctrPlain, sizeof(ctrPlain)) != 0) { + WH_ERROR_PRINT("AES-%d-CTR DMA async KAT dec mismatch\n", + v[i].kSz * 8); + ret = -1; + } + (void)wc_AesFree(aes); + } + } + if (ret == 0) { + WH_TEST_PRINT("AES CTR DMA ASYNC KAT DEVID=0x%X SUCCESS\n", devId); + } +#endif /* WOLFSSL_AES_COUNTER */ + +#ifdef HAVE_AES_ECB + { + const uint8_t ecbIv[16] = { + '1','2','3','4','5','6','7','8', + '9','0','a','b','c','d','e','f' + }; + const uint8_t ecbMsg[16] = { + 0x6e,0x6f,0x77,0x20,0x69,0x73,0x20,0x74, + 0x68,0x65,0x20,0x74,0x69,0x6d,0x65,0x20 + }; + const uint8_t ecbK128[16] = { + '0','1','2','3','4','5','6','7', + '8','9','a','b','c','d','e','f' + }; + const uint8_t ecbC128[16] = { + 0xd0,0xc9,0xd9,0xc9,0x40,0xe8,0x97,0xb6, + 0xc8,0x8c,0x33,0x3b,0xb5,0x8f,0x85,0xd1 + }; + const uint8_t ecbK192[24] = { + '0','1','2','3','4','5','6','7', + '8','9','a','b','c','d','e','f', + '0','1','2','3','4','5','6','7' + }; + const uint8_t ecbC192[16] = { + 0x06,0x57,0xee,0x78,0x3f,0x96,0x00,0xb1, + 0xec,0x76,0x94,0x30,0x29,0xbe,0x15,0xab + }; + const uint8_t ecbK256[32] = { + '0','1','2','3','4','5','6','7', + '8','9','a','b','c','d','e','f', + '0','1','2','3','4','5','6','7', + '8','9','a','b','c','d','e','f' + }; + const uint8_t ecbC256[16] = { + 0xcd,0xf2,0x81,0x3e,0x73,0x3e,0xf7,0x33, + 0x3d,0x18,0xfd,0x41,0x85,0x37,0x04,0x82 + }; + + struct { + const uint8_t* k; + int kSz; + const uint8_t* c; + } v[] = { + {ecbK128, 16, ecbC128}, + {ecbK192, 24, ecbC192}, + {ecbK256, 32, ecbC256}, + }; + size_t i; + for (i = 0; i < sizeof(v) / sizeof(v[0]) && ret == 0; i++) { + memset(outBuf, 0, sizeof(outBuf)); + ret = wc_AesInit(aes, NULL, devId); + if (ret == 0) { + ret = wc_AesSetKey(aes, v[i].k, v[i].kSz, ecbIv, + AES_ENCRYPTION); + } + if (ret == 0) { + ret = wh_Client_AesEcbDmaRequest(ctx, aes, 1, ecbMsg, + AES_BLOCK_SIZE, outBuf); + } + if (ret == 0) { + do { + ret = wh_Client_AesEcbDmaResponse(ctx, aes); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0 && memcmp(outBuf, v[i].c, AES_BLOCK_SIZE) != 0) { + WH_ERROR_PRINT("AES-%d-ECB DMA async KAT enc mismatch\n", + v[i].kSz * 8); + ret = -1; + } + if (ret == 0) { + ret = wc_AesSetKey(aes, v[i].k, v[i].kSz, ecbIv, + AES_DECRYPTION); + } + if (ret == 0) { + memset(outBuf, 0, sizeof(outBuf)); + ret = wh_Client_AesEcbDmaRequest(ctx, aes, 0, v[i].c, + AES_BLOCK_SIZE, outBuf); + } + if (ret == 0) { + do { + ret = wh_Client_AesEcbDmaResponse(ctx, aes); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0 && memcmp(outBuf, ecbMsg, AES_BLOCK_SIZE) != 0) { + WH_ERROR_PRINT("AES-%d-ECB DMA async KAT dec mismatch\n", + v[i].kSz * 8); + ret = -1; + } + (void)wc_AesFree(aes); + } + } + if (ret == 0) { + WH_TEST_PRINT("AES ECB DMA ASYNC KAT DEVID=0x%X SUCCESS\n", devId); + } +#endif /* HAVE_AES_ECB */ + +#ifdef HAVE_AESGCM + { + const uint8_t gcmIv[12] = { + 0xca,0xfe,0xba,0xbe,0xfa,0xce,0xdb,0xad, + 0xde,0xca,0xf8,0x88 + }; + const uint8_t gcm128Key[16] = { + 0xbb,0x01,0xd7,0x03,0x81,0x1c,0x10,0x1a, + 0x35,0xe0,0xff,0xd2,0x91,0xba,0xf2,0x4b + }; + const uint8_t gcm128Plain[16] = { + 0x57,0xce,0x45,0x1f,0xa5,0xe2,0x35,0xa5, + 0x8e,0x1a,0xa2,0x3b,0x77,0xcb,0xaf,0xe2 + }; + const uint8_t gcm128Cipher[16] = { + 0x79,0xa7,0x08,0xd4,0xad,0x1f,0x3b,0xac, + 0x70,0x16,0x64,0x40,0xde,0x03,0xed,0xea + }; + const uint8_t gcm128Tag[16] = { + 0x39,0xb1,0x1e,0x73,0x18,0xda,0x04,0x75, + 0xa1,0xed,0x52,0xb9,0x0d,0x5c,0xe7,0x28 + }; + const uint8_t gcm256Key[32] = { + 0xfe,0xff,0xe9,0x92,0x86,0x65,0x73,0x1c, + 0x6d,0x6a,0x8f,0x94,0x67,0x30,0x83,0x08, + 0xfe,0xff,0xe9,0x92,0x86,0x65,0x73,0x1c, + 0x6d,0x6a,0x8f,0x94,0x67,0x30,0x83,0x08 + }; + const uint8_t gcm256Plain[60] = { + 0xd9,0x31,0x32,0x25,0xf8,0x84,0x06,0xe5, + 0xa5,0x59,0x09,0xc5,0xaf,0xf5,0x26,0x9a, + 0x86,0xa7,0xa9,0x53,0x15,0x34,0xf7,0xda, + 0x2e,0x4c,0x30,0x3d,0x8a,0x31,0x8a,0x72, + 0x1c,0x3c,0x0c,0x95,0x95,0x68,0x09,0x53, + 0x2f,0xcf,0x0e,0x24,0x49,0xa6,0xb5,0x25, + 0xb1,0x6a,0xed,0xf5,0xaa,0x0d,0xe6,0x57, + 0xba,0x63,0x7b,0x39 + }; + const uint8_t gcm256Aad[20] = { + 0xfe,0xed,0xfa,0xce,0xde,0xad,0xbe,0xef, + 0xfe,0xed,0xfa,0xce,0xde,0xad,0xbe,0xef, + 0xab,0xad,0xda,0xd2 + }; + const uint8_t gcm256Cipher[60] = { + 0x52,0x2d,0xc1,0xf0,0x99,0x56,0x7d,0x07, + 0xf4,0x7f,0x37,0xa3,0x2a,0x84,0x42,0x7d, + 0x64,0x3a,0x8c,0xdc,0xbf,0xe5,0xc0,0xc9, + 0x75,0x98,0xa2,0xbd,0x25,0x55,0xd1,0xaa, + 0x8c,0xb0,0x8e,0x48,0x59,0x0d,0xbb,0x3d, + 0xa7,0xb0,0x8b,0x10,0x56,0x82,0x88,0x38, + 0xc5,0xf6,0x1e,0x63,0x93,0xba,0x7a,0x0a, + 0xbc,0xc9,0xf6,0x62 + }; + const uint8_t gcm256Tag[16] = { + 0x76,0xfc,0x6e,0xce,0x0f,0x4e,0x17,0x68, + 0xcd,0xdf,0x88,0x53,0xbb,0x2d,0x55,0x1b + }; + + struct { + const uint8_t* k; + int kSz; + const uint8_t* p; + int pSz; + const uint8_t* aad; + int aadSz; + const uint8_t* c; + const uint8_t* t; + } v[] = { + {gcm128Key, 16, gcm128Plain, sizeof(gcm128Plain), + NULL, 0, gcm128Cipher, gcm128Tag}, + {gcm256Key, 32, gcm256Plain, sizeof(gcm256Plain), + gcm256Aad, sizeof(gcm256Aad), gcm256Cipher, gcm256Tag}, + }; + uint8_t cipherBuf[64]; + uint8_t plainBuf[64]; + uint8_t tagBuf[AES_BLOCK_SIZE]; + size_t i; + for (i = 0; i < sizeof(v) / sizeof(v[0]) && ret == 0; i++) { + memset(cipherBuf, 0, sizeof(cipherBuf)); + memset(plainBuf, 0, sizeof(plainBuf)); + memset(tagBuf, 0, sizeof(tagBuf)); + ret = wc_AesInit(aes, NULL, devId); + if (ret == 0) { + ret = wc_AesGcmSetKey(aes, v[i].k, v[i].kSz); + } + if (ret == 0) { + ret = wh_Client_AesGcmDmaRequest( + ctx, aes, 1, v[i].p, v[i].pSz, cipherBuf, gcmIv, + sizeof(gcmIv), v[i].aad, v[i].aadSz, NULL, + sizeof(tagBuf)); + } + if (ret == 0) { + do { + ret = wh_Client_AesGcmDmaResponse(ctx, aes, tagBuf, + sizeof(tagBuf)); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0 && memcmp(cipherBuf, v[i].c, v[i].pSz) != 0) { + WH_ERROR_PRINT("AES-%d-GCM DMA async KAT cipher mismatch\n", + v[i].kSz * 8); + ret = -1; + } + if (ret == 0 && memcmp(tagBuf, v[i].t, sizeof(tagBuf)) != 0) { + WH_ERROR_PRINT("AES-%d-GCM DMA async KAT tag mismatch\n", + v[i].kSz * 8); + ret = -1; + } + if (ret == 0) { + ret = wh_Client_AesGcmDmaRequest( + ctx, aes, 0, v[i].c, v[i].pSz, plainBuf, gcmIv, + sizeof(gcmIv), v[i].aad, v[i].aadSz, v[i].t, + sizeof(tagBuf)); + } + if (ret == 0) { + do { + ret = wh_Client_AesGcmDmaResponse(ctx, aes, NULL, 0); + } while (ret == WH_ERROR_NOTREADY); + } + if (ret == 0 && memcmp(plainBuf, v[i].p, v[i].pSz) != 0) { + WH_ERROR_PRINT("AES-%d-GCM DMA async KAT plain mismatch\n", + v[i].kSz * 8); + ret = -1; + } + (void)wc_AesFree(aes); + } + } + if (ret == 0) { + WH_TEST_PRINT("AES GCM DMA ASYNC KAT DEVID=0x%X SUCCESS\n", devId); + } +#endif /* HAVE_AESGCM */ + + return ret; +} #endif /* WOLFHSM_CFG_DMA */ #endif /* !NO_AES */ @@ -9243,6 +10193,9 @@ int whTest_CryptoClientConfig(whClientConfig* config) if (ret == WH_ERROR_OK) { ret = whTest_CryptoAesAsync(client, WH_DEV_IDS_ARRAY[i], rng); } + if (ret == WH_ERROR_OK) { + ret = whTest_CryptoAesAsyncKat(client, WH_DEV_IDS_ARRAY[i]); + } if (ret == WH_ERROR_OK) { i++; } @@ -9251,6 +10204,9 @@ int whTest_CryptoClientConfig(whClientConfig* config) if (ret == WH_ERROR_OK) { ret = whTest_CryptoAesDmaAsync(client, WH_DEV_ID_DMA, rng); } + if (ret == WH_ERROR_OK) { + ret = whTest_CryptoAesDmaAsyncKat(client, WH_DEV_ID_DMA); + } #endif /* WOLFHSM_CFG_DMA */ #endif /* !NO_AES */