Conversation
7f3ddc0 to
5ee1a25
Compare
There was a problem hiding this comment.
Pull request overview
Adds “wrapped cached certificates” support so clients can store trusted roots as AES-GCM wrapped blobs and have the server unwrap/cache them in RAM for cert verification, rather than requiring NVM-resident roots.
Changes:
- Added client-side cert wrap/unwrap/export/cache convenience APIs (guarded by
WOLFHSM_CFG_KEYWRAP). - Updated server cert manager + request handlers to route trusted-root reads/verifies to keystore cache when IDs indicate wrapped/cached certs.
- Added documentation and new tests for cached-root verify/readtrusted (including DMA variants).
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
wolfhsm/wh_server_cert.h |
Updates cert APIs to accept whKeyId and documents cache vs NVM behavior. |
src/wh_server_cert.c |
Implements cache-aware trusted-root reads and request-handler routing for wrapped IDs. |
wolfhsm/wh_client.h |
Adds public client API declarations for cert wrap/unwrap/export/cache helpers. |
src/wh_client_cert.c |
Implements the new client cert wrap/unwrap helper functions via keywrap APIs. |
test/wh_test_cert.c |
Adds tests for cached-root verify/readtrusted, including DMA coverage. |
docs/draft/wrapped-certs.md |
Adds draft documentation for workflow, ID encoding, and handler routing. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #318
Scan targets checked: wolfhsm-core-bugs, wolfhsm-src
Findings: 1
Medium (1)
DMA READTRUSTED cache path reads cert data before non-exportable check
File: src/wh_server_cert.c:652-670
Function: wh_Server_HandleCertRequest
Category: Logic errors
In the WH_MESSAGE_CERT_ACTION_READTRUSTED_DMA handler, the new cache path calls wh_Server_KeystoreReadKey which writes the full certificate plaintext into cert_data (a DMA-mapped client-writable buffer obtained via wh_Server_DmaProcessClientAddress) before checking WH_NVM_FLAGS_NONEXPORTABLE on the metadata. In contrast, the NVM path in the same handler correctly calls wh_Nvm_GetMetadata first and only reads the certificate data (via wh_Server_CertReadTrusted) if the non-exportable flag is not set. This means for cached wrapped certs marked non-exportable, the plaintext is written to the DMA buffer before resp.rc is set to WH_ERROR_ACCESS. The non-DMA READTRUSTED handler has the same read-before-check ordering in its cache path, but cert_data is a server-local buffer there and resp.cert_len is not set on error, so the data is not included in the response.
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;
}
}
}Recommendation: Read the cached cert into a temporary server-side buffer first, check WH_NVM_FLAGS_NONEXPORTABLE on the returned metadata, and only copy to the DMA cert_data buffer if the cert is exportable. This matches the check-before-read pattern used in the NVM path. Alternatively, if wh_Server_KeystoreReadKey (or a variant) supports returning metadata without data (e.g., by passing NULL for the data buffer), use that to check exportability before the full read.
This review was generated automatically by Fenrir. Findings are non-blocking.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #318
Scan targets checked: wolfhsm-core-bugs, wolfhsm-src
Findings: 2
Medium (2)
Cache path in wh_Server_CertReadTrusted returns wrong error code on buffer-too-small
File: src/wh_server_cert.c:241-249
Function: wh_Server_CertReadTrusted
Category: Incorrect error handling
The newly added cache path in wh_Server_CertReadTrusted delegates to wh_Server_KeystoreReadKey, which returns WH_ERROR_NOSPACE when the certificate is larger than the provided buffer (see wh_Server_KeystoreReadKey line 652: if (cacheMeta->len > *outSz) return WH_ERROR_NOSPACE). However, the existing NVM path returns WH_ERROR_BUFFER_SIZE for the same condition. Furthermore, this PR's own newly-added @return documentation for this function explicitly states: "If the stored certificate data is too large for the buffer, WH_ERROR_BUFFER_SIZE will be returned." Any caller that checks for WH_ERROR_BUFFER_SIZE to detect and handle buffer-too-small (e.g., to retry with a larger buffer) will not catch WH_ERROR_NOSPACE from the cache path, leading to inconsistent error handling depending on whether the cert is in NVM or cache.
/* 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;
}Recommendation: After the wh_Server_KeystoreReadKey call, translate WH_ERROR_NOSPACE to WH_ERROR_BUFFER_SIZE and update *inout_cert_len with the actual certificate size (available from meta.len after the call, since wh_Server_KeystoreReadKey populates outMeta before the size check). For example:
if (rc == WH_ERROR_NOSPACE) {
*inout_cert_len = meta.len;
return WH_ERROR_BUFFER_SIZE;
}Non-exportable certificate data written to DMA buffer before access check in cache path
File: src/wh_server_cert.c:652-671
Function: wh_Server_HandleCertRequest
Category: DMA security issues
In the WH_MESSAGE_CERT_ACTION_READTRUSTED_DMA handler, the new cache path calls wh_Server_KeystoreReadKey to read both the certificate data and metadata into cert_data before checking WH_NVM_FLAGS_NONEXPORTABLE. If the certificate is non-exportable, resp.rc is set to WH_ERROR_ACCESS, but the plaintext certificate data has already been written into the DMA-mapped cert_data buffer. In shared-memory DMA configurations (e.g., POSIX transport), cert_data may point directly to client-accessible memory, meaning the client can read the non-exportable certificate data from its DMA buffer despite the access denial. This contrasts with the NVM path in the same handler, which correctly checks WH_NVM_FLAGS_NONEXPORTABLE via wh_Nvm_GetMetadata before reading any certificate data into the buffer.
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;
}
}
}Recommendation: Match the NVM path's check-before-read pattern. Either (1) add a metadata-only query to the keystore cache API and check WH_NVM_FLAGS_NONEXPORTABLE before reading the data, or (2) zero the cert_data buffer with XMEMSET(cert_data, 0, cert_len) immediately after setting resp.rc = WH_ERROR_ACCESS to prevent any residual data from being accessible via DMA. Option (2) is the simpler fix and provides defense-in-depth even if the DMA post-processing is conditional on success.
This review was generated automatically by Fenrir. Findings are non-blocking.
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #318
Scan targets checked: wolfhsm-core-bugs, wolfhsm-src
Findings: 1
High (1)
Buffer overflow in non-DMA READTRUSTED handler cache path due to missing cert_len reset
File: src/wh_server_cert.c:498-517
Function: wh_Server_HandleCertRequest
Category: Incorrect error handling
In the WH_MESSAGE_CERT_ACTION_READTRUSTED case, the cache path calls wh_Server_KeystoreReadKey twice: first with out=NULL to get metadata, then with cert_data to get the actual data. The first call overwrites cert_len with cacheMeta->len (the actual stored cert size) via the *outSz = cacheMeta->len assignment in wh_Server_KeystoreReadKey. The second call then passes this overwritten cert_len as *outSz, making the size check (cacheMeta->len > *outSz) always evaluate to false. This means memcpy(out, cacheBuffer, cacheMeta->len) proceeds unconditionally, even if the cert is larger than the actual output buffer (cert_data), which is bounded by min(max_transport_cert_len, WOLFHSM_CFG_MAX_CERT_SIZE). The DMA READTRUSTED handler correctly avoids this by resetting cert_len = req.cert_len before the second call.
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);
if (rc == WH_ERROR_OK) {
resp.cert_len = cert_len;
}Recommendation: Save the original buffer capacity before the first call and restore it before the second call, matching the pattern used in the DMA handler. For example: uint32_t buf_capacity = cert_len; before the first call, then cert_len = buf_capacity; before the second wh_Server_KeystoreReadKey call.
This review was generated automatically by Fenrir. Findings are non-blocking.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Allows clients to use trusted root certificates that are AES-GCM wrapped and cached in server RAM rather than committed to NVM. The client stores the wrapped blob cheaply and unwraps it on demand for verification.
Changes