Skip to content
Open
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
29 changes: 28 additions & 1 deletion docs/src/5-Features.md
Original file line number Diff line number Diff line change
Expand Up @@ -672,10 +672,11 @@ The SHE client API is declared in `wolfhsm/wh_client_she.h` and maps one-to-one
- **Bulk crypto**: `wh_Client_SheEncEcb` / `wh_Client_SheEncCbc` / `wh_Client_SheDecEcb` / `wh_Client_SheDecCbc` (`CMD_ENC_*` / `CMD_DEC_*`) — AES-ECB and AES-CBC encrypt and decrypt against a selected key slot
- **MAC**: `wh_Client_SheGenerateMac` / `wh_Client_SheVerifyMac` (`CMD_GENERATE_MAC` / `CMD_VERIFY_MAC`) — CMAC generation and verification against a selected key slot
- **Status**: `wh_Client_SheGetStatus` (`CMD_GET_STATUS`) — reads the SHE status register (SREG)
- **Module identity**: `wh_Client_SheGetId` (`CMD_GET_ID`) — returns the ECU UID, the status register, and a CMAC over the caller's challenge, UID, and status register computed under the `MASTER_ECU_KEY`, letting a party that holds that key verify the module's identity. If the `MASTER_ECU_KEY` slot is empty the MAC is computed with an all-zero key.

In addition to the spec commands, wolfHSM exposes two non-standard helpers that fill gaps left by the spec's assumption of dedicated hardware:

- `wh_Client_SheSetUid`: explicitly programs the 15-byte ECU UID that the key update protocol binds against. The AUTOSAR spec assumes this value is hardware-fused; wolfHSM needs a software path to install it, and rejects most SHE operations until it has been set.
- `wh_Client_SheSetUid`: explicitly programs the 15-byte ECU UID that the key update protocol binds against. The AUTOSAR spec assumes this value is hardware-fused; wolfHSM needs a software path to install it, and rejects most SHE operations until it has been set. Where the UID really does live in hardware or in NVM, the server can be pointed at it instead with [UID storage callbacks](#she-uid-storage), in which case `CMD_SET_UID` returns `WH_SHE_ERC_WRITE_PROTECTED` on a read-only store.
- `wh_Client_ShePreProgramKey`: writes a key directly into a SHE NVM slot, bypassing the encrypted M1–M5 protocol. This exists to support initial provisioning on a blank device — once a `MASTER_ECU_KEY` exists, all subsequent updates can go through the spec-compliant protocol.

All SHE commands return one of the spec's `WH_SHE_ERC_*` error codes (`SEQUENCE_ERROR`, `KEY_NOT_AVAILABLE`, `WRITE_PROTECTED`, `KEY_UPDATE_ERROR`, etc.) alongside the wolfHSM transport return code, so applications can distinguish protocol-level failures from communication failures.
Expand All @@ -698,6 +699,30 @@ The SHE spec also requires every key to carry a 28-bit monotonic update counter

`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.

### SHE UID Storage

A pair of optional callbacks determines where the 15-byte ECU UID lives. Install them and the server reads it from the integrator's store (fuses, OTP, NVM); leave them unset and it stays in the caller-owned `whServerSheContext`, re-provisioned with `CMD_SET_UID` after every reset.

```c
typedef int (*whServerSheGetUidCb)(whServerContext* server, void* ctx,
uint8_t* outUid);
typedef int (*whServerSheSetUidCb)(whServerContext* server, void* ctx,
const uint8_t* uid);
```

The getter fills `WH_SHE_UID_SZ` bytes and returns `0`, `WH_ERROR_NOTFOUND` if no UID has been provisioned, or any other wolfHSM error to report a backend failure. The setter persists a UID that arrived over the wire via `CMD_SET_UID`; leaving it `NULL` marks the UID read-only, so provisioning attempts are answered with `WH_SHE_ERC_WRITE_PROTECTED` rather than being silently dropped.

Callbacks are supplied at initialization through the optional `whServerConfig.sheConfig` field, or registered later with `wh_Server_SheSetUidCb`:

```c
whServerSheConfig sheConfig = {
.getUidCb = myGetUid,
.setUidCb = NULL, /* fused UID, read-only */
.uidCtx = &myPlatform,
};
whServerConfig serverConfig = { /* ... */ .she = she, .sheConfig = &sheConfig };
```

### Global SHE Keys

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.
Expand Down Expand Up @@ -736,6 +761,8 @@ SHE secure boot is implemented as a three-phase state machine that the client dr

While the state machine is in any state other than `SUCCESS`, the SHE handler refuses every non-boot command except `CMD_GET_STATUS` and `CMD_SET_UID`, returning `WH_SHE_ERC_SEQUENCE_ERROR`. This is what allows the SHE module to gate cryptographic services on a successful boot measurement: once boot has succeeded, the rest of the SHE command set unlocks; on a boot failure the keys remain inaccessible and only status queries are honored.

The same stateful gate also enforces that a UID has been provisioned. When [UID storage callbacks](#she-uid-storage) are installed, that check queries the integrator's store, and a store that reports a failure causes every command except `CMD_GET_STATUS` to return `WH_SHE_ERC_MEMORY_FAILURE`.

The bootloader bytes are supplied through the standard message buffer in chunks of up to `WOLFHSM_CFG_COMM_DATA_LEN`. For large bootloaders this is the natural place to opt into [DMA](#dma-support) — a future variant of the secure boot handler could read the bootloader image directly out of flash using the DMA address-translation path — but the current implementation is purely buffer-based.

### Deterministic PRNG
Expand Down
64 changes: 64 additions & 0 deletions src/wh_client_she.c
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,70 @@ int wh_Client_SheGetStatus(whClientContext* c, uint8_t* sreg)
return ret;
}

int wh_Client_SheGetIdRequest(whClientContext* c, uint8_t* challenge,
uint32_t challengeSz)
{
whMessageShe_GetIdRequest* req = NULL;

if (c == NULL || challenge == NULL || challengeSz < WH_SHE_KEY_SZ) {
return WH_ERROR_BADARGS;
}

req = (whMessageShe_GetIdRequest*)wh_CommClient_GetDataPtr(c->comm);

memcpy(req->challenge, challenge, sizeof(req->challenge));

return wh_Client_SendRequest(c, WH_MESSAGE_GROUP_SHE, WH_SHE_GET_ID,
sizeof(*req), (uint8_t*)req);
}

int wh_Client_SheGetIdResponse(whClientContext* c, uint8_t* uid, uint8_t* sreg,
uint8_t* mac)
{
int ret;
uint16_t group;
uint16_t action;
uint16_t dataSz;
whMessageShe_GetIdResponse* resp = NULL;

if (c == NULL || uid == NULL || sreg == NULL || mac == NULL) {
return WH_ERROR_BADARGS;
}

resp = (whMessageShe_GetIdResponse*)wh_CommClient_GetDataPtr(c->comm);

ret = wh_Client_RecvResponse(c, &group, &action, &dataSz,
WOLFHSM_CFG_COMM_DATA_LEN, (uint8_t*)resp);
if (ret == WH_ERROR_OK && dataSz < sizeof(*resp)) {
ret = WH_ERROR_ABORTED;
}
if (ret == 0) {
if (resp->rc != WH_SHE_ERC_NO_ERROR) {
ret = resp->rc;
}
else {
memcpy(uid, resp->uid, sizeof(resp->uid));
*sreg = resp->sreg;
memcpy(mac, resp->mac, sizeof(resp->mac));
}
}
return ret;
}

int wh_Client_SheGetId(whClientContext* c, uint8_t* challenge,
uint32_t challengeSz, uint8_t* uid, uint8_t* sreg,
uint8_t* mac)
{
int ret;
ret = wh_Client_SheGetIdRequest(c, challenge, challengeSz);
if (ret == 0) {
do {
ret = wh_Client_SheGetIdResponse(c, uid, sreg, mac);
} while (ret == WH_ERROR_NOTREADY);
}
return ret;
}

int wh_Client_SheLoadKeyRequest(whClientContext* c, uint8_t* messageOne,
uint8_t* messageTwo, uint8_t* messageThree)
{
Expand Down
32 changes: 32 additions & 0 deletions src/wh_message_she.c
Original file line number Diff line number Diff line change
Expand Up @@ -429,4 +429,36 @@ int wh_MessageShe_TranslateVerifyMacResponse(
return 0;
}

/* Get ID translation functions */
int wh_MessageShe_TranslateGetIdRequest(uint16_t magic,
const whMessageShe_GetIdRequest* src,
whMessageShe_GetIdRequest* dest)
{
(void)magic;

if ((src == NULL) || (dest == NULL)) {
return WH_ERROR_BADARGS;
}
if (src != dest) {
memcpy(dest->challenge, src->challenge, WH_SHE_KEY_SZ);
}
return 0;
}

int wh_MessageShe_TranslateGetIdResponse(
uint16_t magic, const whMessageShe_GetIdResponse* src,
whMessageShe_GetIdResponse* dest)
{
if ((src == NULL) || (dest == NULL)) {
return WH_ERROR_BADARGS;
}
WH_T32(magic, dest, src, rc);
if (src != dest) {
memcpy(dest->uid, src->uid, WH_SHE_UID_SZ);
memcpy(dest->mac, src->mac, WH_SHE_KEY_SZ);
Comment on lines +457 to +458

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would these not need translation?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are accessed/used as byte arrays on both ends so there wouldn't be any need to translate

}
dest->sreg = src->sreg;
return 0;
}

#endif /* WOLFHSM_CFG_SHE_EXTENSION */
12 changes: 12 additions & 0 deletions src/wh_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,18 @@ int wh_Server_Init(whServerContext* server, whServerConfig* config)
server->devId = config->devId;
#ifdef WOLFHSM_CFG_SHE_EXTENSION
server->she = config->she;
if (server->she != NULL) {
if (config->sheConfig != NULL) {
server->she->getUidCb = config->sheConfig->getUidCb;
server->she->setUidCb = config->sheConfig->setUidCb;
server->she->uidCtx = config->sheConfig->uidCtx;
}
else {
server->she->getUidCb = NULL;
server->she->setUidCb = NULL;
server->she->uidCtx = NULL;
}
}
#endif
#endif

Expand Down
Loading
Loading