mirror of https://github.com/wolfSSL/wolfBoot.git
Add hardware-based DICE on mcxn
parent
b983fa7717
commit
f4d5340641
|
|
@ -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:
|
||||
|
|
|
|||
17
arch.mk
17
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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
35
docs/DICE.md
35
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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
```
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
35
hal/hal.c
35
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 */
|
||||
|
|
|
|||
573
hal/mcxn.c
573
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 <wolfssl/wolfcrypt/sha256.h>
|
||||
|
||||
/* 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 <wolfssl/wolfcrypt/sha256.h>
|
||||
#include <wolfssl/wolfcrypt/sha512.h>
|
||||
|
||||
/* 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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -27,6 +27,11 @@
|
|||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* 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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
156
src/dice/dice.c
156
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 @@ cleanup:
|
|||
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 @@ cleanup:
|
|||
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)
|
||||
{
|
||||
ecc_key key;
|
||||
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];
|
||||
#ifndef WOLFBOOT_DICE_HW
|
||||
ecc_key key;
|
||||
int key_inited = 0;
|
||||
WC_RNG rng;
|
||||
int wc_ret;
|
||||
int rng_inited = 0;
|
||||
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 */
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@
|
|||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#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);
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
Loading…
Reference in New Issue