Skip to content

Commit 2c5c374

Browse files
committed
- Add support for global SHE keys
- change whnvmtool to fail-fast when an image generation error occurs - Enhance whnvmtool test coverage - Small code hygene fixes
1 parent 62eea4e commit 2c5c374

29 files changed

Lines changed: 1756 additions & 203 deletions

‎.github/workflows/build-and-test-refactor.yml‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ jobs:
7373
- name: Build and test refactor ASAN SHE
7474
run: cd test-refactor/posix && make clean && make -j SHE=1 ASAN=1 WOLFSSL_DIR=../../wolfssl && make run
7575

76+
# Build and test with global SHE keys (all SHE slots shared across clients)
77+
- name: Build and test refactor ASAN SHE_GLOBAL
78+
run: cd test-refactor/posix && make clean && make -j SHE_GLOBAL=1 ASAN=1 WOLFSSL_DIR=../../wolfssl && make run
79+
7680
# Build and test with DEBUG=1
7781
- name: Build and test refactor with DEBUG
7882
run: cd test-refactor/posix && make clean && make -j DEBUG=1 WOLFSSL_DIR=../../wolfssl && make run
@@ -85,6 +89,10 @@ jobs:
8589
- name: Build and test refactor with THREADSAFE and everything
8690
run: cd test-refactor/posix && make clean && make -j THREADSAFE=1 DMA=1 SHE=1 ASAN=1 WOLFSSL_DIR=../../wolfssl && make run
8791

92+
# Same, with global SHE keys
93+
- name: Build and test refactor with THREADSAFE and everything and SHE_GLOBAL
94+
run: cd test-refactor/posix && make clean && make -j THREADSAFE=1 DMA=1 SHE_GLOBAL=1 ASAN=1 WOLFSSL_DIR=../../wolfssl && make run
95+
8896
# Build and test in multithreaded mode with everything enabled and wolfCrypt tests
8997
- name: Build and test refactor with THREADSAFE and TESTWOLFCRYPT and everything
9098
run: cd test-refactor/posix && make clean && make -j THREADSAFE=1 TESTWOLFCRYPT=1 DMA=1 SHE=1 ASAN=1 WOLFSSL_DIR=../../wolfssl && make run

‎.github/workflows/build-and-test.yml‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ jobs:
7373
- name: Build and test ASAN SHE
7474
run: cd test && make clean && make -j SHE=1 ASAN=1 WOLFSSL_DIR=../wolfssl && make run
7575

76+
# Build and test with global SHE keys (all SHE slots shared across clients)
77+
- name: Build and test ASAN SHE_GLOBAL
78+
run: cd test && make clean && make -j SHE_GLOBAL=1 ASAN=1 WOLFSSL_DIR=../wolfssl && make run
79+
7680
# Build and test with per-client crypto affinity enabled (runs the crypto
7781
# affinity unit test, gated behind WOLFHSM_CFG_CRYPTO_AFFINITY)
7882
- name: Build and test CRYPTO_AFFINITY ASAN
@@ -94,6 +98,10 @@ jobs:
9498
- name: Build and test with THREADSAFE and everything
9599
run: cd test && make clean && make -j THREADSAFE=1 DMA=1 SHE=1 ASAN=1 WOLFSSL_DIR=../wolfssl && make run
96100

101+
# Same, with global SHE keys
102+
- name: Build and test with THREADSAFE and everything and SHE_GLOBAL
103+
run: cd test && make clean && make -j THREADSAFE=1 DMA=1 SHE_GLOBAL=1 ASAN=1 WOLFSSL_DIR=../wolfssl && make run
104+
97105
# Build and test in multithreaded mode with everything enabled and wolfCrypt tests
98106
- name: Build and test with THREADSAFE and TESTWOLFCRYPT and everything
99107
run: cd test && make clean && make -j THREADSAFE=1 TESTWOLFCRYPT=1 DMA=1 SHE=1 ASAN=1 WOLFSSL_DIR=../wolfssl && make run

‎docs/src/5-Features.md‎

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ This chapter provides a detailed overview of the high level features that wolfHS
4242
- [AUTOSAR SHE Subsystem](#autosar-she-subsystem)
4343
- [Client API and Command Set](#client-api-and-command-set)
4444
- [SHE Key Slots and the wolfHSM Keystore](#she-key-slots-and-the-wolfhsm-keystore)
45+
- [Global SHE Keys](#global-she-keys)
4546
- [Encrypted Key Update Protocol (M1–M5)](#encrypted-key-update-protocol-m1m5)
4647
- [Secure Boot](#secure-boot)
4748
- [Deterministic PRNG](#deterministic-prng)
@@ -347,6 +348,8 @@ Global keys interact with the cache and NVM tiers in the same way as local keys,
347348
348349
> **Security note**: Because a global key is reachable by every client connected to the server, the security boundary it provides is the server itself, not any particular client. Global keys should be reserved for material that is genuinely shared across the trust domains of the connected clients — typically vendor-provisioned roots and shared symmetric keys for inter-client communication — and should not be used as a workaround for per-client key management. Per-key usage flags (see [Key Usage Policies](#key-usage-policies)) apply to global keys exactly as they do to local keys, and should be used to constrain how a shared key may be used regardless of which client invokes the operation.
349350
351+
The [AUTOSAR SHE subsystem](#autosar-she-subsystem) can be layered onto this feature with `WOLFHSM_CFG_SHE_GLOBAL_KEYS`, which places all SHE slots in the global namespace so every client shares one SHE device; see [Global SHE Keys](#global-she-keys).
352+
350353
### Wrapped Keys
351354
352355
A key that lives entirely inside the server is protected by its trust boundary: only the server can read its bytes, and policy is enforced before every use. Some workflows still need to move key material outside that boundary — to back keys up to off-device storage, to transport a key between systems during provisioning, or even to support wolfHSM on an HSM platform without dedicated NVM. wolfHSM's **wrapped keys** feature (`WOLFHSM_CFG_KEYWRAP`) does this safely, with the server mediating every step.
@@ -695,6 +698,26 @@ The SHE spec also requires every key to carry a 28-bit monotonic update counter
695698

696699
`RAM_KEY` is the one exception to NVM-backed storage. The spec defines it as volatile, so the server caches the loaded key in its [key cache](#key-cache-key-ids-and-nvm-backing-store) but never calls into the NVM layer for it; eviction or reset clears it. All other slots, including `PRNG_SEED`, persist.
697700

701+
### Global SHE Keys
702+
703+
By default every SHE keyId carries the connecting client's ID in its USER field, so each client gets its own private set of sixteen SHE slots. That is convenient when clients are mutually distrusting, but it does not match the AUTOSAR model, where SHE is a single physical device with one fixed set of slots shared by every host core. Defining `WOLFHSM_CFG_SHE_GLOBAL_KEYS` (which requires both `WOLFHSM_CFG_GLOBAL_KEYS` and `WOLFHSM_CFG_SHE_EXTENSION`) switches to that model: **all** SHE slots — `SECRET_KEY` through `RAM_KEY` and `PRNG_SEED` — are built in the [global-keys](#global-keys) namespace (USER = `WH_KEYUSER_GLOBAL`, 0) and are shared by every client.
704+
705+
Nothing changes on the wire. Clients still name slots by their raw 0–15 slot number in every SHE command; the server resolves each SHE keyId into the global namespace internally. Two clients that program and use slot 4 now see the same key, and one shared `RAM_KEY` is visible to all. This is a build-time choice rather than a per-request flag because most SHE operations cannot carry one: the `CMD_LOAD_KEY` target and authorization slots are 4-bit fields inside the authenticated M1 message, and secure boot, PRNG, and RAM-key export address fixed slots.
706+
707+
The mode composes with the [wrapped keys](#wrapped-keys) interop:
708+
709+
- **Wrap-export** of a SHE slot needs no special flag — the server routes any `WH_KEYTYPE_SHE` target into the global namespace, so `wh_Client_KeyWrapExport(..., slot, WH_KEYTYPE_SHE, kek, ...)` names the same slot the SHE commands do.
710+
- **Unwrap-and-cache** normalizes a SHE blob's USER field to the global namespace after the ownership check, so a blob minted by a per-client build primes the slot the SHE commands actually read. The server-assigned id returned to the client carries `WH_KEYID_CLIENT_GLOBAL_FLAG`.
711+
- The **counter rollback guard** compares a primed blob's counter against the globally committed slot, so a stale blob cannot shadow a newer shared key.
712+
713+
Some deployment limits follow from where the global cache lives:
714+
715+
- **Sessions are per server, keys are shared.** The per-connection SHE runtime state — the secure-boot state machine, the ECU UID, the plaintext `RAM_KEY` load flag, and the PRNG working state — stays in each server's `whServerSheContext`. Each server must therefore still set its UID and run secure boot on its own even though it reads the shared boot keys. Servers that share an NVM must be provisioned with the same UID, or their `CMD_LOAD_KEY` M1 validation will diverge.
716+
- **NVM-less degrades to per-server.** The global cache is owned by the NVM context (`whNvmContext`), so a server started with `nvm == NULL` has no global cache and its global SHE keys fall back to that server's local cache. This is correct for a single NVM-less server but means multiple NVM-less servers do **not** share SHE state; sharing across servers requires a shared `whNvmContext`.
717+
- **One shared cache, sized for the slots.** With global SHE keys on, every client's SHE keys plus any global crypto keys share the one global cache; the default `WOLFHSM_CFG_SERVER_KEYCACHE_COUNT` (16 with SHE enabled) matches the sixteen SHE slots. Keys primed via unwrap-and-cache are uncommitted and are not evicted under cache pressure, and a client `KeyEvict` cannot target SHE-typed cache entries, so provisioning flows should keep the number of live uncommitted SHE entries small.
718+
719+
When `WOLFHSM_CFG_SHE_GLOBAL_KEYS` is left undefined, SHE behaves exactly as described in the previous section, with per-client slots and no dependency on the global-keys feature.
720+
698721
### Encrypted Key Update Protocol (M1–M5)
699722

700723
The most intricate piece of the SHE spec is the encrypted key update protocol that runs underneath `CMD_LOAD_KEY`. wolfHSM implements it as specified: the client constructs three input messages (M1, M2, M3) by encrypting and CMACing the new key and its metadata under keys derived from a chosen authorization key, sends them to the server, and receives two response messages (M4, M5) that prove the server stored the new key correctly.
@@ -744,7 +767,7 @@ The SHE extension is built on top of the same infrastructure as every other wolf
744767
- **Keystore**: SHE keys live in the same per-client `whKeyId` space as crypto keys, with the TYPE field disambiguating the two. SHE keys do not currently consume the per-key [usage flag policy](#key-usage-policies) machinery — usage constraints are expressed through the SHE-spec flag set in the label instead — but lifecycle flags like `NONMODIFIABLE` apply at the NVM layer just as they do for any other object.
745768
- **Communication layer**: every SHE command is a packet under the `WH_MESSAGE_GROUP_SHE` group and is dispatched through the [comm layer](#communication-layer-and-transports) like any other request. SHE clients work over every available transport without modification.
746769
- **Wrapped keys**: SHE keys interoperate with the [wrapped keys](#wrapped-keys) feature by explicit type rather than by flag (the SHE keyId namespace does not interpret the wrapped flag): the client passes `WH_KEYTYPE_SHE` to *wrap-export* to receive a slot's key wrapped under a [trusted KEK](#trusted-keks), and presents the blob to *unwrap-and-cache* to prime a slot directly in the key cache — the provisioning path for servers with [no NVM](#optional-nvm-backing), guarded by the slot's counter rollback check. *Unwrap-and-export* refuses SHE blobs, so a wrapped SHE key can re-enter the keystore but its plaintext is never returned to a client.
747-
- **Global keys**: not supported for SHE keys — every SHE keyId carries the connection's client ID in the USER field, and the SHE command set has no way to name the [global](#global-keys) (`WH_KEYUSER_GLOBAL`) namespace. Applications that need to share a key across clients must provision it into each client's SHE namespace separately.
770+
- **Global keys**: off by default — every SHE keyId carries the connection's client ID in the USER field, giving each client its own set of slots. Defining `WOLFHSM_CFG_SHE_GLOBAL_KEYS` instead places all SHE slots in the [global](#global-keys) (`WH_KEYUSER_GLOBAL`) namespace so every client shares one SHE device view; see [Global SHE Keys](#global-she-keys). Without that option, applications that need to share a key across clients must provision it into each client's SHE namespace separately.
748771

749772
A typical automotive deployment uses the SHE extension end-to-end: the bootloader and `BOOT_MAC` are programmed into NVM at production using `wh_Client_ShePreProgramKey`, the device's UID is set on first boot with `wh_Client_SheSetUid`, secure boot is run on every reset via `wh_Client_SheSecureBoot`, in-field key updates flow through the encrypted `CMD_LOAD_KEY` protocol, and CAN message authentication uses `wh_Client_SheGenerateMac` / `wh_Client_SheVerifyMac` against pre-provisioned user-slot keys.
750773

‎docs/src/9-Configuration.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ These macros enable or tune optional cryptographic subsystems built on top of wo
7878
| `WOLFHSM_CFG_HWKEYSTORE` | Undefined | If defined, compile the hardware keystore front-end (`wh_HwKeystore_*`) and hardware-only key support (`WH_KEYTYPE_HW`, `WH_CLIENT_KEYID_MAKE_HW()`). Hardware-only keys are served on demand by a user-supplied callback and are usable only as keywrap KEKs; they never enter the key cache or NVM and are never exported. See [Hardware-Only Keys](5-Features.md#hardware-only-keys). |
7979
| `WOLFHSM_CFG_HWKEYSTORE_MAX_KEY_SIZE` | `32` | Maximum size, in bytes, of a key served by the hardware keystore backend; sizes the local buffer that holds a hardware KEK for the duration of a keywrap operation. Only consulted when `WOLFHSM_CFG_HWKEYSTORE` is defined. |
8080
| `WOLFHSM_CFG_GLOBAL_KEYS` | Undefined | If defined, enable the global-keys feature, allowing keys to be cached so that they are visible to every client rather than scoped to the caching client. See [Global Keys](5-Features.md#global-keys) for a full discussion of the API and security implications. |
81+
| `WOLFHSM_CFG_SHE_GLOBAL_KEYS` | Undefined | If defined, all sixteen AUTOSAR SHE key slots live in the global-keys namespace (USER=0) so every connected client shares a single SHE device view, matching AUTOSAR's one-physical-SHE model rather than wolfHSM's default per-client SHE slots. Requires `WOLFHSM_CFG_GLOBAL_KEYS` and `WOLFHSM_CFG_SHE_EXTENSION` (enforced by `#error`). See [Global SHE Keys](5-Features.md#global-she-keys). |
8182
| `WH_DEV_ID` | `0x5748534D` (`"WHSM"`) | Value of the process-global crypto device ID registered by every `wh_Client_Init()` with the unified client crypto callback. Also the device ID bound to a client whose `whClientConfig.devId` is left `0`. Override it if the default collides with another crypto-callback device ID in the application. See [Transparent Offload via Crypto Callbacks](5-Features.md#transparent-offload-via-crypto-callbacks) for registration lifetime, multi-client rules, and wolfCrypt callback-table sizing (`MAX_CRYPTO_DEVID_CALLBACKS`, default 8). |
8283
| `WH_DEV_ID_DMA` | `0x57444D41` (`"WDMA"`) | Value of the process-global DMA-only crypto device ID, registered by every `wh_Client_Init()` when `WOLFHSM_CFG_DMA` is defined. Reserved: not valid as a `whClientConfig.devId`. Override it if the default collides with another crypto-callback device ID in the application. |
8384

‎src/wh_client_she.c‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ int wh_Client_ShePreProgramKey(whClientContext* c, whNvmId keyId,
5757
/* Create a key with 0 counter */
5858
wh_She_Meta2Label(0, flags, label);
5959
ret = wh_Client_NvmAddObject(c,
60-
WH_MAKE_KEYID(WH_KEYTYPE_SHE, c->comm->client_id, keyId),
60+
WH_SHE_MAKE_KEYID(c->comm->client_id, keyId),
6161
0, 0, sizeof(label), label, keySz, key, (int32_t*)&outRc);
6262
if (ret == 0)
6363
ret = outRc;

‎src/wh_server_keystore.c‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1945,6 +1945,16 @@ _HandleKeyWrapExportRequest(whServerContext* server,
19451945
return WH_ERROR_BADARGS;
19461946
}
19471947

1948+
#ifdef WOLFHSM_CFG_SHE_GLOBAL_KEYS
1949+
/* Route SHE targets to the global namespace so wrap-export names slots
1950+
* the same way the SHE commands do, whether or not the client passed the
1951+
* global flag. */
1952+
if (targetKeyType == WH_KEYTYPE_SHE) {
1953+
targetKeyId = WH_MAKE_KEYID(WH_KEYTYPE_SHE, WH_KEYUSER_GLOBAL,
1954+
WH_KEYID_ID(targetKeyId));
1955+
}
1956+
#endif
1957+
19481958
/* Read the key and its real metadata, enforcing export policy
19491959
* (NONEXPORTABLE). For wrapped keys this is a cache-only probe. */
19501960
ret = wh_Server_KeystoreReadKeyChecked(server, targetKeyId, &metadata, key,
@@ -2256,6 +2266,26 @@ static int _HandleKeyUnwrapAndCacheRequest(
22562266
}
22572267
#endif /* WOLFHSM_CFG_GLOBAL_KEYS */
22582268

2269+
#ifdef WOLFHSM_CFG_SHE_GLOBAL_KEYS
2270+
/* Move the blob's id to the global namespace where the SHE commands look;
2271+
* blobs minted by per-client builds would otherwise cache under an id no
2272+
* SHE command uses and no client request can evict. Do this before the
2273+
* in-cache check and counter guard below so both see the final id. */
2274+
if (wrappedKeyType == WH_KEYTYPE_SHE) {
2275+
metadata.id = WH_MAKE_KEYID(WH_KEYTYPE_SHE, WH_KEYUSER_GLOBAL,
2276+
WH_KEYID_ID(metadata.id));
2277+
}
2278+
#elif defined(WOLFHSM_CFG_SHE_EXTENSION) && defined(WOLFHSM_CFG_GLOBAL_KEYS)
2279+
/* SHE slots are per-client on this build, so a blob minted by a global-SHE
2280+
* build would cache under an id no SHE command reads and no client request
2281+
* can evict, pinning the cache slot until reboot. Reject it instead. */
2282+
if (wrappedKeyType == WH_KEYTYPE_SHE &&
2283+
wrappedKeyUser == WH_KEYUSER_GLOBAL) {
2284+
ret = WH_ERROR_ACCESS;
2285+
goto out;
2286+
}
2287+
#endif
2288+
22592289
/* Ensure a key with the unwrapped ID does not already exist in cache */
22602290
if (_ExistsInCache(server, metadata.id)) {
22612291
ret = WH_ERROR_ABORTED;

0 commit comments

Comments
 (0)