Skip to content
Draft
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
1 change: 1 addition & 0 deletions docs/src/5-Features.md
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,7 @@ 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, per the spec.

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

Expand Down
60 changes: 60 additions & 0 deletions src/wh_client_she.c
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,66 @@ 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, (uint8_t*)resp);
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);
}
dest->sreg = src->sreg;
return 0;
}

#endif /* WOLFHSM_CFG_SHE_EXTENSION */
145 changes: 124 additions & 21 deletions src/wh_server_she.c
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ static int _GenerateMac(whServerContext* server, uint16_t magic,
static int _VerifyMac(whServerContext* server, uint16_t magic,
uint16_t req_size, const void* req_packet,
uint16_t* out_resp_size, void* resp_packet);
static int _GetId(whServerContext* server, uint16_t magic, uint16_t req_size,
const void* req_packet, uint16_t* out_resp_size,
void* resp_packet);
static uint8_t _BuildSreg(whServerContext* server);
static int _TranslateSheReturnCode(int ret);
static int _ReportInvalidSheState(whServerContext* server, uint16_t magic,
uint16_t action, uint16_t req_size,
Expand Down Expand Up @@ -431,6 +435,33 @@ static int _SecureBootFinish(whServerContext* server, uint16_t magic,
return ret;
}

/* Compose the 8-bit SHE status register (SREG) from the current server state.
* TODO do we care about all the sreg fields? */
static uint8_t _BuildSreg(whServerContext* server)
{
uint8_t sreg = 0;

/* SECURE_BOOT */
if (server->she->cmacKeyFound) {
sreg |= WH_SHE_SREG_SECURE_BOOT;
}
/* BOOT_FINISHED */
if (server->she->sbState == WH_SHE_SB_SUCCESS ||
server->she->sbState == WH_SHE_SB_FAILURE) {
sreg |= WH_SHE_SREG_BOOT_FINISHED;
}
/* BOOT_OK */
if (server->she->sbState == WH_SHE_SB_SUCCESS) {
sreg |= WH_SHE_SREG_BOOT_OK;
}
/* RND_INIT */
if (server->she->rndInited == 1) {
sreg |= WH_SHE_SREG_RND_INIT;
}

return sreg;
}

static int _GetStatus(whServerContext* server, uint16_t magic,
uint16_t req_size, const void* req_packet,
uint16_t* out_resp_size, void* resp_packet)
Expand All @@ -445,26 +476,7 @@ static int _GetStatus(whServerContext* server, uint16_t magic,
}

if (ret == 0) {
/* TODO do we care about all the sreg fields? */
resp.sreg = 0;
/* SECURE_BOOT */
if (server->she->cmacKeyFound) {
resp.sreg |= WH_SHE_SREG_SECURE_BOOT;
}

/* BOOT_FINISHED */
if (server->she->sbState == WH_SHE_SB_SUCCESS ||
server->she->sbState == WH_SHE_SB_FAILURE) {
resp.sreg |= WH_SHE_SREG_BOOT_FINISHED;
}
/* BOOT_OK */
if (server->she->sbState == WH_SHE_SB_SUCCESS) {
resp.sreg |= WH_SHE_SREG_BOOT_OK;
}
/* RND_INIT */
if (server->she->rndInited == 1) {
resp.sreg |= WH_SHE_SREG_RND_INIT;
}
resp.sreg = _BuildSreg(server);
}

*out_resp_size = sizeof(resp);
Expand Down Expand Up @@ -1606,6 +1618,78 @@ static int _VerifyMac(whServerContext* server, uint16_t magic,
return ret;
}

static int _GetId(whServerContext* server, uint16_t magic, uint16_t req_size,
const void* req_packet, uint16_t* out_resp_size,
void* resp_packet)
{
int ret = 0;
uint32_t field = AES_BLOCK_SIZE;
uint32_t keySz;
uint8_t tmpKey[WH_SHE_KEY_SZ];
/* CMAC input: CHALLENGE || UID || SREG */
uint8_t macIn[WH_SHE_KEY_SZ + WH_SHE_UID_SZ + 1];
whMessageShe_GetIdRequest req = {0};
whMessageShe_GetIdResponse resp = {0};

if (req_size < sizeof(req)) {
ret = WH_ERROR_BUFFER_SIZE;
}

if (ret == 0) {
ret = wh_MessageShe_TranslateGetIdRequest(magic, req_packet, &req);
}

if (ret == 0) {
/* Assemble the CMAC input: challenge || uid || sreg */
uint8_t sreg = _BuildSreg(server);
memcpy(macIn, req.challenge, WH_SHE_KEY_SZ);
memcpy(macIn + WH_SHE_KEY_SZ, server->she->uid, WH_SHE_UID_SZ);
macIn[WH_SHE_KEY_SZ + WH_SHE_UID_SZ] = sreg;

/* Load the MASTER_ECU_KEY. Per the SHE spec, if the slot is empty the
* MAC is computed with an all-zero key rather than returning an error.
* wh_Server_KeystoreReadKey already applies this substitution for the
* MASTER_ECU_KEY slot (returns a zero-filled key and rc 0), so the
* WH_ERROR_NOTFOUND branch below is only a defensive backstop for
* builds/paths where that substitution does not occur. */
keySz = WH_SHE_KEY_SZ;
ret = wh_Server_KeystoreReadKey(
server,
WH_MAKE_KEYID(WH_KEYTYPE_SHE, server->comm->client_id,
WH_SHE_MASTER_ECU_KEY_ID),
NULL, tmpKey, &keySz);
if (ret == WH_ERROR_NOTFOUND) {
memset(tmpKey, 0, WH_SHE_KEY_SZ);
ret = 0;
}
else if (ret == 0 && keySz != WH_SHE_KEY_SZ) {
ret = WH_SHE_ERC_KEY_INVALID;
}

/* Compute the identity MAC over challenge || uid || sreg */
if (ret == 0) {
ret = wc_AesCmacGenerate_ex(server->she->sheCmac, resp.mac,
(word32*)&field, macIn, sizeof(macIn),
tmpKey, WH_SHE_KEY_SZ, NULL,
server->devId);
}

/* Fill the remaining response fields */
if (ret == 0) {
memcpy(resp.uid, server->she->uid, sizeof(resp.uid));
resp.sreg = sreg;
}
}

resp.rc = _TranslateSheReturnCode(ret);
(void)wh_MessageShe_TranslateGetIdResponse(magic, &resp, resp_packet);
*out_resp_size = sizeof(resp);

wh_Utils_ForceZero(tmpKey, sizeof(tmpKey));

return ret;
}


/* TODO: This is terrible, but without implementing a SHE sub-protocol like we
* do for crypto layer, there is no way to return non-request specific error
Expand Down Expand Up @@ -1636,8 +1720,11 @@ static int _ReportInvalidSheState(whServerContext* server, uint16_t magic,
else if (action != WH_SHE_SECURE_BOOT_INIT &&
action != WH_SHE_SECURE_BOOT_UPDATE &&
action != WH_SHE_SECURE_BOOT_FINISH &&
action != WH_SHE_GET_ID &&
server->she->sbState != WH_SHE_SB_SUCCESS) {
/* Non-boot commands are blocked until secure boot succeeds. */
/* Non-boot commands are blocked until secure boot succeeds. GET_ID is
* exempt (the AUTOSAR spec permits it in every state), though it still
* requires a provisioned UID via the uidSet check above. */
ret = WH_SHE_ERC_SEQUENCE_ERROR;
}

Expand Down Expand Up @@ -1782,6 +1869,14 @@ static int _ReportInvalidSheState(whServerContext* server, uint16_t magic,
*out_resp_size = sizeof(resp);
break;
}
case WH_SHE_GET_ID: {
whMessageShe_GetIdResponse resp = {0};
resp.rc = WH_SHE_ERC_SEQUENCE_ERROR;
(void)wh_MessageShe_TranslateGetIdResponse(magic, &resp,
resp_packet);
*out_resp_size = sizeof(resp);
break;
}
}
}

Expand Down Expand Up @@ -1932,6 +2027,14 @@ int wh_Server_HandleSheRequest(whServerContext* server, uint16_t magic,
(void)WH_SERVER_NVM_UNLOCK(server);
} /* WH_SERVER_NVM_LOCK() */
break;
case WH_SHE_GET_ID:
ret = WH_SERVER_NVM_LOCK(server);
if (ret == WH_ERROR_OK) {
ret = _GetId(server, magic, req_size, req_packet, out_resp_size,
resp_packet);
(void)WH_SERVER_NVM_UNLOCK(server);
} /* WH_SERVER_NVM_LOCK() */
break;
default:
ret = WH_ERROR_BADARGS;
break;
Expand Down
40 changes: 40 additions & 0 deletions test-refactor/client-server/wh_test_she.c
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,13 @@ int whTest_She(whClientContext* client)
uint8_t messageThree[WH_SHE_M3_SZ];
uint8_t messageFour[WH_SHE_M4_SZ];
uint8_t messageFive[WH_SHE_M5_SZ];
uint8_t sheChallenge[WH_SHE_KEY_SZ] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55,
0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff};
uint8_t sheGetIdUid[WH_SHE_UID_SZ];
uint8_t sheGetIdMac[WH_SHE_KEY_SZ];
uint8_t expectedGetIdMac[WH_SHE_KEY_SZ];
uint8_t getIdMacInput[WH_SHE_KEY_SZ + WH_SHE_UID_SZ + 1];
word32 expectedGetIdMacSz = sizeof(expectedGetIdMac);
const uint32_t SHE_TEST_VECTOR_KEY_ID = 4;
const uint32_t SHE_WP_KEY_ID = 6;

Expand Down Expand Up @@ -317,6 +324,39 @@ int whTest_She(whClientContext* client)
}
WH_TEST_PRINT("SHE LOAD KEY SUCCESS\n");

/* === GET_ID identity + MAC verification === */

/* CMD_GET_ID: read the module identity and verify the identity MAC. The
* MASTER_ECU_KEY (slot 1) was loaded above with vectorMasterEcuKey, so we
* can recompute the expected CMAC over challenge || uid || sreg. */
if ((ret = wh_Client_SheGetId(client, sheChallenge, sizeof(sheChallenge),
sheGetIdUid, &sreg, sheGetIdMac)) != 0) {
WH_ERROR_PRINT("Failed to wh_Client_SheGetId %d\n", ret);
goto exit;
}
if (memcmp(sheGetIdUid, sheUid, WH_SHE_UID_SZ) != 0) {
ret = WH_ERROR_ABORTED;
WH_ERROR_PRINT("SHE GET_ID returned an unexpected UID\n");
goto exit;
}
/* expected MAC = CMAC(MASTER_ECU_KEY, challenge || uid || sreg) */
memcpy(getIdMacInput, sheChallenge, WH_SHE_KEY_SZ);
memcpy(getIdMacInput + WH_SHE_KEY_SZ, sheGetIdUid, WH_SHE_UID_SZ);
getIdMacInput[WH_SHE_KEY_SZ + WH_SHE_UID_SZ] = sreg;
expectedGetIdMacSz = sizeof(expectedGetIdMac);
if ((ret = wc_AesCmacGenerate(expectedGetIdMac, &expectedGetIdMacSz,
getIdMacInput, sizeof(getIdMacInput), vectorMasterEcuKey,
sizeof(vectorMasterEcuKey))) != 0) {
WH_ERROR_PRINT("Failed to compute expected GET_ID MAC %d\n", ret);
goto exit;
}
if (memcmp(sheGetIdMac, expectedGetIdMac, WH_SHE_KEY_SZ) != 0) {
ret = WH_ERROR_ABORTED;
WH_ERROR_PRINT("SHE GET_ID MAC mismatch\n");
goto exit;
}
WH_TEST_PRINT("SHE GET ID SUCCESS\n");

/* === LoadKey UID handling === */

/* A non-matching UID must be rejected, an all-zero UID must be
Expand Down
2 changes: 2 additions & 0 deletions test-refactor/misc/wh_test_check_struct_padding.c
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,8 @@ whMessageShe_GenMacRequest sheGenMacReq;
whMessageShe_GenMacResponse sheGenMacRes;
whMessageShe_VerifyMacRequest sheVerifyMacReq;
whMessageShe_VerifyMacResponse sheVerifyMacRes;
whMessageShe_GetIdRequest sheGetIdReq;
whMessageShe_GetIdResponse sheGetIdRes;
#endif /* WOLFHSM_CFG_SHE_EXTENSION */

#if defined(WOLFHSM_CFG_CERTIFICATE_MANAGER)
Expand Down
20 changes: 20 additions & 0 deletions test-refactor/server/wh_test_she_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,26 @@ int whTest_SheReqSizeChecking(whServerContext* server)
WH_TEST_ASSERT_RETURN(verifyMacResp->rc != WH_SHE_ERC_NO_ERROR);
}

/*
* Test 18: WH_SHE_GET_ID with truncated request.
* Populate a valid challenge, but pass req_size one byte short.
*/
{
whMessageShe_GetIdRequest* req =
(whMessageShe_GetIdRequest*)req_packet;
whMessageShe_GetIdResponse* getIdResp =
(whMessageShe_GetIdResponse*)resp_packet;
memset(getIdResp, 0, sizeof(*getIdResp));
memset(req->challenge, 0xAA, WH_SHE_KEY_SZ);
req_size = sizeof(whMessageShe_GetIdRequest) - 1;
ret = wh_Server_HandleSheRequest(server, WH_COMM_MAGIC_NATIVE,
WH_SHE_GET_ID, req_size,
req_packet, &resp_size, resp_packet);
WH_TEST_ASSERT_RETURN(ret == 0);
WH_TEST_ASSERT_RETURN(resp_size == sizeof(*getIdResp));
WH_TEST_ASSERT_RETURN(getIdResp->rc != WH_SHE_ERC_NO_ERROR);
}

/* Restore a clean SHE context so the poked uidSet/sbState don't
* leak into the live request loop the server enters next. */
memset(server->she, 0, sizeof(*server->she));
Expand Down
Loading
Loading