diff --git a/.github/workflows/test-configs.yml b/.github/workflows/test-configs.yml index ed99f36542..c23e8d1c13 100644 --- a/.github/workflows/test-configs.yml +++ b/.github/workflows/test-configs.yml @@ -273,6 +273,20 @@ jobs: config-file: ./config/examples/mcxn-wolfcrypt-tz.config board-name: frdmmcxn947 + nxp_mcxn_tz_psa_test: + uses: ./.github/workflows/test-build-mcux-sdk-manifests.yml + with: + arch: arm + config-file: ./config/examples/mcxn-tz-psa.config + board-name: frdmmcxn947 + + nxp_mcxn_tz_psa_hw_test: + uses: ./.github/workflows/test-build-mcux-sdk-manifests.yml + with: + arch: arm + config-file: ./config/examples/mcxn-tz-psa-hw.config + board-name: frdmmcxn947 + nxp_s32k142_test: uses: ./.github/workflows/test-build.yml with: diff --git a/arch.mk b/arch.mk index 0a3067a32d..f22892f0b6 100644 --- a/arch.mk +++ b/arch.mk @@ -974,6 +974,23 @@ ifeq ($(TARGET),mcxn) $(MCUXPRESSO)/drivers/lpuart/fsl_lpuart.o \ $(MCUXPRESSO_DRIVERS)/drivers/fsl_reset.o endif + + # Link ELS PKC only when -DWOLFBOOT_DICE_HW is emitted (WOLFCRYPT_TZ_PSA=1 in options.mk). + ifeq ($(WOLFCRYPT_TZ_PSA),1) + ifeq ($(WOLFBOOT_DICE_HW),1) + ELS_PKC=$(MCUXPRESSO)/components/els_pkc + CFLAGS+=\ + -I$(ELS_PKC)/includes \ + -I$(ELS_PKC)/src/comps/mcuxClEls/inc \ + -I$(ELS_PKC)/src/platforms/mcxn \ + -I$(ELS_PKC)/src/platforms/mcxn/inc + OBJS+=\ + $(ELS_PKC)/src/comps/mcuxClEls/src/mcuxClEls_Common.o \ + $(ELS_PKC)/src/comps/mcuxClEls/src/mcuxClEls_Kdf.o \ + $(ELS_PKC)/src/comps/mcuxClEls/src/mcuxClEls_KeyManagement.o \ + $(ELS_PKC)/src/comps/mcuxClEls/src/mcuxClEls_Ecc.o + endif + endif endif ifeq ($(TARGET),nrf5340) diff --git a/config/examples/mcxn-tz-psa-hw.config b/config/examples/mcxn-tz-psa-hw.config new file mode 100644 index 0000000000..811e18aa28 --- /dev/null +++ b/config/examples/mcxn-tz-psa-hw.config @@ -0,0 +1,50 @@ +ARCH?=ARM +TZEN?=1 +TARGET?=mcxn +SIGN?=ECC384 +HASH?=SHA384 +MCUXSDK?=1 +MCUXPRESSO?=$(PWD)/../NXP/mcuxpresso-sdk/mcuxsdk +MCUXPRESSO_CMSIS?=$(PWD)/../NXP/CMSIS_5/CMSIS +MCUXPRESSO_CPU?=MCXN947VDF_cm33_core0 +MCUXPRESSO_DRIVERS?=$(MCUXPRESSO)/devices/MCX/MCXN/MCXN947 +MCUXPRESSO_PROJECT_TEMPLATE?=$(MCUXPRESSO)/examples/_boards/frdmmcxn947/project_template +DEBUG?=0 +DEBUG_UART?=1 +VTOR?=1 +CORTEX_M0?=0 +CORTEX_M33?=1 +NO_ASM?=0 +NO_MPU=1 +EXT_FLASH?=0 +SPI_FLASH?=0 +ALLOW_DOWNGRADE?=0 +NVM_FLASH_WRITEONCE?=1 +NO_ARM_ASM=1 +WOLFBOOT_VERSION?=0 +V?=0 +SPMATH?=1 +RAM_CODE?=1 +DUALBANK_SWAP?=0 +PKA?=1 +WOLFCRYPT_TZ?=1 +WOLFCRYPT_TZ_PSA?=1 +WOLFCRYPT_SECURE_MODE?=1 +WOLFBOOT_DICE_HW?=1 +IMAGE_HEADER_SIZE?=1024 +WOLFBOOT_ATTESTATION_TEST?=1 +WOLFBOOT_UDS_UID_FALLBACK_FORTEST?=0 + +# 8KB sectors +WOLFBOOT_SECTOR_SIZE?=0x2000 + +# Default configuration +# 192KB boot, 96KB keyvault, 8KB NSC, 72KB partitions, 8KB swap +WOLFBOOT_KEYVAULT_ADDRESS?=0x30000 +WOLFBOOT_KEYVAULT_SIZE?=0x18000 +WOLFBOOT_NSC_ADDRESS?=0x48000 +WOLFBOOT_NSC_SIZE?=0x2000 +WOLFBOOT_PARTITION_SIZE?=0x12000 +WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x60000 +WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x72000 +WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x84000 diff --git a/config/examples/mcxn-tz-psa.config b/config/examples/mcxn-tz-psa.config new file mode 100644 index 0000000000..f32c7aa738 --- /dev/null +++ b/config/examples/mcxn-tz-psa.config @@ -0,0 +1,50 @@ +ARCH?=ARM +TZEN?=1 +TARGET?=mcxn +SIGN?=ECC384 +HASH?=SHA384 +MCUXSDK?=1 +MCUXPRESSO?=$(PWD)/../NXP/mcuxpresso-sdk/mcuxsdk +MCUXPRESSO_CMSIS?=$(PWD)/../NXP/CMSIS_5/CMSIS +MCUXPRESSO_CPU?=MCXN947VDF_cm33_core0 +MCUXPRESSO_DRIVERS?=$(MCUXPRESSO)/devices/MCX/MCXN/MCXN947 +MCUXPRESSO_PROJECT_TEMPLATE?=$(MCUXPRESSO)/examples/_boards/frdmmcxn947/project_template +DEBUG?=0 +DEBUG_UART?=1 +VTOR?=1 +CORTEX_M0?=0 +CORTEX_M33?=1 +NO_ASM?=0 +NO_MPU=1 +EXT_FLASH?=0 +SPI_FLASH?=0 +ALLOW_DOWNGRADE?=0 +NVM_FLASH_WRITEONCE?=1 +NO_ARM_ASM=1 +WOLFBOOT_VERSION?=0 +V?=0 +SPMATH?=1 +RAM_CODE?=1 +DUALBANK_SWAP?=0 +PKA?=1 +WOLFCRYPT_TZ?=1 +WOLFCRYPT_TZ_PSA?=1 +WOLFCRYPT_SECURE_MODE?=1 +WOLFBOOT_DICE_HW?=0 +IMAGE_HEADER_SIZE?=1024 +WOLFBOOT_ATTESTATION_TEST?=1 +WOLFBOOT_UDS_UID_FALLBACK_FORTEST?=1 + +# 8KB sectors +WOLFBOOT_SECTOR_SIZE?=0x2000 + +# Default configuration +# 192KB boot, 96KB keyvault, 8KB NSC, 72KB partitions, 8KB swap +WOLFBOOT_KEYVAULT_ADDRESS?=0x30000 +WOLFBOOT_KEYVAULT_SIZE?=0x18000 +WOLFBOOT_NSC_ADDRESS?=0x48000 +WOLFBOOT_NSC_SIZE?=0x2000 +WOLFBOOT_PARTITION_SIZE?=0x12000 +WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x60000 +WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x72000 +WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x84000 diff --git a/docs/DICE.md b/docs/DICE.md index a109dc00b0..62d7ef6613 100644 --- a/docs/DICE.md +++ b/docs/DICE.md @@ -34,7 +34,7 @@ measurement value, and a description string. ## Keying model -wolfBoot supports two keying modes selected at build time. +wolfBoot supports three keying modes selected at build time. ### DICE derived key (default for no provisioning) @@ -55,6 +55,17 @@ The attestation service calls `hal_attestation_get_iak_private_key()` to retrieve the private key material from secure storage (or a manufacturer injection flow). The IAK is used instead of the DICE derived key. +### Hardware-based DICE derived key + +Some platforms have secure subsystem which supports hardware engines for DICE functionality. +Some of them don't expose the secrets like UDS, CDI and attestation key. +In that case, You can enable hardware-based DICE using `WOLFBOOT_DICE_HW` option. + +wolfBoot calls `hal_dice_update_cdi()` to update CDI inside secure subsystem for each component. +Also, `hal_dice_create_attest_key()` is called to derive the attestation key from updated CDI as well. +Then, wolfBoot uses `hal_dice_sign_hash` to sign the token. +User can implement hardware-specific procedures inside the hooks. + ## HAL integration (per-target) These HAL hooks are optional and have weak stubs for non-TZ boards. Target @@ -76,6 +87,21 @@ families must implement the appropriate subset based on hardware support. - `hal_attestation_get_iak_private_key(uint8_t *buf, size_t *len)` - Optional provisioned IAK private key (used in IAK mode only). +If you enable hardware-based DICE, additional hooks are necessary. + +- `hal_dice_update_cdi(const uint8_t *measurement, size_t meas_len, const char *measurement_desc, size_t measurement_desc_len)` + - Mixes one boot-component measurement into the platform CDI chain. + - measurement_desc identifies the component (e.g. "wolfboot", "boot-image"). +- `hal_dice_create_attest_key(void)` + - Derive and store the attestation key pair from the current CDI state. + - The private key must not be exposed outside the platform security boundary. +- `hal_dice_sign_hash(const uint8_t *hash, size_t hash_len, uint8_t *sig, size_t *sig_len)` + - Sign a pre-computed SHA-256 hash with the platform private attestation key during token generation. + - Output: 64-byte raw R||S (big-endian), same format as wolfCrypt ES256. +- `hal_dice_get_attest_pubkey(uint8_t *buf, size_t *len)` + - Optional hook to get the public part of attestation key. + - This is called via PSA Initial Attestation dispatch `psa_initial_attest_get_iak_pubkey()`. + ## STM32H5 OBKeys UDS (optional) STM32H5 devices provide OBKeys secure storage areas tied to temporal isolation @@ -103,6 +129,7 @@ The non-secure application calls the PSA Initial Attestation API wrappers: - `psa_initial_attest_get_token_size()` - `psa_initial_attest_get_token()` +- `psa_initial_attest_get_iak_pubkey()` These are provided in `zephyr/include/psa/initial_attestation.h` and are implemented as NSC calls in `zephyr/src/arm_tee_attest_api.c`. @@ -114,9 +141,15 @@ When `WOLFCRYPT_TZ_PSA=1`, the NS application can also use PSA Crypto through ## Test application +### STM32H5 The STM32H5 TrustZone test application in `test-app/` exercises PSA crypto, attestation, and store access. It requests a token at boot and can perform PSA crypto operations from the non-secure side. See `docs/Targets.md` for the STM32H5 TrustZone scenarios and how to enable PSA mode. + +### MCXN +MCXN has TrustZone test application in `test-app/` exercises PSA attestation. +Default setting is based on hardware secure subsystem called EdgeLock Secure Subsystem. +See `docs/Targets.md` and `docs/MCXN947-DICE.md` for details. diff --git a/docs/MCXN947-DICE.md b/docs/MCXN947-DICE.md new file mode 100644 index 0000000000..e11698fc50 --- /dev/null +++ b/docs/MCXN947-DICE.md @@ -0,0 +1,79 @@ +# DICE attestation sample on MCXN947 + +General concept of wolfBoot in TrustZone secure domain is explained in `docs/STM32-TZ.md`. +This focuses on a sample of PSA initial attestation for MCXN947. +Sample application is implemented in `test-app/app_mcxn.c`. +This supports hardware-based DICE using EdgeLock Secure Subsystem. + +## How to build +Please use `config/examples/mcxn-tz-psa-hw.config` to build wolfBoot. +You need to update the path of SDK inside the config for your build environment. +``` +cp config/examples/mcxn-tz-psa-hw.config .config +make +``` + +## How to run +MCXN947 requires us to enable secure boot to use DICE functionality of ELS. +So, We need to prepare the specific image file of wolfBoot binary and configuration files of CMPA/CFPA for secure bootROM and ELS. +Fortunately NXP provides the [secure provisioning tool](https://www.nxp.com/design/design-center/software/development-software/mcuxpresso-software-and-tools-/mcuxpresso-secure-provisioning-tool:MCUXPRESSO-SECURE-PROVISIONING) for such purpose. + +At a minimum, you shall configure the following settings and encode the image with signature: +- Enable Image verification for secure boot (SEC_BOOT_EN == 0b10) +- Enable DICE (SKIP_DICE == 0b0) +All of these can be done using the NXP secure provisioning tool. + +If you use secure provisioning tool, you can download the prepared image with configuration files to hardware as well. +Regarding to application image like image_v1_signed.bin, we don't need to encode it like the wolfBoot binary because it's verified by wolfBoot as usual. +But, we still recommend you to download the application image using the flash programmer tool in the secure provisioning tool for a safe download. You can access it via `Tools > Flash Programmer` on GUI. + +Once both images are downloaded successfully, you'll see the log after reboot: +``` +Boot partition: 0x60000 (sz 31508, ver 0x1, type 0x601) +Partition 1 header magic 0xFFFFFFFF invalid at 0x72000 +Boot partition: 0x60000 (sz 31508, ver 0x1, type 0x601) +Booting version: 0x1 +Checking integrity...done +Verifying signature...done +Hello from firmware version 1 +Today's lucky number: 0xAF +PSA crypto init ok +Start attestation verify test +[ATTEST] GET_TOKEN: challenge_len=32 out_len=1024 +Boot partition: 0x60000 (sz 31508, ver 0x1, type 0x601) +Boot partition: 0x60000 (sz 31508, ver 0x1, type 0x601) +[ATTEST] GET_TOKEN: dice_rc=0 token_len=356 +attest_verify: token_len=356 + 84 43 A1 01 26 A0 59 01 19 A5 0A 58 20 01 02 03 | .C..&.Y....X ... + 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 | ................ + 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 19 01 00 | ............ ... + 58 21 01 AD 74 46 22 0C D0 5B 7A 1B 33 5E 37 28 | X!..tF"..[z.3^7( + 0A 54 E6 DF 55 5B 40 A4 57 A0 43 0A 6D A5 AB E7 | .T..U[@.W.C.m... + 5A 2B 5F 19 09 5C 58 30 E4 F9 83 72 7F A9 C5 CE | Z+_..\X0...r.... + DD E0 79 A0 AD 0E 32 59 0A EA D3 10 43 BA 12 DD | ..y...2Y....C... + 76 47 59 D1 42 8B 3A 75 F2 7E 75 CF 7D EE A2 B9 | vGY.B.:u.~u.}... + AA AB 7E E9 91 27 F7 21 19 09 5E 19 30 00 19 09 | ..~..'.!..^.0... + 5F 82 A3 01 67 73 68 61 2D 33 38 34 02 58 30 E4 | _...gsha-384.X0. + F9 83 72 7F A9 C5 CE DD E0 79 A0 AD 0E 32 59 0A | ..r......y...2Y. + EA D3 10 43 BA 12 DD 76 47 59 D1 42 8B 3A 75 F2 | ...C...vGY.B.:u. + 7E 75 CF 7D EE A2 B9 AA AB 7E E9 91 27 F7 21 05 | ~u.}.....~..'.!. + 68 77 6F 6C 66 62 6F 6F 74 A3 01 67 73 68 61 2D | hwolfboot..gsha- + 33 38 34 02 58 30 D4 F2 89 AD E4 4E 6E EB F4 C6 | 384.X0.....Nn... + 7D 24 13 AD 6C E6 DC A6 E9 FC 67 4F CC CB DE 52 | }$..l.....gO...R + 4D FD E2 73 9D 2A D7 B3 DD DF B8 51 F4 43 C5 59 | M..s.*.....Q.C.Y + 48 8A 30 46 D7 E3 05 6A 62 6F 6F 74 2D 69 6D 61 | H.0F...jboot-ima + 67 65 58 40 E8 3F 76 44 15 7C 62 76 33 56 6D F7 | geX@.?vD.|bv3Vm. + 2B E1 57 40 C4 F1 7D 21 D3 E9 90 57 25 8F A3 14 | +.W@..}!...W%... + 44 ED 1B CE 01 CA EF 52 6B 41 4C 44 DF 4E 04 D4 | D......RkALD.N.. + AA CC A8 7F 20 3D AA 39 CA 3A 9F C2 B1 C0 28 43 | .... =.9.:....(C + DF BB 82 53 | ...S +[ATTEST] GET_IAK_PUBKEY: out_len=65 +[ATTEST] GET_IAK_PUBKEY: dice_rc=0 key_len=65 +attest_verify: IAK pubkey (65 bytes): + 04 42 9B 43 75 BE B0 E9 A0 39 61 64 68 20 1E FC | .B.Cu....9adh .. + B6 68 F4 3E 25 74 F1 E2 CB 3D BD 09 A6 45 A7 E2 | .h.>%t...=...E.. + 6F E1 54 B8 20 61 96 79 6A E8 31 83 3A 82 DE 1C | o.T. a.yj.1.:... + 87 46 E1 45 49 3D 6F 56 78 F4 14 B6 42 47 DD F1 | .F.EI=oVx...BG.. + AC | . +attest_verify: IAK signature verified OK +``` diff --git a/docs/Targets.md b/docs/Targets.md index c295d985ed..842b67cce8 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -6107,6 +6107,9 @@ mon reset c ``` +### MCX N: DICE attestation +Sample application for DICE attestation is available on MCXN947. +Please find the details in `docs/MCXN947-DICE.md`. ## NXP S32K1XX diff --git a/hal/hal.c b/hal/hal.c index a3ed771976..5e2545554c 100644 --- a/hal/hal.c +++ b/hal/hal.c @@ -321,3 +321,38 @@ WEAKFUNCTION int hal_attestation_get_iak_private_key(uint8_t *buf, size_t *len) (void)len; return -1; } + +#ifdef WOLFBOOT_DICE_HW +WEAKFUNCTION int hal_dice_update_cdi(const uint8_t *measurement, size_t meas_len, + const char *measurement_desc, + size_t measurement_desc_len) +{ + (void)measurement; + (void)meas_len; + (void)measurement_desc; + (void)measurement_desc_len; + return -1; +} + +WEAKFUNCTION int hal_dice_create_attest_key(void) +{ + return -1; +} + +WEAKFUNCTION int hal_dice_sign_hash(const uint8_t *hash, size_t hash_len, + uint8_t *sig, size_t *sig_len) +{ + (void)hash; + (void)hash_len; + (void)sig; + (void)sig_len; + return -1; +} + +WEAKFUNCTION int hal_dice_get_attest_pubkey(uint8_t *buf, size_t *len) +{ + (void)buf; + (void)len; + return -1; +} +#endif /* WOLFBOOT_DICE_HW */ diff --git a/hal/mcxn.c b/hal/mcxn.c index 6344775c24..b8d8495adb 100644 --- a/hal/mcxn.c +++ b/hal/mcxn.c @@ -40,6 +40,112 @@ #include "hal/armv8m_tz.h" #endif +#if defined(WOLFCRYPT_TZ_PSA) +/* 128-bit device UUID in IPC 1 (SYSCON UUID block) */ +#define MCXN_UUID_ADDR 0x01100000U +#endif + +#if defined(WOLFCRYPT_TZ_PSA) && defined(WOLFBOOT_DICE_HW) +#include "wolfboot/dice.h" +#include "mcuxClEls.h" +#include "mcuxClEls_Kdf.h" +#include "mcuxClEls_Ecc.h" +#include "mcuxClEls_KeyManagement.h" +#include "mcuxCsslFlowProtection.h" +#include + +/* Key slot pre-loaded by ROM DICE: HKDF(UDS, wolfBoot_hash) -> initial CDI. */ +#define MCXN_ELS_DICE_CDI_INITIAL_KEYSLOT 7U + +/* wolfBoot stores the boot-measurement-derived CDI here. */ +#define MCXN_ELS_DICE_CDI_DERIVED_KEYSLOT 11U + +/* wolfBoot stores the per-boot IAK (P-256) here. + * ELS EccKeyGen DETERMINISTIC mode uses privateKeyIdx as both the CDI seed + * source and the IAK output slot, so the IAK overwrites the CDI in-place. */ +#define MCXN_ELS_DICE_IAK_KEYSLOT MCXN_ELS_DICE_CDI_DERIVED_KEYSLOT + +#endif + +#if defined(WOLFCRYPT_TZ_PSA) && !defined(WOLFBOOT_DICE_HW) +#include +#include + +/* Derive UDS from device UUID for software DICE testing. + * NOT secure — UUID is publicly observable. Only enabled with + * WOLFBOOT_UDS_UID_FALLBACK_FORTEST. */ +int hal_uds_derive_key(uint8_t *out, size_t out_len) +{ + volatile const uint32_t *uuid_addr = + (volatile const uint32_t *)MCXN_UUID_ADDR; + uint8_t uuid_be[16]; + uint32_t word; + int i; +#if defined(WOLFBOOT_HASH_SHA384) + wc_Sha384 hash; + uint8_t digest[SHA384_DIGEST_SIZE]; + size_t copy_len = sizeof(digest); +#elif defined(WOLFBOOT_HASH_SHA256) + wc_Sha256 hash; + uint8_t digest[SHA256_DIGEST_SIZE]; + size_t copy_len = sizeof(digest); +#else + (void)out; (void)out_len; + return -1; +#endif + + if (out == NULL || out_len == 0) + return -1; + +#ifndef WOLFBOOT_UDS_UID_FALLBACK_FORTEST + (void)uuid_addr; (void)uuid_be; (void)word; (void)i; +#if defined(WOLFBOOT_HASH_SHA384) || defined(WOLFBOOT_HASH_SHA256) + (void)hash; (void)digest; (void)copy_len; +#endif + return -1; +#else + for (i = 0; i < 4; i++) { + word = uuid_addr[i]; + uuid_be[i * 4 + 0] = (uint8_t)(word >> 24); + uuid_be[i * 4 + 1] = (uint8_t)(word >> 16); + uuid_be[i * 4 + 2] = (uint8_t)(word >> 8); + uuid_be[i * 4 + 3] = (uint8_t)(word); + } + +#if defined(WOLFBOOT_HASH_SHA384) + { + int ret = wc_InitSha384(&hash); + if (ret == 0) { + ret = wc_Sha384Update(&hash, uuid_be, sizeof(uuid_be)); + if (ret == 0) + ret = wc_Sha384Final(&hash, digest); + wc_Sha384Free(&hash); + } + if (ret != 0) + return -1; + } +#elif defined(WOLFBOOT_HASH_SHA256) + { + int ret = wc_InitSha256(&hash); + if (ret == 0) { + ret = wc_Sha256Update(&hash, uuid_be, sizeof(uuid_be)); + if (ret == 0) + ret = wc_Sha256Final(&hash, digest); + wc_Sha256Free(&hash); + } + if (ret != 0) + return -1; + } +#endif + + if (copy_len > out_len) + copy_len = out_len; + XMEMCPY(out, digest, copy_len); + return 0; +#endif /* WOLFBOOT_UDS_UID_FALLBACK_FORTEST */ +} +#endif /* WOLFCRYPT_TZ_PSA && !WOLFBOOT_DICE_HW */ + #ifdef WOLFCRYPT_SECURE_MODE void hal_trng_init(void); int hal_trng_get_entropy(unsigned char *out, unsigned int len); @@ -212,7 +318,7 @@ int RAMFUNCTION hal_flash_erase(uint32_t address, int len) return 0; } -#ifdef WOLFCRYPT_SECURE_MODE +#if defined(WOLFCRYPT_SECURE_MODE) && !defined(NONSECURE_APP) #define ELS_CMD_RND_REQ 24U void hal_trng_init(void) @@ -383,3 +489,468 @@ void uart_write(const char *buf, unsigned int sz) sz -= line_sz + 1U; } } + +#if defined(WOLFCRYPT_TZ_PSA) && defined(WOLFBOOT_DICE_HW) && defined(__WOLFBOOT) + +/* Holds the raw 64-byte P-256 public key (X||Y) written by hal_dice_create_attest_key(). + * Consumed and zeroized by hal_dice_get_attest_pubkey(). */ +static uint8_t s_dice_attest_pubkey[64]; +static int s_dice_attest_pubkey_valid = 0; + +static NOINLINEFUNCTION void hal_dice_zeroize(void *ptr, size_t len) +{ + volatile uint8_t *p = (volatile uint8_t *)ptr; + while (len-- > 0U) { + *p++ = 0U; + } +} + +/* Derive 33-byte UEID from device UUID at IPC1 (MCXN_UUID_ADDR). + * UUID (16 bytes, big-endian) is SHA-256 hashed to produce a 32-byte + * opaque identifier. This matches WOLFBOOT_DICE_UEID_LEN = 33. + * We can't use UDS because NXP_DIE_DICE_UDS_MK_SK is not accessible, + * so we derive UEID from the UUID instead. */ +int hal_attestation_get_ueid(uint8_t *buf, size_t *len) +{ + volatile const uint32_t *uuid_addr = + (volatile const uint32_t *)MCXN_UUID_ADDR; + uint8_t uuid_be[16]; + uint32_t word; + wc_Sha256 sha; + int i, ret = 0; + + if (buf == NULL || len == NULL || *len < 33) + ret = -1; + + if (ret == 0) { + /* Read 4 words (16 bytes) and convert each to big-endian */ + for (i = 0; i < 4; i++) { + word = uuid_addr[i]; + uuid_be[i * 4 + 0] = (uint8_t)(word >> 24); + uuid_be[i * 4 + 1] = (uint8_t)(word >> 16); + uuid_be[i * 4 + 2] = (uint8_t)(word >> 8); + uuid_be[i * 4 + 3] = (uint8_t)(word); + } + +#ifdef DEBUG + wolfBoot_printf("[DICE] UUID:"); + for (i = 0; i < (int)sizeof(uuid_be); i++) + wolfBoot_printf(" %02x", uuid_be[i]); + wolfBoot_printf("\r\n"); +#endif + + /* SHA-256(UUID) -> 32-byte UEID payload */ + ret = wc_InitSha256(&sha); + if (ret == 0) { + ret = wc_Sha256Update(&sha, uuid_be, sizeof(uuid_be)); + if (ret == 0) + ret = wc_Sha256Final(&sha, &buf[1]); + wc_Sha256Free(&sha); + } + + if (ret == 0) { + /* UEID Type RANDOM per EAT spec */ + buf[0] = 0x01; + *len = 33; /* WOLFBOOT_DICE_UEID_LEN */ + +#ifdef DEBUG + wolfBoot_printf("[DICE] UEID:"); + for (i = 0; i < 33; i++) + wolfBoot_printf(" %02x", buf[i]); + wolfBoot_printf("\r\n"); +#endif + } + } + + XMEMSET(uuid_be, 0, sizeof(uuid_be)); + return ret; +} + +int hal_attestation_get_lifecycle(uint32_t *lifecycle) +{ + if (lifecycle == NULL) + return -1; + *lifecycle = 0x3000u; /* PSA_LIFECYCLE_SECURED (default) */ + return 0; +} + +/* Counts actual hardware CDI derivations performed (wolfBoot is always skipped). + * Shared with hal_dice_create_attest_key so it can reset after each complete sequence. */ +static int cdi_derivation_count = 0; + +/* Derive new CDI from measurement and previous CDI */ +int hal_dice_update_cdi(const uint8_t *measurement, size_t meas_len, + const char *measurement_desc, size_t measurement_desc_len) +{ + uint8_t deriv[MCUXCLELS_HKDF_RFC5869_DERIVATIONDATA_SIZE]; + _Static_assert(MCUXCLELS_HKDF_RFC5869_DERIVATIONDATA_SIZE >= SHA256_DIGEST_SIZE, + "MCUXCLELS_HKDF_RFC5869_DERIVATIONDATA_SIZE must be at least SHA256_DIGEST_SIZE"); + mcuxClEls_HkdfOption_t opts = {0}; + mcuxClEls_KeyProp_t props = {0}; + int ret = 0; + +#ifdef DEBUG + wolfBoot_printf("[DICE] update_cdi: derivation_count=%d meas_len=%u\r\n", + cdi_derivation_count, (unsigned)meas_len); +#endif + XMEMSET(deriv, 0, sizeof(deriv)); + + /* ROM DICE already incorporated wolfBoot as HKDF(UDS, wolfBoot_hash) -> initial_CDI. + * Skip re-applying it — doing so would produce the wrong CDI chain. */ + if (measurement_desc != NULL && + measurement_desc_len == (sizeof(WOLFBOOT_DICE_COMPONENT_WOLFBOOT) - 1) && + XMEMCMP(measurement_desc, WOLFBOOT_DICE_COMPONENT_WOLFBOOT, measurement_desc_len) == 0) { +#ifdef DEBUG + wolfBoot_printf("[DICE] update_cdi: skipping wolfboot component (ROM already applied)\r\n"); +#endif + return 0; + } + + if (cdi_derivation_count > 0) { + /* Key-slot constraint: only 1 derived CDI slot available. + * Raise this limit only after adding extra slots. */ +#ifdef DEBUG + wolfBoot_printf("[DICE] update_cdi: cdi_derivation_count=%d > 0, too many components\r\n", + cdi_derivation_count); +#endif + /* Do not return here: the shared epilogue resets cdi_derivation_count on error + * (same as other failure paths). This branch issues no ELS commands; any key + * material in the derived CDI slot is unchanged from the prior successful HKDF. + * After the epilogue, the next successful call KDELETEs that slot then HKDFs + * from the ROM initial CDI slot (count is zero again — fresh chain). */ + ret = -1; + } + + if (ret == 0 && (measurement == NULL || meas_len == 0)) { +#ifdef DEBUG + wolfBoot_printf("[DICE] update_cdi: invalid measurement (NULL or zero len)\r\n"); +#endif + ret = -1; + } + + if (ret == 0) { + if (meas_len > SHA256_DIGEST_SIZE) { + /* Pre-hash to SHA-256 digest */ + wc_Sha256 sha; + ret = wc_InitSha256(&sha); + if (ret == 0) { + ret = wc_Sha256Update(&sha, measurement, (word32)meas_len); + if (ret == 0) { + ret = wc_Sha256Final(&sha, deriv); + } + wc_Sha256Free(&sha); + } +#ifdef DEBUG + if (ret != 0) + wolfBoot_printf("[DICE] update_cdi: wc_Sha256 failed %d\r\n", ret); +#endif + } + else { + XMEMCPY(deriv, measurement, meas_len); + } + } + + if (ret == 0) { + /* Trigger the KDELETE command to free the key slot. + * Note that the slot may be empty on the first update, but that's not an error + * because ELS ignores the KDELETE command if the slot is empty. + * We just give the names of token and return value + * since it's declared within the macro */ + MCUX_CSSL_FP_FUNCTION_CALL_BEGIN(res_kdel, tok_kdel, + mcuxClEls_KeyDelete_Async(MCXN_ELS_DICE_CDI_DERIVED_KEYSLOT)); + + if ((MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClEls_KeyDelete_Async) != tok_kdel) || + (MCUXCLELS_STATUS_OK_WAIT != res_kdel)) { +#ifdef DEBUG + wolfBoot_printf("[DICE] update_cdi: KeyDelete_Async failed" + " res=0x%x tok=0x%x\r\n", + (unsigned)res_kdel, (unsigned)tok_kdel); +#endif + ret = -1; + } + + MCUX_CSSL_FP_FUNCTION_CALL_END(); + + /* Wait for hardware to finish */ + if (ret == 0) { + MCUX_CSSL_FP_FUNCTION_CALL_BEGIN(res_w, tok_w, + mcuxClEls_WaitForOperation(MCUXCLELS_ERROR_FLAGS_CLEAR)); + + if ((MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClEls_WaitForOperation) != tok_w) || + (MCUXCLELS_STATUS_OK != res_w)) { +#ifdef DEBUG + wolfBoot_printf("[DICE] update_cdi: WaitForOperation(KDELETE) failed" + " res=0x%x tok=0x%x\r\n", + (unsigned)res_w, (unsigned)tok_w); +#endif + ret = -1; + } + + MCUX_CSSL_FP_FUNCTION_CALL_END(); + } + } + + if (ret == 0) { + /* first derivation: start from ROM-loaded initial CDI; subsequent: chain from derived CDI */ + mcuxClEls_KeyIndex_t hkdf_src_slot = (cdi_derivation_count == 0) + ? MCXN_ELS_DICE_CDI_INITIAL_KEYSLOT + : MCXN_ELS_DICE_CDI_DERIVED_KEYSLOT; + + /* Set HKDF options */ + opts.bits.hkdf_algo = MCUXCLELS_HKDF_ALGO_RFC5869; + + /* Set key properties */ + props.bits.upprot_priv = MCUXCLELS_KEYPROPERTY_PRIVILEGED_TRUE; + props.bits.upprot_sec = MCUXCLELS_KEYPROPERTY_SECURE_TRUE; + props.bits.ukgsrc = MCUXCLELS_KEYPROPERTY_INPUT_FOR_ECC_TRUE; + props.bits.uhkdf = MCUXCLELS_KEYPROPERTY_HKDF_TRUE; + props.bits.fgp = MCUXCLELS_KEYPROPERTY_GENERAL_PURPOSE_SLOT_TRUE; + props.bits.kbase = MCUXCLELS_KEYPROPERTY_BASE_SLOT; + props.bits.kactv = MCUXCLELS_KEYPROPERTY_ACTIVE_TRUE; + props.bits.ksize = MCUXCLELS_KEYPROPERTY_KEY_SIZE_256; + + /* Trigger the HKDF command. + * We just give the names of token and return value + * since it's declared within the macro */ + MCUX_CSSL_FP_FUNCTION_CALL_BEGIN(res_hkdf, tok_hkdf, + mcuxClEls_Hkdf_Rfc5869_Async(opts, + hkdf_src_slot, + MCXN_ELS_DICE_CDI_DERIVED_KEYSLOT, + props, deriv)); + + if ((MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClEls_Hkdf_Rfc5869_Async) != tok_hkdf) || + (MCUXCLELS_STATUS_OK_WAIT != res_hkdf)) { +#ifdef DEBUG + wolfBoot_printf("[DICE] update_cdi: Hkdf_Rfc5869_Async failed" + " res=0x%x tok=0x%x\r\n", + (unsigned)res_hkdf, (unsigned)tok_hkdf); +#endif + ret = -1; + } + + MCUX_CSSL_FP_FUNCTION_CALL_END(); + + /* Wait for hardware to finish */ + if (ret == 0) { + MCUX_CSSL_FP_FUNCTION_CALL_BEGIN(res_w, tok_w, + mcuxClEls_WaitForOperation(MCUXCLELS_ERROR_FLAGS_CLEAR)); + + if ((MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClEls_WaitForOperation) != tok_w) || + (MCUXCLELS_STATUS_OK != res_w)) { +#ifdef DEBUG + wolfBoot_printf("[DICE] update_cdi: WaitForOperation(HKDF) failed" + " res=0x%x tok=0x%x\r\n", + (unsigned)res_w, (unsigned)tok_w); +#endif + ret = -1; + } + + MCUX_CSSL_FP_FUNCTION_CALL_END(); + } + } + + if (ret == 0) + cdi_derivation_count++; + else + cdi_derivation_count = 0; /* reset on error — create_attest_key won't be called */ + + XMEMSET(deriv, 0, sizeof(deriv)); +#ifdef DEBUG + wolfBoot_printf("[DICE] update_cdi: ret=%d\r\n", ret); +#endif + return ret; +} + +/* Generate P-256 IAK from derived CDI using ELS KEYGEN. + * Private key stays in ELS keystore. Public key written to system memory. */ +int hal_dice_create_attest_key(void) +{ + uint8_t pub_key[64] __attribute__((aligned(4))); + mcuxClEls_EccKeyGenOption_t opts = {0}; + mcuxClEls_KeyProp_t props = {0}; + int ret = 0; + + s_dice_attest_pubkey_valid = 0; + XMEMSET(s_dice_attest_pubkey, 0, sizeof(s_dice_attest_pubkey)); +#ifdef DEBUG + wolfBoot_printf("[DICE] create_attest_key: start\r\n"); +#endif + + /* No KeyDelete here: DETERMINISTIC EccKeyGen reads the CDI from + * IAK_KEYSLOT (= CDI_DERIVED_KEYSLOT) as its seed input. + * Deleting that slot first would destroy the source material. */ + + /* Set KeyGen options */ + opts.bits.kgsrc = MCUXCLELS_ECC_OUTPUTKEY_DETERMINISTIC; + opts.bits.kgtypedh = MCUXCLELS_ECC_OUTPUTKEY_SIGN; + + /* Set key properties */ + props.bits.upprot_priv = MCUXCLELS_KEYPROPERTY_PRIVILEGED_TRUE; + props.bits.upprot_sec = MCUXCLELS_KEYPROPERTY_SECURE_TRUE; + props.bits.uecsg = MCUXCLELS_KEYPROPERTY_ECC_TRUE; + props.bits.uksk = MCUXCLELS_KEYPROPERTY_KSK_TRUE; + props.bits.fgp = MCUXCLELS_KEYPROPERTY_GENERAL_PURPOSE_SLOT_TRUE; + props.bits.kbase = MCUXCLELS_KEYPROPERTY_BASE_SLOT; + props.bits.kactv = MCUXCLELS_KEYPROPERTY_ACTIVE_TRUE; + props.bits.ksize = MCUXCLELS_KEYPROPERTY_KEY_SIZE_256; + +#ifdef DEBUG + wolfBoot_printf("[DICE] create_attest_key: EccKeyGen" + " CDI_DERIVED_SLOT=%d IAK_SLOT=%d\r\n", + MCXN_ELS_DICE_CDI_DERIVED_KEYSLOT, + MCXN_ELS_DICE_IAK_KEYSLOT); +#endif + + /* Trigger the ECC KeyGen command. + * We just give the names of token and return value + * since it's declared within the macro */ + MCUX_CSSL_FP_FUNCTION_CALL_BEGIN(res_kg, tok_kg, + mcuxClEls_EccKeyGen_Async(opts, + MCXN_ELS_DICE_CDI_DERIVED_KEYSLOT, + MCXN_ELS_DICE_IAK_KEYSLOT, + props, NULL, pub_key)); + + if ((MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClEls_EccKeyGen_Async) != tok_kg) || + (MCUXCLELS_STATUS_OK_WAIT != res_kg)) { +#ifdef DEBUG + wolfBoot_printf("[DICE] create_attest_key: EccKeyGen_Async failed" + " res=0x%x tok=0x%x\r\n", + (unsigned)res_kg, (unsigned)tok_kg); +#endif + ret = -1; + } + + MCUX_CSSL_FP_FUNCTION_CALL_END(); + + /* Wait for hardware to finish */ + if (ret == 0) { + MCUX_CSSL_FP_FUNCTION_CALL_BEGIN(res_w, tok_w, + mcuxClEls_WaitForOperation(MCUXCLELS_ERROR_FLAGS_CLEAR)); + + if ((MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClEls_WaitForOperation) != tok_w) || + (MCUXCLELS_STATUS_OK != res_w)) { +#ifdef DEBUG + wolfBoot_printf("[DICE] create_attest_key: WaitForOperation(KEYGEN) failed" + " res=0x%x tok=0x%x\r\n", + (unsigned)res_w, (unsigned)tok_w); +#endif + ret = -1; + } + + MCUX_CSSL_FP_FUNCTION_CALL_END(); + } + + if (ret == 0) { + XMEMCPY(s_dice_attest_pubkey, pub_key, sizeof(s_dice_attest_pubkey)); + s_dice_attest_pubkey_valid = 1; + } + + cdi_derivation_count = 0; /* reset for the next token build */ + XMEMSET(pub_key, 0, sizeof(pub_key)); +#ifdef DEBUG + wolfBoot_printf("[DICE] create_attest_key: ret=%d\r\n", ret); +#endif + return ret; +} + +int hal_dice_get_attest_pubkey(uint8_t *buf, size_t *len) +{ + if (buf == NULL || len == NULL || *len < 65) + return -1; + if (!s_dice_attest_pubkey_valid) + return -1; + + buf[0] = 0x04; /* X9.63 uncompressed prefix */ + XMEMCPY(buf + 1, s_dice_attest_pubkey, sizeof(s_dice_attest_pubkey)); + *len = 65; + + /* Zeroize cached public key after copying out (read-once). */ + hal_dice_zeroize(s_dice_attest_pubkey, sizeof(s_dice_attest_pubkey)); + s_dice_attest_pubkey_valid = 0; + + return 0; +} + +int hal_dice_sign_hash(const uint8_t *hash, size_t hash_len, + uint8_t *sig, size_t *sig_len) +{ + mcuxClEls_EccSignOption_t opts = {0}; + uint8_t hash_buf[SHA256_DIGEST_SIZE] __attribute__((aligned(4))); + uint8_t sig_buf[MCUXCLELS_ECC_SIGNATURE_SIZE] __attribute__((aligned(4))); + int ret = 0; + _Static_assert(MCUXCLELS_ECC_SIGNATURE_SIZE == 64, + "MCUXCLELS_ECC_SIGNATURE_SIZE must equal WOLFBOOT_DICE_SIG_LEN (64)"); + +#ifdef DEBUG + wolfBoot_printf("[DICE] sign_hash: hash_len=%u\r\n", (unsigned)hash_len); +#endif + + if (hash == NULL || sig == NULL || sig_len == NULL || hash_len != SHA256_DIGEST_SIZE || + *sig_len < MCUXCLELS_ECC_SIGNATURE_SIZE) { +#ifdef DEBUG + wolfBoot_printf("[DICE] sign_hash: invalid args" + " hash=%p sig=%p sig_len=%p hash_len=%u\r\n", + hash, sig, sig_len, (unsigned)hash_len); +#endif + ret = -1; + } + + if (ret == 0) { + XMEMCPY(hash_buf, hash, SHA256_DIGEST_SIZE); + + /* Set options */ + opts.bits.echashchl = MCUXCLELS_ECC_HASHED; + + /* Trigger the ECC Sign command. + * We just give the names of token and return value + * since it's declared within the macro */ + MCUX_CSSL_FP_FUNCTION_CALL_BEGIN(res_sign, tok_sign, + mcuxClEls_EccSign_Async(opts, + MCXN_ELS_DICE_IAK_KEYSLOT, + hash_buf, NULL, 0, sig_buf)); + + if ((MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClEls_EccSign_Async) != tok_sign) || + (MCUXCLELS_STATUS_OK_WAIT != res_sign)) { +#ifdef DEBUG + wolfBoot_printf("[DICE] sign_hash: EccSign_Async failed" + " res=0x%x tok=0x%x\r\n", + (unsigned)res_sign, (unsigned)tok_sign); +#endif + ret = -1; + } + + MCUX_CSSL_FP_FUNCTION_CALL_END(); + + /* Wait for hardware to finish */ + if (ret == 0) { + MCUX_CSSL_FP_FUNCTION_CALL_BEGIN(res_w, tok_w, + mcuxClEls_WaitForOperation(MCUXCLELS_ERROR_FLAGS_CLEAR)); + + if ((MCUX_CSSL_FP_FUNCTION_CALLED(mcuxClEls_WaitForOperation) != tok_w) || + (MCUXCLELS_STATUS_OK != res_w)) { +#ifdef DEBUG + wolfBoot_printf("[DICE] sign_hash: WaitForOperation(SIGN) failed" + " res=0x%x tok=0x%x\r\n", + (unsigned)res_w, (unsigned)tok_w); +#endif + ret = -1; + } + + MCUX_CSSL_FP_FUNCTION_CALL_END(); + } + } + + if (ret == 0) { + XMEMCPY(sig, sig_buf, MCUXCLELS_ECC_SIGNATURE_SIZE); + *sig_len = MCUXCLELS_ECC_SIGNATURE_SIZE; + } + + hal_dice_zeroize(hash_buf, sizeof(hash_buf)); + hal_dice_zeroize(sig_buf, sizeof(sig_buf)); +#ifdef DEBUG + wolfBoot_printf("[DICE] sign_hash: ret=%d\r\n", ret); +#endif + return ret; +} + +#endif /* WOLFCRYPT_TZ_PSA && WOLFBOOT_DICE_HW && __WOLFBOOT */ diff --git a/include/hal.h b/include/hal.h index 1082976591..cc96ea2c31 100644 --- a/include/hal.h +++ b/include/hal.h @@ -167,6 +167,35 @@ int hal_attestation_get_implementation_id(uint8_t *buf, size_t *len); int hal_attestation_get_ueid(uint8_t *buf, size_t *len); int hal_attestation_get_iak_private_key(uint8_t *buf, size_t *len); +#ifdef WOLFBOOT_DICE_HW +/* Hardware DICE hooks — implement these to delegate CDI derivation and + * attestation signing to a platform security boundary. */ + +/* Mix one boot-component measurement into the platform CDI chain. + * measurement_desc identifies the component; use the WOLFBOOT_DICE_COMPONENT_* + * macros from wolfboot/dice.h (e.g. WOLFBOOT_DICE_COMPONENT_WOLFBOOT, + * WOLFBOOT_DICE_COMPONENT_BOOTIMAGE). + * The platform updates its internal CDI state; no CDI material is returned. */ +int hal_dice_update_cdi(const uint8_t *measurement, size_t meas_len, + const char *measurement_desc, size_t measurement_desc_len); + +/* Derive and store the attestation keypair from the current CDI state. + * The private key must not be exposed outside the platform security boundary. + * Returns 0 on success. */ +int hal_dice_create_attest_key(void); + +/* Sign a pre-computed SHA-256 hash with the platform attestation key. + * Output: 64-byte raw R||S (big-endian), same format as wolfCrypt ES256. + * Must be called after hal_dice_create_attest_key(). */ +int hal_dice_sign_hash(const uint8_t *hash, size_t hash_len, + uint8_t *sig, size_t *sig_len); + +/* Retrieve the IAK public key cached by hal_dice_create_attest_key(). + * Output: 65-byte X9.63 uncompressed point (0x04 || X || Y). + * The internal copy is zeroized after this call (read-once). */ +int hal_dice_get_attest_pubkey(uint8_t *buf, size_t *len); +#endif /* WOLFBOOT_DICE_HW */ + #ifdef FLASH_OTP_KEYSTORE int hal_flash_otp_write(uint32_t flashAddress, const void* data, uint16_t length); diff --git a/include/wolfboot/arm_tee_api.h b/include/wolfboot/arm_tee_api.h index 16e40497aa..9eacfc0451 100644 --- a/include/wolfboot/arm_tee_api.h +++ b/include/wolfboot/arm_tee_api.h @@ -65,7 +65,7 @@ typedef struct psa_outvec { ((size_t)(((ctrl_param) & WOLFBOOT_ARM_TEE_OUT_LEN_MASK) >> \ WOLFBOOT_ARM_TEE_OUT_LEN_OFFSET)) -#if defined(__ARM_FEATURE_CMSE) && defined(__GNUC__) +#if defined(__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) && defined(__GNUC__) #define WOLFBOOT_CMSE_NS_ENTRY __attribute__((cmse_nonsecure_entry)) #else #define WOLFBOOT_CMSE_NS_ENTRY diff --git a/include/wolfboot/dice.h b/include/wolfboot/dice.h index bf1d646930..6eccd97794 100644 --- a/include/wolfboot/dice.h +++ b/include/wolfboot/dice.h @@ -27,6 +27,11 @@ #include #include +/* Component name used in the DICE claims for the wolfBoot measurement. + * Shared between dice.c (producer) and HAL (consumer) to keep them in sync. */ +#define WOLFBOOT_DICE_COMPONENT_WOLFBOOT "wolfboot" +#define WOLFBOOT_DICE_COMPONENT_BOOTIMAGE "boot-image" + #ifdef __cplusplus extern "C" { #endif @@ -39,6 +44,14 @@ int wolfBoot_dice_get_token(const uint8_t *challenge, int wolfBoot_dice_get_token_size(size_t challenge_size, size_t *token_size); +/* Retrieve the IAK public key as a 65-byte X9.63 uncompressed point + * (0x04 || X[32] || Y[32]). Must be called after wolfBoot_dice_get_token(). + * The internal copy is zeroized after this call (read-once). + * Not available when WOLFBOOT_ATTESTATION_IAK is set (provisioned key is used). */ +#ifndef WOLFBOOT_ATTESTATION_IAK +int wolfBoot_dice_get_attest_pubkey(uint8_t *buf, size_t *len); +#endif /* !WOLFBOOT_ATTESTATION_IAK */ + #ifdef __cplusplus } #endif diff --git a/options.mk b/options.mk index 442e20143a..6311f364bb 100644 --- a/options.mk +++ b/options.mk @@ -943,6 +943,12 @@ ifeq ($(WOLFCRYPT_TZ_PSA),1) endif endif +ifeq ($(WOLFBOOT_DICE_HW),1) + ifneq ($(WOLFCRYPT_TZ_PSA),1) + $(error WOLFBOOT_DICE_HW requires WOLFCRYPT_TZ_PSA=1) + endif +endif + ifeq ($(WOLFCRYPT_TZ_PKCS11),1) CFLAGS+=-DSECURE_PKCS11 CFLAGS+=-DWOLFPKCS11_USER_SETTINGS @@ -998,6 +1004,9 @@ endif ifeq ($(WOLFCRYPT_TZ_PSA),1) CFLAGS+=-DWOLFCRYPT_TZ_PSA CFLAGS+=-DWOLFCRYPT_SECURE_MODE + ifeq ($(WOLFBOOT_DICE_HW),1) + CFLAGS+=-DWOLFBOOT_DICE_HW + endif CFLAGS+=-DWOLFSSL_PSA_ENGINE CFLAGS+=-DWOLFPSA_CUSTOM_STORE CFLAGS+=-DNO_DES3 -DNO_DES3_TLS_SUITES diff --git a/src/arm_tee_psa_ipc.c b/src/arm_tee_psa_ipc.c index f49369c625..417cc8c41b 100644 --- a/src/arm_tee_psa_ipc.c +++ b/src/arm_tee_psa_ipc.c @@ -77,6 +77,7 @@ /* ARM TEE Attestation message types. */ #define ARM_TEE_ATTEST_GET_TOKEN 1001 #define ARM_TEE_ATTEST_GET_TOKEN_SIZE 1002 +#define ARM_TEE_ATTEST_GET_IAK_PUBKEY 1003 #ifndef PSA_STORAGE_FLAG_WRITE_ONCE #define PSA_STORAGE_FLAG_WRITE_ONCE ((psa_storage_create_flags_t)0x00000001) @@ -168,6 +169,8 @@ static psa_status_t wolfboot_attest_status(int dice_rc) return PSA_ERROR_HARDWARE_FAILURE; case -4: return PSA_ERROR_GENERIC_ERROR; + case -5: + return PSA_ERROR_BAD_STATE; default: return PSA_ERROR_GENERIC_ERROR; } @@ -947,6 +950,35 @@ int32_t arm_tee_psa_call(psa_handle_t handle, int32_t type, } return PSA_SUCCESS; } +#ifndef WOLFBOOT_ATTESTATION_IAK + if (type == ARM_TEE_ATTEST_GET_IAK_PUBKEY) { + size_t key_len; + int dice_rc; + psa_status_t status; + + if (out_vec == NULL || out_len < 1 || out_vec[0].base == NULL) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + if (out_vec[0].len < 65) { + return PSA_ERROR_BUFFER_TOO_SMALL; + } + + key_len = out_vec[0].len; + wolfBoot_printf("[ATTEST] GET_IAK_PUBKEY: out_len=%u\r\n", + (unsigned)key_len); + dice_rc = wolfBoot_dice_get_attest_pubkey((uint8_t *)out_vec[0].base, + &key_len); + wolfBoot_printf("[ATTEST] GET_IAK_PUBKEY: dice_rc=%d key_len=%u\r\n", + dice_rc, (unsigned)key_len); + status = wolfboot_attest_status(dice_rc); + if (status != PSA_SUCCESS) { + return status; + } + out_vec[0].len = key_len; + return PSA_SUCCESS; + } +#endif /* !WOLFBOOT_ATTESTATION_IAK */ return PSA_ERROR_NOT_SUPPORTED; } diff --git a/src/dice/dice.c b/src/dice/dice.c index 3b0a59ac8f..39f1cd3235 100644 --- a/src/dice/dice.c +++ b/src/dice/dice.c @@ -66,6 +66,7 @@ #define WOLFBOOT_DICE_ERR_BUFFER_TOO_SMALL -2 #define WOLFBOOT_DICE_ERR_HW -3 #define WOLFBOOT_DICE_ERR_CRYPTO -4 +#define WOLFBOOT_DICE_ERR_KEY_NOT_FOUND -5 static NOINLINEFUNCTION void wolfboot_dice_zeroize(void *ptr, size_t len) { @@ -514,8 +515,10 @@ static int wolfboot_dice_fixup_priv(uint8_t *priv, size_t priv_len) static int wolfboot_dice_collect_claims(struct wolfboot_dice_claims *claims) { +#ifndef WOLFBOOT_DICE_HW uint8_t uds[WOLFBOOT_DICE_CDI_LEN]; size_t uds_len = sizeof(uds); +#endif uint8_t wb_hash[WOLFBOOT_SHA_DIGEST_SIZE]; size_t wb_hash_len = sizeof(wb_hash); uint8_t boot_hash[WOLFBOOT_SHA_DIGEST_SIZE]; @@ -523,6 +526,13 @@ static int wolfboot_dice_collect_claims(struct wolfboot_dice_claims *claims) XMEMSET(claims, 0, sizeof(*claims)); +#ifdef WOLFBOOT_DICE_HW + /* UDS stays inside platform security boundary. wolfboot_dice_get_ueid() + * calls hal_attestation_get_ueid() first; with NULL UDS it uses the HAL path. */ + if (wolfboot_dice_get_ueid(claims->ueid, &claims->ueid_len, NULL, 0) != 0) { + return WOLFBOOT_DICE_ERR_HW; + } +#else if (hal_uds_derive_key(uds, uds_len) != 0) { /* Buffer may be partially filled, zero it to be sure */ wc_ForceZero(uds, sizeof(uds)); @@ -534,6 +544,7 @@ static int wolfboot_dice_collect_claims(struct wolfboot_dice_claims *claims) wc_ForceZero(uds, sizeof(uds)); return WOLFBOOT_DICE_ERR_HW; } +#endif { size_t impl_len = sizeof(claims->implementation_id); @@ -560,9 +571,9 @@ static int wolfboot_dice_collect_claims(struct wolfboot_dice_claims *claims) claims->components[claims->component_count].measurement_type_len = XSTRLEN(WOLFBOOT_MEASUREMENT_HASH_NAME); claims->components[claims->component_count].measurement_desc = - "wolfboot"; + WOLFBOOT_DICE_COMPONENT_WOLFBOOT; claims->components[claims->component_count].measurement_desc_len = - XSTRLEN("wolfboot"); + XSTRLEN(WOLFBOOT_DICE_COMPONENT_WOLFBOOT); XMEMCPY(claims->components[claims->component_count].measurement, wb_hash, wb_hash_len); claims->components[claims->component_count].measurement_len = wb_hash_len; @@ -575,19 +586,28 @@ static int wolfboot_dice_collect_claims(struct wolfboot_dice_claims *claims) claims->components[claims->component_count].measurement_type_len = XSTRLEN(WOLFBOOT_MEASUREMENT_HASH_NAME); claims->components[claims->component_count].measurement_desc = - "boot-image"; + WOLFBOOT_DICE_COMPONENT_BOOTIMAGE; claims->components[claims->component_count].measurement_desc_len = - XSTRLEN("boot-image"); + XSTRLEN(WOLFBOOT_DICE_COMPONENT_BOOTIMAGE); XMEMCPY(claims->components[claims->component_count].measurement, boot_hash, boot_hash_len); claims->components[claims->component_count].measurement_len = boot_hash_len; claims->component_count++; } - +#ifndef WOLFBOOT_DICE_HW wc_ForceZero(uds, sizeof(uds)); +#endif return WOLFBOOT_DICE_SUCCESS; } +#ifndef WOLFBOOT_DICE_HW +/* Cached IAK public key (X9.63, 65 bytes) populated during key derivation. + * Consumed and zeroized by wolfBoot_dice_get_attest_pubkey(). */ +static uint8_t s_dice_attest_pubkey[65]; +static word32 s_dice_attest_pubkey_len; +#endif /* !WOLFBOOT_DICE_HW */ + +#ifndef WOLFBOOT_DICE_HW static int wolfboot_dice_derive_attestation_key(ecc_key *key, const uint8_t *uds, size_t uds_len, @@ -652,6 +672,24 @@ static int wolfboot_dice_derive_attestation_key(ecc_key *key, goto cleanup; } + /* Compute and cache the public key for wolfBoot_dice_get_attest_pubkey(). + * wc_ecc_import_private_key_ex with pub=NULL sets PRIVATEKEY_ONLY, so the + * public point must be computed explicitly before export. */ + { + word32 pub_len = sizeof(s_dice_attest_pubkey); + /* Clear cached public key before new generation */ + XMEMSET(s_dice_attest_pubkey, 0, sizeof(s_dice_attest_pubkey)); + s_dice_attest_pubkey_len = 0; + + if (wc_ecc_make_pub(key, NULL) == 0 && + wc_ecc_export_x963(key, s_dice_attest_pubkey, &pub_len) == 0) { + s_dice_attest_pubkey_len = pub_len; + } + else { + goto cleanup; + } + } + ret = 0; cleanup: @@ -660,7 +698,32 @@ static int wolfboot_dice_derive_attestation_key(ecc_key *key, wolfboot_dice_zeroize(cdi, sizeof(cdi)); return ret; } +#endif /* !WOLFBOOT_DICE_HW */ + +#ifdef WOLFBOOT_DICE_HW +static int wolfboot_attest_get_private_key_hw( + const struct wolfboot_dice_claims *claims) +{ + size_t i; + /* Mix each boot component measurement into the platform CDI chain */ + for (i = 0; i < claims->component_count; i++) { + if (hal_dice_update_cdi(claims->components[i].measurement, + claims->components[i].measurement_len, + claims->components[i].measurement_desc, + claims->components[i].measurement_desc_len) != 0) { + return -1; + } + } + /* Derive attestation keypair from current CDI state. + * Private key stays inside the platform security boundary. */ + if (hal_dice_create_attest_key() != 0) { + return -1; + } + return 0; +} +#endif /* WOLFBOOT_DICE_HW */ +#ifndef WOLFBOOT_DICE_HW static int wolfboot_attest_get_private_key(ecc_key *key, const struct wolfboot_dice_claims *claims) { @@ -699,6 +762,7 @@ static int wolfboot_attest_get_private_key(ecc_key *key, return ret; #endif } +#endif /* !WOLFBOOT_DICE_HW */ static int wolfboot_dice_encode_payload(uint8_t *buf, size_t buf_len, @@ -818,45 +882,75 @@ static int wolfboot_dice_sign_tbs(const uint8_t *tbs, size_t *sig_len, const struct wolfboot_dice_claims *claims) { + int ret = WOLFBOOT_DICE_ERR_CRYPTO; + uint8_t hash[SHA256_DIGEST_SIZE]; +#ifndef WOLFBOOT_DICE_HW ecc_key key; + int key_inited = 0; WC_RNG rng; - int ret = WOLFBOOT_DICE_ERR_CRYPTO; int wc_ret; - int key_inited = 0; int rng_inited = 0; - uint8_t hash[SHA256_DIGEST_SIZE]; uint8_t der_sig[128]; word32 der_sig_len = sizeof(der_sig); uint8_t r[WOLFBOOT_DICE_SIG_LEN / 2]; uint8_t s[WOLFBOOT_DICE_SIG_LEN / 2]; word32 r_len = sizeof(r); word32 s_len = sizeof(s); +#endif /* !WOLFBOOT_DICE_HW */ if (sig == NULL || sig_len == NULL || *sig_len < WOLFBOOT_DICE_SIG_LEN) { return WOLFBOOT_DICE_ERR_INVALID_ARGUMENT; } +#ifdef WOLFBOOT_DICE_HW + if (wolfboot_attest_get_private_key_hw(claims) != 0) { + ret = WOLFBOOT_DICE_ERR_HW; + goto cleanup; + } +#else wc_ecc_init(&key); key_inited = 1; if (wolfboot_attest_get_private_key(&key, claims) != 0) { ret = WOLFBOOT_DICE_ERR_HW; goto cleanup; } +#endif +#ifndef WOLFBOOT_DICE_HW (void)wc_ecc_set_deterministic(&key, 1); if (wc_InitRng(&rng) != 0) { ret = WOLFBOOT_DICE_ERR_HW; goto cleanup; } rng_inited = 1; +#endif /* !WOLFBOOT_DICE_HW */ { wc_Sha256 sha; - wc_InitSha256(&sha); - wc_Sha256Update(&sha, tbs, (word32)tbs_len); - wc_Sha256Final(&sha, hash); + ret = wc_InitSha256(&sha); + if (ret == 0) { + ret = wc_Sha256Update(&sha, tbs, (word32)tbs_len); + if (ret == 0) { + ret = wc_Sha256Final(&sha, hash); + } + wc_Sha256Free(&sha); + } + + if (ret != 0) { + ret = WOLFBOOT_DICE_ERR_CRYPTO; + goto cleanup; + } } +#ifdef WOLFBOOT_DICE_HW + /* Platform attestation key is ready. Sign pre-computed hash via HAL. + * hal_dice_sign_hash() MUST output 64-byte raw R||S (big-endian), NOT DER. + * This matches WOLFBOOT_DICE_SIG_LEN and the COSE_Sign1 signature field directly, + * bypassing the wc_ecc_sig_to_rs DER->raw conversion that the software path needs. */ + ret = hal_dice_sign_hash(hash, sizeof(hash), sig, sig_len); + if (ret != 0) + ret = WOLFBOOT_DICE_ERR_HW; +#else /* !WOLFBOOT_DICE_HW */ wc_ret = wc_ecc_sign_hash(hash, sizeof(hash), der_sig, &der_sig_len, &rng, &key); if (wc_ret != 0) { ret = WOLFBOOT_DICE_ERR_CRYPTO; @@ -874,17 +968,20 @@ static int wolfboot_dice_sign_tbs(const uint8_t *tbs, XMEMCPY(sig + sizeof(r) + (sizeof(s) - s_len), s, s_len); *sig_len = WOLFBOOT_DICE_SIG_LEN; ret = WOLFBOOT_DICE_SUCCESS; +#endif /* !WOLFBOOT_DICE_HW */ cleanup: - if (rng_inited) { - wc_FreeRng(&rng); - } +#ifndef WOLFBOOT_DICE_HW if (key_inited) { wc_ecc_free(&key); wolfboot_dice_zeroize(&key, sizeof(key)); } - wolfboot_dice_zeroize(hash, sizeof(hash)); + if (rng_inited) { + wc_FreeRng(&rng); + } wolfboot_dice_zeroize(der_sig, sizeof(der_sig)); +#endif /* !WOLFBOOT_DICE_HW */ + wolfboot_dice_zeroize(hash, sizeof(hash)); return ret; } @@ -1026,3 +1123,28 @@ int wolfBoot_dice_get_token_size(size_t challenge_size, size_t *token_size) *token_size = needed; return WOLFBOOT_DICE_SUCCESS; } + +#ifndef WOLFBOOT_ATTESTATION_IAK +int wolfBoot_dice_get_attest_pubkey(uint8_t *buf, size_t *len) +{ + if (buf == NULL || len == NULL || *len < 65) + return WOLFBOOT_DICE_ERR_INVALID_ARGUMENT; + +#ifdef WOLFBOOT_DICE_HW + if (hal_dice_get_attest_pubkey(buf, len) != 0) + return WOLFBOOT_DICE_ERR_KEY_NOT_FOUND; /* key not yet derived; call wolfBoot_dice_get_token() first */ + else + return WOLFBOOT_DICE_SUCCESS; +#else + if (s_dice_attest_pubkey_len == 0) + return WOLFBOOT_DICE_ERR_KEY_NOT_FOUND; /* key not yet derived; call wolfBoot_dice_get_token() first */ + + XMEMCPY(buf, s_dice_attest_pubkey, s_dice_attest_pubkey_len); + *len = s_dice_attest_pubkey_len; + wolfboot_dice_zeroize(s_dice_attest_pubkey, sizeof(s_dice_attest_pubkey)); + s_dice_attest_pubkey_len = 0; + + return WOLFBOOT_DICE_SUCCESS; +#endif +} +#endif /* !WOLFBOOT_ATTESTATION_IAK */ diff --git a/test-app/app_mcxn.c b/test-app/app_mcxn.c index b4e6d5300e..04e2a7c3f3 100644 --- a/test-app/app_mcxn.c +++ b/test-app/app_mcxn.c @@ -20,6 +20,8 @@ */ #include +#include +#include #include "fsl_clock.h" #include "fsl_gpio.h" #include "fsl_port.h" @@ -33,6 +35,13 @@ #include "wolfssl/wolfcrypt/random.h" #endif +#ifdef WOLFCRYPT_TZ_PSA +#include "psa/crypto.h" +#include "psa/error.h" +#include "psa/initial_attestation.h" +#include "wolfssl/wolfcrypt/types.h" +#endif + extern void hal_init(void); static void gpio_init_output(GPIO_Type *gpio, PORT_Type *port, @@ -82,25 +91,221 @@ static void gpio_init_output(GPIO_Type *gpio, PORT_Type *port, PORT_SetPinConfig(port, pin, &pin_config); } +#ifdef WOLFCRYPT_TZ_PSA +#define LINE_LEN 16 + +static void print_hex(const uint8_t *buffer, uint32_t length, int dumpChars) +{ + uint32_t i, sz; + + if (!buffer) { + wolfBoot_printf("\tNULL\r\n"); + return; + } + + while (length > 0) { + sz = length; + if (sz > LINE_LEN) + sz = LINE_LEN; + + wolfBoot_printf("\t"); + for (i = 0; i < LINE_LEN; i++) { + if (i < sz) + wolfBoot_printf("%02x ", buffer[i]); + else + wolfBoot_printf(" "); + } + if (dumpChars) { + wolfBoot_printf("| "); + for (i = 0; i < sz; i++) { + if (buffer[i] > 31 && buffer[i] < 127) + wolfBoot_printf("%c", buffer[i]); + else + wolfBoot_printf("."); + } + } + wolfBoot_printf("\r\n"); + + buffer += sz; + length -= sz; + } +} +#endif + +#if defined(WOLFBOOT_ATTESTATION_TEST) && defined(WOLFCRYPT_TZ_PSA) + +/* Read a CBOR bstr at *offset; sets *data and *data_len, advances *offset. */ +static int cbor_get_bstr(const uint8_t *buf, size_t buf_len, size_t *offset, + const uint8_t **data, size_t *data_len) +{ + uint8_t b; + size_t len; + if (*offset >= buf_len) return -1; + b = buf[(*offset)++]; + if ((b >> 5) != 2) return -1; /* must be CBOR major type 2 (bstr) */ + switch (b & 0x1F) { + case 24: + if (*offset >= buf_len) return -1; + len = buf[(*offset)++]; break; + case 25: + if (*offset + 2 > buf_len) return -1; + len = ((size_t)buf[*offset] << 8) | buf[*offset + 1]; + *offset += 2; break; + default: + if ((b & 0x1F) > 23) return -1; /* rejects AI>=26 */ + len = b & 0x1F; + } + if (*offset + len > buf_len) return -1; + *data = buf + *offset; + *data_len = len; + *offset += len; + return 0; +} + +/* Maximum fixed bytes in a COSE_Sign1 Sig_Structure before variable content: + * 1 (array(4) tag) + 1 (tstr(10) tag) + 10 ("Signature1") + + * 3 (max bstr hdr for prot) + 1 (empty-AAD 0x40) + 3 (max bstr hdr for payload) + */ +#define COSE_SIGN1_TBS_FIXED_OVERHEAD (1 + 1 + 10 + 3 + 1 + 3) + +/* Write a CBOR bstr length header into buf; returns number of bytes written. */ +static int cbor_put_bstr_hdr(uint8_t *buf, size_t len) +{ + if (len <= 23) { + buf[0] = 0x40 | (uint8_t)len; + return 1; + } + if (len <= 0xFF) { + buf[0] = 0x58; buf[1] = (uint8_t)len; + return 2; + } + if (len > 0xFFFF) + return -1; + buf[0] = 0x59; buf[1] = (uint8_t)(len >> 8); + buf[2] = (uint8_t)len; + return 3; +} + +static int run_attest_verify_test(void) +{ + static const uint8_t challenge[PSA_INITIAL_ATTEST_CHALLENGE_SIZE_32] = { + 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08, + 0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x10, + 0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18, + 0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20 + }; + static uint8_t token[1024]; + static uint8_t tbs[1024]; /* Sig_Structure buffer */ + uint8_t pubkey[65]; + uint8_t hash[32]; + size_t token_len = sizeof(token); + size_t pubkey_len = sizeof(pubkey); + size_t hash_len = 0; + size_t tbs_len = 0; + const uint8_t *prot_bytes, *payload_bytes, *sig_bytes; + size_t prot_len, payload_len, sig_len; + size_t off = 0; + int hdr; + psa_status_t st; + psa_key_attributes_t attrs = PSA_KEY_ATTRIBUTES_INIT; + psa_key_id_t key_id; + + wolfBoot_printf("Start attestation verify test\r\n"); + + /* 1. Generate token — also caches the IAK public key on the secure side */ + st = psa_initial_attest_get_token(challenge, sizeof(challenge), + token, sizeof(token), &token_len); + if (st != PSA_SUCCESS) { + wolfBoot_printf("attest_verify: get_token failed (%d)\r\n", (int)st); + return -1; + } + wolfBoot_printf("attest_verify: token_len=%lu\r\n", (unsigned long)token_len); + print_hex(token, (uint32_t)token_len, 1); + + /* 2. Retrieve IAK public key via PSA attestation service (read-once) */ + st = psa_initial_attest_get_iak_pubkey(pubkey, sizeof(pubkey), &pubkey_len); + if (st != PSA_SUCCESS) { + wolfBoot_printf("attest_verify: get_iak_pubkey failed (%d)\r\n", (int)st); + return -1; + } + wolfBoot_printf("attest_verify: IAK pubkey (%lu bytes):\r\n", + (unsigned long)pubkey_len); + print_hex(pubkey, (uint32_t)pubkey_len, 1); + + /* 3. Parse COSE_Sign1: 84 | prot_bstr | A0 | payload_bstr | sig_bstr */ + if (off >= token_len || token[off++] != 0x84) { + wolfBoot_printf("attest_verify: bad array tag\r\n"); return -1; + } + if (cbor_get_bstr(token, token_len, &off, &prot_bytes, &prot_len) != 0 || + off >= token_len || token[off++] != 0xA0 || + cbor_get_bstr(token, token_len, &off, &payload_bytes, &payload_len) != 0 || + cbor_get_bstr(token, token_len, &off, &sig_bytes, &sig_len) != 0 || + sig_len != 64) { + wolfBoot_printf("attest_verify: COSE parse failed\r\n"); return -1; + } + + /* 4. Reconstruct Sig_Structure = ["Signature1", prot_bstr, b"", payload_bstr] */ + if (COSE_SIGN1_TBS_FIXED_OVERHEAD + prot_len + payload_len > sizeof(tbs)) { + wolfBoot_printf("attest_verify: tbs buffer too small\r\n"); + return -1; + } + tbs[tbs_len++] = 0x84; /* array(4) */ + tbs[tbs_len++] = 0x6A; /* tstr(10) */ + memcpy(tbs + tbs_len, "Signature1", 10); tbs_len += 10; + hdr = cbor_put_bstr_hdr(tbs + tbs_len, prot_len); + if (hdr < 0) { wolfBoot_printf("attest_verify: cbor hdr failed\r\n"); return -1; } + tbs_len += hdr; + memcpy(tbs + tbs_len, prot_bytes, prot_len); + tbs_len += prot_len; + tbs[tbs_len++] = 0x40; /* bstr(0): empty AAD */ + hdr = cbor_put_bstr_hdr(tbs + tbs_len, payload_len); + if (hdr < 0) { wolfBoot_printf("attest_verify: cbor hdr failed\r\n"); return -1; } + tbs_len += hdr; + memcpy(tbs + tbs_len, payload_bytes, payload_len); + tbs_len += payload_len; + + /* 5. SHA-256 hash of Sig_Structure */ + if (psa_hash_compute(PSA_ALG_SHA_256, tbs, tbs_len, + hash, sizeof(hash), &hash_len) != PSA_SUCCESS) { + wolfBoot_printf("attest_verify: hash failed\r\n"); + return -1; + } + + /* 6. Import IAK public key and verify ES256 signature (raw R||S, 64 bytes) */ + psa_set_key_type(&attrs, PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1)); + psa_set_key_bits(&attrs, 256); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_VERIFY_HASH); + psa_set_key_algorithm(&attrs, PSA_ALG_ECDSA(PSA_ALG_SHA_256)); + if (psa_import_key(&attrs, pubkey, pubkey_len, &key_id) != PSA_SUCCESS) { + psa_reset_key_attributes(&attrs); + wolfBoot_printf("attest_verify: import_key failed\r\n"); + return -1; + } + psa_reset_key_attributes(&attrs); + st = psa_verify_hash(key_id, PSA_ALG_ECDSA(PSA_ALG_SHA_256), + hash, hash_len, sig_bytes, sig_len); + psa_destroy_key(key_id); + + if (st != PSA_SUCCESS) { + wolfBoot_printf("attest_verify: FAILED (%d)\r\n", (int)st); + return -1; + } + wolfBoot_printf("attest_verify: IAK signature verified OK\r\n"); + return 0; +} +#endif + #ifdef WOLFCRYPT_SECURE_MODE static void print_random_number(void) { - WC_RNG rng; - uint32_t rnd; + uint8_t rnd; int ret; - ret = wc_InitRng(&rng); - if (ret != 0) { - wolfBoot_printf("Random number: init failed (%d)\n", ret); - } - else { - ret = wc_RNG_GenerateBlock(&rng, (byte *)&rnd, sizeof(rnd)); - if (ret != 0) - wolfBoot_printf("Random number: generate failed (%d)\n", ret); - else - wolfBoot_printf("Today's lucky number: 0x%08lx\n", (unsigned long)rnd); - wc_FreeRng(&rng); - } + ret = wcs_get_random(&rnd, sizeof(rnd)); + if (ret != 0) + wolfBoot_printf("Random number: generate failed (%d)\n", ret); + else + wolfBoot_printf("Today's lucky number: 0x%02x\n", rnd); } #endif @@ -122,6 +327,21 @@ void main(void) print_random_number(); #endif +#ifdef WOLFCRYPT_TZ_PSA + { + psa_status_t psa_ret = psa_crypto_init(); + + if (psa_ret == PSA_SUCCESS) + wolfBoot_printf("PSA crypto init ok\r\n"); + else + wolfBoot_printf("PSA crypto init failed (%d)\r\n", (int)psa_ret); + } +#endif + +#if defined(WOLFBOOT_ATTESTATION_TEST) && defined(WOLFCRYPT_TZ_PSA) + (void)run_attest_verify_test(); +#endif + if (boot_ver == 1) { /* Red off */ gpio_init_output(GPIO0, PORT0, kCLOCK_Gpio0, kCLOCK_Port0, 10U, 1U); diff --git a/tools/config.mk b/tools/config.mk index 15eefc3e01..0cab5295c0 100644 --- a/tools/config.mk +++ b/tools/config.mk @@ -66,6 +66,7 @@ ifeq ($(ARCH),) WOLFCRYPT_TZ?=0 WOLFCRYPT_TZ_PKCS11?=0 WOLFCRYPT_TZ_PSA?=0 + WOLFBOOT_DICE_HW?=0 WOLFCRYPT_TZ_FWTPM?=0 WOLFBOOT_PARTITION_SIZE?=0x20000 WOLFBOOT_SECTOR_SIZE?=0x20000 @@ -110,6 +111,7 @@ CONFIG_VARS:= ARCH TARGET SIGN HASH MCUXSDK MCUXPRESSO MCUXPRESSO_CPU MCUXPRESSO WOLFBOOT_UDS_OBKEYS \ WOLFCRYPT_TZ WOLFCRYPT_TZ_PKCS11 \ WOLFCRYPT_TZ_PSA \ + WOLFBOOT_DICE_HW \ WOLFCRYPT_TZ_FWTPM \ WOLFBOOT_PARTITION_SIZE WOLFBOOT_SECTOR_SIZE \ WOLFBOOT_PARTITION_BOOT_ADDRESS WOLFBOOT_PARTITION_UPDATE_ADDRESS \ diff --git a/zephyr/include/arm_tee_attest_defs.h b/zephyr/include/arm_tee_attest_defs.h index 0e71107599..8281631a50 100644 --- a/zephyr/include/arm_tee_attest_defs.h +++ b/zephyr/include/arm_tee_attest_defs.h @@ -31,6 +31,7 @@ extern "C" { /* Initial Attestation message types that distinguish Attest services. */ #define ARM_TEE_ATTEST_GET_TOKEN 1001 #define ARM_TEE_ATTEST_GET_TOKEN_SIZE 1002 +#define ARM_TEE_ATTEST_GET_IAK_PUBKEY 1003 #ifdef __cplusplus } diff --git a/zephyr/include/psa/initial_attestation.h b/zephyr/include/psa/initial_attestation.h index 91e8b27531..55d3557bf5 100644 --- a/zephyr/include/psa/initial_attestation.h +++ b/zephyr/include/psa/initial_attestation.h @@ -47,6 +47,17 @@ psa_status_t psa_initial_attest_get_token(const uint8_t *auth_challenge, psa_status_t psa_initial_attest_get_token_size(size_t challenge_size, size_t *token_size); +/* This function retrieves the public key for the IAK generated during + * the most recent psa_initial_attest_get_token call. The public key is + * returned in X9.63 uncompressed format (0x04 || X || Y). The internal + * copy of the public key is zeroized after this call (read-once). + * Not available when WOLFBOOT_ATTESTATION_IAK is set (provisioned key is used). */ +#ifndef WOLFBOOT_ATTESTATION_IAK +psa_status_t psa_initial_attest_get_iak_pubkey(uint8_t *buf, + size_t buf_size, + size_t *key_len); +#endif /* !WOLFBOOT_ATTESTATION_IAK */ + #ifdef __cplusplus } #endif diff --git a/zephyr/src/arm_tee_attest_api.c b/zephyr/src/arm_tee_attest_api.c index 17f2088ba6..f238dee537 100644 --- a/zephyr/src/arm_tee_attest_api.c +++ b/zephyr/src/arm_tee_attest_api.c @@ -97,3 +97,35 @@ psa_initial_attest_get_token_size(size_t challenge_size, return status; } + +#ifndef WOLFBOOT_ATTESTATION_IAK +psa_status_t +psa_initial_attest_get_iak_pubkey(uint8_t *buf, + size_t buf_size, + size_t *key_len) +{ + psa_status_t status; + + if (buf == NULL || key_len == NULL) { + return PSA_ERROR_INVALID_ARGUMENT; + } + if (buf_size < 65) { /* 65 bytes for uncompressed ECC P-256 public key */ + return PSA_ERROR_BUFFER_TOO_SMALL; + } + psa_outvec out_vec[] = { + { buf, buf_size } + }; + + printf("[ATTEST-NS] get_iak_pubkey: buf_size=%u\r\n", (unsigned)buf_size); + status = psa_call(ARM_TEE_ATTESTATION_SERVICE_HANDLE, + ARM_TEE_ATTEST_GET_IAK_PUBKEY, + NULL, 0, + out_vec, IOVEC_LEN(out_vec)); + printf("[ATTEST-NS] get_iak_pubkey: status=%ld key_len=%u\r\n", + (long)status, (unsigned)out_vec[0].len); + if (status == PSA_SUCCESS) { + *key_len = out_vec[0].len; + } + return status; +} +#endif /* !WOLFBOOT_ATTESTATION_IAK */