Skip to content

Commit 42a8f0c

Browse files
Add hardware-based DICE on mcxn
1 parent 8b7ce4e commit 42a8f0c

7 files changed

Lines changed: 341 additions & 8 deletions

File tree

arch.mk

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -865,6 +865,15 @@ ifeq ($(TARGET),mcxn)
865865
$(MCUXPRESSO)/drivers/lpuart/fsl_lpuart.o \
866866
$(MCUXPRESSO_DRIVERS)/drivers/fsl_reset.o
867867
endif
868+
869+
ifeq ($(WOLFCRYPT_TZ_PSA),1)
870+
ELS_PKC=$(MCUXPRESSO)/components/els_pkc
871+
CFLAGS+=\
872+
-I$(ELS_PKC)/includes \
873+
-I$(ELS_PKC)/src/comps/mcuxClEls/inc \
874+
-I$(ELS_PKC)/src/platforms/mcxn
875+
# mcuxClEls_* functions dispatch through the ROM API; verify if .o files are needed.
876+
endif
868877
endif
869878

870879
ifeq ($(TARGET),nrf5340)

config/examples/mcxn-tz-psa.config

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
ARCH?=ARM
2+
TZEN?=1
3+
TARGET?=mcxn
4+
SIGN?=ECC384
5+
HASH?=SHA384
6+
MCUXSDK?=1
7+
MCUXPRESSO?=$(PWD)/../NXP/mcuxpresso-sdk/mcuxsdk
8+
MCUXPRESSO_CMSIS?=$(PWD)/../NXP/CMSIS_5/CMSIS
9+
MCUXPRESSO_CPU?=MCXN947VDF_cm33_core0
10+
MCUXPRESSO_DRIVERS?=$(MCUXPRESSO)/devices/MCX/MCXN/MCXN947
11+
MCUXPRESSO_PROJECT_TEMPLATE?=$(MCUXPRESSO)/examples/_boards/frdmmcxn947/project_template
12+
DEBUG?=0
13+
DEBUG_UART?=1
14+
VTOR?=1
15+
CORTEX_M0?=0
16+
CORTEX_M33?=1
17+
NO_ASM?=0
18+
NO_MPU=1
19+
EXT_FLASH?=0
20+
SPI_FLASH?=0
21+
ALLOW_DOWNGRADE?=0
22+
NVM_FLASH_WRITEONCE?=1
23+
NO_ARM_ASM=1
24+
WOLFBOOT_VERSION?=0
25+
V?=0
26+
SPMATH?=1
27+
RAM_CODE?=1
28+
DUALBANK_SWAP?=0
29+
PKA?=1
30+
WOLFCRYPT_TZ?=1
31+
WOLFCRYPT_TZ_PSA?=1
32+
WOLFCRYPT_SECURE_MODE?=1
33+
WOLFBOOT_DICE_HW?=1
34+
IMAGE_HEADER_SIZE?=1024
35+
36+
# 8KB sectors
37+
WOLFBOOT_SECTOR_SIZE?=0x2000
38+
39+
# Default configuration
40+
# 192KB boot, 96KB keyvault, 8KB NSC, 72KB partitions, 8KB swap
41+
WOLFBOOT_KEYVAULT_ADDRESS?=0x30000
42+
WOLFBOOT_KEYVAULT_SIZE?=0x18000
43+
WOLFBOOT_NSC_ADDRESS?=0x48000
44+
WOLFBOOT_NSC_SIZE?=0x2000
45+
WOLFBOOT_PARTITION_SIZE?=0x12000
46+
WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x4A000
47+
WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x5C000
48+
WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x6E000

hal/hal.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,3 +321,27 @@ WEAKFUNCTION int hal_attestation_get_iak_private_key(uint8_t *buf, size_t *len)
321321
(void)len;
322322
return -1;
323323
}
324+
325+
#ifdef WOLFBOOT_DICE_HW
326+
WEAKFUNCTION int hal_dice_update_cdi(const uint8_t *measurement, size_t meas_len)
327+
{
328+
(void)measurement;
329+
(void)meas_len;
330+
return -1;
331+
}
332+
333+
WEAKFUNCTION int hal_dice_create_attest_key(void)
334+
{
335+
return -1;
336+
}
337+
338+
WEAKFUNCTION int hal_dice_sign_hash(const uint8_t *hash, size_t hash_len,
339+
uint8_t *sig, size_t *sig_len)
340+
{
341+
(void)hash;
342+
(void)hash_len;
343+
(void)sig;
344+
(void)sig_len;
345+
return -1;
346+
}
347+
#endif /* WOLFBOOT_DICE_HW */

hal/mcxn.c

Lines changed: 177 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,13 @@
4040
#include "hal/armv8m_tz.h"
4141
#endif
4242

43+
#if defined(WOLFCRYPT_TZ_PSA) && defined(WOLFBOOT_DICE_HW)
44+
#include "mcuxClEls.h"
45+
#include "mcuxClEls_Kdf.h"
46+
#include "mcuxClEls_Ecc.h"
47+
#include "mcuxClEls_KeyManagement.h"
48+
#endif
49+
4350
#ifdef WOLFCRYPT_SECURE_MODE
4451
void hal_trng_init(void);
4552
int hal_trng_get_entropy(unsigned char *out, unsigned int len);
@@ -212,7 +219,7 @@ int RAMFUNCTION hal_flash_erase(uint32_t address, int len)
212219
return 0;
213220
}
214221

215-
#ifdef WOLFCRYPT_SECURE_MODE
222+
#if defined(WOLFCRYPT_SECURE_MODE) && !defined(NONSECURE_APP)
216223
#define ELS_CMD_RND_REQ 24U
217224

218225
void hal_trng_init(void)
@@ -383,3 +390,172 @@ void uart_write(const char *buf, unsigned int sz)
383390
sz -= line_sz + 1U;
384391
}
385392
}
393+
394+
#if defined(WOLFCRYPT_TZ_PSA) && defined(WOLFBOOT_DICE_HW)
395+
396+
/* Key slot holding the UDS (pre-loaded by ROM before DICE).
397+
* TODO: Verify slot number from MCXN947 RM / ROM source. */
398+
#define MCXN_ELS_DICE_UDS_KEYSLOT 0U /* TODO: verify */
399+
400+
/* Key slot pre-loaded by ROM DICE: HKDF(UDS, wolfBoot_hash) -> initial CDI.
401+
* TODO: Verify slot number from MCXN947 RM / ROM source. */
402+
#define MCXN_ELS_DICE_CDI_INITIAL_KEYSLOT 0U /* TODO: verify */
403+
404+
/* wolfBoot stores the boot-measurement-derived CDI here.
405+
* TODO: Confirm this slot is free at runtime (not reserved by ROM or PSA). */
406+
#define MCXN_ELS_DICE_CDI_DERIVED_KEYSLOT 20U /* example — verify availability */
407+
408+
/* wolfBoot stores the per-boot IAK (P-256) here.
409+
* TODO: Confirm this slot is free at runtime (not reserved by ROM or PSA). */
410+
#define MCXN_ELS_DICE_IAK_KEYSLOT 21U /* example — verify availability */
411+
412+
int hal_attestation_get_ueid(uint8_t *buf, size_t *len)
413+
{
414+
/*
415+
* Derive 32-byte UEID from UDS via ELS HKDF SP800-56C.
416+
* Using the UDS key slot matches the software path hash(UDS) -> UEID.
417+
* Output goes to system memory (not a keystore slot).
418+
*
419+
* buf[0] = WOLFBOOT_UEID_TYPE_RANDOM (0x01)
420+
* buf[1..32] = 32 bytes from ELS HKDF SP800-56C
421+
* *len = 33 (= WOLFBOOT_DICE_UEID_LEN)
422+
*
423+
* TODO: Implement using mcuxClEls_Hkdf_Sp80056c_Async:
424+
* MCUX_CSSL_FP_FUNCTION_CALL_BEGIN(...,
425+
* mcuxClEls_Hkdf_Sp80056c_Async(
426+
* MCXN_ELS_DICE_UDS_KEYSLOT,
427+
* &buf[1],
428+
* (uint8_t*)"WOLFBOOT-UEID", 13));
429+
* mcuxClEls_WaitForOperation(MCUXCLELS_ERROR_FLAGS_CLEAR);
430+
* buf[0] = 0x01;
431+
* *len = 33;
432+
*/
433+
(void)buf;
434+
(void)len;
435+
return -1; /* placeholder */
436+
}
437+
438+
int hal_attestation_get_lifecycle(uint32_t *lifecycle)
439+
{
440+
if (lifecycle == NULL)
441+
return -1;
442+
*lifecycle = 0x3000u; /* PSA_LIFECYCLE_SECURED */
443+
return 0;
444+
}
445+
446+
int hal_dice_update_cdi(const uint8_t *measurement, size_t meas_len)
447+
{
448+
static int cdi_step = 0;
449+
450+
if (cdi_step == 0) {
451+
/*
452+
* Component [0] is the wolfBoot hash. The ROM already incorporated
453+
* it as: HKDF(UDS, wolfBoot_hash) -> initial_CDI.
454+
* Skip re-applying it — doing so would produce the wrong CDI chain.
455+
*/
456+
cdi_step++;
457+
return 0;
458+
}
459+
460+
/*
461+
* Component [1+]: mix this measurement into the CDI chain via ELS HKDF RFC5869.
462+
*
463+
* The 32-byte derivation data buffer must be composed as:
464+
* deriv[0..30] = info/label (31 bytes, zero-padded)
465+
* deriv[31] = 0x01 (RFC5869 counter byte for single-block expansion)
466+
* The SDK does NOT add this formatting automatically.
467+
*
468+
* NOTE: The exact label policy (truncate SHA-384 to 31 bytes, or pre-hash
469+
* to SHA-256 then take first 31 bytes) and alignment with the ROM's approach
470+
* for component[0] must be confirmed before finalizing this implementation.
471+
* See also: whether a separate RFC5869 Extract step is needed before Expand.
472+
*
473+
* TODO: Implement using mcuxClEls_KeyDelete_Async + mcuxClEls_Hkdf_Rfc5869_Async:
474+
*
475+
* // Step 1: Free target slot (KDELETE, cmd 0xB)
476+
* MCUX_CSSL_FP_FUNCTION_CALL_BEGIN(...,
477+
* mcuxClEls_KeyDelete_Async(MCXN_ELS_DICE_CDI_DERIVED_KEYSLOT));
478+
* mcuxClEls_WaitForOperation(MCUXCLELS_ERROR_FLAGS_CLEAR);
479+
*
480+
* // Step 2: Compose derivation data {label[31 bytes], counter[1 byte]}
481+
* uint8_t deriv[MCUXCLELS_HKDF_RFC5869_DERIVATIONDATA_SIZE] = {0};
482+
* size_t label_len = (meas_len < 31u) ? meas_len : 31u;
483+
* memcpy(deriv, measurement, label_len);
484+
* deriv[31] = 0x01u;
485+
*
486+
* // Step 3: Derive new CDI into freed slot
487+
* mcuxClEls_HkdfOption_t opts = {0};
488+
* opts.bits.rtfdrvdat = MCUXCLELS_HKDF_VALUE_RTF_DERIV_DISABLE;
489+
* mcuxClEls_KeyProp_t props = {.word.value =
490+
* MCUXCLELS_KEYPROPERTY_VALUE_HKDF | MCUXCLELS_KEYPROPERTY_VALUE_ACTIVE};
491+
* MCUX_CSSL_FP_FUNCTION_CALL_BEGIN(...,
492+
* mcuxClEls_Hkdf_Rfc5869_Async(opts,
493+
* MCXN_ELS_DICE_CDI_INITIAL_KEYSLOT,
494+
* MCXN_ELS_DICE_CDI_DERIVED_KEYSLOT,
495+
* props, deriv));
496+
* mcuxClEls_WaitForOperation(MCUXCLELS_ERROR_FLAGS_CLEAR);
497+
*/
498+
cdi_step++;
499+
(void)measurement;
500+
(void)meas_len;
501+
return -1; /* placeholder */
502+
}
503+
504+
int hal_dice_create_attest_key(void)
505+
{
506+
/*
507+
* Generate P-256 IAK from derived CDI using ELS KEYGEN.
508+
* Private key stays in ELS keystore. Public key written to system memory.
509+
*
510+
* TODO: Implement using mcuxClEls_KeyDelete_Async + mcuxClEls_EccKeyGen_Async:
511+
*
512+
* // Step 1: Free target slot (KDELETE, cmd 0xB)
513+
* MCUX_CSSL_FP_FUNCTION_CALL_BEGIN(...,
514+
* mcuxClEls_KeyDelete_Async(MCXN_ELS_DICE_IAK_KEYSLOT));
515+
* mcuxClEls_WaitForOperation(MCUXCLELS_ERROR_FLAGS_CLEAR);
516+
*
517+
* // Step 2: Generate IAK into freed slot from derived CDI
518+
* static uint8_t pub_key[64] __attribute__((aligned(4)));
519+
* mcuxClEls_EccKeyGenOption_t opts = {0};
520+
* opts.bits.kgsrc = 0; // deterministic from CDI key material
521+
* opts.bits.kgtypedh = 0; // signing key (not DH)
522+
* mcuxClEls_KeyProp_t props = {.word.value =
523+
* MCUXCLELS_KEYPROPERTY_VALUE_ECSGN | MCUXCLELS_KEYPROPERTY_VALUE_ACTIVE};
524+
* MCUX_CSSL_FP_FUNCTION_CALL_BEGIN(...,
525+
* mcuxClEls_EccKeyGen_Async(opts,
526+
* MCXN_ELS_DICE_CDI_DERIVED_KEYSLOT,
527+
* MCXN_ELS_DICE_IAK_KEYSLOT,
528+
* props, NULL, pub_key));
529+
* mcuxClEls_WaitForOperation(MCUXCLELS_ERROR_FLAGS_CLEAR);
530+
*/
531+
return -1; /* placeholder */
532+
}
533+
534+
int hal_dice_sign_hash(const uint8_t *hash, size_t hash_len,
535+
uint8_t *sig, size_t *sig_len)
536+
{
537+
/*
538+
* Sign pre-hashed TBS using ELS ECSIGN with the IAK slot.
539+
* Output MUST be 64-byte raw R||S (big-endian), NOT DER-encoded.
540+
* This matches WOLFBOOT_DICE_SIG_LEN and is used directly in COSE_Sign1.
541+
*
542+
* TODO: Implement using mcuxClEls_EccSign_Async:
543+
* static uint8_t sig_buf[64] __attribute__((aligned(4)));
544+
* mcuxClEls_EccSignOption_t opts = {0};
545+
* opts.bits.echashchl = 0; // pre-hashed input (not plain message)
546+
* MCUX_CSSL_FP_FUNCTION_CALL_BEGIN(...,
547+
* mcuxClEls_EccSign_Async(opts,
548+
* MCXN_ELS_DICE_IAK_KEYSLOT,
549+
* hash, NULL, 0, sig_buf));
550+
* mcuxClEls_WaitForOperation(MCUXCLELS_ERROR_FLAGS_CLEAR);
551+
* memcpy(sig, sig_buf, 64);
552+
* *sig_len = 64;
553+
*/
554+
(void)hash;
555+
(void)hash_len;
556+
(void)sig;
557+
(void)sig_len;
558+
return -1; /* placeholder */
559+
}
560+
561+
#endif /* WOLFCRYPT_TZ_PSA && WOLFBOOT_DICE_HW */

include/hal.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,27 @@ int hal_attestation_get_implementation_id(uint8_t *buf, size_t *len);
167167
int hal_attestation_get_ueid(uint8_t *buf, size_t *len);
168168
int hal_attestation_get_iak_private_key(uint8_t *buf, size_t *len);
169169

170+
#ifdef WOLFBOOT_DICE_HW
171+
/* Hardware DICE hooks — implement these to delegate CDI derivation and
172+
* attestation signing to a platform security boundary. */
173+
174+
/* Mix one boot-component measurement into the platform CDI chain.
175+
* Called once per component in order (wolfBoot hash first, then boot image hash).
176+
* The platform updates its internal CDI state; no CDI material is returned. */
177+
int hal_dice_update_cdi(const uint8_t *measurement, size_t meas_len);
178+
179+
/* Derive and store the attestation keypair from the current CDI state.
180+
* The private key must not be exposed outside the platform security boundary.
181+
* Returns 0 on success. */
182+
int hal_dice_create_attest_key(void);
183+
184+
/* Sign a pre-computed SHA-256 hash with the platform attestation key.
185+
* Output: 64-byte raw R||S (big-endian), same format as wolfCrypt ES256.
186+
* Must be called after hal_dice_create_attest_key(). */
187+
int hal_dice_sign_hash(const uint8_t *hash, size_t hash_len,
188+
uint8_t *sig, size_t *sig_len);
189+
#endif /* WOLFBOOT_DICE_HW */
190+
170191
#ifdef FLASH_OTP_KEYSTORE
171192

172193
int hal_flash_otp_write(uint32_t flashAddress, const void* data, uint16_t length);

options.mk

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -876,6 +876,9 @@ endif
876876
ifeq ($(WOLFCRYPT_TZ_PSA),1)
877877
CFLAGS+=-DWOLFCRYPT_TZ_PSA
878878
CFLAGS+=-DWOLFCRYPT_SECURE_MODE
879+
ifeq ($(WOLFBOOT_DICE_HW),1)
880+
CFLAGS+=-DWOLFBOOT_DICE_HW
881+
endif
879882
CFLAGS+=-DWOLFSSL_PSA_ENGINE
880883
CFLAGS+=-DWOLFPSA_CUSTOM_STORE
881884
CFLAGS+=-DNO_DES3 -DNO_DES3_TLS_SUITES

0 commit comments

Comments
 (0)