From 4f383fbad1b860e3a5ce12676ab0a53653a00703 Mon Sep 17 00:00:00 2001 From: Brett Nicholas <7547222+bigbrett@users.noreply.github.com> Date: Thu, 19 Mar 2026 10:57:53 -0600 Subject: [PATCH 1/8] Modify cert layer to support wrapped/cached certs --- docs/draft/wrapped-certs.md | 290 ++++++++++++++++++++++++++++++++++++ src/wh_client_cert.c | 164 ++++++++++++++++++++ src/wh_server_cert.c | 160 ++++++++++++++------ test/wh_test_cert.c | 238 +++++++++++++++++++++++++++++ wolfhsm/wh_client.h | 195 ++++++++++++++++++++++++ wolfhsm/wh_server_cert.h | 22 ++- 6 files changed, 1017 insertions(+), 52 deletions(-) create mode 100644 docs/draft/wrapped-certs.md diff --git a/docs/draft/wrapped-certs.md b/docs/draft/wrapped-certs.md new file mode 100644 index 000000000..b9abf6794 --- /dev/null +++ b/docs/draft/wrapped-certs.md @@ -0,0 +1,290 @@ +# Wrapped Cached Certificates + +## Overview + +This documents the "wrapped certificate" use case, showing how to leverage the +certificate manager to use trusted root certificates that live in the server's +**keystore cache** (RAM) after being unwrapped via keywrap funcitonality, +rather than exclusively in **NVM** (flash). A root certificate is wrapped +(AES-GCM encrypted) by the server, handed back to the client as an opaque blob, +and later unwrapped into the server's key cache on demand. Once cached, it can +be used in all certificate verification paths — standard, DMA, and ACERT — +exactly like an NVM-resident root certificate. + +This is useful when a client needs to use a trusted root for verification but +does not want to (or cannot) commit it to NVM. The wrapped blob can be stored +cheaply on the client side, while the server only holds the unwrapped plaintext +in its volatile cache for as long as it is needed. + +## High-Level Usage + +The lifecycle has three stages: **wrap**, **unwrap-and-cache**, and **use**. + +### 1. Provision a wrapping key (KEK) + +Before wrapping anything the server needs an AES-256 key to use as the +key-encryption key. Cache it on the server with the `WH_NVM_FLAGS_USAGE_WRAP` +flag: + +```c +whKeyId kekId = 10; +uint8_t kek[32] = { /* 256-bit AES key */ }; + +wh_Client_KeyCache(client, + WH_NVM_FLAGS_USAGE_WRAP, NULL, 0, + kek, sizeof(kek), &kekId); +``` + +The KEK is now sitting in the server's `localCache` (or `globalCache` if marked +global), indexed by `kekId`. + +### 2. Wrap the certificate + +Call `wh_Client_CertWrap` with the raw certificate DER and the KEK's ID. The +server encrypts the certificate using AES-GCM and returns the wrapped blob: + +```c +uint8_t wrappedCert[2048]; +uint16_t wrappedCertSz = sizeof(wrappedCert); + +/* Build metadata: id embeds TYPE=WRAPPED and the client's USER id; + * caller controls flags, access, and optionally label */ +whNvmMetadata certMeta = {0}; +certMeta.id = WH_CLIENT_KEYID_MAKE_WRAPPED_META( + client->comm->client_id, 5); +certMeta.flags = WH_NVM_FLAGS_USAGE_ANY; +certMeta.access = WH_NVM_ACCESS_ANY; + +wh_Client_CertWrap(client, WC_CIPHER_AES_GCM, kekId, + rootCaCert, rootCaCertLen, + &certMeta, + wrappedCert, &wrappedCertSz); +``` + +After this call: + +| Data | Location | +|---|---| +| KEK | Server key cache (`localCache[kekId]`) | +| Wrapped cert blob (ciphertext + GCM tag + IV + metadata) | Client memory (`wrappedCert` buffer) | +| Raw certificate | Nowhere on the server — only the client supplied it transiently | + +The client can now persist `wrappedCert` to its own storage (file, flash, +external memory, etc.). + +### 3. Unwrap and cache the certificate on the server + +When the client needs the root for verification, it pushes the wrapped blob back +to the server: + +```c +whKeyId cachedCertId = WH_KEYID_ERASED; + +wh_Client_CertUnwrapAndCache(client, WC_CIPHER_AES_GCM, kekId, + wrappedCert, wrappedCertSz, + &cachedCertId); +``` + +The server decrypts the blob using the KEK, verifies the GCM authentication +tag, and places the plaintext certificate into its key cache. The returned +`cachedCertId` is the server-internal key ID (with `TYPE=WH_KEYTYPE_WRAPPED` +already encoded). + +After this call: + +| Data | Location | +|---|---| +| KEK | Server key cache | +| Plaintext certificate | Server key cache (`localCache[cachedCertId]`) | +| Wrapped cert blob | Still in client memory (unchanged) | + +### 4. Use the cached cert for verification + +Pass the cached cert's ID — decorated with the wrapped flag — as the trusted +root to any verify API: + +```c +int32_t verifyResult; + +wh_Client_CertVerify(client, + intermediateCert, intermediateCertLen, + WH_CLIENT_KEYID_MAKE_WRAPPED(cachedCertId), + &verifyResult); +``` + +`WH_CLIENT_KEYID_MAKE_WRAPPED(cachedCertId)` sets bit 9 +(`WH_KEYID_CLIENT_WRAPPED_FLAG = 0x0200`) on the ID the client sends to the +server. This is the signal that tells the server "this root cert is in the +cache, not in NVM." + +The same pattern works for: +- `wh_Client_CertVerifyDma` (DMA path) +- `wh_Client_CertReadTrusted` / `wh_Client_CertReadTrustedDma` (read-back) +- `wh_Client_CertVerifyAcert` / `wh_Client_CertVerifyAcertDma` (attribute certs) + +### 5. Cleanup + +Evict the cached cert and KEK when done: + +```c +wh_Client_KeyEvict(client, WH_CLIENT_KEYID_MAKE_WRAPPED(cachedCertId)); +wh_Client_KeyEvict(client, kekId); +``` + +## Low-Level Implementation Details + +### Client-side functions + +Nine thin wrappers in `src/wh_client_cert.c` (guarded by +`WOLFHSM_CFG_KEYWRAP`), mirroring the Key wrap/unwrap API: + +- **`wh_Client_CertWrap`** / **`wh_Client_CertWrapRequest`** / + **`wh_Client_CertWrapResponse`** — Wrap a certificate. Accepts a + caller-provided `whNvmMetadata*` (with `id`, `flags`, `access`, and + optionally `label` set by the caller), sets `meta->len = certSz`, then + delegates to the corresponding `wh_Client_KeyWrap*` function. The metadata's + `id` field must have `TYPE=WH_KEYTYPE_WRAPPED` encoded via + `WH_CLIENT_KEYID_MAKE_WRAPPED_META`. + +- **`wh_Client_CertUnwrapAndExport`** / **`wh_Client_CertUnwrapAndExportRequest`** / + **`wh_Client_CertUnwrapAndExportResponse`** — Unwrap a wrapped certificate + and export both the plaintext certificate and its metadata back to the client. + Delegates to the corresponding `wh_Client_KeyUnwrapAndExport*` function. + +- **`wh_Client_CertUnwrapAndCache`** / **`wh_Client_CertUnwrapAndCacheRequest`** / + **`wh_Client_CertUnwrapAndCacheResponse`** — Unwrap and cache on the server. + Delegates to the corresponding `wh_Client_KeyUnwrapAndCache*` function. + Returns the server-assigned cache slot ID in `*out_certId`. + +All functions accept an `enum wc_CipherType cipherType` parameter (e.g. +`WC_CIPHER_AES_GCM`) to specify the wrapping cipher. The blocking variants +call their respective Request/Response functions in a do-while-NOTREADY loop. + +These are pure convenience; a caller could use `wh_Client_KeyWrap*` / +`wh_Client_KeyUnwrapAndExport*` / `wh_Client_KeyUnwrapAndCache*` directly if +it needed custom metadata. + +### Server-side routing (the key change) + +#### `wh_Server_CertReadTrusted` (`src/wh_server_cert.c`) + +Previously accepted only `whNvmId` and always read from NVM. Now accepts +`whKeyId` and branches on the TYPE field: + +``` +if WH_KEYID_TYPE(id) == WH_KEYTYPE_WRAPPED + → wh_Server_KeystoreReadKey(server, id, &meta, cert, &sz) // cache path +else + → wh_Nvm_GetMetadata / wh_Nvm_Read // NVM path (unchanged) +``` + +`wh_Server_KeystoreReadKey` looks up the key in the server's `localCache` (or +`globalCache` if global keys are enabled and the USER field is 0). It copies +both the metadata and the raw data into the caller's buffers. + +#### `wh_Server_CertVerify` / `wh_Server_CertVerifyAcert` + +Signature changed from `whNvmId trustedRootNvmId` to `whKeyId trustedRootId`. +Internally they just call `wh_Server_CertReadTrusted`, which now handles the +routing. + +#### Request handlers in `wh_Server_HandleCertRequest` + +Every handler that accepts a trusted root ID (`READTRUSTED`, `VERIFY`, +`READTRUSTED_DMA`, `VERIFY_DMA`, `VERIFY_ACERT`, `VERIFY_ACERT_DMA`) was +updated with the same pattern: + +1. **Translate the client ID**: If the incoming `req.id` (or + `req.trustedRootNvmId`) has `WH_KEYID_CLIENT_WRAPPED_FLAG` set, call + `wh_KeyId_TranslateFromClient(WH_KEYTYPE_NVM, server->comm->client_id, req.id)` + to produce a full server-internal key ID with `TYPE=WH_KEYTYPE_WRAPPED`, + `USER=client_id`, and the bare key `ID` in the low byte. + +2. **Branch on key type** for the read/verify: + - **Cache path** (`WH_KEYID_TYPE(certId) == WH_KEYTYPE_WRAPPED`): Calls + `wh_Server_KeystoreReadKey` to fetch the cert from the cache. Checks + `WH_NVM_FLAGS_NONEXPORTABLE` on the metadata for read-back requests. + - **NVM path** (original, `WH_KEYID_TYPE != WH_KEYTYPE_WRAPPED`): Unchanged + behavior — reads from flash via `wh_Nvm_GetMetadata` / `wh_Nvm_Read`. + +### Key ID encoding walkthrough + +Consider a client with `client_id = 1` wrapping a cert with bare ID `5`: + +| Stage | Value | Encoding | +|---|---|---| +| `WH_CLIENT_KEYID_MAKE_WRAPPED_META(1, 5)` | `0x4105` | TYPE=4 (WRAPPED), USER=1, ID=5 — stored *inside* the wrapped blob metadata | +| Server returns `cachedCertId` after unwrap | `0x4105` | Same — the server preserved the metadata ID | +| Client sends `WH_CLIENT_KEYID_MAKE_WRAPPED(0x4105)` | `0x4305` | Bit 9 (0x0200) set as client flag | +| Server calls `wh_KeyId_TranslateFromClient(...)` | `0x4105` | Flag stripped, TYPE=WRAPPED confirmed, USER=1, ID=5 | +| `WH_KEYID_TYPE(0x4105)` | `4` | Equals `WH_KEYTYPE_WRAPPED` (4) → routes to cache | + +### Data stored at each point + +| Point in flow | Server key cache | Server NVM | Client memory | +|---|---|---|---| +| After `KeyCache` (KEK) | KEK at `kekId` | — | — | +| After `CertWrap` | KEK at `kekId` | — | Wrapped blob (ciphertext + tag + IV + metadata) | +| After `CertUnwrapAndCache` | KEK at `kekId`, plaintext cert at `cachedCertId` | — | Wrapped blob (unchanged) | +| During `CertVerify` | KEK, plaintext cert (read into stack buffer `root_cert[WOLFHSM_CFG_MAX_CERT_SIZE]` by `CertReadTrusted`) | — | — | +| After `KeyEvict` (cert) | KEK at `kekId` | — | Wrapped blob | +| After `KeyEvict` (KEK) | — | — | Wrapped blob | + +## Interaction with Locking and Thread Safety + +### The NVM lock (`WH_SERVER_NVM_LOCK` / `WH_SERVER_NVM_UNLOCK`) + +When `WOLFHSM_CFG_THREADSAFE` is defined, `WH_SERVER_NVM_LOCK(server)` calls +`wh_Server_NvmLock(server)`, which acquires a mutex protecting NVM state. When +not threadsafe, the macros expand to `(WH_ERROR_OK)` (no-ops). + +The existing (pre-branch) code unconditionally called `WH_SERVER_NVM_LOCK` +around every cert read/verify handler, because the cert always came from NVM. + +### What changes for cached certs + +Cached certs do not touch NVM at all — they are read from the in-memory key +cache via `wh_Server_KeystoreReadKey`. However, the NVM lock is still +unconditionally acquired around both cache and NVM paths. This is conservative +but correct: the key cache (`localCache` / `globalCache`) does not have its own +lock, so the NVM lock serves as the coarse serialization mechanism for all +server-side storage operations (both NVM and cache) when +`WOLFHSM_CFG_THREADSAFE` is enabled. + +The pattern used in every updated handler is: + +```c +rc = WH_SERVER_NVM_LOCK(server); +if (rc == WH_ERROR_OK) { + if (req.id & WH_KEYID_CLIENT_WRAPPED_FLAG) { + /* Cache path: translate and read from keystore cache */ + whKeyId certId = wh_KeyId_TranslateFromClient( + WH_KEYTYPE_WRAPPED, server->comm->client_id, req.id); + rc = wh_Server_KeystoreReadKey(server, certId, &meta, cert_data, &cert_len); + /* ... exportability check for read-back requests ... */ + } else { + /* NVM path (unchanged) */ + rc = wh_Nvm_GetMetadata(server->nvm, req.id, &meta); + /* ... NVM reads ... */ + } + (void)WH_SERVER_NVM_UNLOCK(server); +} +``` + +Key points: + +- **Both paths hold the NVM lock**: The lock is always acquired before + branching. While the cache read itself doesn't strictly need NVM protection, + holding the lock ensures serialization with any concurrent operations that + access the `localCache` array on other threads. + +- **NVM path**: Unchanged — same behavior as before this branch. + +### Backward compatibility + +- All existing NVM-based certificate operations continue to work identically. + The routing branch only activates when the key type is `WH_KEYTYPE_WRAPPED`. +- The `wh_Server_CertReadTrusted` and `wh_Server_CertVerify` function + signatures changed from `whNvmId` to `whKeyId`. Since `whNvmId` and `whKeyId` + are both `uint16_t`, this is ABI-compatible. Any existing callers passing a + plain NVM ID (with TYPE=0) will hit the NVM path as before. diff --git a/src/wh_client_cert.c b/src/wh_client_cert.c index d233da5e8..d4168c0a7 100644 --- a/src/wh_client_cert.c +++ b/src/wh_client_cert.c @@ -958,6 +958,170 @@ int wh_Client_CertVerifyAcertDma(whClientContext* c, const void* cert, #endif /* WOLFHSM_CFG_CERTIFICATE_MANAGER_ACERT */ +#ifdef WOLFHSM_CFG_KEYWRAP + +int wh_Client_CertWrapRequest(whClientContext* ctx, + enum wc_CipherType cipherType, + whKeyId serverKeyId, const uint8_t* cert, + uint32_t certSz, whNvmMetadata* meta) +{ + if ((ctx == NULL) || (cert == NULL) || (certSz == 0) || (meta == NULL) || + (certSz > UINT16_MAX)) { + return WH_ERROR_BADARGS; + } + + meta->len = certSz; + + return wh_Client_KeyWrapRequest(ctx, cipherType, serverKeyId, (void*)cert, + (uint16_t)certSz, meta); +} + +int wh_Client_CertWrapResponse(whClientContext* ctx, + enum wc_CipherType cipherType, + uint8_t* wrappedOut, uint16_t* inout_wrappedSz) +{ + if ((ctx == NULL) || (wrappedOut == NULL) || (inout_wrappedSz == NULL)) { + return WH_ERROR_BADARGS; + } + + return wh_Client_KeyWrapResponse(ctx, cipherType, wrappedOut, + inout_wrappedSz); +} + +int wh_Client_CertWrap(whClientContext* ctx, enum wc_CipherType cipherType, + whKeyId serverKeyId, const uint8_t* cert, + uint32_t certSz, whNvmMetadata* meta, + uint8_t* wrappedOut, uint16_t* inout_wrappedSz) +{ + int rc; + + if ((ctx == NULL) || (cert == NULL) || (certSz == 0) || (meta == NULL) || + (wrappedOut == NULL) || (inout_wrappedSz == NULL) || + (certSz > UINT16_MAX)) { + return WH_ERROR_BADARGS; + } + + rc = wh_Client_CertWrapRequest(ctx, cipherType, serverKeyId, cert, certSz, + meta); + if (rc != WH_ERROR_OK) { + return rc; + } + + do { + rc = wh_Client_CertWrapResponse(ctx, cipherType, wrappedOut, + inout_wrappedSz); + } while (rc == WH_ERROR_NOTREADY); + + return rc; +} + +int wh_Client_CertUnwrapAndExportRequest(whClientContext* ctx, + enum wc_CipherType cipherType, + whKeyId serverKeyId, + const uint8_t* wrappedCert, + uint16_t wrappedCertSz) +{ + if ((ctx == NULL) || (wrappedCert == NULL) || (wrappedCertSz == 0)) { + return WH_ERROR_BADARGS; + } + + return wh_Client_KeyUnwrapAndExportRequest( + ctx, cipherType, serverKeyId, (void*)wrappedCert, wrappedCertSz); +} + +int wh_Client_CertUnwrapAndExportResponse(whClientContext* ctx, + enum wc_CipherType cipherType, + whNvmMetadata* metadataOut, + uint8_t* certOut, + uint16_t* inout_certSz) +{ + if ((ctx == NULL) || (certOut == NULL) || (inout_certSz == NULL)) { + return WH_ERROR_BADARGS; + } + + return wh_Client_KeyUnwrapAndExportResponse(ctx, cipherType, metadataOut, + certOut, inout_certSz); +} + +int wh_Client_CertUnwrapAndExport( + whClientContext* ctx, enum wc_CipherType cipherType, whKeyId serverKeyId, + const uint8_t* wrappedCert, uint16_t wrappedCertSz, + whNvmMetadata* metadataOut, uint8_t* certOut, uint16_t* inout_certSz) +{ + int rc; + + if ((ctx == NULL) || (wrappedCert == NULL) || (wrappedCertSz == 0) || + (certOut == NULL) || (inout_certSz == NULL)) { + return WH_ERROR_BADARGS; + } + + rc = wh_Client_CertUnwrapAndExportRequest(ctx, cipherType, serverKeyId, + wrappedCert, wrappedCertSz); + if (rc != WH_ERROR_OK) { + return rc; + } + + do { + rc = wh_Client_CertUnwrapAndExportResponse(ctx, cipherType, metadataOut, + certOut, inout_certSz); + } while (rc == WH_ERROR_NOTREADY); + + return rc; +} + +int wh_Client_CertUnwrapAndCacheRequest(whClientContext* ctx, + enum wc_CipherType cipherType, + whKeyId serverKeyId, + const uint8_t* wrappedCert, + uint16_t wrappedCertSz) +{ + if ((ctx == NULL) || (wrappedCert == NULL) || (wrappedCertSz == 0)) { + return WH_ERROR_BADARGS; + } + + return wh_Client_KeyUnwrapAndCacheRequest( + ctx, cipherType, serverKeyId, (void*)wrappedCert, wrappedCertSz); +} + +int wh_Client_CertUnwrapAndCacheResponse(whClientContext* ctx, + enum wc_CipherType cipherType, + whKeyId* out_certId) +{ + if ((ctx == NULL) || (out_certId == NULL)) { + return WH_ERROR_BADARGS; + } + + return wh_Client_KeyUnwrapAndCacheResponse(ctx, cipherType, out_certId); +} + +int wh_Client_CertUnwrapAndCache(whClientContext* ctx, + enum wc_CipherType cipherType, + whKeyId serverKeyId, + const uint8_t* wrappedCert, + uint16_t wrappedCertSz, whKeyId* out_certId) +{ + int rc; + + if ((ctx == NULL) || (wrappedCert == NULL) || (wrappedCertSz == 0) || + (out_certId == NULL)) { + return WH_ERROR_BADARGS; + } + + rc = wh_Client_CertUnwrapAndCacheRequest(ctx, cipherType, serverKeyId, + wrappedCert, wrappedCertSz); + if (rc != WH_ERROR_OK) { + return rc; + } + + do { + rc = wh_Client_CertUnwrapAndCacheResponse(ctx, cipherType, out_certId); + } while (rc == WH_ERROR_NOTREADY); + + return rc; +} + +#endif /* WOLFHSM_CFG_KEYWRAP */ + #endif /* WOLFHSM_CFG_CERTIFICATE_MANAGER && !WOLFHSM_CFG_NO_CRYPTO */ #endif /* WOLFHSM_CFG_ENABLE_CLIENT */ diff --git a/src/wh_server_cert.c b/src/wh_server_cert.c index 06d1a211e..8421f23bf 100644 --- a/src/wh_server_cert.c +++ b/src/wh_server_cert.c @@ -226,8 +226,8 @@ int wh_Server_CertEraseTrusted(whServerContext* server, whNvmId id) return rc; } -/* Get a trusted certificate from NVM storage */ -int wh_Server_CertReadTrusted(whServerContext* server, whNvmId id, +/* Get a trusted certificate from NVM storage or keystore cache */ +int wh_Server_CertReadTrusted(whServerContext* server, whKeyId id, uint8_t* cert, uint32_t* inout_cert_len) { int rc; @@ -238,6 +238,15 @@ int wh_Server_CertReadTrusted(whServerContext* server, whNvmId id, return WH_ERROR_BADARGS; } + /* Cache path: wrapped certs are read from keystore cache, not NVM */ + if (WH_KEYID_TYPE(id) == WH_KEYTYPE_WRAPPED) { + uint32_t sz = *inout_cert_len; + rc = wh_Server_KeystoreReadKey(server, id, &meta, cert, &sz); + if (rc == WH_ERROR_OK) { + *inout_cert_len = sz; + } + return rc; + } /* Get metadata to check the certificate size */ rc = wh_Nvm_GetMetadata(server->nvm, id, &meta); @@ -259,7 +268,7 @@ int wh_Server_CertReadTrusted(whServerContext* server, whNvmId id, /* Verify a certificate against trusted certificates */ int wh_Server_CertVerify(whServerContext* server, const uint8_t* cert, - uint32_t cert_len, whNvmId trustedRootNvmId, + uint32_t cert_len, whKeyId trustedRootId, whCertFlags flags, whNvmFlags cachedKeyFlags, whKeyId* inout_keyId) { @@ -286,8 +295,8 @@ int wh_Server_CertVerify(whServerContext* server, const uint8_t* cert, return WH_ERROR_ABORTED; } - /* Get the trusted root certificate */ - rc = wh_Server_CertReadTrusted(server, trustedRootNvmId, root_cert, + /* Get the trusted root certificate (from NVM or cache based on id type) */ + rc = wh_Server_CertReadTrusted(server, trustedRootId, root_cert, &root_cert_len); if (rc == WH_ERROR_OK) { /* Load the trusted root certificate */ @@ -314,7 +323,7 @@ int wh_Server_CertVerify(whServerContext* server, const uint8_t* cert, #if defined(WOLFHSM_CFG_CERTIFICATE_MANAGER_ACERT) int wh_Server_CertVerifyAcert(whServerContext* server, const uint8_t* cert, - uint32_t cert_len, whNvmId trustedRootNvmId) + uint32_t cert_len, whKeyId trustedRootId) { int rc; @@ -322,8 +331,8 @@ int wh_Server_CertVerifyAcert(whServerContext* server, const uint8_t* cert, uint8_t root_cert[WOLFHSM_CFG_MAX_CERT_SIZE]; uint32_t root_cert_len = sizeof(root_cert); - /* Load the trusted root certificate into the buffer */ - rc = wh_Server_CertReadTrusted(server, trustedRootNvmId, root_cert, + /* Load the trusted root certificate into the buffer (NVM or cache) */ + rc = wh_Server_CertReadTrusted(server, trustedRootId, root_cert, &root_cert_len); if (rc != WH_ERROR_OK) { return rc; @@ -473,21 +482,35 @@ int wh_Server_HandleCertRequest(whServerContext* server, uint16_t magic, ? max_transport_cert_len : WOLFHSM_CFG_MAX_CERT_SIZE; - /* Check metadata to check if the certificate is non-exportable. - * This is unfortunately redundant since metadata is checked in - * wh_Server_CertReadTrusted(). */ rc = WH_SERVER_NVM_LOCK(server); if (rc == WH_ERROR_OK) { - rc = wh_Nvm_GetMetadata(server->nvm, req.id, &meta); - if (rc == WH_ERROR_OK) { - /* Check if the certificate is non-exportable */ - if (meta.flags & WH_NVM_FLAGS_NONEXPORTABLE) { - rc = WH_ERROR_ACCESS; + if (req.id & WH_KEYID_CLIENT_WRAPPED_FLAG) { + /* Cache path: translate and read cert + metadata */ + whKeyId certId = wh_KeyId_TranslateFromClient( + WH_KEYTYPE_WRAPPED, server->comm->client_id, req.id); + rc = wh_Server_KeystoreReadKey(server, certId, &meta, + cert_data, &cert_len); + if (rc == WH_ERROR_OK) { + if (meta.flags & WH_NVM_FLAGS_NONEXPORTABLE) { + rc = WH_ERROR_ACCESS; + } + else { + resp.cert_len = cert_len; + } } - else { - rc = wh_Server_CertReadTrusted(server, req.id, - cert_data, &cert_len); - resp.cert_len = cert_len; + } + else { + /* NVM path */ + rc = wh_Nvm_GetMetadata(server->nvm, req.id, &meta); + if (rc == WH_ERROR_OK) { + if (meta.flags & WH_NVM_FLAGS_NONEXPORTABLE) { + rc = WH_ERROR_ACCESS; + } + else { + rc = wh_Server_CertReadTrusted( + server, req.id, cert_data, &cert_len); + resp.cert_len = cert_len; + } } } @@ -532,12 +555,19 @@ int wh_Server_HandleCertRequest(whServerContext* server, uint16_t magic, whKeyId keyId = wh_KeyId_TranslateFromClient( WH_KEYTYPE_CRYPTO, server->comm->client_id, req.keyId); + /* Translate trustedRootNvmId if client set wrapped flag */ + whKeyId trustedRootId = req.trustedRootNvmId; + if (req.trustedRootNvmId & WH_KEYID_CLIENT_WRAPPED_FLAG) { + trustedRootId = wh_KeyId_TranslateFromClient( + WH_KEYTYPE_WRAPPED, server->comm->client_id, + req.trustedRootNvmId); + } rc = WH_SERVER_NVM_LOCK(server); if (rc == WH_ERROR_OK) { /* Process the verify action */ rc = wh_Server_CertVerify(server, cert_data, req.cert_len, - req.trustedRootNvmId, req.flags, + trustedRootId, req.flags, req.cachedKeyFlags, &keyId); /* Propagate the keyId back to the client with flags * preserved */ @@ -622,19 +652,37 @@ int wh_Server_HandleCertRequest(whServerContext* server, uint16_t magic, WH_DMA_OPER_CLIENT_WRITE_PRE, (whServerDmaFlags){0}); } if (resp.rc == WH_ERROR_OK) { - /* Check metadata to see if the certificate is non-exportable */ resp.rc = WH_SERVER_NVM_LOCK(server); if (resp.rc == WH_ERROR_OK) { - resp.rc = wh_Nvm_GetMetadata(server->nvm, req.id, &meta); - if (resp.rc == WH_ERROR_OK) { - if ((meta.flags & WH_NVM_FLAGS_NONEXPORTABLE) != 0) { - resp.rc = WH_ERROR_ACCESS; + if (req.id & WH_KEYID_CLIENT_WRAPPED_FLAG) { + /* Cache path: translate and read cert + metadata */ + whKeyId certId = wh_KeyId_TranslateFromClient( + WH_KEYTYPE_WRAPPED, server->comm->client_id, + req.id); + cert_len = req.cert_len; + resp.rc = wh_Server_KeystoreReadKey( + server, certId, &meta, cert_data, &cert_len); + if (resp.rc == WH_ERROR_OK) { + if ((meta.flags & WH_NVM_FLAGS_NONEXPORTABLE) != + 0) { + resp.rc = WH_ERROR_ACCESS; + } } - else { - /* Clamp cert_len to actual stored length */ - cert_len = req.cert_len; - resp.rc = wh_Server_CertReadTrusted( - server, req.id, cert_data, &cert_len); + } + else { + /* NVM path */ + resp.rc = + wh_Nvm_GetMetadata(server->nvm, req.id, &meta); + if (resp.rc == WH_ERROR_OK) { + if ((meta.flags & WH_NVM_FLAGS_NONEXPORTABLE) != + 0) { + resp.rc = WH_ERROR_ACCESS; + } + else { + cert_len = req.cert_len; + resp.rc = wh_Server_CertReadTrusted( + server, req.id, cert_data, &cert_len); + } } } @@ -655,10 +703,11 @@ int wh_Server_HandleCertRequest(whServerContext* server, uint16_t magic, }; break; case WH_MESSAGE_CERT_ACTION_VERIFY_DMA: { - whMessageCert_VerifyDmaRequest req = {0}; - whMessageCert_VerifyDmaResponse resp = {0}; - void* cert_data = NULL; - whKeyId keyId = WH_KEYID_ERASED; + whMessageCert_VerifyDmaRequest req = {0}; + whMessageCert_VerifyDmaResponse resp = {0}; + void* cert_data = NULL; + whKeyId keyId = WH_KEYID_ERASED; + whKeyId trustedRootId = 0; if (req_size != sizeof(req)) { /* Request is malformed */ @@ -673,6 +722,14 @@ int wh_Server_HandleCertRequest(whServerContext* server, uint16_t magic, keyId = wh_KeyId_TranslateFromClient( WH_KEYTYPE_CRYPTO, server->comm->client_id, req.keyId); + /* Translate trustedRootNvmId if client set wrapped flag */ + trustedRootId = req.trustedRootNvmId; + if (req.trustedRootNvmId & WH_KEYID_CLIENT_WRAPPED_FLAG) { + trustedRootId = wh_KeyId_TranslateFromClient( + WH_KEYTYPE_WRAPPED, server->comm->client_id, + req.trustedRootNvmId); + } + /* Process client address */ resp.rc = wh_Server_DmaProcessClientAddress( server, req.cert_addr, &cert_data, req.cert_len, @@ -683,7 +740,7 @@ int wh_Server_HandleCertRequest(whServerContext* server, uint16_t magic, if (resp.rc == WH_ERROR_OK) { /* Process the verify action */ resp.rc = wh_Server_CertVerify( - server, cert_data, req.cert_len, req.trustedRootNvmId, + server, cert_data, req.cert_len, trustedRootId, req.flags, req.cachedKeyFlags, &keyId); /* Propagate the keyId back to the client with flags @@ -737,11 +794,18 @@ int wh_Server_HandleCertRequest(whServerContext* server, uint16_t magic, cert_data = (const uint8_t*)req_packet + sizeof(req); - /* Process the verify action */ + /* Translate trustedRootNvmId if client set wrapped flag */ + whKeyId trustedRootId = req.trustedRootNvmId; + if (req.trustedRootNvmId & WH_KEYID_CLIENT_WRAPPED_FLAG) { + trustedRootId = wh_KeyId_TranslateFromClient( + WH_KEYTYPE_WRAPPED, server->comm->client_id, + req.trustedRootNvmId); + } + rc = WH_SERVER_NVM_LOCK(server); if (rc == WH_ERROR_OK) { rc = wh_Server_CertVerifyAcert(server, cert_data, req.cert_len, - req.trustedRootNvmId); + trustedRootId); (void)WH_SERVER_NVM_UNLOCK(server); } /* WH_SERVER_NVM_LOCK() */ @@ -766,9 +830,10 @@ int wh_Server_HandleCertRequest(whServerContext* server, uint16_t magic, #if defined(WOLFHSM_CFG_DMA) case WH_MESSAGE_CERT_ACTION_VERIFY_ACERT_DMA: { /* Acert verify request uses standard cert verify request struct */ - whMessageCert_VerifyDmaRequest req = {0}; - whMessageCert_SimpleResponse resp = {0}; - void* cert_data = NULL; + whMessageCert_VerifyDmaRequest req = {0}; + whMessageCert_SimpleResponse resp = {0}; + void* cert_data = NULL; + whKeyId trustedRootId = 0; if (req_size != sizeof(req)) { /* Request is malformed */ @@ -779,17 +844,24 @@ int wh_Server_HandleCertRequest(whServerContext* server, uint16_t magic, wh_MessageCert_TranslateVerifyDmaRequest( magic, (whMessageCert_VerifyDmaRequest*)req_packet, &req); + /* Translate trustedRootNvmId if client set wrapped flag */ + trustedRootId = req.trustedRootNvmId; + if (req.trustedRootNvmId & WH_KEYID_CLIENT_WRAPPED_FLAG) { + trustedRootId = wh_KeyId_TranslateFromClient( + WH_KEYTYPE_WRAPPED, server->comm->client_id, + req.trustedRootNvmId); + } + /* Process client address */ rc = wh_Server_DmaProcessClientAddress( server, req.cert_addr, &cert_data, req.cert_len, WH_DMA_OPER_CLIENT_READ_PRE, (whServerDmaFlags){0}); } if (rc == WH_ERROR_OK) { - /* Process the verify action */ rc = WH_SERVER_NVM_LOCK(server); if (rc == WH_ERROR_OK) { - rc = wh_Server_CertVerifyAcert( - server, cert_data, req.cert_len, req.trustedRootNvmId); + rc = wh_Server_CertVerifyAcert(server, cert_data, + req.cert_len, trustedRootId); (void)WH_SERVER_NVM_UNLOCK(server); } /* WH_SERVER_NVM_LOCK() */ diff --git a/test/wh_test_cert.c b/test/wh_test_cert.c index ede3a0b4a..32b728f6f 100644 --- a/test/wh_test_cert.c +++ b/test/wh_test_cert.c @@ -51,6 +51,13 @@ #ifdef WOLFHSM_CFG_ENABLE_CLIENT static int whTest_CertNonExportable(whClientContext* client); +#ifdef WOLFHSM_CFG_KEYWRAP +static int whTest_CertCacheVerify(whClientContext* client); +static int whTest_CertCacheReadTrusted(whClientContext* client); +#if defined(WOLFHSM_CFG_DMA) +static int whTest_CertCacheVerifyDma(whClientContext* client); +#endif +#endif #endif #define FLASH_RAM_SIZE (1024 * 1024) /* 1MB */ @@ -259,6 +266,13 @@ int whTest_CertClient(whClientContext* client) /* Test non-exportable flag enforcement */ WH_TEST_RETURN_ON_FAIL(whTest_CertNonExportable(client)); +#ifdef WOLFHSM_CFG_KEYWRAP + /* Test cache-based cert verification (wrapped cert in cache instead of + * NVM) */ + WH_TEST_RETURN_ON_FAIL(whTest_CertCacheVerify(client)); + WH_TEST_RETURN_ON_FAIL(whTest_CertCacheReadTrusted(client)); +#endif + WH_TEST_PRINT("Certificate client test completed successfully\n"); return rc; @@ -442,6 +456,11 @@ int whTest_CertClientDma_ClientServerTestInternal(whClientContext* client) wh_Client_CertEraseTrusted(client, rootCertB_id, &out_rc)); WH_TEST_ASSERT_RETURN(out_rc == WH_ERROR_OK); +#ifdef WOLFHSM_CFG_KEYWRAP + /* Test DMA verify and ReadTrusted with wrapped/cached root cert */ + WH_TEST_RETURN_ON_FAIL(whTest_CertCacheVerifyDma(client)); +#endif + WH_TEST_PRINT("Certificate client DMA test completed successfully\n"); return rc; @@ -513,6 +532,225 @@ int whTest_CertClientAcertDma_ClientServerTestInternal(whClientContext* client) #endif /* WOLFHSM_CFG_DMA */ +#if defined(WOLFHSM_CFG_DMA) && defined(WOLFHSM_CFG_KEYWRAP) +/* Test DMA verify and DMA ReadTrusted with a wrapped/cached root cert. This + * exercises the conditional-locking logic for wrapped certs in the DMA + * handlers. */ +static int whTest_CertCacheVerifyDma(whClientContext* client) +{ + int rc = WH_ERROR_OK; + int32_t out_rc = 0; + whKeyId kekId = 12; + uint8_t wrappedCert[2048]; + uint16_t wrappedCertSz = sizeof(wrappedCert); + whKeyId cachedCertId = WH_KEYID_ERASED; + uint8_t readBackCert[2048]; + + /* AES-256 key for wrapping */ + uint8_t kek[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20}; + + WH_TEST_PRINT("Starting DMA cache-based cert verification test...\n"); + + /* Provision the wrapping key (KEK) on the server (non-DMA, no DMA wrap + * variant exists) */ + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCache( + client, WH_NVM_FLAGS_USAGE_WRAP, NULL, 0, kek, sizeof(kek), &kekId)); + + /* Wrap the root CA cert */ + whNvmMetadata certMeta = {0}; + certMeta.id = WH_CLIENT_KEYID_MAKE_WRAPPED_META(client->comm->client_id, 7); + certMeta.flags = WH_NVM_FLAGS_USAGE_ANY; + certMeta.access = WH_NVM_ACCESS_ANY; + WH_TEST_RETURN_ON_FAIL(wh_Client_CertWrap( + client, WC_CIPHER_AES_GCM, kekId, ROOT_A_CERT, ROOT_A_CERT_len, + &certMeta, wrappedCert, &wrappedCertSz)); + + /* Unwrap and cache the root cert on the server */ + WH_TEST_RETURN_ON_FAIL(wh_Client_CertUnwrapAndCache( + client, WC_CIPHER_AES_GCM, kekId, wrappedCert, wrappedCertSz, + &cachedCertId)); + + /* DMA Verify intermediate cert against the cached root cert */ + WH_TEST_PRINT("DMA verifying intermediate cert against cached root...\n"); + WH_TEST_RETURN_ON_FAIL(wh_Client_CertVerifyDma( + client, INTERMEDIATE_A_CERT, INTERMEDIATE_A_CERT_len, + WH_CLIENT_KEYID_MAKE_WRAPPED(cachedCertId), &out_rc)); + WH_TEST_ASSERT_RETURN(out_rc == WH_ERROR_OK); + + /* DMA Verify a full chain against the cached root cert */ + WH_TEST_PRINT("DMA verifying certificate chain against cached root...\n"); + WH_TEST_RETURN_ON_FAIL(wh_Client_CertVerifyDma( + client, RAW_CERT_CHAIN_A, RAW_CERT_CHAIN_A_len, + WH_CLIENT_KEYID_MAKE_WRAPPED(cachedCertId), &out_rc)); + WH_TEST_ASSERT_RETURN(out_rc == WH_ERROR_OK); + + /* DMA Verify that a mismatched chain fails against the cached root */ + WH_TEST_PRINT("DMA verifying mismatched chain against cached root (should " + "fail)...\n"); + WH_TEST_RETURN_ON_FAIL(wh_Client_CertVerifyDma( + client, RAW_CERT_CHAIN_B, RAW_CERT_CHAIN_B_len, + WH_CLIENT_KEYID_MAKE_WRAPPED(cachedCertId), &out_rc)); + WH_TEST_ASSERT_RETURN(out_rc == WH_ERROR_CERT_VERIFY); + + /* DMA ReadTrusted of cached cert */ + WH_TEST_PRINT("DMA reading back cached cert via CertReadTrustedDma...\n"); + WH_TEST_RETURN_ON_FAIL(wh_Client_CertReadTrustedDma( + client, WH_CLIENT_KEYID_MAKE_WRAPPED(cachedCertId), readBackCert, + sizeof(readBackCert), &out_rc)); + WH_TEST_ASSERT_RETURN(out_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(0 == + memcmp(readBackCert, ROOT_A_CERT, ROOT_A_CERT_len)); + + /* Cleanup: evict cached cert and KEK */ + WH_TEST_RETURN_ON_FAIL( + wh_Client_KeyEvict(client, WH_CLIENT_KEYID_MAKE_WRAPPED(cachedCertId))); + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvict(client, kekId)); + + WH_TEST_PRINT( + "DMA cache-based cert verification test completed successfully\n"); + return rc; +} +#endif /* WOLFHSM_CFG_DMA && WOLFHSM_CFG_KEYWRAP */ + +#ifdef WOLFHSM_CFG_KEYWRAP +/* Test certificate verification using a wrapped/cached root cert instead of + * NVM. This validates the cache-aware path in wh_Server_CertReadTrusted and + * the conditional NVM locking in the handlers. */ +static int whTest_CertCacheVerify(whClientContext* client) +{ + int rc = WH_ERROR_OK; + int32_t out_rc = 0; + whKeyId kekId = 10; + uint8_t wrappedCert[2048]; + uint16_t wrappedCertSz = sizeof(wrappedCert); + whKeyId cachedCertId = WH_KEYID_ERASED; + + /* AES-256 key for wrapping */ + uint8_t kek[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20}; + + WH_TEST_PRINT("Starting cache-based cert verification test...\n"); + + /* Provision the wrapping key (KEK) on the server */ + WH_TEST_PRINT("Provisioning wrapping key on server...\n"); + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCache( + client, WH_NVM_FLAGS_USAGE_WRAP, NULL, 0, kek, sizeof(kek), &kekId)); + + /* Wrap the root CA cert for client-side storage. + * certMeta.id must have WH_KEYTYPE_WRAPPED set in metadata. */ + whNvmMetadata certMeta = {0}; + certMeta.id = WH_CLIENT_KEYID_MAKE_WRAPPED_META(client->comm->client_id, 5); + certMeta.flags = WH_NVM_FLAGS_USAGE_ANY; + certMeta.access = WH_NVM_ACCESS_ANY; + WH_TEST_PRINT("Wrapping root certificate...\n"); + WH_TEST_RETURN_ON_FAIL(wh_Client_CertWrap( + client, WC_CIPHER_AES_GCM, kekId, ROOT_A_CERT, ROOT_A_CERT_len, + &certMeta, wrappedCert, &wrappedCertSz)); + + /* Unwrap and cache the root cert on the server */ + WH_TEST_PRINT("Unwrapping and caching root certificate on server...\n"); + WH_TEST_RETURN_ON_FAIL(wh_Client_CertUnwrapAndCache( + client, WC_CIPHER_AES_GCM, kekId, wrappedCert, wrappedCertSz, + &cachedCertId)); + + /* Verify intermediate cert against the cached root cert. + * Must mark the cachedCertId as wrapped so the server routes to cache */ + WH_TEST_PRINT("Verifying intermediate cert against cached root...\n"); + WH_TEST_RETURN_ON_FAIL(wh_Client_CertVerify( + client, INTERMEDIATE_A_CERT, INTERMEDIATE_A_CERT_len, + WH_CLIENT_KEYID_MAKE_WRAPPED(cachedCertId), &out_rc)); + WH_TEST_ASSERT_RETURN(out_rc == WH_ERROR_OK); + + /* Verify a full chain against the cached root cert */ + WH_TEST_PRINT("Verifying certificate chain against cached root...\n"); + WH_TEST_RETURN_ON_FAIL(wh_Client_CertVerify( + client, RAW_CERT_CHAIN_A, RAW_CERT_CHAIN_A_len, + WH_CLIENT_KEYID_MAKE_WRAPPED(cachedCertId), &out_rc)); + WH_TEST_ASSERT_RETURN(out_rc == WH_ERROR_OK); + + /* Verify that a mismatched chain fails against the cached root */ + WH_TEST_PRINT( + "Verifying mismatched chain against cached root (should fail)...\n"); + WH_TEST_RETURN_ON_FAIL(wh_Client_CertVerify( + client, RAW_CERT_CHAIN_B, RAW_CERT_CHAIN_B_len, + WH_CLIENT_KEYID_MAKE_WRAPPED(cachedCertId), &out_rc)); + WH_TEST_ASSERT_RETURN(out_rc == WH_ERROR_CERT_VERIFY); + + /* Cleanup: evict cached cert and KEK */ + WH_TEST_PRINT("Cleaning up cached cert and KEK...\n"); + WH_TEST_RETURN_ON_FAIL( + wh_Client_KeyEvict(client, WH_CLIENT_KEYID_MAKE_WRAPPED(cachedCertId))); + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvict(client, kekId)); + + WH_TEST_PRINT( + "Cache-based cert verification test completed successfully\n"); + return rc; +} + +/* Test reading back a wrapped/cached cert via CertReadTrusted. This exercises + * the cache path in the READTRUSTED handler. */ +static int whTest_CertCacheReadTrusted(whClientContext* client) +{ + int rc = WH_ERROR_OK; + int32_t out_rc = 0; + whKeyId kekId = 11; + uint8_t wrappedCert[2048]; + uint16_t wrappedCertSz = sizeof(wrappedCert); + whKeyId cachedCertId = WH_KEYID_ERASED; + uint8_t readBackCert[2048]; + uint32_t readBackCertLen = sizeof(readBackCert); + + /* AES-256 key for wrapping */ + uint8_t kek[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20}; + + WH_TEST_PRINT("Starting cache-based cert ReadTrusted test...\n"); + + /* Provision the wrapping key (KEK) on the server */ + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCache( + client, WH_NVM_FLAGS_USAGE_WRAP, NULL, 0, kek, sizeof(kek), &kekId)); + + /* Wrap the root CA cert */ + whNvmMetadata certMeta = {0}; + certMeta.id = WH_CLIENT_KEYID_MAKE_WRAPPED_META(client->comm->client_id, 6); + certMeta.flags = WH_NVM_FLAGS_USAGE_ANY; + certMeta.access = WH_NVM_ACCESS_ANY; + WH_TEST_RETURN_ON_FAIL(wh_Client_CertWrap( + client, WC_CIPHER_AES_GCM, kekId, ROOT_A_CERT, ROOT_A_CERT_len, + &certMeta, wrappedCert, &wrappedCertSz)); + + /* Unwrap and cache the root cert on the server */ + WH_TEST_RETURN_ON_FAIL(wh_Client_CertUnwrapAndCache( + client, WC_CIPHER_AES_GCM, kekId, wrappedCert, wrappedCertSz, + &cachedCertId)); + + /* Read back the cached cert via CertReadTrusted */ + WH_TEST_PRINT("Reading back cached cert via CertReadTrusted...\n"); + WH_TEST_RETURN_ON_FAIL(wh_Client_CertReadTrusted( + client, WH_CLIENT_KEYID_MAKE_WRAPPED(cachedCertId), readBackCert, + &readBackCertLen, &out_rc)); + WH_TEST_ASSERT_RETURN(out_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(readBackCertLen == ROOT_A_CERT_len); + WH_TEST_ASSERT_RETURN(0 == + memcmp(readBackCert, ROOT_A_CERT, ROOT_A_CERT_len)); + + /* Cleanup: evict cached cert and KEK */ + WH_TEST_RETURN_ON_FAIL( + wh_Client_KeyEvict(client, WH_CLIENT_KEYID_MAKE_WRAPPED(cachedCertId))); + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvict(client, kekId)); + + WH_TEST_PRINT("Cache-based cert ReadTrusted test completed successfully\n"); + return rc; +} +#endif /* WOLFHSM_CFG_KEYWRAP */ + static int whTest_CertNonExportable(whClientContext* client) { int rc = WH_ERROR_OK; diff --git a/wolfhsm/wh_client.h b/wolfhsm/wh_client.h index 5fcac92fd..4663a2d5c 100644 --- a/wolfhsm/wh_client.h +++ b/wolfhsm/wh_client.h @@ -2557,6 +2557,201 @@ int wh_Client_CertVerifyAcertDma(whClientContext* c, const void* cert, #endif /* WOLFHSM_CFG_DMA */ +#ifdef WOLFHSM_CFG_KEYWRAP + +/** + * @brief Wrap a certificate for client-side storage + * + * Convenience wrapper around wh_Client_KeyWrap that sets up the appropriate + * metadata for wrapping a certificate. The wrapped blob can be stored + * client-side and later unwrapped/cached on the server for verification. + * This function will block until the entire operation completes or an error + * occurs. + * + * @param[in] ctx Pointer to the client context. + * @param[in] cipherType Cipher used to wrap the certificate. + * @param[in] serverKeyId Key ID of the wrapping key (KEK) on the server. + * @param[in] cert Pointer to the certificate data to wrap. + * @param[in] certSz Size of the certificate data in bytes. + * @param[in] meta Pointer to caller-populated NVM metadata. Caller must set + * id (use WH_CLIENT_KEYID_MAKE_WRAPPED_META), flags, access, and optionally + * label. The len field is set internally from certSz. + * @param[out] wrappedOut Buffer to store the wrapped certificate blob. + * @param[in,out] inout_wrappedSz IN: size of wrappedOut buffer. + * OUT: actual size of the wrapped blob. + * @return int Returns 0 on success, or a negative error code on failure. + */ +int wh_Client_CertWrap(whClientContext* ctx, enum wc_CipherType cipherType, + whKeyId serverKeyId, const uint8_t* cert, + uint32_t certSz, whNvmMetadata* meta, + uint8_t* wrappedOut, uint16_t* inout_wrappedSz); + +/** + * @brief Sends a certificate wrap request to the server + * + * This function prepares and sends a certificate wrap request to the server. + * This function does not block; it returns immediately after sending the + * request. + * + * @param[in] ctx Pointer to the client context. + * @param[in] cipherType Cipher used to wrap the certificate. + * @param[in] serverKeyId Key ID of the wrapping key (KEK) on the server. + * @param[in] cert Pointer to the certificate data to wrap. + * @param[in] certSz Size of the certificate data in bytes. + * @param[in] meta Pointer to caller-populated NVM metadata. Caller must set + * id (use WH_CLIENT_KEYID_MAKE_WRAPPED_META), flags, access, and optionally + * label. The len field is set internally from certSz. + * @return int Returns 0 on success, or a negative error code on failure. + */ +int wh_Client_CertWrapRequest(whClientContext* ctx, + enum wc_CipherType cipherType, + whKeyId serverKeyId, const uint8_t* cert, + uint32_t certSz, whNvmMetadata* meta); + +/** + * @brief Receives a certificate wrap response from the server + * + * This function attempts to process a certificate wrap response message from + * the server. This function does not block; it returns WH_ERROR_NOTREADY if a + * response has not been received. + * + * @param[in] ctx Pointer to the client context. + * @param[in] cipherType Cipher used to wrap the certificate. + * @param[out] wrappedOut Buffer to store the wrapped certificate blob. + * @param[in,out] inout_wrappedSz IN: size of wrappedOut buffer. + * OUT: actual size of the wrapped blob. + * @return int Returns 0 on success, or a negative error code on failure. + */ +int wh_Client_CertWrapResponse(whClientContext* ctx, + enum wc_CipherType cipherType, + uint8_t* wrappedOut, uint16_t* inout_wrappedSz); + +/** + * @brief Unwrap a wrapped certificate and export it to the client + * + * Convenience wrapper around wh_Client_KeyUnwrapAndExport. This function will + * block until the entire operation completes or an error occurs. + * + * @param[in] ctx Pointer to the client context. + * @param[in] cipherType Cipher used when unwrapping the certificate. + * @param[in] serverKeyId Key ID of the wrapping key (KEK) on the server. + * @param[in] wrappedCert Pointer to the wrapped certificate blob. + * @param[in] wrappedCertSz Size of the wrapped certificate blob in bytes. + * @param[out] metadataOut Pointer to store the unwrapped certificate metadata. + * @param[out] certOut Pointer to store the unwrapped certificate. + * @param[in,out] inout_certSz IN: size of certOut buffer. + * OUT: actual size of the unwrapped certificate. + * @return int Returns 0 on success, or a negative error code on failure. + */ +int wh_Client_CertUnwrapAndExport( + whClientContext* ctx, enum wc_CipherType cipherType, whKeyId serverKeyId, + const uint8_t* wrappedCert, uint16_t wrappedCertSz, + whNvmMetadata* metadataOut, uint8_t* certOut, uint16_t* inout_certSz); + +/** + * @brief Sends a certificate unwrap-and-export request to the server + * + * This function prepares and sends a certificate unwrap-and-export request to + * the server. This function does not block; it returns immediately after + * sending the request. + * + * @param[in] ctx Pointer to the client context. + * @param[in] cipherType Cipher used when unwrapping the certificate. + * @param[in] serverKeyId Key ID of the wrapping key (KEK) on the server. + * @param[in] wrappedCert Pointer to the wrapped certificate blob. + * @param[in] wrappedCertSz Size of the wrapped certificate blob in bytes. + * @return int Returns 0 on success, or a negative error code on failure. + */ +int wh_Client_CertUnwrapAndExportRequest(whClientContext* ctx, + enum wc_CipherType cipherType, + whKeyId serverKeyId, + const uint8_t* wrappedCert, + uint16_t wrappedCertSz); + +/** + * @brief Receives a certificate unwrap-and-export response from the server + * + * This function attempts to process a certificate unwrap-and-export response + * message from the server. This function does not block; it returns + * WH_ERROR_NOTREADY if a response has not been received. + * + * @param[in] ctx Pointer to the client context. + * @param[in] cipherType Cipher used when unwrapping the certificate. + * @param[out] metadataOut Pointer to store the unwrapped certificate metadata. + * @param[out] certOut Pointer to store the unwrapped certificate. + * @param[in,out] inout_certSz IN: size of certOut buffer. + * OUT: actual size of the unwrapped certificate. + * @return int Returns 0 on success, or a negative error code on failure. + */ +int wh_Client_CertUnwrapAndExportResponse(whClientContext* ctx, + enum wc_CipherType cipherType, + whNvmMetadata* metadataOut, + uint8_t* certOut, + uint16_t* inout_certSz); + +/** + * @brief Unwrap a wrapped certificate and cache it on the server + * + * Convenience wrapper around wh_Client_KeyUnwrapAndCache. After this call, + * the certificate is cached on the server and can be used as the trusted root + * in wh_Client_CertVerify by passing WH_CLIENT_KEYID_MAKE_WRAPPED(*out_certId) + * as the trustedRootNvmId parameter. This function will block until the entire + * operation completes or an error occurs. + * + * @param[in] ctx Pointer to the client context. + * @param[in] cipherType Cipher used when unwrapping the certificate. + * @param[in] serverKeyId Key ID of the wrapping key (KEK) on the server. + * @param[in] wrappedCert Pointer to the wrapped certificate blob. + * @param[in] wrappedCertSz Size of the wrapped certificate blob in bytes. + * @param[out] out_certId Pointer to store the cached cert's key ID. Use + * WH_CLIENT_KEYID_MAKE_WRAPPED(*out_certId) when passing to cert verify. + * @return int Returns 0 on success, or a negative error code on failure. + */ +int wh_Client_CertUnwrapAndCache(whClientContext* ctx, + enum wc_CipherType cipherType, + whKeyId serverKeyId, + const uint8_t* wrappedCert, + uint16_t wrappedCertSz, whKeyId* out_certId); + +/** + * @brief Sends a certificate unwrap-and-cache request to the server + * + * This function prepares and sends a certificate unwrap-and-cache request to + * the server. This function does not block; it returns immediately after + * sending the request. + * + * @param[in] ctx Pointer to the client context. + * @param[in] cipherType Cipher used when unwrapping the certificate. + * @param[in] serverKeyId Key ID of the wrapping key (KEK) on the server. + * @param[in] wrappedCert Pointer to the wrapped certificate blob. + * @param[in] wrappedCertSz Size of the wrapped certificate blob in bytes. + * @return int Returns 0 on success, or a negative error code on failure. + */ +int wh_Client_CertUnwrapAndCacheRequest(whClientContext* ctx, + enum wc_CipherType cipherType, + whKeyId serverKeyId, + const uint8_t* wrappedCert, + uint16_t wrappedCertSz); + +/** + * @brief Receives a certificate unwrap-and-cache response from the server + * + * This function attempts to process a certificate unwrap-and-cache response + * message from the server. This function does not block; it returns + * WH_ERROR_NOTREADY if a response has not been received. + * + * @param[in] ctx Pointer to the client context. + * @param[in] cipherType Cipher used when unwrapping the certificate. + * @param[out] out_certId Pointer to store the cached cert's key ID. Use + * WH_CLIENT_KEYID_MAKE_WRAPPED(*out_certId) when passing to cert verify. + * @return int Returns 0 on success, or a negative error code on failure. + */ +int wh_Client_CertUnwrapAndCacheResponse(whClientContext* ctx, + enum wc_CipherType cipherType, + whKeyId* out_certId); + +#endif /* WOLFHSM_CFG_KEYWRAP */ + /** * @brief Mark a key ID as global (shared across all clients) diff --git a/wolfhsm/wh_server_cert.h b/wolfhsm/wh_server_cert.h index 0d0be42b7..f456321f6 100644 --- a/wolfhsm/wh_server_cert.h +++ b/wolfhsm/wh_server_cert.h @@ -30,6 +30,7 @@ #include "wolfhsm/wh_server.h" #include "wolfhsm/wh_nvm.h" +#include "wolfhsm/wh_keyid.h" /** * @brief Initialize the certificate manager @@ -61,9 +62,12 @@ int wh_Server_CertAddTrusted(whServerContext* server, whNvmId id, int wh_Server_CertEraseTrusted(whServerContext* server, whNvmId id); /** - * @brief Get a trusted certificate from NVM storage + * @brief Get a trusted certificate from NVM storage or keystore cache * @param server The server context - * @param id The NVM ID of the certificate to read + * @param id The key ID of the certificate to read. If the key type is + * WH_KEYTYPE_NVM, the certificate is read from NVM. Otherwise, the certificate + * is read from the keystore cache (e.g. for wrapped certs cached via + * wh_Client_KeyUnwrapAndCache). * @param cert Buffer to store the certificate data * @param inout_cert_len On input, size of cert buffer. On output, actual cert * size @@ -71,7 +75,7 @@ int wh_Server_CertEraseTrusted(whServerContext* server, whNvmId id); * large for the buffer, WH_ERROR_BUFFER_SIZE will be returned and * inout_cert_len will be updated to the actual certificate size. */ -int wh_Server_CertReadTrusted(whServerContext* server, whNvmId id, +int wh_Server_CertReadTrusted(whServerContext* server, whKeyId id, uint8_t* cert, uint32_t* inout_cert_len); /** @@ -79,7 +83,9 @@ int wh_Server_CertReadTrusted(whServerContext* server, whNvmId id, * @param server The server context * @param cert The certificate data to verify * @param cert_len Length of the certificate data - * @param trustedRootNvmId NVM ID of the trusted root certificate + * @param trustedRootId Key ID of the trusted root certificate. Can be an NVM ID + * (WH_KEYTYPE_NVM) or a cached key ID (e.g. WH_KEYTYPE_WRAPPED from + * wh_Client_KeyUnwrapAndCache). * @param flags Flags for the certificate verification (see WH_CERT_FLAGS_* in * wh_common.h) * @param cachedKeyFlags NVM usage flags to apply when caching the leaf public @@ -91,7 +97,7 @@ int wh_Server_CertReadTrusted(whServerContext* server, whNvmId id, * @return WH_ERROR_OK on success, error code on failure */ int wh_Server_CertVerify(whServerContext* server, const uint8_t* cert, - uint32_t cert_len, whNvmId trustedRootNvmId, + uint32_t cert_len, whKeyId trustedRootId, whCertFlags flags, whNvmFlags cachedKeyFlags, whKeyId* inout_keyId); @@ -105,12 +111,12 @@ int wh_Server_CertVerify(whServerContext* server, const uint8_t* cert, * @param[in] server Pointer to the server context * @param[in] cert Pointer to the attribute certificate data to verify * @param[in] cert_len Length of the certificate data in bytes - * @param[in] trustedRootNvmId NVM ID of the trusted root certificate to verify - * against + * @param[in] trustedRootId Key ID of the trusted root certificate to verify + * against. Can be an NVM ID or a cached key ID. * @return int Returns 0 on success, or a negative error code on failure. */ int wh_Server_CertVerifyAcert(whServerContext* server, const uint8_t* cert, - uint32_t cert_len, whNvmId trustedRootNvmId); + uint32_t cert_len, whKeyId trustedRootId); #endif /** From 9d7e93f23e312fddba389ed0485422f05f9c31fa Mon Sep 17 00:00:00 2001 From: Brett Nicholas <7547222+bigbrett@users.noreply.github.com> Date: Mon, 23 Mar 2026 08:48:48 -0600 Subject: [PATCH 2/8] copilot review feedback --- docs/draft/wrapped-certs.md | 4 ++-- src/wh_client_cert.c | 5 +++-- src/wh_server_cert.c | 10 ++++++++-- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/docs/draft/wrapped-certs.md b/docs/draft/wrapped-certs.md index b9abf6794..591f8c311 100644 --- a/docs/draft/wrapped-certs.md +++ b/docs/draft/wrapped-certs.md @@ -4,7 +4,7 @@ This documents the "wrapped certificate" use case, showing how to leverage the certificate manager to use trusted root certificates that live in the server's -**keystore cache** (RAM) after being unwrapped via keywrap funcitonality, +**keystore cache** (RAM) after being unwrapped via keywrap functionality, rather than exclusively in **NVM** (flash). A root certificate is wrapped (AES-GCM encrypted) by the server, handed back to the client as an opaque blob, and later unwrapped into the server's key cache on demand. Once cached, it can @@ -196,7 +196,7 @@ updated with the same pattern: 1. **Translate the client ID**: If the incoming `req.id` (or `req.trustedRootNvmId`) has `WH_KEYID_CLIENT_WRAPPED_FLAG` set, call - `wh_KeyId_TranslateFromClient(WH_KEYTYPE_NVM, server->comm->client_id, req.id)` + `wh_KeyId_TranslateFromClient(WH_KEYTYPE_WRAPPED, server->comm->client_id, req.id)` to produce a full server-internal key ID with `TYPE=WH_KEYTYPE_WRAPPED`, `USER=client_id`, and the bare key `ID` in the low byte. diff --git a/src/wh_client_cert.c b/src/wh_client_cert.c index d4168c0a7..250726d61 100644 --- a/src/wh_client_cert.c +++ b/src/wh_client_cert.c @@ -1035,7 +1035,8 @@ int wh_Client_CertUnwrapAndExportResponse(whClientContext* ctx, uint8_t* certOut, uint16_t* inout_certSz) { - if ((ctx == NULL) || (certOut == NULL) || (inout_certSz == NULL)) { + if ((ctx == NULL) || (metadataOut == NULL) || (certOut == NULL) || + (inout_certSz == NULL)) { return WH_ERROR_BADARGS; } @@ -1051,7 +1052,7 @@ int wh_Client_CertUnwrapAndExport( int rc; if ((ctx == NULL) || (wrappedCert == NULL) || (wrappedCertSz == 0) || - (certOut == NULL) || (inout_certSz == NULL)) { + (metadataOut == NULL) || (certOut == NULL) || (inout_certSz == NULL)) { return WH_ERROR_BADARGS; } diff --git a/src/wh_server_cert.c b/src/wh_server_cert.c index 8421f23bf..ac76b4887 100644 --- a/src/wh_server_cert.c +++ b/src/wh_server_cert.c @@ -245,6 +245,12 @@ int wh_Server_CertReadTrusted(whServerContext* server, whKeyId id, if (rc == WH_ERROR_OK) { *inout_cert_len = sz; } + else if (rc == WH_ERROR_NOSPACE) { + /* Map keystore error to cert API's documented error and report the + * required size so the caller can retry with a larger buffer */ + *inout_cert_len = meta.len; + rc = WH_ERROR_BUFFER_SIZE; + } return rc; } @@ -256,11 +262,11 @@ int wh_Server_CertReadTrusted(whServerContext* server, whKeyId id, /* Check if the provided buffer is large enough */ if (meta.len > *inout_cert_len) { + *inout_cert_len = meta.len; return WH_ERROR_BUFFER_SIZE; } - /* Clamp the input length to the actual length of the certificate. This will - * be reflected back to the user on length mismatch failure */ + /* Clamp the input length to the actual length of the certificate */ *inout_cert_len = meta.len; return wh_Nvm_Read(server->nvm, id, 0, meta.len, cert); From 312746df9c5a68210a407944c6d9f4c700aab3a3 Mon Sep 17 00:00:00 2001 From: Brett Nicholas <7547222+bigbrett@users.noreply.github.com> Date: Mon, 23 Mar 2026 10:52:03 -0600 Subject: [PATCH 3/8] fix improperly applied export permissions --- src/wh_server_cert.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/wh_server_cert.c b/src/wh_server_cert.c index ac76b4887..94409c2db 100644 --- a/src/wh_server_cert.c +++ b/src/wh_server_cert.c @@ -491,16 +491,18 @@ int wh_Server_HandleCertRequest(whServerContext* server, uint16_t magic, rc = WH_SERVER_NVM_LOCK(server); if (rc == WH_ERROR_OK) { if (req.id & WH_KEYID_CLIENT_WRAPPED_FLAG) { - /* Cache path: translate and read cert + metadata */ + /* Cache path: check metadata before reading cert data */ whKeyId certId = wh_KeyId_TranslateFromClient( WH_KEYTYPE_WRAPPED, server->comm->client_id, req.id); - rc = wh_Server_KeystoreReadKey(server, certId, &meta, - cert_data, &cert_len); + rc = wh_Server_KeystoreReadKey(server, certId, &meta, NULL, + &cert_len); if (rc == WH_ERROR_OK) { if (meta.flags & WH_NVM_FLAGS_NONEXPORTABLE) { rc = WH_ERROR_ACCESS; } else { + rc = wh_Server_KeystoreReadKey( + server, certId, NULL, cert_data, &cert_len); resp.cert_len = cert_len; } } @@ -661,18 +663,24 @@ int wh_Server_HandleCertRequest(whServerContext* server, uint16_t magic, resp.rc = WH_SERVER_NVM_LOCK(server); if (resp.rc == WH_ERROR_OK) { if (req.id & WH_KEYID_CLIENT_WRAPPED_FLAG) { - /* Cache path: translate and read cert + metadata */ + /* Cache path: translate and check metadata before + * reading cert data into client DMA buffer */ whKeyId certId = wh_KeyId_TranslateFromClient( WH_KEYTYPE_WRAPPED, server->comm->client_id, req.id); cert_len = req.cert_len; resp.rc = wh_Server_KeystoreReadKey( - server, certId, &meta, cert_data, &cert_len); + server, certId, &meta, NULL, &cert_len); if (resp.rc == WH_ERROR_OK) { if ((meta.flags & WH_NVM_FLAGS_NONEXPORTABLE) != 0) { resp.rc = WH_ERROR_ACCESS; } + else { + cert_len = req.cert_len; + resp.rc = wh_Server_KeystoreReadKey( + server, certId, NULL, cert_data, &cert_len); + } } } else { From c48b0f63a4ee8e1c7178eba8c3111b30bf49c6ec Mon Sep 17 00:00:00 2001 From: Brett Nicholas <7547222+bigbrett@users.noreply.github.com> Date: Mon, 23 Mar 2026 11:41:39 -0600 Subject: [PATCH 4/8] review feedback --- src/wh_server_cert.c | 11 ++++++++++- src/wh_server_keystore.c | 9 ++++++--- wolfhsm/wh_server_cert.h | 4 ++-- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/wh_server_cert.c b/src/wh_server_cert.c index 94409c2db..a5c8a88f3 100644 --- a/src/wh_server_cert.c +++ b/src/wh_server_cert.c @@ -503,7 +503,13 @@ int wh_Server_HandleCertRequest(whServerContext* server, uint16_t magic, else { rc = wh_Server_KeystoreReadKey( server, certId, NULL, cert_data, &cert_len); - resp.cert_len = cert_len; + if (rc == WH_ERROR_OK) { + resp.cert_len = cert_len; + } + else if (rc == WH_ERROR_NOSPACE) { + resp.cert_len = cert_len; + rc = WH_ERROR_BUFFER_SIZE; + } } } } @@ -680,6 +686,9 @@ int wh_Server_HandleCertRequest(whServerContext* server, uint16_t magic, cert_len = req.cert_len; resp.rc = wh_Server_KeystoreReadKey( server, certId, NULL, cert_data, &cert_len); + if (resp.rc == WH_ERROR_NOSPACE) { + resp.rc = WH_ERROR_BUFFER_SIZE; + } } } } diff --git a/src/wh_server_keystore.c b/src/wh_server_keystore.c index e3724474e..f3da474f4 100644 --- a/src/wh_server_keystore.c +++ b/src/wh_server_keystore.c @@ -647,13 +647,16 @@ int wh_Server_KeystoreReadKey(whServerContext* server, whKeyId keyId, /* Check the cache using unified function */ ret = _FindInCache(server, keyId, NULL, NULL, &cacheBuffer, &cacheMeta); if (ret == WH_ERROR_OK) { - /* Found in cache */ - if (cacheMeta->len > *outSz) - return WH_ERROR_NOSPACE; + /* Found in cache - always populate metadata if requested */ if (outMeta != NULL) { memcpy((uint8_t*)outMeta, (uint8_t*)cacheMeta, sizeof(whNvmMetadata)); } + /* Check buffer size only when data output is requested */ + if (out != NULL && cacheMeta->len > *outSz) { + *outSz = cacheMeta->len; + return WH_ERROR_NOSPACE; + } if (out != NULL) { memcpy(out, cacheBuffer, cacheMeta->len); } diff --git a/wolfhsm/wh_server_cert.h b/wolfhsm/wh_server_cert.h index f456321f6..0c7ebce0e 100644 --- a/wolfhsm/wh_server_cert.h +++ b/wolfhsm/wh_server_cert.h @@ -67,7 +67,7 @@ int wh_Server_CertEraseTrusted(whServerContext* server, whNvmId id); * @param id The key ID of the certificate to read. If the key type is * WH_KEYTYPE_NVM, the certificate is read from NVM. Otherwise, the certificate * is read from the keystore cache (e.g. for wrapped certs cached via - * wh_Client_KeyUnwrapAndCache). + * wh_Client_CertUnwrapAndCache). * @param cert Buffer to store the certificate data * @param inout_cert_len On input, size of cert buffer. On output, actual cert * size @@ -85,7 +85,7 @@ int wh_Server_CertReadTrusted(whServerContext* server, whKeyId id, * @param cert_len Length of the certificate data * @param trustedRootId Key ID of the trusted root certificate. Can be an NVM ID * (WH_KEYTYPE_NVM) or a cached key ID (e.g. WH_KEYTYPE_WRAPPED from - * wh_Client_KeyUnwrapAndCache). + * wh_Client_CertUnwrapAndCache). * @param flags Flags for the certificate verification (see WH_CERT_FLAGS_* in * wh_common.h) * @param cachedKeyFlags NVM usage flags to apply when caching the leaf public From 91507d4be050ea13d242816a32318caea796f05f Mon Sep 17 00:00:00 2001 From: Brett Nicholas <7547222+bigbrett@users.noreply.github.com> Date: Mon, 23 Mar 2026 13:31:54 -0600 Subject: [PATCH 5/8] fix fenrir buffer size bug --- src/wh_server_cert.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/wh_server_cert.c b/src/wh_server_cert.c index a5c8a88f3..59cf9ef6c 100644 --- a/src/wh_server_cert.c +++ b/src/wh_server_cert.c @@ -494,6 +494,7 @@ int wh_Server_HandleCertRequest(whServerContext* server, uint16_t magic, /* Cache path: check metadata before reading cert data */ whKeyId certId = wh_KeyId_TranslateFromClient( WH_KEYTYPE_WRAPPED, server->comm->client_id, req.id); + uint32_t buf_capacity = cert_len; rc = wh_Server_KeystoreReadKey(server, certId, &meta, NULL, &cert_len); if (rc == WH_ERROR_OK) { @@ -501,6 +502,7 @@ int wh_Server_HandleCertRequest(whServerContext* server, uint16_t magic, rc = WH_ERROR_ACCESS; } else { + cert_len = buf_capacity; rc = wh_Server_KeystoreReadKey( server, certId, NULL, cert_data, &cert_len); if (rc == WH_ERROR_OK) { From 1b764495a4dcb9e8f92e9414480ec9cce3869c7c Mon Sep 17 00:00:00 2001 From: Brett Nicholas <7547222+bigbrett@users.noreply.github.com> Date: Wed, 25 Mar 2026 12:50:36 -0600 Subject: [PATCH 6/8] Review feedback: - Add doc warning about KEK - Add ACERT tests - Fix cert size check --- docs/draft/wrapped-certs.md | 4 + src/wh_client_cert.c | 4 +- test/wh_test_cert.c | 200 ++++++++++++++++++++++++++++++++++++ 3 files changed, 206 insertions(+), 2 deletions(-) diff --git a/docs/draft/wrapped-certs.md b/docs/draft/wrapped-certs.md index 591f8c311..59dc851f2 100644 --- a/docs/draft/wrapped-certs.md +++ b/docs/draft/wrapped-certs.md @@ -35,6 +35,10 @@ wh_Client_KeyCache(client, kek, sizeof(kek), &kekId); ``` +> **Note:** This is an example only. In production, the KEK is normally +> provisioned and protected by target-specific hardware and should never be +> hardcoded. + The KEK is now sitting in the server's `localCache` (or `globalCache` if marked global), indexed by `kekId`. diff --git a/src/wh_client_cert.c b/src/wh_client_cert.c index 250726d61..ea90222bb 100644 --- a/src/wh_client_cert.c +++ b/src/wh_client_cert.c @@ -966,7 +966,7 @@ int wh_Client_CertWrapRequest(whClientContext* ctx, uint32_t certSz, whNvmMetadata* meta) { if ((ctx == NULL) || (cert == NULL) || (certSz == 0) || (meta == NULL) || - (certSz > UINT16_MAX)) { + (certSz > WOLFHSM_CFG_MAX_CERT_SIZE)) { return WH_ERROR_BADARGS; } @@ -997,7 +997,7 @@ int wh_Client_CertWrap(whClientContext* ctx, enum wc_CipherType cipherType, if ((ctx == NULL) || (cert == NULL) || (certSz == 0) || (meta == NULL) || (wrappedOut == NULL) || (inout_wrappedSz == NULL) || - (certSz > UINT16_MAX)) { + (certSz > WOLFHSM_CFG_MAX_CERT_SIZE)) { return WH_ERROR_BADARGS; } diff --git a/test/wh_test_cert.c b/test/wh_test_cert.c index 32b728f6f..f27460290 100644 --- a/test/wh_test_cert.c +++ b/test/wh_test_cert.c @@ -54,8 +54,14 @@ static int whTest_CertNonExportable(whClientContext* client); #ifdef WOLFHSM_CFG_KEYWRAP static int whTest_CertCacheVerify(whClientContext* client); static int whTest_CertCacheReadTrusted(whClientContext* client); +#if defined(WOLFHSM_CFG_CERTIFICATE_MANAGER_ACERT) +static int whTest_CertCacheVerifyAcert(whClientContext* client); +#endif #if defined(WOLFHSM_CFG_DMA) static int whTest_CertCacheVerifyDma(whClientContext* client); +#if defined(WOLFHSM_CFG_CERTIFICATE_MANAGER_ACERT) +static int whTest_CertCacheVerifyAcertDma(whClientContext* client); +#endif #endif #endif #endif @@ -331,6 +337,12 @@ int whTest_CertClientAcert(whClientContext* client) wh_Client_CertEraseTrusted(client, rootCertB_id, &out_rc)); WH_TEST_ASSERT_RETURN(out_rc == WH_ERROR_OK); +#ifdef WOLFHSM_CFG_KEYWRAP + /* Test cache-based ACERT verification (wrapped cert in cache instead of + * NVM) */ + WH_TEST_RETURN_ON_FAIL(whTest_CertCacheVerifyAcert(client)); +#endif + WH_TEST_PRINT( "Attribute certificate client test completed successfully\n"); @@ -523,6 +535,12 @@ int whTest_CertClientAcertDma_ClientServerTestInternal(whClientContext* client) wh_Client_CertEraseTrusted(client, rootCertB_id, &out_rc)); WH_TEST_ASSERT_RETURN(out_rc == WH_ERROR_OK); +#ifdef WOLFHSM_CFG_KEYWRAP + /* Test DMA cache-based ACERT verification (wrapped cert in cache instead of + * NVM) */ + WH_TEST_RETURN_ON_FAIL(whTest_CertCacheVerifyAcertDma(client)); +#endif + WH_TEST_PRINT( "Attribute certificate client test completed successfully\n"); @@ -749,6 +767,188 @@ static int whTest_CertCacheReadTrusted(whClientContext* client) WH_TEST_PRINT("Cache-based cert ReadTrusted test completed successfully\n"); return rc; } + +#if defined(WOLFHSM_CFG_CERTIFICATE_MANAGER_ACERT) +/* Test attribute certificate verification using a wrapped/cached root cert + * instead of NVM. This validates the cache-aware path in the VERIFY_ACERT + * handler. */ +static int whTest_CertCacheVerifyAcert(whClientContext* client) +{ + int rc = WH_ERROR_OK; + int32_t out_rc = 0; + whKeyId kekId = 13; + uint8_t wrappedCert[2048]; + uint16_t wrappedCertSz = sizeof(wrappedCert); + whKeyId cachedCertId = WH_KEYID_ERASED; + + /* AES-256 key for wrapping */ + uint8_t kek[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20}; + + whNvmMetadata certMeta = {0}; + + WH_TEST_PRINT("Starting cache-based ACERT verification test...\n"); + + /* Provision the wrapping key (KEK) on the server */ + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCache( + client, WH_NVM_FLAGS_USAGE_WRAP, NULL, 0, kek, sizeof(kek), &kekId)); + + /* Wrap the CA cert (ACERT issuer) for client-side storage */ + certMeta.id = WH_CLIENT_KEYID_MAKE_WRAPPED_META(client->comm->client_id, 8); + certMeta.flags = WH_NVM_FLAGS_USAGE_ANY; + certMeta.access = WH_NVM_ACCESS_ANY; + WH_TEST_RETURN_ON_FAIL(wh_Client_CertWrap( + client, WC_CIPHER_AES_GCM, kekId, caCert_der, caCert_der_len, &certMeta, + wrappedCert, &wrappedCertSz)); + + /* Unwrap and cache the CA cert on the server */ + WH_TEST_RETURN_ON_FAIL(wh_Client_CertUnwrapAndCache( + client, WC_CIPHER_AES_GCM, kekId, wrappedCert, wrappedCertSz, + &cachedCertId)); + + /* Verify attribute cert against the cached root cert */ + WH_TEST_PRINT("Verifying ACERT against cached root...\n"); + WH_TEST_RETURN_ON_FAIL(wh_Client_CertVerifyAcert( + client, attrCert_der, attrCert_der_len, + WH_CLIENT_KEYID_MAKE_WRAPPED(cachedCertId), &out_rc)); + WH_TEST_ASSERT_RETURN(out_rc == WH_ERROR_OK); + + /* Verify that a mismatched root fails */ + WH_TEST_PRINT( + "Verifying ACERT against mismatched cached root (should fail)...\n"); + /* Wrap ROOT_B_CERT as a different cached root */ + { + uint8_t wrappedCertB[2048]; + uint16_t wrappedCertBSz = sizeof(wrappedCertB); + whKeyId cachedCertBId = WH_KEYID_ERASED; + whNvmMetadata certMetaB = {0}; + certMetaB.id = + WH_CLIENT_KEYID_MAKE_WRAPPED_META(client->comm->client_id, 9); + certMetaB.flags = WH_NVM_FLAGS_USAGE_ANY; + certMetaB.access = WH_NVM_ACCESS_ANY; + + WH_TEST_RETURN_ON_FAIL(wh_Client_CertWrap( + client, WC_CIPHER_AES_GCM, kekId, ROOT_B_CERT, ROOT_B_CERT_len, + &certMetaB, wrappedCertB, &wrappedCertBSz)); + + WH_TEST_RETURN_ON_FAIL(wh_Client_CertUnwrapAndCache( + client, WC_CIPHER_AES_GCM, kekId, wrappedCertB, wrappedCertBSz, + &cachedCertBId)); + + WH_TEST_RETURN_ON_FAIL(wh_Client_CertVerifyAcert( + client, attrCert_der, attrCert_der_len, + WH_CLIENT_KEYID_MAKE_WRAPPED(cachedCertBId), &out_rc)); + + WH_TEST_ASSERT_RETURN(out_rc == WH_ERROR_CERT_VERIFY); + + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvict( + client, WH_CLIENT_KEYID_MAKE_WRAPPED(cachedCertBId))); + } + + /* Cleanup: evict cached cert and KEK */ + WH_TEST_RETURN_ON_FAIL( + wh_Client_KeyEvict(client, WH_CLIENT_KEYID_MAKE_WRAPPED(cachedCertId))); + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvict(client, kekId)); + + WH_TEST_PRINT( + "Cache-based ACERT verification test completed successfully\n"); + return rc; +} + +#if defined(WOLFHSM_CFG_DMA) +/* Test DMA attribute certificate verification using a wrapped/cached root cert + * instead of NVM. This validates the cache-aware path in the VERIFY_ACERT_DMA + * handler. */ +static int whTest_CertCacheVerifyAcertDma(whClientContext* client) +{ + int rc = WH_ERROR_OK; + int32_t out_rc = 0; + whKeyId kekId = 14; + uint8_t wrappedCert[2048]; + uint16_t wrappedCertSz = sizeof(wrappedCert); + whKeyId cachedCertId = WH_KEYID_ERASED; + + /* AES-256 key for wrapping */ + uint8_t kek[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20}; + + whNvmMetadata certMeta = {0}; + + WH_TEST_PRINT("Starting DMA cache-based ACERT verification test...\n"); + + /* Provision the wrapping key (KEK) on the server */ + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCache( + client, WH_NVM_FLAGS_USAGE_WRAP, NULL, 0, kek, sizeof(kek), &kekId)); + + /* Wrap the CA cert (ACERT issuer) for client-side storage */ + certMeta.id = + WH_CLIENT_KEYID_MAKE_WRAPPED_META(client->comm->client_id, 10); + certMeta.flags = WH_NVM_FLAGS_USAGE_ANY; + certMeta.access = WH_NVM_ACCESS_ANY; + WH_TEST_RETURN_ON_FAIL(wh_Client_CertWrap( + client, WC_CIPHER_AES_GCM, kekId, caCert_der, caCert_der_len, &certMeta, + wrappedCert, &wrappedCertSz)); + + /* Unwrap and cache the CA cert on the server */ + WH_TEST_RETURN_ON_FAIL(wh_Client_CertUnwrapAndCache( + client, WC_CIPHER_AES_GCM, kekId, wrappedCert, wrappedCertSz, + &cachedCertId)); + + /* DMA Verify attribute cert against the cached root cert */ + WH_TEST_PRINT("DMA verifying ACERT against cached root...\n"); + WH_TEST_RETURN_ON_FAIL(wh_Client_CertVerifyAcertDma( + client, attrCert_der, attrCert_der_len, + WH_CLIENT_KEYID_MAKE_WRAPPED(cachedCertId), &out_rc)); + WH_TEST_ASSERT_RETURN(out_rc == WH_ERROR_OK); + + /* Verify that a mismatched root fails */ + WH_TEST_PRINT("DMA verifying ACERT against mismatched cached root (should " + "fail)...\n"); + { + uint8_t wrappedCertB[2048]; + uint16_t wrappedCertBSz = sizeof(wrappedCertB); + whKeyId cachedCertBId = WH_KEYID_ERASED; + whNvmMetadata certMetaB = {0}; + certMetaB.id = + WH_CLIENT_KEYID_MAKE_WRAPPED_META(client->comm->client_id, 11); + certMetaB.flags = WH_NVM_FLAGS_USAGE_ANY; + certMetaB.access = WH_NVM_ACCESS_ANY; + + WH_TEST_RETURN_ON_FAIL(wh_Client_CertWrap( + client, WC_CIPHER_AES_GCM, kekId, ROOT_B_CERT, ROOT_B_CERT_len, + &certMetaB, wrappedCertB, &wrappedCertBSz)); + + WH_TEST_RETURN_ON_FAIL(wh_Client_CertUnwrapAndCache( + client, WC_CIPHER_AES_GCM, kekId, wrappedCertB, wrappedCertBSz, + &cachedCertBId)); + + WH_TEST_RETURN_ON_FAIL(wh_Client_CertVerifyAcertDma( + client, attrCert_der, attrCert_der_len, + WH_CLIENT_KEYID_MAKE_WRAPPED(cachedCertBId), &out_rc)); + + WH_TEST_ASSERT_RETURN(out_rc == WH_ERROR_CERT_VERIFY); + + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvict( + client, WH_CLIENT_KEYID_MAKE_WRAPPED(cachedCertBId))); + } + + /* Cleanup: evict cached cert and KEK */ + WH_TEST_RETURN_ON_FAIL( + wh_Client_KeyEvict(client, WH_CLIENT_KEYID_MAKE_WRAPPED(cachedCertId))); + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvict(client, kekId)); + + WH_TEST_PRINT( + "DMA cache-based ACERT verification test completed successfully\n"); + return rc; +} +#endif /* WOLFHSM_CFG_DMA */ + +#endif /* WOLFHSM_CFG_CERTIFICATE_MANAGER_ACERT */ + #endif /* WOLFHSM_CFG_KEYWRAP */ static int whTest_CertNonExportable(whClientContext* client) From 079db1df1167fd77df1bb9785a0dceccbc947f2f Mon Sep 17 00:00:00 2001 From: Brett Nicholas <7547222+bigbrett@users.noreply.github.com> Date: Wed, 25 Mar 2026 12:58:13 -0600 Subject: [PATCH 7/8] review: rename trustedRootNvmId -> trustedRootId --- docs/draft/wrapped-certs.md | 4 +- src/wh_client_cert.c | 80 ++++++++++++++++++------------------- src/wh_message_cert.c | 6 +-- src/wh_server_cert.c | 32 +++++++-------- wolfhsm/wh_client.h | 54 ++++++++++++------------- wolfhsm/wh_message_cert.h | 6 +-- 6 files changed, 89 insertions(+), 93 deletions(-) diff --git a/docs/draft/wrapped-certs.md b/docs/draft/wrapped-certs.md index 59dc851f2..8c40d3022 100644 --- a/docs/draft/wrapped-certs.md +++ b/docs/draft/wrapped-certs.md @@ -188,7 +188,7 @@ both the metadata and the raw data into the caller's buffers. #### `wh_Server_CertVerify` / `wh_Server_CertVerifyAcert` -Signature changed from `whNvmId trustedRootNvmId` to `whKeyId trustedRootId`. +Signature changed from `whNvmId trustedRootId` to `whKeyId trustedRootId`. Internally they just call `wh_Server_CertReadTrusted`, which now handles the routing. @@ -199,7 +199,7 @@ Every handler that accepts a trusted root ID (`READTRUSTED`, `VERIFY`, updated with the same pattern: 1. **Translate the client ID**: If the incoming `req.id` (or - `req.trustedRootNvmId`) has `WH_KEYID_CLIENT_WRAPPED_FLAG` set, call + `req.trustedRootId`) has `WH_KEYID_CLIENT_WRAPPED_FLAG` set, call `wh_KeyId_TranslateFromClient(WH_KEYTYPE_WRAPPED, server->comm->client_id, req.id)` to produce a full server-internal key ID with `TYPE=WH_KEYTYPE_WRAPPED`, `USER=client_id`, and the bare key `ID` in the low byte. diff --git a/src/wh_client_cert.c b/src/wh_client_cert.c index ea90222bb..4ac9a53f9 100644 --- a/src/wh_client_cert.c +++ b/src/wh_client_cert.c @@ -38,7 +38,7 @@ /* Helper function to send a certificate verification request */ static int _certVerifyRequest(whClientContext* c, const uint8_t* cert, - uint32_t cert_len, whNvmId trustedRootNvmId, + uint32_t cert_len, whKeyId trustedRootId, uint16_t verifyFlags, whNvmFlags cachedKeyFlags, whKeyId keyId); @@ -48,7 +48,7 @@ static int _certVerifyResponse(whClientContext* c, whKeyId* out_keyId, /* Helper function to perform certificate verification */ static int _certVerify(whClientContext* c, const uint8_t* cert, - uint32_t cert_len, whNvmId trustedRootNvmId, + uint32_t cert_len, whKeyId trustedRootId, uint16_t verifyFlags, whNvmFlags cachedKeyFlags, whKeyId* inout_keyId, int32_t* out_rc); @@ -363,7 +363,7 @@ int wh_Client_CertReadTrusted(whClientContext* c, whNvmId id, uint8_t* cert, /* Helper function to send a verify request */ static int _certVerifyRequest(whClientContext* c, const uint8_t* cert, - uint32_t cert_len, whNvmId trustedRootNvmId, + uint32_t cert_len, whKeyId trustedRootId, uint16_t verifyFlags, whNvmFlags cachedKeyFlags, whKeyId keyId) { @@ -379,7 +379,7 @@ static int _certVerifyRequest(whClientContext* c, const uint8_t* cert, /* Prepare request */ req.cert_len = cert_len; - req.trustedRootNvmId = trustedRootNvmId; + req.trustedRootId = trustedRootId; req.flags = verifyFlags; req.cachedKeyFlags = cachedKeyFlags; req.keyId = keyId; @@ -430,7 +430,7 @@ static int _certVerifyResponse(whClientContext* c, whKeyId* out_keyId, } static int _certVerify(whClientContext* c, const uint8_t* cert, - uint32_t cert_len, whNvmId trustedRootNvmId, + uint32_t cert_len, whKeyId trustedRootId, uint16_t verifyFlags, whNvmFlags cachedKeyFlags, whKeyId* inout_keyId, int32_t* out_rc) { @@ -446,8 +446,8 @@ static int _certVerify(whClientContext* c, const uint8_t* cert, } do { - rc = _certVerifyRequest(c, cert, cert_len, trustedRootNvmId, - verifyFlags, cachedKeyFlags, keyId); + rc = _certVerifyRequest(c, cert, cert_len, trustedRootId, verifyFlags, + cachedKeyFlags, keyId); } while (rc == WH_ERROR_NOTREADY); if (rc == 0) { @@ -460,9 +460,9 @@ static int _certVerify(whClientContext* c, const uint8_t* cert, } int wh_Client_CertVerifyRequest(whClientContext* c, const uint8_t* cert, - uint32_t cert_len, whNvmId trustedRootNvmId) + uint32_t cert_len, whKeyId trustedRootId) { - return _certVerifyRequest(c, cert, cert_len, trustedRootNvmId, + return _certVerifyRequest(c, cert, cert_len, trustedRootId, WH_CERT_FLAGS_NONE, WH_NVM_FLAGS_USAGE_ANY, WH_KEYID_ERASED); } @@ -473,18 +473,18 @@ int wh_Client_CertVerifyResponse(whClientContext* c, int32_t* out_rc) } int wh_Client_CertVerify(whClientContext* c, const uint8_t* cert, - uint32_t cert_len, whNvmId trustedRootNvmId, + uint32_t cert_len, whKeyId trustedRootId, int32_t* out_rc) { - return _certVerify(c, cert, cert_len, trustedRootNvmId, WH_CERT_FLAGS_NONE, + return _certVerify(c, cert, cert_len, trustedRootId, WH_CERT_FLAGS_NONE, WH_NVM_FLAGS_USAGE_ANY, NULL, out_rc); } int wh_Client_CertVerifyAndCacheLeafPubKeyRequest( whClientContext* c, const uint8_t* cert, uint32_t cert_len, - whNvmId trustedRootNvmId, whNvmFlags cachedKeyFlags, whKeyId keyId) + whKeyId trustedRootId, whNvmFlags cachedKeyFlags, whKeyId keyId) { - return _certVerifyRequest(c, cert, cert_len, trustedRootNvmId, + return _certVerifyRequest(c, cert, cert_len, trustedRootId, WH_CERT_FLAGS_CACHE_LEAF_PUBKEY, cachedKeyFlags, keyId); } @@ -499,10 +499,10 @@ int wh_Client_CertVerifyAndCacheLeafPubKeyResponse(whClientContext* c, int wh_Client_CertVerifyAndCacheLeafPubKey( whClientContext* c, const uint8_t* cert, uint32_t cert_len, - whNvmId trustedRootNvmId, whNvmFlags cachedKeyFlags, whKeyId* inout_keyId, + whKeyId trustedRootId, whNvmFlags cachedKeyFlags, whKeyId* inout_keyId, int32_t* out_rc) { - return _certVerify(c, cert, cert_len, trustedRootNvmId, + return _certVerify(c, cert, cert_len, trustedRootId, WH_CERT_FLAGS_CACHE_LEAF_PUBKEY, cachedKeyFlags, inout_keyId, out_rc); } @@ -664,7 +664,7 @@ int wh_Client_CertReadTrustedDma(whClientContext* c, whNvmId id, void* cert, } static int _certVerifyDmaRequest(whClientContext* c, const void* cert, - uint32_t cert_len, whNvmId trustedRootNvmId, + uint32_t cert_len, whKeyId trustedRootId, uint16_t flags, whNvmFlags cachedKeyFlags, whKeyId keyId) { @@ -677,7 +677,7 @@ static int _certVerifyDmaRequest(whClientContext* c, const void* cert, /* Prepare and send request */ req.cert_addr = (uint64_t)(uintptr_t)cert; req.cert_len = cert_len; - req.trustedRootNvmId = trustedRootNvmId; + req.trustedRootId = trustedRootId; req.flags = flags; req.cachedKeyFlags = cachedKeyFlags; req.keyId = keyId; @@ -721,7 +721,7 @@ static int _certVerifyDmaResponse(whClientContext* c, whKeyId* out_keyId, } static int _certVerifyDma(whClientContext* c, const void* cert, - uint32_t cert_len, whNvmId trustedRootNvmId, + uint32_t cert_len, whKeyId trustedRootId, uint16_t flags, whNvmFlags cachedKeyFlags, whKeyId* inout_keyId, int32_t* out_rc) { @@ -740,7 +740,7 @@ static int _certVerifyDma(whClientContext* c, const void* cert, } do { - rc = _certVerifyDmaRequest(c, cert, cert_len, trustedRootNvmId, flags, + rc = _certVerifyDmaRequest(c, cert, cert_len, trustedRootId, flags, cachedKeyFlags, keyId); } while (rc == WH_ERROR_NOTREADY); @@ -754,9 +754,9 @@ static int _certVerifyDma(whClientContext* c, const void* cert, } int wh_Client_CertVerifyDmaRequest(whClientContext* c, const void* cert, - uint32_t cert_len, whNvmId trustedRootNvmId) + uint32_t cert_len, whKeyId trustedRootId) { - return _certVerifyDmaRequest(c, cert, cert_len, trustedRootNvmId, + return _certVerifyDmaRequest(c, cert, cert_len, trustedRootId, WH_CERT_FLAGS_NONE, WH_NVM_FLAGS_USAGE_ANY, WH_KEYID_ERASED); } @@ -767,19 +767,18 @@ int wh_Client_CertVerifyDmaResponse(whClientContext* c, int32_t* out_rc) } int wh_Client_CertVerifyDma(whClientContext* c, const void* cert, - uint32_t cert_len, whNvmId trustedRootNvmId, + uint32_t cert_len, whKeyId trustedRootId, int32_t* out_rc) { - return _certVerifyDma(c, cert, cert_len, trustedRootNvmId, - WH_CERT_FLAGS_NONE, WH_NVM_FLAGS_USAGE_ANY, NULL, - out_rc); + return _certVerifyDma(c, cert, cert_len, trustedRootId, WH_CERT_FLAGS_NONE, + WH_NVM_FLAGS_USAGE_ANY, NULL, out_rc); } int wh_Client_CertVerifyDmaAndCacheLeafPubKeyRequest( whClientContext* c, const void* cert, uint32_t cert_len, - whNvmId trustedRootNvmId, whNvmFlags cachedKeyFlags, whKeyId keyId) + whKeyId trustedRootId, whNvmFlags cachedKeyFlags, whKeyId keyId) { - return _certVerifyDmaRequest(c, cert, cert_len, trustedRootNvmId, + return _certVerifyDmaRequest(c, cert, cert_len, trustedRootId, WH_CERT_FLAGS_CACHE_LEAF_PUBKEY, cachedKeyFlags, keyId); } @@ -793,10 +792,10 @@ int wh_Client_CertVerifyDmaAndCacheLeafPubKeyResponse(whClientContext* c, int wh_Client_CertVerifyDmaAndCacheLeafPubKey( whClientContext* c, const void* cert, uint32_t cert_len, - whNvmId trustedRootNvmId, whNvmFlags cachedKeyFlags, whKeyId* inout_keyId, + whKeyId trustedRootId, whNvmFlags cachedKeyFlags, whKeyId* inout_keyId, int32_t* out_rc) { - return _certVerifyDma(c, cert, cert_len, trustedRootNvmId, + return _certVerifyDma(c, cert, cert_len, trustedRootId, WH_CERT_FLAGS_CACHE_LEAF_PUBKEY, cachedKeyFlags, inout_keyId, out_rc); } @@ -807,8 +806,7 @@ int wh_Client_CertVerifyDmaAndCacheLeafPubKey( int wh_Client_CertVerifyAcertRequest(whClientContext* c, const void* cert, - uint32_t cert_len, - whNvmId trustedRootNvmId) + uint32_t cert_len, whKeyId trustedRootId) { whMessageCert_VerifyAcertRequest req = {0}; uint8_t buffer[WOLFHSM_CFG_COMM_DATA_LEN]; @@ -816,14 +814,13 @@ int wh_Client_CertVerifyAcertRequest(whClientContext* c, const void* cert, uint8_t* payload = buffer + hdr_len; - if ((c == NULL) || (trustedRootNvmId == WH_NVM_ID_INVALID) || - (cert == NULL) || (cert_len == 0) || - (cert_len > (sizeof(buffer) - hdr_len))) { + if ((c == NULL) || (trustedRootId == WH_NVM_ID_INVALID) || (cert == NULL) || + (cert_len == 0) || (cert_len > (sizeof(buffer) - hdr_len))) { return WH_ERROR_BADARGS; } req.cert_len = cert_len; - req.trustedRootNvmId = trustedRootNvmId; + req.trustedRootId = trustedRootId; memcpy(buffer, &req, sizeof(req)); memcpy(payload, cert, cert_len); @@ -859,7 +856,7 @@ int wh_Client_CertVerifyAcertResponse(whClientContext* c, int32_t* out_rc) } int wh_Client_CertVerifyAcert(whClientContext* c, const void* cert, - uint32_t cert_len, whNvmId trustedRootNvmId, + uint32_t cert_len, whKeyId trustedRootId, int32_t* out_rc) { int rc = 0; @@ -869,8 +866,7 @@ int wh_Client_CertVerifyAcert(whClientContext* c, const void* cert, } do { - rc = wh_Client_CertVerifyAcertRequest(c, cert, cert_len, - trustedRootNvmId); + rc = wh_Client_CertVerifyAcertRequest(c, cert, cert_len, trustedRootId); } while (rc == WH_ERROR_NOTREADY); if (rc == 0) { @@ -886,7 +882,7 @@ int wh_Client_CertVerifyAcert(whClientContext* c, const void* cert, int wh_Client_CertVerifyAcertDmaRequest(whClientContext* c, const void* cert, uint32_t cert_len, - whNvmId trustedRootNvmId) + whKeyId trustedRootId) { whMessageCert_VerifyDmaRequest req = {0}; @@ -896,7 +892,7 @@ int wh_Client_CertVerifyAcertDmaRequest(whClientContext* c, const void* cert, req.cert_addr = (uint64_t)(intptr_t)cert; req.cert_len = cert_len; - req.trustedRootNvmId = trustedRootNvmId; + req.trustedRootId = trustedRootId; return wh_Client_SendRequest(c, WH_MESSAGE_GROUP_CERT, WH_MESSAGE_CERT_ACTION_VERIFY_ACERT_DMA, sizeof(req), &req); @@ -932,7 +928,7 @@ int wh_Client_CertVerifyAcertDmaResponse(whClientContext* c, int32_t* out_rc) } int wh_Client_CertVerifyAcertDma(whClientContext* c, const void* cert, - uint32_t cert_len, whNvmId trustedRootNvmId, + uint32_t cert_len, whKeyId trustedRootId, int32_t* out_rc) { int rc = 0; @@ -943,7 +939,7 @@ int wh_Client_CertVerifyAcertDma(whClientContext* c, const void* cert, do { rc = wh_Client_CertVerifyAcertDmaRequest(c, cert, cert_len, - trustedRootNvmId); + trustedRootId); } while (rc == WH_ERROR_NOTREADY); if (rc == 0) { diff --git a/src/wh_message_cert.c b/src/wh_message_cert.c index 35d9b86ad..b17819c02 100644 --- a/src/wh_message_cert.c +++ b/src/wh_message_cert.c @@ -103,7 +103,7 @@ int wh_MessageCert_TranslateVerifyRequest( return WH_ERROR_BADARGS; } WH_T32(magic, dest, src, cert_len); - WH_T16(magic, dest, src, trustedRootNvmId); + WH_T16(magic, dest, src, trustedRootId); WH_T16(magic, dest, src, flags); WH_T16(magic, dest, src, cachedKeyFlags); WH_T16(magic, dest, src, keyId); @@ -163,7 +163,7 @@ int wh_MessageCert_TranslateVerifyDmaRequest( } WH_T64(magic, dest, src, cert_addr); WH_T32(magic, dest, src, cert_len); - WH_T16(magic, dest, src, trustedRootNvmId); + WH_T16(magic, dest, src, trustedRootId); WH_T16(magic, dest, src, flags); WH_T16(magic, dest, src, cachedKeyFlags); WH_T16(magic, dest, src, keyId); @@ -192,7 +192,7 @@ int wh_MessageCert_TranslateVerifyAcertRequest( return WH_ERROR_BADARGS; } WH_T32(magic, dest, src, cert_len); - WH_T16(magic, dest, src, trustedRootNvmId); + WH_T16(magic, dest, src, trustedRootId); return 0; } #endif /* WOLFHSM_CFG_CERTIFICATE_MANAGER_ACERT */ diff --git a/src/wh_server_cert.c b/src/wh_server_cert.c index 59cf9ef6c..7bf9c549f 100644 --- a/src/wh_server_cert.c +++ b/src/wh_server_cert.c @@ -571,12 +571,12 @@ int wh_Server_HandleCertRequest(whServerContext* server, uint16_t magic, whKeyId keyId = wh_KeyId_TranslateFromClient( WH_KEYTYPE_CRYPTO, server->comm->client_id, req.keyId); - /* Translate trustedRootNvmId if client set wrapped flag */ - whKeyId trustedRootId = req.trustedRootNvmId; - if (req.trustedRootNvmId & WH_KEYID_CLIENT_WRAPPED_FLAG) { + /* Translate trustedRootId if client set wrapped flag */ + whKeyId trustedRootId = req.trustedRootId; + if (req.trustedRootId & WH_KEYID_CLIENT_WRAPPED_FLAG) { trustedRootId = wh_KeyId_TranslateFromClient( WH_KEYTYPE_WRAPPED, server->comm->client_id, - req.trustedRootNvmId); + req.trustedRootId); } rc = WH_SERVER_NVM_LOCK(server); @@ -747,12 +747,12 @@ int wh_Server_HandleCertRequest(whServerContext* server, uint16_t magic, keyId = wh_KeyId_TranslateFromClient( WH_KEYTYPE_CRYPTO, server->comm->client_id, req.keyId); - /* Translate trustedRootNvmId if client set wrapped flag */ - trustedRootId = req.trustedRootNvmId; - if (req.trustedRootNvmId & WH_KEYID_CLIENT_WRAPPED_FLAG) { + /* Translate trustedRootId if client set wrapped flag */ + trustedRootId = req.trustedRootId; + if (req.trustedRootId & WH_KEYID_CLIENT_WRAPPED_FLAG) { trustedRootId = wh_KeyId_TranslateFromClient( WH_KEYTYPE_WRAPPED, server->comm->client_id, - req.trustedRootNvmId); + req.trustedRootId); } /* Process client address */ @@ -819,12 +819,12 @@ int wh_Server_HandleCertRequest(whServerContext* server, uint16_t magic, cert_data = (const uint8_t*)req_packet + sizeof(req); - /* Translate trustedRootNvmId if client set wrapped flag */ - whKeyId trustedRootId = req.trustedRootNvmId; - if (req.trustedRootNvmId & WH_KEYID_CLIENT_WRAPPED_FLAG) { + /* Translate trustedRootId if client set wrapped flag */ + whKeyId trustedRootId = req.trustedRootId; + if (req.trustedRootId & WH_KEYID_CLIENT_WRAPPED_FLAG) { trustedRootId = wh_KeyId_TranslateFromClient( WH_KEYTYPE_WRAPPED, server->comm->client_id, - req.trustedRootNvmId); + req.trustedRootId); } rc = WH_SERVER_NVM_LOCK(server); @@ -869,12 +869,12 @@ int wh_Server_HandleCertRequest(whServerContext* server, uint16_t magic, wh_MessageCert_TranslateVerifyDmaRequest( magic, (whMessageCert_VerifyDmaRequest*)req_packet, &req); - /* Translate trustedRootNvmId if client set wrapped flag */ - trustedRootId = req.trustedRootNvmId; - if (req.trustedRootNvmId & WH_KEYID_CLIENT_WRAPPED_FLAG) { + /* Translate trustedRootId if client set wrapped flag */ + trustedRootId = req.trustedRootId; + if (req.trustedRootId & WH_KEYID_CLIENT_WRAPPED_FLAG) { trustedRootId = wh_KeyId_TranslateFromClient( WH_KEYTYPE_WRAPPED, server->comm->client_id, - req.trustedRootNvmId); + req.trustedRootId); } /* Process client address */ diff --git a/wolfhsm/wh_client.h b/wolfhsm/wh_client.h index 4663a2d5c..7e7f1ae84 100644 --- a/wolfhsm/wh_client.h +++ b/wolfhsm/wh_client.h @@ -2054,12 +2054,12 @@ int wh_Client_CertReadTrusted(whClientContext* c, whNvmId id, uint8_t* cert, * @param[in] c Pointer to the client context. * @param[in] cert Pointer to the certificate data to verify. * @param[in] cert_len Length of the certificate data. - * @param[in] trustedRootNvmId NVM ID of the trusted root certificate to verify + * @param[in] trustedRootId ID of the trusted root certificate to verify * against. * @return int Returns 0 on success, or a negative error code on failure. */ int wh_Client_CertVerifyRequest(whClientContext* c, const uint8_t* cert, - uint32_t cert_len, whNvmId trustedRootNvmId); + uint32_t cert_len, whKeyId trustedRootId); /** * @brief Receives a response from the server after verifying a certificate. @@ -2085,13 +2085,13 @@ int wh_Client_CertVerifyResponse(whClientContext* c, int32_t* out_rc); * @param[in] c Pointer to the client context. * @param[in] cert Pointer to the certificate data to verify. * @param[in] cert_len Length of the certificate data. - * @param[in] trustedRootNvmId NVM ID of the trusted root certificate to verify + * @param[in] trustedRootId ID of the trusted root certificate to verify * against. * @param[out] out_rc Pointer to store the response code from the server. * @return int Returns 0 on success, or a negative error code on failure. */ int wh_Client_CertVerify(whClientContext* c, const uint8_t* cert, - uint32_t cert_len, whNvmId trustedRootNvmId, + uint32_t cert_len, whKeyId trustedRootId, int32_t* out_rc); /** @@ -2104,7 +2104,7 @@ int wh_Client_CertVerify(whClientContext* c, const uint8_t* cert, * @param[in] c Pointer to the client context. * @param[in] cert Pointer to the certificate data to verify. * @param[in] cert_len Length of the certificate data. - * @param[in] trustedRootNvmId NVM ID of the trusted root certificate to verify + * @param[in] trustedRootId ID of the trusted root certificate to verify * against. * @param[in] cachedKeyFlags NVM usage flags to apply when caching the leaf * public key (e.g., WH_NVM_FLAGS_USAGE_VERIFY, WH_NVM_FLAGS_USAGE_SIGN). @@ -2114,7 +2114,7 @@ int wh_Client_CertVerify(whClientContext* c, const uint8_t* cert, */ int wh_Client_CertVerifyAndCacheLeafPubKeyRequest( whClientContext* c, const uint8_t* cert, uint32_t cert_len, - whNvmId trustedRootNvmId, whNvmFlags cachedKeyFlags, whKeyId keyId); + whKeyId trustedRootId, whNvmFlags cachedKeyFlags, whKeyId keyId); /** * @brief Receives a response from the server after verifying a certificate and @@ -2146,7 +2146,7 @@ int wh_Client_CertVerifyAndCacheLeafPubKeyResponse(whClientContext* c, * @param[in] c Pointer to the client context. * @param[in] cert Pointer to the certificate data to verify. * @param[in] cert_len Length of the certificate data. - * @param[in] trustedRootNvmId NVM ID of the trusted root certificate to verify + * @param[in] trustedRootId ID of the trusted root certificate to verify * against. * @param[in] cachedKeyFlags NVM usage flags to apply when caching the leaf * public key (e.g., WH_NVM_FLAGS_USAGE_VERIFY, WH_NVM_FLAGS_USAGE_SIGN). @@ -2158,7 +2158,7 @@ int wh_Client_CertVerifyAndCacheLeafPubKeyResponse(whClientContext* c, */ int wh_Client_CertVerifyAndCacheLeafPubKey( whClientContext* c, const uint8_t* cert, uint32_t cert_len, - whNvmId trustedRootNvmId, whNvmFlags cachedKeyFlags, whKeyId* inout_keyId, + whKeyId trustedRootId, whNvmFlags cachedKeyFlags, whKeyId* inout_keyId, int32_t* out_rc); @@ -2272,17 +2272,18 @@ int wh_Client_CertReadTrustedDma(whClientContext* c, whNvmId id, void* cert, * @brief Sends a request to verify a certificate using DMA. * * This function prepares and sends a request to verify a certificate using DMA. - * This function does not block; it returns immediately after sending the request. + * This function does not block; it returns immediately after sending the + * request. * * @param[in] c Pointer to the client context. * @param[in] cert Pointer to the certificate data to verify. * @param[in] cert_len Length of the certificate data. - * @param[in] trustedRootNvmId NVM ID of the trusted root certificate to verify + * @param[in] trustedRootId ID of the trusted root certificate to verify * against. * @return int Returns 0 on success, or a negative error code on failure. */ int wh_Client_CertVerifyDmaRequest(whClientContext* c, const void* cert, - uint32_t cert_len, whNvmId trustedRootNvmId); + uint32_t cert_len, whKeyId trustedRootId); /** * @brief Receives a response from the server after verifying a certificate @@ -2310,13 +2311,13 @@ int wh_Client_CertVerifyDmaResponse(whClientContext* c, int32_t* out_rc); * @param[in] c Pointer to the client context. * @param[in] cert Pointer to the certificate data to verify. * @param[in] cert_len Length of the certificate data. - * @param[in] trustedRootNvmId NVM ID of the trusted root certificate to verify + * @param[in] trustedRootId ID of the trusted root certificate to verify * against. * @param[out] out_rc Pointer to store the response code from the server. * @return int Returns 0 on success, or a negative error code on failure. */ int wh_Client_CertVerifyDma(whClientContext* c, const void* cert, - uint32_t cert_len, whNvmId trustedRootNvmId, + uint32_t cert_len, whKeyId trustedRootId, int32_t* out_rc); @@ -2332,7 +2333,7 @@ int wh_Client_CertVerifyDma(whClientContext* c, const void* cert, * @param[in] c Pointer to the client context. * @param[in] cert Pointer to the certificate data to verify. * @param[in] cert_len Length of the certificate data. - * @param[in] trustedRootNvmId NVM ID of the trusted root certificate to verify + * @param[in] trustedRootId ID of the trusted root certificate to verify * against. * @param[in] cachedKeyFlags NVM usage flags to apply when caching the leaf * public key (e.g., WH_NVM_FLAGS_USAGE_VERIFY, WH_NVM_FLAGS_USAGE_SIGN). @@ -2342,7 +2343,7 @@ int wh_Client_CertVerifyDma(whClientContext* c, const void* cert, */ int wh_Client_CertVerifyDmaAndCacheLeafPubKeyRequest( whClientContext* c, const void* cert, uint32_t cert_len, - whNvmId trustedRootNvmId, whNvmFlags cachedKeyFlags, whKeyId keyId); + whKeyId trustedRootId, whNvmFlags cachedKeyFlags, whKeyId keyId); /** * @brief Receives a response from the server after verifying a certificate @@ -2376,7 +2377,7 @@ int wh_Client_CertVerifyDmaAndCacheLeafPubKeyResponse(whClientContext* c, * @param[in] c Pointer to the client context. * @param[in] cert Pointer to the certificate data to verify. * @param[in] cert_len Length of the certificate data. - * @param[in] trustedRootNvmId NVM ID of the trusted root certificate to verify + * @param[in] trustedRootId ID of the trusted root certificate to verify * against. * @param[in] cachedKeyFlags NVM usage flags to apply when caching the leaf * public key (e.g., WH_NVM_FLAGS_USAGE_VERIFY, WH_NVM_FLAGS_USAGE_SIGN). @@ -2388,7 +2389,7 @@ int wh_Client_CertVerifyDmaAndCacheLeafPubKeyResponse(whClientContext* c, */ int wh_Client_CertVerifyDmaAndCacheLeafPubKey( whClientContext* c, const void* cert, uint32_t cert_len, - whNvmId trustedRootNvmId, whNvmFlags cachedKeyFlags, whKeyId* inout_keyId, + whKeyId trustedRootId, whNvmFlags cachedKeyFlags, whKeyId* inout_keyId, int32_t* out_rc); @@ -2404,13 +2405,12 @@ int wh_Client_CertVerifyDmaAndCacheLeafPubKey( * @param[in] c Pointer to the client context. * @param[in] cert Pointer to the attribute certificate data to verify. * @param[in] cert_len Length of the attribute certificate data. - * @param[in] trustedRootNvmId NVM ID of the trusted root certificate to verify + * @param[in] trustedRootId ID of the trusted root certificate to verify * against. * @return int Returns 0 on success, or a negative error code on failure. */ int wh_Client_CertVerifyAcertRequest(whClientContext* c, const void* cert, - uint32_t cert_len, - whNvmId trustedRootNvmId); + uint32_t cert_len, whKeyId trustedRootId); /** * @brief Receives a response from the server after verifying an attribute @@ -2438,13 +2438,13 @@ int wh_Client_CertVerifyAcertResponse(whClientContext* c, int32_t* out_rc); * @param[in] c Pointer to the client context. * @param[in] cert Pointer to the attribute certificate data to verify. * @param[in] cert_len Length of the attribute certificate data. - * @param[in] trustedRootNvmId NVM ID of the trusted root certificate to verify + * @param[in] trustedRootId ID of the trusted root certificate to verify * against. * @param[out] out_rc Pointer to store the response code from the server. * @return int Returns 0 on success, or a negative error code on failure. */ int wh_Client_CertVerifyAcert(whClientContext* c, const void* cert, - uint32_t cert_len, whNvmId trustedRootNvmId, + uint32_t cert_len, whKeyId trustedRootId, int32_t* out_rc); /** @@ -2457,13 +2457,13 @@ int wh_Client_CertVerifyAcert(whClientContext* c, const void* cert, * @param[in] c Pointer to the client context. * @param[in] cert Pointer to the attribute certificate data to verify. * @param[in] cert_len Length of the attribute certificate data. - * @param[in] trustedRootNvmId NVM ID of the trusted root certificate to verify + * @param[in] trustedRootId ID of the trusted root certificate to verify * against. * @return int Returns 0 on success, or a negative error code on failure. */ int wh_Client_CertVerifyAcertDmaRequest(whClientContext* c, const void* cert, uint32_t cert_len, - whNvmId trustedRootNvmId); + whKeyId trustedRootId); /** * @brief Receives a response from the server after verifying an attribute @@ -2546,13 +2546,13 @@ int wh_Client_DmaProcessClientAddress(struct whClientContext_t* client, * @param[in] c Pointer to the client context. * @param[in] cert Pointer to the attribute certificate data to verify. * @param[in] cert_len Length of the attribute certificate data. - * @param[in] trustedRootNvmId NVM ID of the trusted root certificate to verify + * @param[in] trustedRootId ID of the trusted root certificate to verify * against. * @param[out] out_rc Pointer to store the response code from the server. * @return int Returns 0 on success, or a negative error code on failure. */ int wh_Client_CertVerifyAcertDma(whClientContext* c, const void* cert, - uint32_t cert_len, whNvmId trustedRootNvmId, + uint32_t cert_len, whKeyId trustedRootId, int32_t* out_rc); #endif /* WOLFHSM_CFG_DMA */ @@ -2695,7 +2695,7 @@ int wh_Client_CertUnwrapAndExportResponse(whClientContext* ctx, * Convenience wrapper around wh_Client_KeyUnwrapAndCache. After this call, * the certificate is cached on the server and can be used as the trusted root * in wh_Client_CertVerify by passing WH_CLIENT_KEYID_MAKE_WRAPPED(*out_certId) - * as the trustedRootNvmId parameter. This function will block until the entire + * as the trustedRootId parameter. This function will block until the entire * operation completes or an error occurs. * * @param[in] ctx Pointer to the client context. diff --git a/wolfhsm/wh_message_cert.h b/wolfhsm/wh_message_cert.h index c2370bd31..3e448138f 100644 --- a/wolfhsm/wh_message_cert.h +++ b/wolfhsm/wh_message_cert.h @@ -115,7 +115,7 @@ int wh_MessageCert_TranslateReadTrustedResponse( /* Verify Request */ typedef struct { uint32_t cert_len; - whNvmId trustedRootNvmId; + whKeyId trustedRootId; uint16_t flags; whNvmFlags cachedKeyFlags; whKeyId keyId; @@ -170,7 +170,7 @@ int wh_MessageCert_TranslateReadTrustedDmaRequest( typedef struct { uint64_t cert_addr; uint32_t cert_len; - whNvmId trustedRootNvmId; + whKeyId trustedRootId; uint16_t flags; whNvmFlags cachedKeyFlags; whKeyId keyId; @@ -198,7 +198,7 @@ int wh_MessageCert_TranslateVerifyDmaResponse( /* Verify ACERT Request */ typedef struct { uint32_t cert_len; - whNvmId trustedRootNvmId; + whKeyId trustedRootId; uint8_t WH_PAD[2]; /* Attribute Certificate data follows */ } whMessageCert_VerifyAcertRequest; From 5ca142e247bdcb801b2dc0f7584ed82557ce5a8a Mon Sep 17 00:00:00 2001 From: Brett Nicholas <7547222+bigbrett@users.noreply.github.com> Date: Wed, 25 Mar 2026 14:23:57 -0600 Subject: [PATCH 8/8] copilot feedback --- docs/draft/wrapped-certs.md | 4 +++- src/wh_client_cert.c | 8 +++---- wolfhsm/wh_client.h | 47 ++++++++++++++++++++++--------------- 3 files changed, 35 insertions(+), 24 deletions(-) diff --git a/docs/draft/wrapped-certs.md b/docs/draft/wrapped-certs.md index 8c40d3022..9fef05e68 100644 --- a/docs/draft/wrapped-certs.md +++ b/docs/draft/wrapped-certs.md @@ -123,7 +123,9 @@ cache, not in NVM." The same pattern works for: - `wh_Client_CertVerifyDma` (DMA path) -- `wh_Client_CertReadTrusted` / `wh_Client_CertReadTrustedDma` (read-back) +- `wh_Client_CertReadTrusted` / `wh_Client_CertReadTrustedDma` (read-back a + cached cert by passing `WH_CLIENT_KEYID_MAKE_WRAPPED(cachedCertId)` as the + `id` parameter) - `wh_Client_CertVerifyAcert` / `wh_Client_CertVerifyAcertDma` (attribute certs) ### 5. Cleanup diff --git a/src/wh_client_cert.c b/src/wh_client_cert.c index 4ac9a53f9..b823fd0a2 100644 --- a/src/wh_client_cert.c +++ b/src/wh_client_cert.c @@ -276,7 +276,7 @@ int wh_Client_CertEraseTrusted(whClientContext* c, whNvmId id, int32_t* out_rc) } /* Get a trusted certificate */ -int wh_Client_CertReadTrustedRequest(whClientContext* c, whNvmId id, +int wh_Client_CertReadTrustedRequest(whClientContext* c, whKeyId id, uint32_t cert_len) { (void)cert_len; @@ -339,7 +339,7 @@ int wh_Client_CertReadTrustedResponse(whClientContext* c, uint8_t* cert, return rc; } -int wh_Client_CertReadTrusted(whClientContext* c, whNvmId id, uint8_t* cert, +int wh_Client_CertReadTrusted(whClientContext* c, whKeyId id, uint8_t* cert, uint32_t* cert_len, int32_t* out_rc) { int rc = 0; @@ -593,7 +593,7 @@ int wh_Client_CertAddTrustedDma(whClientContext* c, whNvmId id, return rc; } -int wh_Client_CertReadTrustedDmaRequest(whClientContext* c, whNvmId id, +int wh_Client_CertReadTrustedDmaRequest(whClientContext* c, whKeyId id, void* cert, uint32_t cert_len) { whMessageCert_ReadTrustedDmaRequest req = {0}; @@ -641,7 +641,7 @@ int wh_Client_CertReadTrustedDmaResponse(whClientContext* c, int32_t* out_rc) return rc; } -int wh_Client_CertReadTrustedDma(whClientContext* c, whNvmId id, void* cert, +int wh_Client_CertReadTrustedDma(whClientContext* c, whKeyId id, void* cert, uint32_t cert_len, int32_t* out_rc) { int rc = 0; diff --git a/wolfhsm/wh_client.h b/wolfhsm/wh_client.h index 7e7f1ae84..17c92caed 100644 --- a/wolfhsm/wh_client.h +++ b/wolfhsm/wh_client.h @@ -1993,18 +1993,20 @@ int wh_Client_CertEraseTrustedResponse(whClientContext* c, int32_t* out_rc); int wh_Client_CertEraseTrusted(whClientContext* c, whNvmId id, int32_t* out_rc); /** - * @brief Sends a request to read a trusted certificate from NVM storage. + * @brief Sends a request to read a trusted certificate. * - * This function prepares and sends a request to read a trusted certificate - * from NVM storage. This function does not block; it returns immediately after - * sending the request. + * This function prepares and sends a request to read a trusted certificate. + * The id parameter accepts either an NVM ID for certificates stored in NVM, + * or a wrapped/cached key ID (via WH_CLIENT_KEYID_MAKE_WRAPPED()) for + * certificates cached through wh_Client_CertUnwrapAndCache(). This function + * does not block; it returns immediately after sending the request. * * @param[in] c Pointer to the client context. - * @param[in] id The NVM ID of the certificate to retrieve. + * @param[in] id The key ID of the certificate to retrieve (NVM or wrapped). * @param[in] cert_len Maximum length of the certificate buffer. * @return int Returns 0 on success, or a negative error code on failure. */ -int wh_Client_CertReadTrustedRequest(whClientContext* c, whNvmId id, +int wh_Client_CertReadTrustedRequest(whClientContext* c, whKeyId id, uint32_t cert_len); /** @@ -2031,17 +2033,20 @@ int wh_Client_CertReadTrustedResponse(whClientContext* c, uint8_t* cert, * * This function handles the complete process of sending a request to read a * trusted certificate and receiving the response. It blocks until the entire - * operation is complete or an error occurs. + * operation is complete or an error occurs. The id parameter accepts either an + * NVM ID for certificates stored in NVM, or a wrapped/cached key ID (via + * WH_CLIENT_KEYID_MAKE_WRAPPED()) for certificates cached through + * wh_Client_CertUnwrapAndCache(). * * @param[in] c Pointer to the client context. - * @param[in] id The NVM ID of the certificate to retrieve. + * @param[in] id The key ID of the certificate to retrieve (NVM or wrapped). * @param[out] cert Pointer to store the certificate data. * @param[in,out] cert_len Pointer to the maximum length of the certificate * buffer. On output, contains the actual length of the certificate. * @param[out] out_rc Pointer to store the response code from the server. * @return int Returns 0 on success, or a negative error code on failure. */ -int wh_Client_CertReadTrusted(whClientContext* c, whNvmId id, uint8_t* cert, +int wh_Client_CertReadTrusted(whClientContext* c, whKeyId id, uint8_t* cert, uint32_t* cert_len, int32_t* out_rc); /** @@ -2219,20 +2224,21 @@ int wh_Client_CertAddTrustedDma(whClientContext* c, whNvmId id, int32_t* out_rc); /** - * @brief Sends a request to read a trusted certificate from NVM storage using - * DMA. + * @brief Sends a request to read a trusted certificate using DMA. * - * This function prepares and sends a request to read a trusted certificate from - * NVM storage using DMA. This function does not block; it returns immediately - * after sending the request. + * This function prepares and sends a request to read a trusted certificate + * using DMA. The id parameter accepts either an NVM ID for certificates stored + * in NVM, or a wrapped/cached key ID (via WH_CLIENT_KEYID_MAKE_WRAPPED()) for + * certificates cached through wh_Client_CertUnwrapAndCache(). This function + * does not block; it returns immediately after sending the request. * * @param[in] c Pointer to the client context. - * @param[in] id NVM ID of the trusted certificate to get. + * @param[in] id The key ID of the trusted certificate to get (NVM or wrapped). * @param[in] cert Pointer to buffer to store the certificate data. * @param[in] cert_len Length of the certificate buffer. * @return int Returns 0 on success, or a negative error code on failure. */ -int wh_Client_CertReadTrustedDmaRequest(whClientContext* c, whNvmId id, +int wh_Client_CertReadTrustedDmaRequest(whClientContext* c, whKeyId id, void* cert, uint32_t cert_len); /** @@ -2256,16 +2262,19 @@ int wh_Client_CertReadTrustedDmaResponse(whClientContext* c, int32_t* out_rc); * * This function handles the complete process of sending a request to read a * trusted certificate using DMA and receiving the response. It blocks until the - * entire operation is complete or an error occurs. + * entire operation is complete or an error occurs. The id parameter accepts + * either an NVM ID for certificates stored in NVM, or a wrapped/cached key ID + * (via WH_CLIENT_KEYID_MAKE_WRAPPED()) for certificates cached through + * wh_Client_CertUnwrapAndCache(). * * @param[in] c Pointer to the client context. - * @param[in] id NVM ID of the trusted certificate to get. + * @param[in] id The key ID of the trusted certificate to get (NVM or wrapped). * @param[in] cert Pointer to buffer to store the certificate data. * @param[in] cert_len Length of the certificate buffer. * @param[out] out_rc Pointer to store the response code from the server. * @return int Returns 0 on success, or a negative error code on failure. */ -int wh_Client_CertReadTrustedDma(whClientContext* c, whNvmId id, void* cert, +int wh_Client_CertReadTrustedDma(whClientContext* c, whKeyId id, void* cert, uint32_t cert_len, int32_t* out_rc); /**