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
3 changes: 2 additions & 1 deletion docs/src/5-Features.md
Original file line number Diff line number Diff line change
Expand Up @@ -870,6 +870,7 @@ A verify method returns `WH_ERROR_OK` on a successful verification, `WH_ERROR_NO
- `wh_Server_ImgMgrVerifyMethodRsaSslWithSha256`: RSA PKCS#1 v1.5 signature over the SHA-256 hash of the image
- `wh_Server_ImgMgrVerifyMethodAesCmac`: AES-128 CMAC over the image bytes
- `wh_Server_ImgMgrVerifyMethodWolfBootRsa4096WithSha256`: RSA-4096 verification of a wolfBoot-formatted image (see [wolfBoot Image Support](#wolfboot-image-support))
- `wh_Server_ImgMgrVerifyMethodWolfBootEcc256WithSha256`: ECDSA P-256 verification of a wolfBoot-formatted image
- `wh_Server_ImgMgrVerifyMethodWolfBootCertChainRsa4096WithSha256`: cert-chain-based RSA-4096 verification of a wolfBoot image

Applications can supply their own verify method to support algorithms not represented in the built-in set, or to layer additional checks on top of an existing one — for example, validating a monotonic counter against a [non-volatile counter](#non-volatile-monotonic-counters) inside a wrapper verify method to add anti-rollback protection. The maximum signature size handled by the framework is `WOLFHSM_CFG_SERVER_IMG_MGR_MAX_SIG_SIZE`, whose default accommodates RSA-4096.
Expand All @@ -892,7 +893,7 @@ A default no-op action, `wh_Server_ImgMgrVerifyActionDefault`, is provided for c

wolfHSM understands the [wolfBoot](https://github.com/wolfSSL/wolfBoot) image header format natively so that a wolfHSM-equipped system can serve as the verifier for a wolfBoot-staged image without the client having to parse the header itself. Two wolfBoot image types are recognized:

- `WH_IMG_MGR_IMG_TYPE_WOLFBOOT`: the signature is extracted from the wolfBoot TLV header and verified against a key resident in the server's keystore (identified by `keyId`). This corresponds to the standard wolfBoot signing model where the signing key is known in advance and provisioned into the HSM.
- `WH_IMG_MGR_IMG_TYPE_WOLFBOOT`: the signature is extracted from the wolfBoot TLV header and verified against a key resident in the server's keystore (identified by `keyId`). This corresponds to the standard wolfBoot signing model where the signing key is known in advance and provisioned into the HSM. Pair it with the verify method that matches how the image was signed, `wh_Server_ImgMgrVerifyMethodWolfBootRsa4096WithSha256` or `wh_Server_ImgMgrVerifyMethodWolfBootEcc256WithSha256`. Each method rejects a header whose auth type does not match its algorithm.
- `WH_IMG_MGR_IMG_TYPE_WOLFBOOT_CERT`: the image carries a certificate chain inside its wolfBoot header; the chain is verified against a trusted root in NVM (identified by `sigNvmId`) using the [certificate manager](#certificate-management), and the leaf certificate's public key is then used to verify the image signature. This matches the wolfBoot cert-chain mode and is the right choice when the signing key is rotated independently of the on-device trust anchor.

In both cases the framework parses the header at `hdrAddr`, locates the signature TLV, validates the wolfBoot magic and public key hint, and feeds the appropriate `(image, key, signature)` triple into the wolfBoot verify method. The application's `verifyAction` is invoked exactly as for a raw image.
Expand Down
4 changes: 2 additions & 2 deletions docs/src/8-Integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ Server mode is the right choice when the bootloader has no separate secure core

### Verifying wolfBoot Images from a Server Application

Independently of which mode wolfBoot itself is running in, a wolfHSM **server application** can verify wolfBoot-formatted images on behalf of its clients using the [image manager](5-Features.md#image-manager). The image manager understands the wolfBoot TLV header natively and exposes two verify methods specifically for wolfBoot images:
Independently of which mode wolfBoot itself is running in, a wolfHSM **server application** can verify wolfBoot-formatted images on behalf of its clients using the [image manager](5-Features.md#image-manager). The image manager understands the wolfBoot TLV header natively and exposes two image types specifically for wolfBoot images:

- `WH_IMG_MGR_IMG_TYPE_WOLFBOOT` — verifies the image against a key resident in the server's keystore, matching wolfBoot's standard signing model.
- `WH_IMG_MGR_IMG_TYPE_WOLFBOOT` — verifies the image against a key resident in the server's keystore, matching wolfBoot's standard signing model. Both the RSA-4096 and the ECC P-256 wolfBoot verify methods use this type.
- `WH_IMG_MGR_IMG_TYPE_WOLFBOOT_CERT` — verifies a certificate chain embedded in the wolfBoot header against a trusted root in NVM and then uses the leaf public key to verify the image, matching wolfBoot's cert-chain signing mode.

The full mechanism — header parsing, signature TLV extraction, public-key-hint validation, and the DMA-aware payload reads that make verifying multi-megabyte images practical — is documented in [wolfBoot Image Support](5-Features.md#wolfboot-image-support). The practical upshot is that a wolfBoot client and a wolfHSM-equipped system can share a single image format and a single trust anchor: the same `.bin` that wolfBoot would verify locally can be verified by a wolfHSM server through the image manager, and the same root CA provisioned on the HSM works for both flows.
Expand Down
234 changes: 221 additions & 13 deletions src/wh_server_img_mgr.c
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,8 @@ int wh_Server_ImgMgrVerifyMethodRsaSslWithSha256(
}
#endif /* !NO_RSA */

#ifndef NO_RSA
/* wolfBoot header helpers for RSA4096 and ECC256 methods. */
#if !defined(NO_RSA) || defined(HAVE_ECC)

/**
* Find a TLV field in a wolfBoot image header.
Expand Down Expand Up @@ -608,6 +609,7 @@ static int _wolfBootImgHashSha256(const uint8_t* hdr,
* NOTE: Code lifted directly from wolfBoot. Should remain as unmodified as
* possible for easy diffs
*/
#ifndef NO_RSA
static int _wolfBootImgDecodeAsn1Tag(const uint8_t* input, int inputSz,
int* inOutIdx, int* tag_len, uint8_t tag)
{
Expand Down Expand Up @@ -725,6 +727,7 @@ static int _wolfBootImgVerifySigRsa4096(const uint8_t* sig, uint16_t sigSz,

return WH_ERROR_OK;
}
#endif /* !NO_RSA */


/**
Expand Down Expand Up @@ -815,12 +818,10 @@ static int _wolfBootImgPeekImgSize(const uint8_t* hdr, size_t hdrSize,
* Returns WH_ERROR_OK on success and populates computed_hash.
* On success, sig_out and sig_sz_out point to the signature in the header.
*/
static int _wolfBootImgValidateAndHash(const uint8_t* hdr, size_t hdrSize,
const uint8_t* payload,
size_t payloadSize,
uint8_t* computed_hash,
const uint8_t** sig_out,
uint16_t* sig_sz_out, int devId)
static int _wolfBootImgValidateAndHash(
const uint8_t* hdr, size_t hdrSize, const uint8_t* payload,
size_t payloadSize, uint8_t* computed_hash, const uint8_t** sig_out,
uint16_t* sig_sz_out, int devId, uint8_t expected_auth_type)
{
uint32_t magic;
uint32_t img_size;
Expand Down Expand Up @@ -877,7 +878,7 @@ static int _wolfBootImgValidateAndHash(const uint8_t* hdr, size_t hdrSize,
auth_type =
(uint8_t)((image_type & WH_IMG_MGR_WOLFBOOT_HDR_IMG_TYPE_AUTH_MASK) >>
8);
if (auth_type != WH_IMG_MGR_WOLFBOOT_AUTH_RSA4096) {
if (auth_type != expected_auth_type) {
return WH_ERROR_NOTVERIFIED;
}

Expand Down Expand Up @@ -914,6 +915,7 @@ static int _wolfBootImgValidateAndHash(const uint8_t* hdr, size_t hdrSize,
}


#ifndef NO_RSA
int wh_Server_ImgMgrVerifyMethodWolfBootRsa4096WithSha256(
whServerImgMgrContext* context, const whServerImgMgrImg* img,
const uint8_t* key, size_t keySz, const uint8_t* sig, size_t sigSz)
Expand Down Expand Up @@ -988,7 +990,7 @@ int wh_Server_ImgMgrVerifyMethodWolfBootRsa4096WithSha256(
/* Validate header, compute and verify hash, extract signature */
ret = _wolfBootImgValidateAndHash(hdr, img->hdrSize, payload, payloadSize,
computed_hash, &hdr_sig, &hdr_sig_sz,
devId);
devId, WH_IMG_MGR_WOLFBOOT_AUTH_RSA4096);
if (ret != WH_ERROR_OK) {
goto cleanup;
}
Expand Down Expand Up @@ -1020,8 +1022,214 @@ int wh_Server_ImgMgrVerifyMethodWolfBootRsa4096WithSha256(
#endif
return ret;
}
#endif /* !NO_RSA */

#ifdef HAVE_ECC
/* Normalize an ECC P-256 key to raw 64-byte X||Y form.
* Accepts raw X||Y or a DER public key. */
static int _wolfBootImgEcc256KeyToRaw(const uint8_t* key, size_t keySz,
int devId, uint8_t* rawKey /* [64] */)
{
ecc_key eccKey;
word32 qxLen = 32;
word32 qyLen = 32;
word32 idx = 0;
int ret;

if (key == NULL || keySz == 0) {
return WH_ERROR_BADARGS;
}

if (keySz == 64) {
memcpy(rawKey, key, 64);
return WH_ERROR_OK;
}

ret = wc_ecc_init_ex(&eccKey, NULL, devId);
if (ret != 0) {
return WH_ERROR_ABORTED;
}
ret = wc_EccPublicKeyDecode(key, &idx, &eccKey, (word32)keySz);
if (ret == 0) {
/* Export with 32-byte coordinates. */
ret = wc_ecc_export_public_raw(&eccKey, rawKey, &qxLen, rawKey + 32,
&qyLen);
}
(void)wc_ecc_free(&eccKey);

if (ret != 0 || qxLen != 32 || qyLen != 32) {
return WH_ERROR_NOTVERIFIED;
}
return WH_ERROR_OK;
}

/* Verify raw ECC P-256 R||S against a hash. */
static int _wolfBootImgVerifySigEcc256(const uint8_t* sig, uint16_t sigSz,
const uint8_t* hash, uint32_t hashSz,
const uint8_t* pubkey, uint32_t pubkeySz,
int devId)
{
ecc_key eccKey;
uint8_t derSig[ECC_MAX_SIG_SIZE];
word32 derSigSz = sizeof(derSig);
int verifyResult = 0;
int ret;

/* wolfBoot ECC256 uses raw X||Y keys and raw R||S signatures. */
if (sigSz != 64 || pubkeySz != 64) {
return WH_ERROR_NOTVERIFIED;
}

ret = wc_ecc_init_ex(&eccKey, NULL, devId);
if (ret != 0) {
return WH_ERROR_ABORTED;
}

ret = wc_ecc_import_unsigned(&eccKey, pubkey, pubkey + 32, NULL,
ECC_SECP256R1);
Comment thread
padelsbach marked this conversation as resolved.
if (ret != 0) {
(void)wc_ecc_free(&eccKey);
return WH_ERROR_ABORTED;
}

/* wc_ecc_verify_hash takes a DER signature. */
ret = wc_ecc_rs_raw_to_sig(sig, 32, sig + 32, 32, derSig, &derSigSz);
if (ret == 0) {
ret = wc_ecc_verify_hash(derSig, derSigSz, hash, hashSz, &verifyResult,
&eccKey);
}

(void)wc_ecc_free(&eccKey);

/* An R or S of zero or at least the curve order is an invalid signature.
* wolfCrypt reports that as an error instead of a failed check. */
if (ret == MP_ZERO_E || ret == MP_VAL) {
return WH_ERROR_NOTVERIFIED;
}
/* Pass other wolfCrypt errors through. */
if (ret != 0) {
return ret;
}
if (verifyResult != 1) {
return WH_ERROR_NOTVERIFIED;
}
return WH_ERROR_OK;
}

int wh_Server_ImgMgrVerifyMethodWolfBootEcc256WithSha256(
whServerImgMgrContext* context, const whServerImgMgrImg* img,
const uint8_t* key, size_t keySz, const uint8_t* sig, size_t sigSz)
{
int ret;
uint8_t computed_hash[WC_SHA256_DIGEST_SIZE];
uint8_t raw_key[64];
const uint8_t* hdr;
const uint8_t* payload;
const uint8_t* hdr_sig;
uint16_t hdr_sig_sz;
const uint8_t* pubkey_hint;
uint16_t pubkey_hint_size;
size_t payloadSize;
whServerContext* server;
int devId;
#ifdef WOLFHSM_CFG_DMA
void* serverHdrPtr = NULL;
void* serverPayloadPtr = NULL;
uint32_t peekedImgSize = 0;
int payloadMapped = 0;
#endif

(void)sig;
(void)sigSz;

if (context == NULL || context->server == NULL || img == NULL ||
key == NULL || keySz == 0) {
return WH_ERROR_BADARGS;
}

server = context->server;
devId = server->devId;

/* Normalize the key to raw X||Y. */
ret = _wolfBootImgEcc256KeyToRaw(key, keySz, devId, raw_key);
if (ret != WH_ERROR_OK) {
return ret;
}

#ifdef WOLFHSM_CFG_DMA
/* Map the header for DMA read. */
ret = wh_Server_DmaProcessClientAddress(
server, img->hdrAddr, &serverHdrPtr, img->hdrSize,
WH_DMA_OPER_CLIENT_READ_PRE, (whServerDmaFlags){0});
if (ret != WH_ERROR_OK) {
return ret;
}
hdr = (const uint8_t*)serverHdrPtr;

/* Map only the image size declared in the header. */
ret = _wolfBootImgPeekImgSize(hdr, img->hdrSize, &peekedImgSize);
if (ret != WH_ERROR_OK) {
goto cleanup;
}
if ((size_t)peekedImgSize > img->size) {
ret = WH_ERROR_BADARGS;
goto cleanup;
}
payloadSize = (size_t)peekedImgSize;

/* Map the payload for DMA read. */
ret = wh_Server_DmaProcessClientAddress(
server, img->addr, &serverPayloadPtr, payloadSize,
WH_DMA_OPER_CLIENT_READ_PRE, (whServerDmaFlags){0});
if (ret != WH_ERROR_OK) {
goto cleanup;
}
payloadMapped = 1;
payload = (const uint8_t*)serverPayloadPtr;
#else
hdr = (const uint8_t*)img->hdrAddr;
payload = (const uint8_t*)img->addr;
payloadSize = img->size;
#endif

/* Validate the header and compute the payload hash. */
ret = _wolfBootImgValidateAndHash(hdr, img->hdrSize, payload, payloadSize,
computed_hash, &hdr_sig, &hdr_sig_sz,
devId, WH_IMG_MGR_WOLFBOOT_AUTH_ECC256);
if (ret != WH_ERROR_OK) {
goto cleanup;
}

/* Verify the header public-key hint. */
pubkey_hint_size = _wolfBootImgFindHeaderField(
hdr, img->hdrSize, WH_IMG_MGR_WOLFBOOT_HDR_PUBKEY, &pubkey_hint);
ret = _wolfBootImgVerifyPubKeyHint(raw_key, sizeof(raw_key), pubkey_hint,
pubkey_hint_size, devId);
if (ret != WH_ERROR_OK) {
goto cleanup;
}

/* Verify the ECC256 signature. */
ret = _wolfBootImgVerifySigEcc256(hdr_sig, hdr_sig_sz, computed_hash,
WC_SHA256_DIGEST_SIZE, raw_key,
sizeof(raw_key), devId);

cleanup:
#ifdef WOLFHSM_CFG_DMA
if (payloadMapped) {
(void)wh_Server_DmaProcessClientAddress(
server, img->addr, &serverPayloadPtr, payloadSize,
WH_DMA_OPER_CLIENT_READ_POST, (whServerDmaFlags){0});
}
(void)wh_Server_DmaProcessClientAddress(
server, img->hdrAddr, &serverHdrPtr, img->hdrSize,
WH_DMA_OPER_CLIENT_READ_POST, (whServerDmaFlags){0});
#endif
return ret;
}
#endif /* HAVE_ECC */

#ifdef WOLFHSM_CFG_CERTIFICATE_MANAGER
#if defined(WOLFHSM_CFG_CERTIFICATE_MANAGER) && !defined(NO_RSA)
int wh_Server_ImgMgrVerifyMethodWolfBootCertChainRsa4096WithSha256(
whServerImgMgrContext* context, const whServerImgMgrImg* img,
const uint8_t* key, size_t keySz, const uint8_t* sig, size_t sigSz)
Expand Down Expand Up @@ -1105,7 +1313,7 @@ int wh_Server_ImgMgrVerifyMethodWolfBootCertChainRsa4096WithSha256(
/* Validate header, compute and verify hash, extract signature */
ret = _wolfBootImgValidateAndHash(hdr, img->hdrSize, payload, payloadSize,
computed_hash, &hdr_sig, &hdr_sig_sz,
devId);
devId, WH_IMG_MGR_WOLFBOOT_AUTH_RSA4096);
if (ret != WH_ERROR_OK) {
goto cleanup;
}
Expand Down Expand Up @@ -1164,9 +1372,9 @@ int wh_Server_ImgMgrVerifyMethodWolfBootCertChainRsa4096WithSha256(
#endif
return ret;
}
#endif /* WOLFHSM_CFG_CERTIFICATE_MANAGER */
#endif /* WOLFHSM_CFG_CERTIFICATE_MANAGER && !NO_RSA */

#endif /* !NO_RSA */
#endif /* !NO_RSA || HAVE_ECC */
#endif /* !WOLFHSM_CFG_NO_CRYPTO */

int wh_Server_ImgMgrVerifyActionDefault(whServerImgMgrContext* context,
Expand Down
Loading
Loading