From 050acef0616d5f8f262725ae98a04102eee87d30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Frauenschl=C3=A4ger?= Date: Mon, 15 Jun 2026 15:35:15 +0200 Subject: [PATCH] client cert DMA: add missing buffer translation Add missing address translations via the DMA callbacks for the certificate handling API. This has been missed in PR #403 as the relevant API is not exercised in the added test infrastructure for this. Also extend the test infrastructure to cover the certificate API now as well. --- src/wh_client_cert.c | 236 +++++++++++++++++++++++++++--------- test/wh_test_clientserver.c | 84 ++++++++++++- wolfhsm/wh_client.h | 12 ++ 3 files changed, 274 insertions(+), 58 deletions(-) diff --git a/src/wh_client_cert.c b/src/wh_client_cert.c index 5abc8c687..851436af6 100644 --- a/src/wh_client_cert.c +++ b/src/wh_client_cert.c @@ -811,27 +811,42 @@ int wh_Client_CertAddTrustedDmaRequest(whClientContext* c, whNvmId id, uint8_t* label, whNvmSize label_len, const void* cert, uint32_t cert_len) { - whMessageCert_AddTrustedDmaRequest req = {0}; + whMessageCert_AddTrustedDmaRequest req = {0}; + uintptr_t certAddr = 0; + int rc = WH_ERROR_OK; if (c == NULL || cert_len > WOLFHSM_CFG_MAX_CERT_SIZE) { return WH_ERROR_BADARGS; } + /* Fail fast if busy (a rejected send would leak the mapping). */ + if (wh_CommClient_IsRequestPending(c->comm) == 1) { + return WH_ERROR_REQUEST_PENDING; + } - /* Prepare and send request */ - memset(&req, 0, sizeof(req)); - req.id = id; - req.access = access; - req.flags = flags; - req.cert_addr = (uint64_t)(uintptr_t)cert; - req.cert_len = cert_len; - if (label != NULL && label_len > 0) { - whNvmSize copy_len = - (label_len > WH_NVM_LABEL_LEN) ? WH_NVM_LABEL_LEN : label_len; - memcpy(req.label, label, copy_len); + /* Translate the cert buffer (server reads it). */ + rc = wh_Client_DmaAsyncPre(c, &c->dma.asyncCtx.buf, (uintptr_t)cert, + cert_len, WH_DMA_OPER_CLIENT_READ_PRE, &certAddr); + if (rc == WH_ERROR_OK) { + req.id = id; + req.access = access; + req.flags = flags; + req.cert_addr = (uint64_t)certAddr; + req.cert_len = cert_len; + if (label != NULL && label_len > 0) { + whNvmSize copy_len = + (label_len > WH_NVM_LABEL_LEN) ? WH_NVM_LABEL_LEN : label_len; + memcpy(req.label, label, copy_len); + } + rc = wh_Client_SendRequest(c, WH_MESSAGE_GROUP_CERT, + WH_MESSAGE_CERT_ACTION_ADDTRUSTED_DMA, + sizeof(req), &req); } - return wh_Client_SendRequest(c, WH_MESSAGE_GROUP_CERT, - WH_MESSAGE_CERT_ACTION_ADDTRUSTED_DMA, - sizeof(req), &req); + + /* PRE or send failed: release the mapping (no-op if unset). */ + if (rc != WH_ERROR_OK) { + (void)wh_Client_DmaAsyncPost(c, &c->dma.asyncCtx.buf); + } + return rc; } int wh_Client_CertAddTrustedDmaResponse(whClientContext* c, int32_t* out_rc) @@ -848,6 +863,10 @@ int wh_Client_CertAddTrustedDmaResponse(whClientContext* c, int32_t* out_rc) /* Receive and validate response */ rc = wh_Client_RecvResponse(c, &group, &action, &size, &resp); + /* Not ready yet: keep the mapping; POST runs when the response arrives. */ + if (rc == WH_ERROR_NOTREADY) { + return rc; + } if (rc == 0) { if ((group != WH_MESSAGE_GROUP_CERT) || (action != WH_MESSAGE_CERT_ACTION_ADDTRUSTED_DMA) || @@ -861,6 +880,9 @@ int wh_Client_CertAddTrustedDmaResponse(whClientContext* c, int32_t* out_rc) } } + /* Release the mapping; the server already read the cert, so a POST (free) + * failure can't invalidate the result -- discard it (as other READ *Dma). */ + (void)wh_Client_DmaAsyncPost(c, &c->dma.asyncCtx.buf); return rc; } @@ -893,19 +915,36 @@ int wh_Client_CertAddTrustedDma(whClientContext* c, whNvmId id, int wh_Client_CertReadTrustedDmaRequest(whClientContext* c, whNvmId id, void* cert, uint32_t cert_len) { - whMessageCert_ReadTrustedDmaRequest req = {0}; + whMessageCert_ReadTrustedDmaRequest req = {0}; + uintptr_t certAddr = 0; + int rc = WH_ERROR_OK; if (c == NULL) { return WH_ERROR_BADARGS; } + /* Fail fast if busy (a rejected send would leak the mapping). */ + if (wh_CommClient_IsRequestPending(c->comm) == 1) { + return WH_ERROR_REQUEST_PENDING; + } - /* Prepare and send request */ - req.id = id; - req.cert_addr = (uint64_t)(uintptr_t)cert; - req.cert_len = cert_len; - return wh_Client_SendRequest(c, WH_MESSAGE_GROUP_CERT, - WH_MESSAGE_CERT_ACTION_READTRUSTED_DMA, - sizeof(req), &req); + /* Translate the output buffer (server writes the cert into it). */ + rc = wh_Client_DmaAsyncPre(c, &c->dma.asyncCtx.buf, (uintptr_t)cert, + cert_len, WH_DMA_OPER_CLIENT_WRITE_PRE, + &certAddr); + if (rc == WH_ERROR_OK) { + req.id = id; + req.cert_addr = (uint64_t)certAddr; + req.cert_len = cert_len; + rc = wh_Client_SendRequest(c, WH_MESSAGE_GROUP_CERT, + WH_MESSAGE_CERT_ACTION_READTRUSTED_DMA, + sizeof(req), &req); + } + + /* PRE or send failed: release the mapping (no-op if unset). */ + if (rc != WH_ERROR_OK) { + (void)wh_Client_DmaAsyncPost(c, &c->dma.asyncCtx.buf); + } + return rc; } int wh_Client_CertReadTrustedDmaResponse(whClientContext* c, int32_t* out_rc) @@ -922,6 +961,10 @@ int wh_Client_CertReadTrustedDmaResponse(whClientContext* c, int32_t* out_rc) /* Receive and validate response */ rc = wh_Client_RecvResponse(c, &group, &action, &size, &resp); + /* Not ready yet: keep the mapping; POST runs when the response arrives. */ + if (rc == WH_ERROR_NOTREADY) { + return rc; + } if (rc == 0) { if ((group != WH_MESSAGE_GROUP_CERT) || (action != WH_MESSAGE_CERT_ACTION_READTRUSTED_DMA) || @@ -935,6 +978,15 @@ int wh_Client_CertReadTrustedDmaResponse(whClientContext* c, int32_t* out_rc) } } + /* WRITE-back: copy the cert into the caller's buffer and release the + * mapping. As with the other output *Dma APIs, on a failed read the buffer + * is left undefined. Surface a POST failure over an OK result. */ + { + int postRc = wh_Client_DmaAsyncPost(c, &c->dma.asyncCtx.buf); + if (rc == WH_ERROR_OK) { + rc = postRc; + } + } return rc; } @@ -965,22 +1017,38 @@ static int _certVerifyDmaRequest(whClientContext* c, const void* cert, uint16_t flags, whNvmFlags cachedKeyFlags, whKeyId keyId) { - whMessageCert_VerifyDmaRequest req = {0}; + whMessageCert_VerifyDmaRequest req = {0}; + uintptr_t certAddr = 0; + int rc = WH_ERROR_OK; if (c == NULL) { return WH_ERROR_BADARGS; } + /* Fail fast if busy (a rejected send would leak the mapping). */ + if (wh_CommClient_IsRequestPending(c->comm) == 1) { + return WH_ERROR_REQUEST_PENDING; + } - /* Prepare and send request */ - req.cert_addr = (uint64_t)(uintptr_t)cert; - req.cert_len = cert_len; - req.trustedRootNvmId = trustedRootNvmId; - req.flags = flags; - req.cachedKeyFlags = cachedKeyFlags; - req.keyId = keyId; - return wh_Client_SendRequest(c, WH_MESSAGE_GROUP_CERT, - WH_MESSAGE_CERT_ACTION_VERIFY_DMA, sizeof(req), - &req); + /* Translate the cert buffer (server reads it). */ + rc = wh_Client_DmaAsyncPre(c, &c->dma.asyncCtx.buf, (uintptr_t)cert, + cert_len, WH_DMA_OPER_CLIENT_READ_PRE, &certAddr); + if (rc == WH_ERROR_OK) { + req.cert_addr = (uint64_t)certAddr; + req.cert_len = cert_len; + req.trustedRootNvmId = trustedRootNvmId; + req.flags = flags; + req.cachedKeyFlags = cachedKeyFlags; + req.keyId = keyId; + rc = wh_Client_SendRequest(c, WH_MESSAGE_GROUP_CERT, + WH_MESSAGE_CERT_ACTION_VERIFY_DMA, + sizeof(req), &req); + } + + /* PRE or send failed: release the mapping (no-op if unset). */ + if (rc != WH_ERROR_OK) { + (void)wh_Client_DmaAsyncPost(c, &c->dma.asyncCtx.buf); + } + return rc; } static int _certVerifyDmaResponse(whClientContext* c, whKeyId* out_keyId, @@ -998,6 +1066,10 @@ static int _certVerifyDmaResponse(whClientContext* c, whKeyId* out_keyId, /* Receive and validate response */ rc = wh_Client_RecvResponse(c, &group, &action, &size, &resp); + /* Not ready yet: keep the mapping; POST runs when the response arrives. */ + if (rc == WH_ERROR_NOTREADY) { + return rc; + } if (rc == 0) { if ((group != WH_MESSAGE_GROUP_CERT) || (action != WH_MESSAGE_CERT_ACTION_VERIFY_DMA) || @@ -1014,6 +1086,9 @@ static int _certVerifyDmaResponse(whClientContext* c, whKeyId* out_keyId, } } + /* Release the mapping; the server already read the cert, so a POST (free) + * failure can't invalidate the result -- discard it (as other READ *Dma). */ + (void)wh_Client_DmaAsyncPost(c, &c->dma.asyncCtx.buf); return rc; } @@ -1104,27 +1179,45 @@ static int _certVerifyMultiRootDmaRequest( const whNvmId* trustedRootNvmIds, uint16_t numRoots, uint16_t verifyFlags, whNvmFlags cachedKeyFlags, whKeyId keyId) { - whMessageCert_VerifyMultiRootDmaRequest req = {0}; + whMessageCert_VerifyMultiRootDmaRequest req = {0}; + uintptr_t certAddr = 0; + int rc = WH_ERROR_OK; if ((c == NULL) || (trustedRootNvmIds == NULL) || (numRoots == 0) || (numRoots > WOLFHSM_CFG_CERT_MAX_VERIFY_ROOTS)) { return WH_ERROR_BADARGS; } + /* Fail fast if busy (a rejected send would leak the mapping). */ + if (wh_CommClient_IsRequestPending(c->comm) == 1) { + return WH_ERROR_REQUEST_PENDING; + } - req.cert_addr = (uint64_t)(uintptr_t)cert; - req.cert_len = cert_len; - req.numRoots = numRoots; - req.flags = verifyFlags; - req.cachedKeyFlags = cachedKeyFlags; - req.keyId = keyId; - /* Only the first numRoots entries are meaningful; remaining slots stay - * zeroed by the initializer above. */ - memcpy(req.trustedRootNvmIds, trustedRootNvmIds, - (size_t)numRoots * sizeof(whNvmId)); - - return wh_Client_SendRequest(c, WH_MESSAGE_GROUP_CERT, - WH_MESSAGE_CERT_ACTION_VERIFY_MULTI_ROOT_DMA, - sizeof(req), &req); + /* Translate the candidate cert buffer (server reads it; roots are NVM + * IDs, not DMA). */ + rc = wh_Client_DmaAsyncPre(c, &c->dma.asyncCtx.buf, (uintptr_t)cert, + cert_len, WH_DMA_OPER_CLIENT_READ_PRE, &certAddr); + if (rc == WH_ERROR_OK) { + req.cert_addr = (uint64_t)certAddr; + req.cert_len = cert_len; + req.numRoots = numRoots; + req.flags = verifyFlags; + req.cachedKeyFlags = cachedKeyFlags; + req.keyId = keyId; + /* Only the first numRoots entries are meaningful; remaining slots stay + * zeroed by the initializer above. */ + memcpy(req.trustedRootNvmIds, trustedRootNvmIds, + (size_t)numRoots * sizeof(whNvmId)); + + rc = wh_Client_SendRequest( + c, WH_MESSAGE_GROUP_CERT, + WH_MESSAGE_CERT_ACTION_VERIFY_MULTI_ROOT_DMA, sizeof(req), &req); + } + + /* PRE or send failed: release the mapping (no-op if unset). */ + if (rc != WH_ERROR_OK) { + (void)wh_Client_DmaAsyncPost(c, &c->dma.asyncCtx.buf); + } + return rc; } /* Helper: receive a multi-root DMA verify response */ @@ -1142,6 +1235,10 @@ static int _certVerifyMultiRootDmaResponse(whClientContext* c, } rc = wh_Client_RecvResponse(c, &group, &action, &size, &resp); + /* Not ready yet: keep the mapping; POST runs when the response arrives. */ + if (rc == WH_ERROR_NOTREADY) { + return rc; + } if (rc == 0) { if ((group != WH_MESSAGE_GROUP_CERT) || (action != WH_MESSAGE_CERT_ACTION_VERIFY_MULTI_ROOT_DMA) || @@ -1158,6 +1255,9 @@ static int _certVerifyMultiRootDmaResponse(whClientContext* c, } } + /* Release the mapping; the server already read the cert, so a POST (free) + * failure can't invalidate the result -- discard it (as other READ *Dma). */ + (void)wh_Client_DmaAsyncPost(c, &c->dma.asyncCtx.buf); return rc; } @@ -1334,18 +1434,35 @@ int wh_Client_CertVerifyAcertDmaRequest(whClientContext* c, const void* cert, uint32_t cert_len, whNvmId trustedRootNvmId) { - whMessageCert_VerifyDmaRequest req = {0}; + whMessageCert_VerifyDmaRequest req = {0}; + uintptr_t certAddr = 0; + int rc = WH_ERROR_OK; if (c == NULL) { return WH_ERROR_BADARGS; } + /* Fail fast if busy (a rejected send would leak the mapping). */ + if (wh_CommClient_IsRequestPending(c->comm) == 1) { + return WH_ERROR_REQUEST_PENDING; + } - req.cert_addr = (uint64_t)(intptr_t)cert; - req.cert_len = cert_len; - req.trustedRootNvmId = trustedRootNvmId; - return wh_Client_SendRequest(c, WH_MESSAGE_GROUP_CERT, - WH_MESSAGE_CERT_ACTION_VERIFY_ACERT_DMA, - sizeof(req), &req); + /* Translate the acert buffer (server reads it). */ + rc = wh_Client_DmaAsyncPre(c, &c->dma.asyncCtx.buf, (uintptr_t)cert, + cert_len, WH_DMA_OPER_CLIENT_READ_PRE, &certAddr); + if (rc == WH_ERROR_OK) { + req.cert_addr = (uint64_t)certAddr; + req.cert_len = cert_len; + req.trustedRootNvmId = trustedRootNvmId; + rc = wh_Client_SendRequest(c, WH_MESSAGE_GROUP_CERT, + WH_MESSAGE_CERT_ACTION_VERIFY_ACERT_DMA, + sizeof(req), &req); + } + + /* PRE or send failed: release the mapping (no-op if unset). */ + if (rc != WH_ERROR_OK) { + (void)wh_Client_DmaAsyncPost(c, &c->dma.asyncCtx.buf); + } + return rc; } int wh_Client_CertVerifyAcertDmaResponse(whClientContext* c, int32_t* out_rc) @@ -1361,6 +1478,10 @@ int wh_Client_CertVerifyAcertDmaResponse(whClientContext* c, int32_t* out_rc) } rc = wh_Client_RecvResponse(c, &group, &action, &size, &resp); + /* Not ready yet: keep the mapping; POST runs when the response arrives. */ + if (rc == WH_ERROR_NOTREADY) { + return rc; + } if (rc == 0) { if ((group != WH_MESSAGE_GROUP_CERT) || (action != WH_MESSAGE_CERT_ACTION_VERIFY_ACERT_DMA) || @@ -1374,6 +1495,9 @@ int wh_Client_CertVerifyAcertDmaResponse(whClientContext* c, int32_t* out_rc) } } + /* Release the mapping; the server already read the cert, so a POST (free) + * failure can't invalidate the result -- discard it (as other READ *Dma). */ + (void)wh_Client_DmaAsyncPost(c, &c->dma.asyncCtx.buf); return rc; } diff --git a/test/wh_test_clientserver.c b/test/wh_test_clientserver.c index baacc9f58..56caebc70 100644 --- a/test/wh_test_clientserver.c +++ b/test/wh_test_clientserver.c @@ -1859,6 +1859,7 @@ static void _whClientServerThreadTest(whClientConfig* c_conf, static int wh_ClientServer_MemThreadTest(whTestNvmBackendType nvmType) { + int ret = WH_ERROR_OK; uint8_t req[BUFFER_SIZE] = {0}; uint8_t resp[BUFFER_SIZE] = {0}; @@ -1877,8 +1878,18 @@ static int wh_ClientServer_MemThreadTest(whTestNvmBackendType nvmType) .transport_config = (void*)tmcf, .client_id = WH_TEST_DEFAULT_CLIENT_ID, }}; +#ifdef WOLFHSM_CFG_DMA + /* Route every *Dma op (NVM + cert) through the bounce-pool callback so a + * missing translation is rejected (see test/wh_test_dma.c). */ + whClientDmaConfig clientDmaConfig = { + .cb = whTestDma_BounceClientCb, + }; +#endif whClientConfig c_conf[1] = {{ .comm = cc_conf, +#ifdef WOLFHSM_CFG_DMA + .dmaConfig = &clientDmaConfig, +#endif }}; /* Server configuration/contexts */ whTransportServerCb tscb[1] = {WH_TRANSPORT_MEM_SERVER_CB}; @@ -1889,6 +1900,12 @@ static int wh_ClientServer_MemThreadTest(whTestNvmBackendType nvmType) .transport_config = (void*)tmcf, .server_id = 124, }}; +#ifdef WOLFHSM_CFG_DMA + /* Server rejects any untranslated client pointer (out of the pool). */ + whServerDmaConfig serverDmaConfig = { + .cb = whTestDma_BounceServerCb, + }; +#endif /* RamSim Flash state and configuration */ uint8_t memory[FLASH_RAM_SIZE] = {0}; @@ -1921,17 +1938,40 @@ static int wh_ClientServer_MemThreadTest(whTestNvmBackendType nvmType) #ifndef WOLFHSM_CFG_NO_CRYPTO .crypto = crypto, .devId = INVALID_DEVID, +#endif +#ifdef WOLFHSM_CFG_DMA + .dmaConfig = &serverDmaConfig, #endif }}; WH_TEST_RETURN_ON_FAIL(wh_Nvm_Init(nvm, n_conf)); +#ifdef WOLFHSM_CFG_DMA + whTestDma_BounceReset(); +#endif + #ifndef WOLFHSM_CFG_NO_CRYPTO WH_TEST_RETURN_ON_FAIL(wolfCrypt_Init()); WH_TEST_RETURN_ON_FAIL(wc_InitRng_ex(crypto->rng, NULL, INVALID_DEVID)); #endif _whClientServerThreadTest(c_conf, s_conf); +#ifdef WOLFHSM_CFG_DMA + /* No mapping may be outstanding and no POST may have hit a stale slot. */ + if (whTestDma_BounceOutstanding() != 0) { + WH_ERROR_PRINT("wh_test bounce: %d DMA mapping(s) leaked across the " + "clientserver suite\n", + whTestDma_BounceOutstanding()); + ret = WH_ERROR_ABORTED; + } + if (whTestDma_BounceStrayPosts() != 0) { + WH_ERROR_PRINT("wh_test bounce: %d stray/double DMA POST(s) across the " + "clientserver suite\n", + whTestDma_BounceStrayPosts()); + ret = WH_ERROR_ABORTED; + } +#endif + wh_Nvm_Cleanup(nvm); #ifndef WOLFHSM_CFG_NO_CRYPTO @@ -1939,12 +1979,13 @@ static int wh_ClientServer_MemThreadTest(whTestNvmBackendType nvmType) wolfCrypt_Cleanup(); #endif - return WH_ERROR_OK; + return ret; } static int wh_ClientServer_PosixMemMapThreadTest(whTestNvmBackendType nvmType) { + int ret = WH_ERROR_OK; posixTransportShmConfig tmcf[1] = {{ .name = "/wh_test_clientserver_shm", .req_size = BUFFER_SIZE, @@ -1960,8 +2001,18 @@ static int wh_ClientServer_PosixMemMapThreadTest(whTestNvmBackendType nvmType) .transport_config = (void*)tmcf, .client_id = WH_TEST_DEFAULT_CLIENT_ID, }}; +#ifdef WOLFHSM_CFG_DMA + /* Route every *Dma op (NVM + cert) through the bounce-pool callback so a + * missing translation is rejected (see test/wh_test_dma.c). */ + whClientDmaConfig clientDmaConfig = { + .cb = whTestDma_BounceClientCb, + }; +#endif whClientConfig c_conf[1] = {{ .comm = cc_conf, +#ifdef WOLFHSM_CFG_DMA + .dmaConfig = &clientDmaConfig, +#endif }}; /* Server configuration/contexts */ whTransportServerCb tscb[1] = {POSIX_TRANSPORT_SHM_SERVER_CB}; @@ -1972,6 +2023,12 @@ static int wh_ClientServer_PosixMemMapThreadTest(whTestNvmBackendType nvmType) .transport_config = (void*)tmcf, .server_id = 124, }}; +#ifdef WOLFHSM_CFG_DMA + /* Server rejects any untranslated client pointer (out of the pool). */ + whServerDmaConfig serverDmaConfig = { + .cb = whTestDma_BounceServerCb, + }; +#endif /* RamSim Flash state and configuration */ uint8_t memory[FLASH_RAM_SIZE] = {0}; @@ -2002,6 +2059,9 @@ static int wh_ClientServer_PosixMemMapThreadTest(whTestNvmBackendType nvmType) .nvm = nvm, #ifndef WOLFHSM_CFG_NO_CRYPTO .crypto = crypto, +#endif +#ifdef WOLFHSM_CFG_DMA + .dmaConfig = &serverDmaConfig, #endif }}; #ifdef WOLFHSM_CFG_ENABLE_AUTHENTICATION @@ -2011,12 +2071,32 @@ static int wh_ClientServer_PosixMemMapThreadTest(whTestNvmBackendType nvmType) WH_TEST_RETURN_ON_FAIL(wh_Nvm_Init(nvm, n_conf)); +#ifdef WOLFHSM_CFG_DMA + whTestDma_BounceReset(); +#endif + #ifndef WOLFHSM_CFG_NO_CRYPTO WH_TEST_RETURN_ON_FAIL(wolfCrypt_Init()); WH_TEST_RETURN_ON_FAIL(wc_InitRng_ex(crypto->rng, NULL, INVALID_DEVID)); #endif _whClientServerThreadTest(c_conf, s_conf); +#ifdef WOLFHSM_CFG_DMA + /* No mapping may be outstanding and no POST may have hit a stale slot. */ + if (whTestDma_BounceOutstanding() != 0) { + WH_ERROR_PRINT("wh_test bounce: %d DMA mapping(s) leaked across the " + "clientserver suite\n", + whTestDma_BounceOutstanding()); + ret = WH_ERROR_ABORTED; + } + if (whTestDma_BounceStrayPosts() != 0) { + WH_ERROR_PRINT("wh_test bounce: %d stray/double DMA POST(s) across the " + "clientserver suite\n", + whTestDma_BounceStrayPosts()); + ret = WH_ERROR_ABORTED; + } +#endif + wh_Nvm_Cleanup(nvm); #ifndef WOLFHSM_CFG_NO_CRYPTO @@ -2024,7 +2104,7 @@ static int wh_ClientServer_PosixMemMapThreadTest(whTestNvmBackendType nvmType) wolfCrypt_Cleanup(); #endif - return WH_ERROR_OK; + return ret; } #endif /* WOLFHSM_CFG_TEST_POSIX && WOLFHSM_CFG_ENABLE_CLIENT && \ WOLFHSM_CFG_ENABLE_SERVER */ diff --git a/wolfhsm/wh_client.h b/wolfhsm/wh_client.h index 37284e1f1..568d5a033 100644 --- a/wolfhsm/wh_client.h +++ b/wolfhsm/wh_client.h @@ -2908,6 +2908,18 @@ int wh_Client_CertVerifyCacheSetEnabled(whClientContext* c, uint8_t enable, #ifdef WOLFHSM_CFG_DMA +/* + * Certificate DMA API notes (apply to every wh_Client_Cert*Dma* below): + * + * - Each *DmaRequest translates the cert buffer and the matching *DmaResponse + * releases it, using the single shared per-client DMA slot (see + * whClientDmaAsyncCtx): only ONE *Dma operation (cert/key/NVM) may be in + * flight at a time. Pair the split Request/Response one-at-a-time; issuing a + * second Request first overwrites the slot and leaks the earlier mapping. + * - A *DmaRequest (hence a blocking *Dma call) may return + * WH_ERROR_REQUEST_PENDING if a prior request has not been consumed. + */ + /** * @brief Sends a request to add a trusted certificate to NVM storage using DMA. *