Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions docs/src/5-Features.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ This chapter provides a detailed overview of the high level features that wolfHS
- [Object Metadata and Access Attributes](#object-metadata-and-access-attributes)
- [NVM Backends](#nvm-backends)
- [Flash Abstraction](#flash-abstraction)
- [Optional NVM Backing](#optional-nvm-backing)
- [Keystore](#keystore)
- [Key Cache, Key IDs, and NVM Backing Store](#key-cache-key-ids-and-nvm-backing-store)
- [Global Keys](#global-keys)
Expand Down Expand Up @@ -236,6 +237,31 @@ wolfHSM ships with two reference flash drivers usable on host platforms and in t

Vendor-supplied flash drivers ship with the platform ports under `port/<vendor>/`. New platforms are integrated into wolfHSM by implementing the `whFlashCb` callback set against the device's flash controller; nothing in the NVM library above this layer needs to change.

### Optional NVM Backing

The NVM subsystem described above is **optional**. A server can be initialized with `whServerConfig.nvm == NULL`, in which case it runs with no persistent object store at all. This suits clients and cores that only need cached-key cryptography and have no flash available for an NVM partition — at the cost of a reduced feature set, since everything that depends on persistent storage becomes unavailable.

With no NVM, the [keystore](#keystore) is effectively cache-only. A key is served from the RAM [key cache](#key-cache-key-ids-and-nvm-backing-store) when present; a cache miss would normally fall back to NVM, but with no NVM configured it simply reports `WH_ERROR_NOTFOUND` — the same result as if the key were absent from the store. Keys are made available by *priming* the cache out of band: either by caching key material directly on the server, or by having the client supply [wrapped keys](#wrapped-keys) that are unwrapped directly into the cache.

What works with no NVM:

- Cryptographic operations against keys that are primed in the cache.
- Key caching, eviction, and (cache-only) erase.
- SHE encrypt/decrypt/CMAC and secure boot against keys primed in the cache.
- Key wrap/unwrap and unwrap-and-cache, provided the wrapping key (KEK) is primed in the cache.

What requires NVM, and so fails gracefully at runtime when it is absent (returning an error rather than crashing):

- The NVM object request API (list/read/add/destroy).
- [Certificate-chain verification](#certificate-management) against trusted roots stored in NVM.
- [Monotonic counters](#non-volatile-monotonic-counters).
- Committing a cached key to persistent storage (`wh_Server_KeystoreCommitKey`).
- [SHE](#autosar-she-subsystem) key persistence and the SHE PRNG seed (`LOAD_KEY` of non-RAM keys, `INIT_RND`, `EXTEND_SEED`), and image-signature loading.

> **Note**: When [global keys](#global-keys) (`WOLFHSM_CFG_GLOBAL_KEYS`) are enabled, the shared global key cache normally lives inside the NVM context. With no NVM there is no shared store, so global keys (USER `0`) are served from the per-context local cache instead. They remain usable when primed, but are not shared across server contexts as they would be with NVM present.

When NVM **is** configured, all of the above behavior is unchanged.

## Keystore

The keystore manages the lifecycle of cryptographic key material on the server. It sits on top of [NVM](#non-volatile-memory-nvm) and is the layer every crypto operation goes through to reference a key. Clients refer to keys by a stable 16-bit identifier, not by the key bytes; the material stays server-side and is only returned through explicit, policy-checked operations. This is what lets the server enforce per-key usage policy, isolate keys between clients, and offload bulk crypto to hardware without exposing key bytes outside the trust boundary.
Expand Down
12 changes: 10 additions & 2 deletions src/wh_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -712,17 +712,25 @@ int wh_Server_HandleRequestMessage(whServerContext* server)
#ifdef WOLFHSM_CFG_THREADSAFE
int wh_Server_NvmLock(whServerContext* server)
{
if (server == NULL || server->nvm == NULL) {
if (server == NULL) {
return WH_ERROR_BADARGS;
}
/* NVM is optional. With no NVM there's no shared state to protect, so the
* lock is a no-op and cache-only operations can still run. */
if (server->nvm == NULL) {
return WH_ERROR_OK;
}
return wh_Lock_Acquire(&server->nvm->lock);
}

int wh_Server_NvmUnlock(whServerContext* server)
{
if (server == NULL || server->nvm == NULL) {
if (server == NULL) {
return WH_ERROR_BADARGS;
}
if (server->nvm == NULL) {
return WH_ERROR_OK;
}
return wh_Lock_Release(&server->nvm->lock);
}
#endif /* WOLFHSM_CFG_THREADSAFE */
Expand Down
3 changes: 1 addition & 2 deletions src/wh_server_counter.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,7 @@ int wh_Server_HandleCounter(whServerContext* server, uint16_t magic,
whNvmMetadata meta[1] = {{0}};
uint32_t* counter = (uint32_t*)(&meta->label);

if (server == NULL || server->nvm == NULL || req_packet == NULL ||
out_resp_size == NULL) {
if (server == NULL || req_packet == NULL || out_resp_size == NULL) {
return WH_ERROR_BADARGS;
}

Expand Down
66 changes: 52 additions & 14 deletions src/wh_server_keystore.c
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,15 @@ static int _IsGlobalKey(whKeyId keyId)
* When WOLFHSM_CFG_GLOBAL_KEYS is enabled, routes to global cache if keyId
* has USER == 0, otherwise routes to local cache. When disabled, always
* routes to local cache.
*
* The global cache lives in the NVM context, which is optional. With no NVM
* there is no global cache, so global keys fall back to the local cache.
*/
static whKeyCacheContext* _GetCacheContext(whServerContext* server,
whKeyId keyId)
{
#ifdef WOLFHSM_CFG_GLOBAL_KEYS
if (_IsGlobalKey(keyId)) {
if (_IsGlobalKey(keyId) && (server->nvm != NULL)) {
return &server->nvm->globalCache;
}
#else
Expand Down Expand Up @@ -160,8 +163,8 @@ static int _KeystoreCheckPolicy(whServerContext* server, whKsOp op,
return ret;
}

/* Check NVM if not in cache */
if (!foundInCache) {
/* Check NVM if not in cache. No NVM means the key can't be there. */
if (!foundInCache && (server->nvm != NULL)) {
ret = wh_Nvm_GetMetadata(server->nvm, keyId, &nvmMeta);
if (ret == WH_ERROR_OK) {
foundInNvm = 1;
Expand Down Expand Up @@ -619,7 +622,12 @@ int wh_Server_KeystoreGetUniqueId(whServerContext* server, whNvmId* inout_id)
return ret;
}

/* Check if keyId exists in NVM */
/* Check if keyId exists in NVM. With no NVM, not being in the cache
* is enough to make this ID unique. */
if (server->nvm == NULL) {
found = 1;
break;
}
ret = wh_Nvm_GetMetadata(server->nvm, buildId, NULL);
if (ret == WH_ERROR_NOTFOUND) {
/* key doesn't exist in NVM, we found a candidate ID */
Expand Down Expand Up @@ -806,6 +814,11 @@ int wh_Server_KeystoreFreshenKey(whServerContext* server, whKeyId keyId,
return WH_ERROR_NOTFOUND;
}

/* No NVM to check, so a cache miss means not found. */
if (server->nvm == NULL) {
return WH_ERROR_NOTFOUND;
}

/* Not in cache. Check if it is in NVM */
ret = wh_Nvm_GetMetadata(server->nvm, keyId, tmpMeta);
if (ret == WH_ERROR_OK) {
Expand Down Expand Up @@ -870,8 +883,14 @@ int wh_Server_KeystoreReadKey(whServerContext* server, whKeyId keyId,
return WH_ERROR_NOTFOUND;
}

/* Not in cache, try to read the metadata from NVM */
ret = wh_Nvm_GetMetadata(server->nvm, keyId, meta);
/* Not in cache, try to read the metadata from NVM. With no NVM the key is
* not found, but the SHE master-ecu fallback below still applies. */
if (server->nvm != NULL) {
ret = wh_Nvm_GetMetadata(server->nvm, keyId, meta);
}
else {
ret = WH_ERROR_NOTFOUND;
}
if (ret == 0) {
if (meta->len > *outSz)
return WH_ERROR_NOSPACE;
Expand Down Expand Up @@ -978,6 +997,8 @@ int wh_Server_KeystoreCommitKey(whServerContext* server, whNvmId keyId)
ret = _FindInKeyCache(ctx, keyId, NULL, NULL, &slotBuf, &slotMeta);
if (ret == WH_ERROR_OK) {
size = slotMeta->len;
/* Committing writes the cached key to NVM. With no NVM there is
* nowhere to persist it, so wh_Nvm_* returns an error. */
ret = wh_Nvm_AddObjectWithReclaim(server->nvm, slotMeta, size, slotBuf);
if (ret == 0) {
/* Mark key as committed using unified function */
Expand Down Expand Up @@ -1011,12 +1032,20 @@ int wh_Server_KeystoreEraseKey(whServerContext* server, whNvmId keyId)
/* remove the key from the cache if present */
(void)wh_Server_KeystoreEvictKey(server, keyId);

/* No NVM means there is nothing to destroy, same as erasing a key that
* was never there. */
if (server->nvm == NULL) {
return WH_ERROR_OK;
}

/* destroy the object */
return wh_Nvm_DestroyObjects(server->nvm, 1, &keyId);
}

int wh_Server_KeystoreEraseKeyChecked(whServerContext* server, whNvmId keyId)
{
int ret;

if ((server == NULL) || (WH_KEYID_ISERASED(keyId))) {
return WH_ERROR_BADARGS;
}
Expand All @@ -1025,8 +1054,14 @@ int wh_Server_KeystoreEraseKeyChecked(whServerContext* server, whNvmId keyId)
return WH_ERROR_ABORTED;
}

/* remove the key from the cache if present */
(void)wh_Server_KeystoreEvictKeyChecked(server, keyId);
/* remove the key from the cache if present, enforcing policy */
ret = wh_Server_KeystoreEvictKeyChecked(server, keyId);

/* With no NVM, the cache eviction above is the whole erase; return its
* result so policy and not-found errors still propagate. */
if (server->nvm == NULL) {
return ret;
}

/* destroy the object */
return wh_Nvm_DestroyObjectsChecked(server->nvm, 1, &keyId);
Expand Down Expand Up @@ -1066,12 +1101,15 @@ int wh_Server_KeystoreRevokeKey(whServerContext* server, whNvmId keyId)
return ret;
}

ret = wh_Nvm_GetMetadata(server->nvm, keyId, NULL);
if (ret == WH_ERROR_OK) {
isInNvm = 1;
}
else if (ret != WH_ERROR_NOTFOUND) {
return ret;
/* No NVM means the key can't be in NVM. */
if (server->nvm != NULL) {
ret = wh_Nvm_GetMetadata(server->nvm, keyId, NULL);
if (ret == WH_ERROR_OK) {
isInNvm = 1;
}
else if (ret != WH_ERROR_NOTFOUND) {
return ret;
}
}

/* be sure to have the key in the cache */
Expand Down
Loading
Loading