diff --git a/.gitmodules b/.gitmodules index 29bca89a69..e528d9353a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "lib/wolfHSM"] path = lib/wolfHSM url = https://github.com/wolfssl/wolfhsm.git +[submodule "lib/wolfPSA"] + path = lib/wolfPSA + url = git@github.com:wolfSSL/wolfPSA.git diff --git a/Makefile b/Makefile index 67067ba40a..bb62d3d320 100644 --- a/Makefile +++ b/Makefile @@ -140,18 +140,21 @@ endif WOLFBOOT_LIB_WOLFSSL?=lib/wolfssl WOLFBOOT_LIB_WOLFTPM?=lib/wolfTPM WOLFBOOT_LIB_WOLFPKCS11?=lib/wolfPKCS11 +WOLFBOOT_LIB_WOLFPSA?=lib/wolfPSA WOLFBOOT_LIB_WOLFHSM?=lib/wolfHSM # Convert to absolute paths using abspath function WOLFBOOT_LIB_WOLFSSL:=$(abspath $(WOLFBOOT_LIB_WOLFSSL)) WOLFBOOT_LIB_WOLFTPM:=$(abspath $(WOLFBOOT_LIB_WOLFTPM)) WOLFBOOT_LIB_WOLFPKCS11:=$(abspath $(WOLFBOOT_LIB_WOLFPKCS11)) +WOLFBOOT_LIB_WOLFPSA:=$(abspath $(WOLFBOOT_LIB_WOLFPSA)) WOLFBOOT_LIB_WOLFHSM:=$(abspath $(WOLFBOOT_LIB_WOLFHSM)) # Export variables so they are available to sub-makefiles export WOLFBOOT_LIB_WOLFSSL export WOLFBOOT_LIB_WOLFTPM export WOLFBOOT_LIB_WOLFPKCS11 +export WOLFBOOT_LIB_WOLFPSA export WOLFBOOT_LIB_WOLFHSM ## Architecture/CPU configuration @@ -169,6 +172,7 @@ CFLAGS+= \ -Wno-array-bounds \ -D"WOLFSSL_USER_SETTINGS" \ -D"WOLFTPM_USER_SETTINGS" +CFLAGS+=$(WOLFPSA_CFLAGS) # Setup default optimizations (for GCC) ifeq ($(USE_GCC_HEADLESS),1) diff --git a/config/examples/stm32h5-tz-psa.config b/config/examples/stm32h5-tz-psa.config new file mode 100644 index 0000000000..41303b8b28 --- /dev/null +++ b/config/examples/stm32h5-tz-psa.config @@ -0,0 +1,35 @@ +ARCH?=ARM +TZEN?=1 +TARGET?=stm32h5 +SIGN?=ECC256 +HASH?=SHA256 +DEBUG?=0 +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 +WOLFBOOT_VERSION?=1 +V?=0 +SPMATH?=1 +RAM_CODE?=1 +DUALBANK_SWAP?=0 +WOLFBOOT_PARTITION_SIZE?=0xA0000 +WOLFBOOT_SECTOR_SIZE?=0x2000 +WOLFBOOT_KEYVAULT_ADDRESS?=0x0C040000 +WOLFBOOT_KEYVAULT_SIZE?=0x1C000 +WOLFBOOT_NSC_ADDRESS?=0x0C05C000 +WOLFBOOT_NSC_SIZE?=0x4000 +WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x08060000 +WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x0C100000 +WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x0C1A0000 +FLAGS_HOME=0 +DISABLE_BACKUP=0 +WOLFCRYPT_TZ=1 +WOLFCRYPT_TZ_PSA=1 +IMAGE_HEADER_SIZE?=1024 +ARMORED=1 diff --git a/docs/STM32-TZ.md b/docs/STM32-TZ.md index 2e7b0dccc3..ad8dd388de 100644 --- a/docs/STM32-TZ.md +++ b/docs/STM32-TZ.md @@ -27,6 +27,12 @@ non-secure domain can access wolfCrypt through a standard PKCS11 interface and use the crypto library with pre-provisioned keys that are never exposed to the non-secure domain. +### PSA Crypto API in non-secure world + +The `WOLFCRYPT_TZ_PSA` option provides a standard PSA Crypto interface using +wolfPSA in the secure domain. The key storage uses the same secure flash +keystore backend as PKCS11, exposed through the wolfPSA store API. + ### Image header size The `IMAGE_HEADER_SIZE` option has to be carefully tuned to accommodate for the diff --git a/docs/Targets.md b/docs/Targets.md index f78c7095cf..49d4c1e453 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -208,7 +208,7 @@ SECWM2_STRT=0x1 SECWM2_END=0x0 No page of internal Flash Bank2 set as secure, This is similar to Scenario 1, but also includes wolfCrypt in secure mode, and that can be accessed via PKCS11 interface by non-secure applications. -This option can be enabled with the `WOLFCRYPT_TZ=1` and `WOLFCRYPT_TZ_PKCS11=1` +This option can be enabled with `WOLFCRYPT_TZ=1` and `WOLFCRYPT_TZ_PKCS11=1` or `WOLFCRYPT_TZ_PSA=1` options in your configuration. This enables a PKCS11 accessible from NS domain via non-secure callables (NSC). @@ -349,7 +349,7 @@ SECWM2_STRT=0x1 SECWM2_END=0x0 No page of internal Flash Bank2 set as secure, This is similar to Scenario 1, but also includes wolfCrypt in secure mode, and that can be accessed via PKCS11 interface by non-secure applications. -This option can be enabled with the `WOLFCRYPT_TZ=1` and `WOLFCRYPT_TZ_PKCS11=1` +This option can be enabled with `WOLFCRYPT_TZ=1` and `WOLFCRYPT_TZ_PKCS11=1` or `WOLFCRYPT_TZ_PSA=1` options in your configuration. This enables a PKCS11 accessible from NS domain via non-secure callables (NSC). @@ -1337,6 +1337,7 @@ SRAM memories into two parts: The example configuration for this scenario is available in [/config/examples/stm32h5.config](/config/examples/stm32h5.config). + #### How to use it - set the option bytes to enable trustzone: @@ -1372,7 +1373,7 @@ STM32_Programmer_CLI -c port=swd -d update.bin 0x0C100000 This is similar to Scenario 1, but also includes wolfCrypt in secure mode, and that can be accessed via PKCS11 interface by non-secure applications. -This option can be enabled with the `WOLFCRYPT_TZ=1` and `WOLFCRYPT_TZ_PKCS11=1` +This option can be enabled with `WOLFCRYPT_TZ=1` and `WOLFCRYPT_TZ_PKCS11=1` or `WOLFCRYPT_TZ_PSA=1` options in your configuration. This enables a PKCS11 accessible from NS domain via non-secure callables (NSC). @@ -1424,6 +1425,11 @@ STM32_Programmer_CLI -c port=swd -d test-app/image_v3_signed.bin 0x08160000 Reboot the board to initiate an update via DUALBANK hw-assisted swap. Any version except the first one will also turn on the orange LED. +### Scenario 4: Replace TF-M with wolfBoot in Zephyr + +For a full Zephyr integration walkthrough (build + flash), see: +[/zephyr/README.md](/zephyr/README.md) + ### STM32H5 Debugging diff --git a/docs/compile.md b/docs/compile.md index 6639b430a5..cf0d566ac0 100644 --- a/docs/compile.md +++ b/docs/compile.md @@ -354,6 +354,7 @@ Available overrides: - `WOLFBOOT_LIB_WOLFSSL`: Path to the [wolfSSL](https://github.com/wolfSSL/wolfssl) library source code - `WOLFBOOT_LIB_WOLFTPM`: Path to the [wolfTPM](https://github.com/wolfSSL/wolfTPM) library source code - `WOLFBOOT_LIB_WOLFPKCS11`: Path to the [wolfPKCS11](https://github.com/wolfssl/wolfpkcs11) library source code +- `WOLFBOOT_LIB_WOLFPSA`: Path to the wolfPSA library source code - `WOLFBOOT_LIB_WOLFHSM`: Path to the [wolfHSM](https://github.com/wolfSSL/wolfHSM) library source code ## Key Generation and Signing diff --git a/include/user_settings.h b/include/user_settings.h index 2459dbf9ed..26a08f276b 100644 --- a/include/user_settings.h +++ b/include/user_settings.h @@ -367,6 +367,32 @@ extern int tolower(int c); # define HAVE_AESGCM # define HAVE_PKCS8 #endif + +#if defined(WOLFCRYPT_TZ_PSA) +# define WOLFSSL_AES_COUNTER +# define WOLFSSL_AES_GCM +# define HAVE_AESGCM +# define HAVE_AESCCM +# define HAVE_AES_ECB +# define WOLFSSL_AES_CFB +# define WOLFSSL_AES_OFB +# define WOLFSSL_DES3 +# define WOLFSSL_DES_ECB +# define HAVE_CHACHA +# define HAVE_POLY1305 +# define WOLFSSL_CMAC +# define WOLFSSL_ECDSA_DETERMINISTIC_K +# define WOLFSSL_HAVE_PRF +# define HAVE_HKDF +# define HAVE_PBKDF2 +# define HAVE_PWDBASED +# define WOLFSSL_KEY_GEN +# define WC_RSA_PSS +# define WOLFSSL_PSS_SALT_LEN_DISCOVER +# define WOLFSSL_RSA_OAEP +# define HAVE_ECC_KEY_EXPORT +# define HAVE_ECC_KEY_IMPORT +#endif /* PKCS11 for wolfBoot is always static */ #define HAVE_PKCS11_STATIC @@ -502,6 +528,12 @@ extern int tolower(int c); #define NO_CRYPT_TEST #define NO_CRYPT_BENCHMARK +#if defined(WOLFCRYPT_TZ_PSA) +#undef NO_CMAC +#undef NO_DES3 +#undef NO_KDF +#endif + #ifdef __QNX__ # define WOLFSSL_HAVE_MIN # define WOLFSSL_HAVE_MAX @@ -524,7 +556,8 @@ extern int tolower(int c); # define WOLFSSL_SP_NO_MALLOC # define WOLFSSL_SP_NO_DYN_STACK # endif -# if !defined(SECURE_PKCS11) && !defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER) +# if !defined(SECURE_PKCS11) && !defined(WOLFCRYPT_TZ_PSA) && \ + !defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER) # define NO_WOLFSSL_MEMORY # define WOLFSSL_NO_MALLOC # endif diff --git a/include/wolfboot/arm_tee_api.h b/include/wolfboot/arm_tee_api.h new file mode 100644 index 0000000000..1c79175efd --- /dev/null +++ b/include/wolfboot/arm_tee_api.h @@ -0,0 +1,97 @@ +/* arm_tee_api.h + * + * ARM TEE style PSA client veneers for Zephyr integration. + * + * Copyright (C) 2025 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + */ + +#ifndef WOLFBOOT_ARM_TEE_API_H +#define WOLFBOOT_ARM_TEE_API_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Provide minimal PSA client types if PSA client headers are unavailable. */ +#ifndef __PSA_CLIENT_H__ +typedef int32_t psa_handle_t; +typedef struct psa_invec { + const void *base; + size_t len; +} psa_invec; +typedef struct psa_outvec { + void *base; + size_t len; +} psa_outvec; +#endif + +#ifndef PSA_ERROR_INVALID_ARGUMENT +#define PSA_ERROR_INVALID_ARGUMENT ((int32_t)-132) +#endif +#ifndef PSA_ERROR_NOT_SUPPORTED +#define PSA_ERROR_NOT_SUPPORTED ((int32_t)-138) +#endif + +/* Pack extra args to keep veneers <= 4 args (ARM TEE style). */ +#define WOLFBOOT_ARM_TEE_TYPE_MASK 0xFFFFUL +#define WOLFBOOT_ARM_TEE_IN_LEN_OFFSET 24 +#define WOLFBOOT_ARM_TEE_IN_LEN_MASK (0x7UL << WOLFBOOT_ARM_TEE_IN_LEN_OFFSET) +#define WOLFBOOT_ARM_TEE_OUT_LEN_OFFSET 16 +#define WOLFBOOT_ARM_TEE_OUT_LEN_MASK (0x7UL << WOLFBOOT_ARM_TEE_OUT_LEN_OFFSET) + +#define WOLFBOOT_ARM_TEE_PARAM_PACK(type, in_len, out_len) \ + ((((uint32_t)(type)) & WOLFBOOT_ARM_TEE_TYPE_MASK) | \ + ((((uint32_t)(in_len)) << WOLFBOOT_ARM_TEE_IN_LEN_OFFSET) & \ + WOLFBOOT_ARM_TEE_IN_LEN_MASK) | \ + ((((uint32_t)(out_len)) << WOLFBOOT_ARM_TEE_OUT_LEN_OFFSET) & \ + WOLFBOOT_ARM_TEE_OUT_LEN_MASK)) + +#define WOLFBOOT_ARM_TEE_PARAM_UNPACK_TYPE(ctrl_param) \ + ((int32_t)(int16_t)((ctrl_param) & WOLFBOOT_ARM_TEE_TYPE_MASK)) +#define WOLFBOOT_ARM_TEE_PARAM_UNPACK_IN_LEN(ctrl_param) \ + ((size_t)(((ctrl_param) & WOLFBOOT_ARM_TEE_IN_LEN_MASK) >> \ + WOLFBOOT_ARM_TEE_IN_LEN_OFFSET)) +#define WOLFBOOT_ARM_TEE_PARAM_UNPACK_OUT_LEN(ctrl_param) \ + ((size_t)(((ctrl_param) & WOLFBOOT_ARM_TEE_OUT_LEN_MASK) >> \ + WOLFBOOT_ARM_TEE_OUT_LEN_OFFSET)) + +#if defined(__ARM_FEATURE_CMSE) && defined(__GNUC__) +#define WOLFBOOT_CMSE_NS_ENTRY __attribute__((cmse_nonsecure_entry)) +#else +#define WOLFBOOT_CMSE_NS_ENTRY +#endif + +/* Secure-side NSC veneers expected by Zephyr ARM TEE client. */ +uint32_t WOLFBOOT_CMSE_NS_ENTRY arm_tee_psa_framework_version_veneer(void); +uint32_t WOLFBOOT_CMSE_NS_ENTRY arm_tee_psa_version_veneer(uint32_t sid); +psa_handle_t WOLFBOOT_CMSE_NS_ENTRY arm_tee_psa_connect_veneer(uint32_t sid, + uint32_t version); +int32_t WOLFBOOT_CMSE_NS_ENTRY arm_tee_psa_call_veneer(psa_handle_t handle, + uint32_t ctrl_param, + const psa_invec *in_vec, psa_outvec *out_vec); +void WOLFBOOT_CMSE_NS_ENTRY arm_tee_psa_close_veneer(psa_handle_t handle); + +/* Backing PSA IPC hooks (override in secure code). */ +uint32_t arm_tee_psa_framework_version(void); +uint32_t arm_tee_psa_version(uint32_t sid); +psa_handle_t arm_tee_psa_connect(uint32_t sid, uint32_t version); +int32_t arm_tee_psa_call(psa_handle_t handle, int32_t type, + const psa_invec *in_vec, size_t in_len, + psa_outvec *out_vec, size_t out_len); +void arm_tee_psa_close(psa_handle_t handle); + +#ifdef __cplusplus +} +#endif + +#endif /* WOLFBOOT_ARM_TEE_API_H */ diff --git a/lib/wolfPSA b/lib/wolfPSA new file mode 160000 index 0000000000..b194eadb14 --- /dev/null +++ b/lib/wolfPSA @@ -0,0 +1 @@ +Subproject commit b194eadb14827c5ae53368ac82c89e1d6d1b9d17 diff --git a/options.mk b/options.mk index 4190bc9f4e..d55dd160c4 100644 --- a/options.mk +++ b/options.mk @@ -681,6 +681,12 @@ ifeq ($(WOLFBOOT_HUGE_STACK),1) CFLAGS+=-DWOLFBOOT_HUGE_STACK endif +ifeq ($(WOLFCRYPT_TZ_PKCS11),1) + ifeq ($(WOLFCRYPT_TZ_PSA),1) + $(error WOLFCRYPT_TZ_PKCS11 and WOLFCRYPT_TZ_PSA are mutually exclusive) + endif +endif + ifeq ($(WOLFCRYPT_TZ_PKCS11),1) CFLAGS+=-DSECURE_PKCS11 CFLAGS+=-DWOLFSSL_PKCS11_RW_TOKENS @@ -725,6 +731,51 @@ ifeq ($(WOLFCRYPT_TZ_PKCS11),1) endif endif +ifeq ($(WOLFCRYPT_TZ_PSA),1) + CFLAGS+=-DWOLFCRYPT_TZ_PSA + CFLAGS+=-DWOLFSSL_PSA_ENGINE + CFLAGS+=-DWOLFPSA_CUSTOM_STORE + WOLFPSA_CFLAGS+=-I$(WOLFBOOT_LIB_WOLFPSA) + WOLFPSA_CFLAGS+=-I$(WOLFBOOT_LIB_WOLFPSA)/wolfpsa + LDFLAGS+=--specs=nano.specs + WOLFCRYPT_OBJS+=src/psa_store.o + WOLFCRYPT_OBJS+=src/arm_tee_psa_veneer.o + WOLFCRYPT_OBJS+=src/arm_tee_psa_ipc.o + WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/pwdbased.o + WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/hmac.o + WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/dh.o + ifeq ($(findstring random.o,$(WOLFCRYPT_OBJS)),) + WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/random.o + endif + WOLFPSA_SRCS := $(filter-out $(WOLFBOOT_LIB_WOLFPSA)/src/psa_store_posix.c, \ + $(wildcard $(WOLFBOOT_LIB_WOLFPSA)/src/*.c)) + WOLFPSA_OBJS := $(patsubst %.c,%.o,$(WOLFPSA_SRCS)) + WOLFCRYPT_OBJS+=$(WOLFPSA_OBJS) + STACK_USAGE=16688 + ifneq ($(ENCRYPT),1) + WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/aes.o + endif + ifeq ($(findstring RSA,$(SIGN)),) + ifeq ($(findstring RSA,$(SIGN_SECONDARY)),) + WOLFCRYPT_OBJS+=$(RSA_OBJS) + endif + endif + ifeq ($(findstring ECC,$(SIGN)),) + ifeq ($(findstring ECC,$(SIGN_SECONDARY)),) + WOLFCRYPT_OBJS+=$(ECC_OBJS) + endif + endif + ifeq ($(findstring ECC,$(SIGN)),) + ifeq ($(findstring ECC,$(SIGN_SECONDARY)),) + ifeq ($(findstring RSA,$(SIGN)),) + ifeq ($(findstring RSA,$(SIGN_SECONDARY)),) + WOLFCRYPT_OBJS+=$(MATH_OBJS) + endif + endif + endif + endif +endif + OBJS+=$(PUBLIC_KEY_OBJS) ifneq ($(STAGE1),1) OBJS+=$(UPDATE_OBJS) @@ -762,12 +813,14 @@ ifeq ($(WOLFTPM),1) endif endif ifneq ($(WOLFCRYPT_TZ_PKCS11),1) + ifneq ($(WOLFCRYPT_TZ_PSA),1) ifneq ($(ENCRYPT),1) WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/aes.o endif WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/hmac.o WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/random.o endif + endif ifeq ($(DEBUG),1) CFLAGS+=-DWOLFBOOT_DEBUG_TPM=1 endif diff --git a/src/arm_tee_psa_ipc.c b/src/arm_tee_psa_ipc.c new file mode 100644 index 0000000000..5c81f53dd8 --- /dev/null +++ b/src/arm_tee_psa_ipc.c @@ -0,0 +1,688 @@ +/* arm_tee_psa_ipc.c + * + * PSA IPC hooks for ARM TEE style veneers. + * + * Copyright (C) 2025 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include + +#include +#include + +#include +#include + +/* Service IDs/handles aligned with ARM TEE defaults. */ +#define ARM_TEE_CRYPTO_SID (0x00000080U) +#define ARM_TEE_CRYPTO_HANDLE (1U) +#define ARM_TEE_PROTECTED_STORAGE_SID (0x00000060U) +#define ARM_TEE_PROTECTED_STORAGE_HANDLE (2U) +#define ARM_TEE_ATTESTATION_SID (0x00000020U) +#define ARM_TEE_ATTESTATION_HANDLE (4U) + +/* Minimal ARM TEE crypto pack definitions (subset used by wolfBoot). */ +#define ARM_TEE_CRYPTO_GENERATE_RANDOM_SID (0x0100U) +#define ARM_TEE_CRYPTO_GET_KEY_ATTRIBUTES_SID (0x0200U) +#define ARM_TEE_CRYPTO_OPEN_KEY_SID (0x0201U) +#define ARM_TEE_CRYPTO_CLOSE_KEY_SID (0x0202U) +#define ARM_TEE_CRYPTO_IMPORT_KEY_SID (0x0203U) +#define ARM_TEE_CRYPTO_DESTROY_KEY_SID (0x0204U) +#define ARM_TEE_CRYPTO_EXPORT_KEY_SID (0x0205U) +#define ARM_TEE_CRYPTO_EXPORT_PUBLIC_KEY_SID (0x0206U) +#define ARM_TEE_CRYPTO_GENERATE_KEY_SID (0x0209U) +#define ARM_TEE_CRYPTO_HASH_COMPUTE_SID (0x0300U) +#define ARM_TEE_CRYPTO_HASH_SETUP_SID (0x0302U) +#define ARM_TEE_CRYPTO_HASH_UPDATE_SID (0x0303U) +#define ARM_TEE_CRYPTO_HASH_FINISH_SID (0x0305U) +#define ARM_TEE_CRYPTO_HASH_ABORT_SID (0x0307U) +#define ARM_TEE_CRYPTO_ASYMMETRIC_SIGN_HASH_SID (0x0702U) +#define ARM_TEE_CRYPTO_ASYMMETRIC_VERIFY_HASH_SID (0x0703U) + +/* ARM TEE Protected Storage message types. */ +#define ARM_TEE_PS_SET 1001 +#define ARM_TEE_PS_GET 1002 +#define ARM_TEE_PS_GET_INFO 1003 +#define ARM_TEE_PS_REMOVE 1004 +#define ARM_TEE_PS_GET_SUPPORT 1005 + +/* ARM TEE Attestation message types. */ +#define ARM_TEE_ATTEST_GET_TOKEN 1001 +#define ARM_TEE_ATTEST_GET_TOKEN_SIZE 1002 + +#ifndef PSA_STORAGE_FLAG_WRITE_ONCE +#define PSA_STORAGE_FLAG_WRITE_ONCE ((psa_storage_create_flags_t)0x00000001) +#endif + +typedef uint64_t psa_storage_uid_t; +typedef uint32_t psa_storage_create_flags_t; +typedef size_t rot_size_t; + +struct psa_storage_info_t { + size_t capacity; + size_t size; + psa_storage_create_flags_t flags; +}; + +struct arm_tee_crypto_aead_pack_input { + uint8_t nonce[16]; + uint32_t nonce_length; +}; + +struct arm_tee_crypto_pack_iovec { + psa_key_id_t key_id; + psa_algorithm_t alg; + uint32_t op_handle; + uint32_t ad_length; + uint32_t plaintext_length; + struct arm_tee_crypto_aead_pack_input aead_in; + uint16_t function_id; + uint16_t step; + union { + uint32_t capacity; + uint64_t value; + }; +}; + +struct wolfboot_hash_slot { + uint32_t handle; + psa_hash_operation_t op; + int in_use; +}; + +#ifndef WOLFBOOT_ARM_TEE_HASH_SLOTS +#define WOLFBOOT_ARM_TEE_HASH_SLOTS 4 +#endif + +static struct wolfboot_hash_slot g_hash_slots[WOLFBOOT_ARM_TEE_HASH_SLOTS]; +static uint32_t g_hash_next_handle = 1; + +#ifndef WOLFBOOT_PS_MAX_DATA +#define WOLFBOOT_PS_MAX_DATA 512 +#endif +#ifndef WOLFBOOT_PS_MAX_ENTRIES +#define WOLFBOOT_PS_MAX_ENTRIES 4 +#endif + +struct wolfboot_ps_entry { + psa_storage_uid_t uid; + size_t size; + psa_storage_create_flags_t flags; + uint8_t data[WOLFBOOT_PS_MAX_DATA]; + int in_use; +}; + +static struct wolfboot_ps_entry g_ps_entries[WOLFBOOT_PS_MAX_ENTRIES]; + +/* Minimal newlib syscall stubs to avoid link errors in bare-metal builds. */ +#ifndef WOLFBOOT_NO_SYSCALL_STUBS +#if defined(__GNUC__) +#define WOLFBOOT_WEAK_SYSCALL __attribute__((weak)) +#else +#define WOLFBOOT_WEAK_SYSCALL +#endif + +WOLFBOOT_WEAK_SYSCALL int _write(int fd, const void *buf, unsigned int count) +{ + (void)fd; + (void)buf; + (void)count; + return -1; +} + +WOLFBOOT_WEAK_SYSCALL int _read(int fd, void *buf, unsigned int count) +{ + (void)fd; + (void)buf; + (void)count; + return -1; +} + +WOLFBOOT_WEAK_SYSCALL int _close(int fd) +{ + (void)fd; + return -1; +} + +WOLFBOOT_WEAK_SYSCALL int _lseek(int fd, int offset, int whence) +{ + (void)fd; + (void)offset; + (void)whence; + return -1; +} + +WOLFBOOT_WEAK_SYSCALL int _fstat(int fd, struct stat *st) +{ + (void)fd; + if (st != NULL) { + st->st_mode = S_IFCHR; + } + return 0; +} + +WOLFBOOT_WEAK_SYSCALL int _isatty(int fd) +{ + (void)fd; + return 1; +} +#endif /* WOLFBOOT_NO_SYSCALL_STUBS */ + +static struct wolfboot_ps_entry *wolfboot_ps_find(psa_storage_uid_t uid) +{ + for (size_t i = 0; i < WOLFBOOT_PS_MAX_ENTRIES; i++) { + if (g_ps_entries[i].in_use && g_ps_entries[i].uid == uid) { + return &g_ps_entries[i]; + } + } + return NULL; +} + +static struct wolfboot_ps_entry *wolfboot_ps_alloc(psa_storage_uid_t uid) +{ + for (size_t i = 0; i < WOLFBOOT_PS_MAX_ENTRIES; i++) { + if (!g_ps_entries[i].in_use) { + g_ps_entries[i].in_use = 1; + g_ps_entries[i].uid = uid; + g_ps_entries[i].size = 0; + g_ps_entries[i].flags = 0; + return &g_ps_entries[i]; + } + } + return NULL; +} + +static psa_status_t wolfboot_psa_open_key(psa_key_id_t id, psa_key_id_t *key) +{ + psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT; + psa_status_t status = psa_get_key_attributes(id, &attr); + psa_reset_key_attributes(&attr); + if (status != PSA_SUCCESS) { + return status; + } + if (key != NULL) { + *key = id; + } + return PSA_SUCCESS; +} + +static psa_status_t wolfboot_psa_close_key(psa_key_id_t key) +{ + (void)key; + return PSA_SUCCESS; +} + +static struct wolfboot_hash_slot *wolfboot_hash_find(uint32_t handle) +{ + for (size_t i = 0; i < WOLFBOOT_ARM_TEE_HASH_SLOTS; i++) { + if (g_hash_slots[i].in_use && g_hash_slots[i].handle == handle) { + return &g_hash_slots[i]; + } + } + return NULL; +} + +static struct wolfboot_hash_slot *wolfboot_hash_alloc(uint32_t *handle) +{ + for (size_t i = 0; i < WOLFBOOT_ARM_TEE_HASH_SLOTS; i++) { + if (!g_hash_slots[i].in_use) { + g_hash_slots[i].in_use = 1; + g_hash_slots[i].handle = g_hash_next_handle++; + g_hash_slots[i].op = psa_hash_operation_init(); + *handle = g_hash_slots[i].handle; + return &g_hash_slots[i]; + } + } + return NULL; +} + +static void wolfboot_hash_free(uint32_t handle) +{ + struct wolfboot_hash_slot *slot = wolfboot_hash_find(handle); + if (slot != NULL) { + (void)psa_hash_abort(&slot->op); + slot->in_use = 0; + slot->handle = 0; + } +} + +static psa_status_t wolfboot_crypto_dispatch(const psa_invec *in_vec, + size_t in_len, + psa_outvec *out_vec, + size_t out_len) +{ + const struct arm_tee_crypto_pack_iovec *iov; + psa_status_t init_status; + + if (in_vec == NULL || in_len == 0) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + init_status = psa_crypto_init(); + if (init_status != PSA_SUCCESS) { + return init_status; + } + + iov = (const struct arm_tee_crypto_pack_iovec *)in_vec[0].base; + if (iov == NULL || in_vec[0].len < sizeof(struct arm_tee_crypto_pack_iovec)) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + switch (iov->function_id) { + case ARM_TEE_CRYPTO_GENERATE_RANDOM_SID: + if (out_vec == NULL || out_len < 1) { + return PSA_ERROR_INVALID_ARGUMENT; + } + if (out_vec[0].len == 0) { + return PSA_SUCCESS; + } + return psa_generate_random((uint8_t *)out_vec[0].base, out_vec[0].len); + + case ARM_TEE_CRYPTO_OPEN_KEY_SID: + if (out_vec == NULL || out_len < 1) { + return PSA_ERROR_INVALID_ARGUMENT; + } + return wolfboot_psa_open_key(iov->key_id, + (psa_key_id_t *)out_vec[0].base); + + case ARM_TEE_CRYPTO_CLOSE_KEY_SID: + return wolfboot_psa_close_key(iov->key_id); + + case ARM_TEE_CRYPTO_IMPORT_KEY_SID: + if (in_len < 3 || out_vec == NULL || out_len < 1) { + return PSA_ERROR_INVALID_ARGUMENT; + } + { + psa_key_attributes_t attr = *(const psa_key_attributes_t *)in_vec[1].base; + /* Fallback to volatile storage if persistent storage is unavailable. */ + if (!PSA_KEY_LIFETIME_IS_VOLATILE(attr.lifetime)) { + attr.lifetime = PSA_KEY_LIFETIME_VOLATILE; + } + return psa_import_key(&attr, + (const uint8_t *)in_vec[2].base, + in_vec[2].len, + (psa_key_id_t *)out_vec[0].base); + } + + case ARM_TEE_CRYPTO_GENERATE_KEY_SID: + if (in_len < 2 || out_vec == NULL || out_len < 1) { + return PSA_ERROR_INVALID_ARGUMENT; + } + { + psa_key_attributes_t attr = *(const psa_key_attributes_t *)in_vec[1].base; + /* Fallback to volatile storage if persistent storage is unavailable. */ + if (!PSA_KEY_LIFETIME_IS_VOLATILE(attr.lifetime)) { + attr.lifetime = PSA_KEY_LIFETIME_VOLATILE; + } + return psa_generate_key(&attr, + (psa_key_id_t *)out_vec[0].base); + } + + case ARM_TEE_CRYPTO_DESTROY_KEY_SID: + return psa_destroy_key(iov->key_id); + + case ARM_TEE_CRYPTO_EXPORT_KEY_SID: { + size_t data_len = 0; + psa_status_t status; + if (out_vec == NULL || out_len < 1) { + return PSA_ERROR_INVALID_ARGUMENT; + } + status = psa_export_key(iov->key_id, + (uint8_t *)out_vec[0].base, + out_vec[0].len, + &data_len); + if (status == PSA_SUCCESS) { + out_vec[0].len = data_len; + } + return status; + } + + case ARM_TEE_CRYPTO_EXPORT_PUBLIC_KEY_SID: { + size_t data_len = 0; + psa_status_t status; + if (out_vec == NULL || out_len < 1) { + return PSA_ERROR_INVALID_ARGUMENT; + } + status = psa_export_public_key(iov->key_id, + (uint8_t *)out_vec[0].base, + out_vec[0].len, + &data_len); + if (status == PSA_SUCCESS) { + out_vec[0].len = data_len; + } + return status; + } + + case ARM_TEE_CRYPTO_GET_KEY_ATTRIBUTES_SID: + if (out_vec == NULL || out_len < 1) { + return PSA_ERROR_INVALID_ARGUMENT; + } + return psa_get_key_attributes(iov->key_id, + (psa_key_attributes_t *)out_vec[0].base); + + case ARM_TEE_CRYPTO_HASH_COMPUTE_SID: { + size_t hash_len = 0; + psa_status_t status; + if (in_len < 2 || out_vec == NULL || out_len < 1) { + return PSA_ERROR_INVALID_ARGUMENT; + } + status = psa_hash_compute(iov->alg, + (const uint8_t *)in_vec[1].base, + in_vec[1].len, + (uint8_t *)out_vec[0].base, + out_vec[0].len, + &hash_len); + if (status == PSA_SUCCESS) { + out_vec[0].len = hash_len; + } + return status; + } + + case ARM_TEE_CRYPTO_HASH_SETUP_SID: { + struct wolfboot_hash_slot *slot; + uint32_t handle = 0; + psa_status_t status; + if (out_vec == NULL || out_len < 1) { + return PSA_ERROR_INVALID_ARGUMENT; + } + slot = wolfboot_hash_alloc(&handle); + if (slot == NULL) { + return PSA_ERROR_INSUFFICIENT_MEMORY; + } + status = psa_hash_setup(&slot->op, iov->alg); + if (status != PSA_SUCCESS) { + wolfboot_hash_free(handle); + return status; + } + *(uint32_t *)out_vec[0].base = handle; + out_vec[0].len = sizeof(uint32_t); + return PSA_SUCCESS; + } + + case ARM_TEE_CRYPTO_HASH_UPDATE_SID: { + struct wolfboot_hash_slot *slot; + if (in_len < 2) { + return PSA_ERROR_INVALID_ARGUMENT; + } + slot = wolfboot_hash_find(iov->op_handle); + if (slot == NULL) { + return PSA_ERROR_BAD_STATE; + } + return psa_hash_update(&slot->op, + (const uint8_t *)in_vec[1].base, + in_vec[1].len); + } + + case ARM_TEE_CRYPTO_HASH_FINISH_SID: { + struct wolfboot_hash_slot *slot; + size_t hash_len = 0; + psa_status_t status; + if (out_vec == NULL || out_len < 2) { + return PSA_ERROR_INVALID_ARGUMENT; + } + slot = wolfboot_hash_find(iov->op_handle); + if (slot == NULL) { + return PSA_ERROR_BAD_STATE; + } + status = psa_hash_finish(&slot->op, + (uint8_t *)out_vec[1].base, + out_vec[1].len, + &hash_len); + if (status == PSA_SUCCESS) { + out_vec[0].len = sizeof(uint32_t); + *(uint32_t *)out_vec[0].base = 0; + out_vec[1].len = hash_len; + wolfboot_hash_free(iov->op_handle); + } + return status; + } + + case ARM_TEE_CRYPTO_HASH_ABORT_SID: { + struct wolfboot_hash_slot *slot; + if (out_vec == NULL || out_len < 1) { + return PSA_ERROR_INVALID_ARGUMENT; + } + slot = wolfboot_hash_find(iov->op_handle); + if (slot == NULL) { + *(uint32_t *)out_vec[0].base = 0; + out_vec[0].len = sizeof(uint32_t); + return PSA_SUCCESS; + } + (void)psa_hash_abort(&slot->op); + wolfboot_hash_free(iov->op_handle); + *(uint32_t *)out_vec[0].base = 0; + out_vec[0].len = sizeof(uint32_t); + return PSA_SUCCESS; + } + + case ARM_TEE_CRYPTO_ASYMMETRIC_SIGN_HASH_SID: { + size_t sig_len = 0; + psa_status_t status; + if (in_len < 2 || out_vec == NULL || out_len < 1) { + return PSA_ERROR_INVALID_ARGUMENT; + } + status = psa_sign_hash(iov->key_id, + iov->alg, + (const uint8_t *)in_vec[1].base, + in_vec[1].len, + (uint8_t *)out_vec[0].base, + out_vec[0].len, + &sig_len); + if (status == PSA_SUCCESS) { + out_vec[0].len = sig_len; + } + return status; + } + + case ARM_TEE_CRYPTO_ASYMMETRIC_VERIFY_HASH_SID: + if (in_len < 3) { + return PSA_ERROR_INVALID_ARGUMENT; + } + return psa_verify_hash(iov->key_id, + iov->alg, + (const uint8_t *)in_vec[1].base, + in_vec[1].len, + (const uint8_t *)in_vec[2].base, + in_vec[2].len); + + default: + return PSA_ERROR_NOT_SUPPORTED; + } +} + +uint32_t arm_tee_psa_framework_version(void) +{ + return 1U; +} + +uint32_t arm_tee_psa_version(uint32_t sid) +{ + if (sid == ARM_TEE_CRYPTO_SID) { + return 1U; + } + return 0U; +} + +psa_handle_t arm_tee_psa_connect(uint32_t sid, uint32_t version) +{ + (void)version; + if (sid == ARM_TEE_CRYPTO_SID) { + return (psa_handle_t)ARM_TEE_CRYPTO_HANDLE; + } + return (psa_handle_t)PSA_ERROR_CONNECTION_REFUSED; +} + +int32_t arm_tee_psa_call(psa_handle_t handle, int32_t type, + const psa_invec *in_vec, size_t in_len, + psa_outvec *out_vec, size_t out_len) +{ + (void)type; + + if (handle == (psa_handle_t)ARM_TEE_CRYPTO_HANDLE) { + return wolfboot_crypto_dispatch(in_vec, in_len, out_vec, out_len); + } + + if (handle == (psa_handle_t)ARM_TEE_PROTECTED_STORAGE_HANDLE) { + if (type == ARM_TEE_PS_SET) { + const psa_storage_uid_t *uid; + const void *data; + const psa_storage_create_flags_t *flags; + struct wolfboot_ps_entry *entry; + if (in_vec == NULL || in_len < 3) { + return PSA_ERROR_INVALID_ARGUMENT; + } + uid = (const psa_storage_uid_t *)in_vec[0].base; + data = in_vec[1].base; + flags = (const psa_storage_create_flags_t *)in_vec[2].base; + if (uid == NULL || flags == NULL) { + return PSA_ERROR_INVALID_ARGUMENT; + } + if (in_vec[1].len > WOLFBOOT_PS_MAX_DATA) { + return PSA_ERROR_INSUFFICIENT_STORAGE; + } + entry = wolfboot_ps_find(*uid); + if (entry == NULL) { + entry = wolfboot_ps_alloc(*uid); + if (entry == NULL) { + return PSA_ERROR_INSUFFICIENT_STORAGE; + } + } else if ((entry->flags & PSA_STORAGE_FLAG_WRITE_ONCE) != 0U) { + return PSA_ERROR_NOT_PERMITTED; + } + if (in_vec[1].len > 0 && data == NULL) { + return PSA_ERROR_INVALID_ARGUMENT; + } + if (in_vec[1].len > 0) { + XMEMCPY(entry->data, data, in_vec[1].len); + } + entry->size = in_vec[1].len; + entry->flags = *flags; + return PSA_SUCCESS; + } + if (type == ARM_TEE_PS_GET) { + const psa_storage_uid_t *uid; + const rot_size_t *offset; + struct wolfboot_ps_entry *entry; + size_t read_len; + if (in_vec == NULL || in_len < 2 || out_vec == NULL || out_len < 1) { + return PSA_ERROR_INVALID_ARGUMENT; + } + uid = (const psa_storage_uid_t *)in_vec[0].base; + offset = (const rot_size_t *)in_vec[1].base; + if (uid == NULL || offset == NULL) { + return PSA_ERROR_INVALID_ARGUMENT; + } + entry = wolfboot_ps_find(*uid); + if (entry == NULL) { + return PSA_ERROR_DOES_NOT_EXIST; + } + if (*offset > entry->size) { + return PSA_ERROR_INVALID_ARGUMENT; + } + read_len = entry->size - *offset; + if (read_len > out_vec[0].len) { + read_len = out_vec[0].len; + } + if (read_len > 0 && out_vec[0].base != NULL) { + XMEMCPY(out_vec[0].base, entry->data + *offset, read_len); + } + out_vec[0].len = read_len; + return PSA_SUCCESS; + } + if (type == ARM_TEE_PS_GET_INFO) { + const psa_storage_uid_t *uid; + struct wolfboot_ps_entry *entry; + if (in_vec == NULL || in_len < 1 || out_vec == NULL || out_len < 1) { + return PSA_ERROR_INVALID_ARGUMENT; + } + uid = (const psa_storage_uid_t *)in_vec[0].base; + if (uid == NULL) { + return PSA_ERROR_INVALID_ARGUMENT; + } + entry = wolfboot_ps_find(*uid); + if (entry == NULL) { + return PSA_ERROR_DOES_NOT_EXIST; + } + { + struct psa_storage_info_t info; + info.capacity = WOLFBOOT_PS_MAX_DATA; + info.size = entry->size; + info.flags = entry->flags; + if (out_vec[0].len < sizeof(info)) { + return PSA_ERROR_BUFFER_TOO_SMALL; + } + XMEMCPY(out_vec[0].base, &info, sizeof(info)); + out_vec[0].len = sizeof(info); + } + return PSA_SUCCESS; + } + if (type == ARM_TEE_PS_REMOVE) { + const psa_storage_uid_t *uid; + struct wolfboot_ps_entry *entry; + if (in_vec == NULL || in_len < 1) { + return PSA_ERROR_INVALID_ARGUMENT; + } + uid = (const psa_storage_uid_t *)in_vec[0].base; + if (uid == NULL) { + return PSA_ERROR_INVALID_ARGUMENT; + } + entry = wolfboot_ps_find(*uid); + if (entry == NULL) { + return PSA_ERROR_DOES_NOT_EXIST; + } + entry->in_use = 0; + entry->uid = 0; + entry->size = 0; + entry->flags = 0; + return PSA_SUCCESS; + } + if (type == ARM_TEE_PS_GET_SUPPORT) { + if (out_vec != NULL && out_len >= 1 && out_vec[0].base != NULL) { + uint32_t support = 0; + XMEMCPY(out_vec[0].base, &support, sizeof(support)); + out_vec[0].len = sizeof(support); + } + return PSA_SUCCESS; + } + return PSA_ERROR_NOT_SUPPORTED; + } + + if (handle == (psa_handle_t)ARM_TEE_ATTESTATION_HANDLE) { + if (type == ARM_TEE_ATTEST_GET_TOKEN) { + if (out_vec == NULL || out_len < 1) { + return PSA_ERROR_INVALID_ARGUMENT; + } + out_vec[0].len = 0; + return PSA_SUCCESS; + } + if (type == ARM_TEE_ATTEST_GET_TOKEN_SIZE) { + if (out_vec == NULL || out_len < 1 || out_vec[0].base == NULL || + out_vec[0].len < sizeof(rot_size_t)) { + return PSA_ERROR_INVALID_ARGUMENT; + } + { + rot_size_t token_size = 0; + XMEMCPY(out_vec[0].base, &token_size, sizeof(token_size)); + out_vec[0].len = sizeof(token_size); + } + return PSA_SUCCESS; + } + return PSA_ERROR_NOT_SUPPORTED; + } + + return PSA_ERROR_NOT_SUPPORTED; +} + +void arm_tee_psa_close(psa_handle_t handle) +{ + (void)handle; +} diff --git a/src/arm_tee_psa_veneer.c b/src/arm_tee_psa_veneer.c new file mode 100644 index 0000000000..87232dbea4 --- /dev/null +++ b/src/arm_tee_psa_veneer.c @@ -0,0 +1,47 @@ +/* arm_tee_psa_veneer.c + * + * ARM TEE style PSA IPC CMSE veneers for Zephyr integration. + * + * Copyright (C) 2025 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + */ + +#include + +uint32_t WOLFBOOT_CMSE_NS_ENTRY arm_tee_psa_framework_version_veneer(void) +{ + return arm_tee_psa_framework_version(); +} + +uint32_t WOLFBOOT_CMSE_NS_ENTRY arm_tee_psa_version_veneer(uint32_t sid) +{ + return arm_tee_psa_version(sid); +} + +psa_handle_t WOLFBOOT_CMSE_NS_ENTRY arm_tee_psa_connect_veneer(uint32_t sid, + uint32_t version) +{ + return arm_tee_psa_connect(sid, version); +} + +int32_t WOLFBOOT_CMSE_NS_ENTRY arm_tee_psa_call_veneer(psa_handle_t handle, + uint32_t ctrl_param, + const psa_invec *in_vec, psa_outvec *out_vec) +{ + int32_t type = WOLFBOOT_ARM_TEE_PARAM_UNPACK_TYPE(ctrl_param); + size_t in_len = WOLFBOOT_ARM_TEE_PARAM_UNPACK_IN_LEN(ctrl_param); + size_t out_len = WOLFBOOT_ARM_TEE_PARAM_UNPACK_OUT_LEN(ctrl_param); + + return arm_tee_psa_call(handle, type, in_vec, in_len, out_vec, out_len); +} + +void WOLFBOOT_CMSE_NS_ENTRY arm_tee_psa_close_veneer(psa_handle_t handle) +{ + arm_tee_psa_close(handle); +} diff --git a/src/psa_store.c b/src/psa_store.c new file mode 100644 index 0000000000..3257e40b58 --- /dev/null +++ b/src/psa_store.c @@ -0,0 +1,546 @@ +/* psa_store.c + * + * Copyright (C) 2025 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + + +#include +#include + +#include "hal.h" + +#ifdef WOLFCRYPT_TZ_PSA + +#include "wolfpsa/psa_store.h" + +#include +#include + +#ifndef KEYVAULT_OBJ_SIZE + #define KEYVAULT_OBJ_SIZE 0x1000 /* 4KB per object */ +#endif + +#ifndef KEYVAULT_MAX_ITEMS + #define KEYVAULT_MAX_ITEMS 20 /* Total memory: 0x16000 (20 items) + 2 sector overhead = 0x18000 */ +#endif + +/* Internal errors mirrored from wolfPKCS11 store semantics */ +#define PIN_INVALID_E -1 +#define PIN_NOT_SET_E -2 +#define READ_ONLY_E -3 +#define NOT_AVAILABLE_E -4 +#define FIND_FULL_E -5 +#define FIND_NO_MORE_E -6 +#define SESSION_EXISTS_E -7 +#define SESSION_COUNT_E -8 +#define LOGGED_IN_E -9 +#define OBJ_COUNT_E -10 + + +#ifndef UNIT_TEST + +/* From linker script: origin and size of vault flash */ +extern uint32_t _flash_keyvault; +extern uint32_t _flash_keyvault_size; + +#define vault_base ((uint8_t*)&_flash_keyvault) +#define vault_size ((uint32_t)&_flash_keyvault_size) + + +/* Back-end for malloc, used by crypto storage clients */ +extern unsigned int _start_heap; /* From linker script: heap memory */ +extern unsigned int _heap_size; /* From linker script: heap limit */ + +void * _sbrk(unsigned int incr) +{ + static uint8_t *heap = NULL; + static uint32_t heapsize = (uint32_t)&_heap_size; + void *old_heap = heap; + (void)heapsize; + if (((incr >> 2) << 2) != incr) + incr = ((incr >> 2) + 1) << 2; + + if (heap == NULL) { + heap = (uint8_t*)&_start_heap; + old_heap = heap; + } else + heap += incr; + return old_heap; +} +#endif + +struct obj_hdr +{ + uint32_t token_id; + uint32_t object_id; + int32_t type; + uint32_t pos; + uint32_t size; + uint32_t __pad[3]; +}; +#define STORE_PRIV_HDR_SIZE 0x20 +#define STORE_PRIV_HDR_OFFSET 0x80 +#define WOLFPSA_INVALID_ID 0xFFFFFFFF + +#define BITMAP_OFFSET (4) +#define BITMAP_SIZE (KEYVAULT_MAX_ITEMS / 8 + 1) + +#if (BITMAP_SIZE > (STORE_PRIV_HDR_OFFSET - 4)) + #error Too many keyvault items +#endif + +/* This spells "PKCS" */ +#ifndef BIG_ENDIAN_ORDER + #define VAULT_HEADER_MAGIC 0x53434B50 +#else + #define VAULT_HEADER_MAGIC 0x504B4353 +#endif + +#define MAX_OPEN_STORES 16 + +struct store_handle { + uint32_t flags; + uint32_t pos; + void *buffer; + struct obj_hdr *hdr; + uint32_t in_buffer_offset; +}; + +#define STORE_FLAGS_OPEN (1 << 0) +#define STORE_FLAGS_READONLY (1 << 1) + +static struct store_handle openstores_handles[MAX_OPEN_STORES] = {}; + +static uint8_t cached_sector[WOLFBOOT_SECTOR_SIZE]; + +static void bitmap_put(uint32_t pos, int val) +{ + uint32_t octet = pos / 8; + uint32_t bit = pos % 8; + uint8_t *bitmap = cached_sector + sizeof(uint32_t); + + if (val != 0) { + bitmap[octet] |= (1 << bit); + } else { + bitmap[octet] &= ~(1 << bit); + } +} + +static int bitmap_get(uint32_t pos) +{ + uint32_t octet = pos / 8; + uint32_t bit = pos % 8; + uint8_t *bitmap = vault_base + sizeof(uint32_t); + return (bitmap[octet] & (1 << bit)) >> bit; +} + +static int bitmap_find_free_pos(void) +{ + int i; + for (i = 0; i < KEYVAULT_MAX_ITEMS; i++) { + if (bitmap_get(i) == 0) + return i; + } + return -1; +} + +/* A table with nodes is stored at the beginning of the keyvault + * - 4 B: Magic (spells "PKCS" when the vault is initialized) + * - N B: bitmap (N = KEYVAULT_MAX_ITEMS / 8 + 1) + * + * At byte 0x80: + * - Start of the obj hdr structures array + */ + +#define NODES_TABLE ( (struct obj_hdr *)(vault_base + STORE_PRIV_HDR_OFFSET) ) + +/* A backup sector immediately after the header sector */ + +#define BACKUP_SECTOR_ADDRESS (vault_base + WOLFBOOT_SECTOR_SIZE) + +static void cache_commit(uint32_t offset) +{ + hal_flash_unlock(); + + /* Write backup sector first */ + hal_flash_erase((uintptr_t)BACKUP_SECTOR_ADDRESS, WOLFBOOT_SECTOR_SIZE); + hal_flash_write((uintptr_t)BACKUP_SECTOR_ADDRESS, cached_sector, WOLFBOOT_SECTOR_SIZE); + + /* Erase + write actual destination sector */ + hal_flash_erase((uintptr_t)vault_base + offset, WOLFBOOT_SECTOR_SIZE); + hal_flash_write((uintptr_t)vault_base + offset, cached_sector, WOLFBOOT_SECTOR_SIZE); + + hal_flash_lock(); +} + +static void restore_backup(uint32_t offset) +{ + hal_flash_unlock(); + /* Erase + copy from backup */ + hal_flash_erase((uintptr_t)vault_base + offset, WOLFBOOT_SECTOR_SIZE); + hal_flash_write((uintptr_t)vault_base + offset, BACKUP_SECTOR_ADDRESS, + WOLFBOOT_SECTOR_SIZE); + hal_flash_lock(); +} + +static void check_vault(void) +{ + uint32_t *magic = (uint32_t *)vault_base; + uint32_t total_vault_size = KEYVAULT_MAX_ITEMS * KEYVAULT_OBJ_SIZE; + + if ((total_vault_size % WOLFBOOT_SECTOR_SIZE) != 0) + total_vault_size = (total_vault_size / WOLFBOOT_SECTOR_SIZE) * WOLFBOOT_SECTOR_SIZE + WOLFBOOT_SECTOR_SIZE; + + if (*magic != VAULT_HEADER_MAGIC) { + uint32_t *magic = (uint32_t *)BACKUP_SECTOR_ADDRESS; + if (*magic == VAULT_HEADER_MAGIC) { + restore_backup(0); + return; + } + memset(cached_sector, 0xFF, WOLFBOOT_SECTOR_SIZE); + magic = (uint32_t *)cached_sector; + *magic = VAULT_HEADER_MAGIC; + memset(cached_sector + sizeof(uint32_t), 0x00, BITMAP_SIZE); + cache_commit(0); + hal_flash_unlock(); + hal_flash_erase((uintptr_t)vault_base + WOLFBOOT_SECTOR_SIZE * 2, total_vault_size); + hal_flash_lock(); + } +} + +static void delete_object(int32_t type, uint32_t tok_id, uint32_t obj_id) +{ + struct obj_hdr *hdr = (struct obj_hdr *)cached_sector; + check_vault(); + memcpy(cached_sector, vault_base, WOLFBOOT_SECTOR_SIZE); + + while ((uintptr_t)hdr < ((uintptr_t)cached_sector + WOLFBOOT_SECTOR_SIZE)) { + if ((hdr->token_id == tok_id) && (hdr->object_id == obj_id) && + (hdr->type == type)) { + hdr->token_id = WOLFPSA_INVALID_ID; + hdr->object_id = WOLFPSA_INVALID_ID; + bitmap_put(hdr->pos, 0); + cache_commit(0); + return; + } + hdr++; + } +} + +/* Returns a pointer to the selected object in flash. + * NULL is OK here as error return value, even if the keystore + * started at physical 0x0000 0000, the buffers are stored from sector + * 2 onwards. + */ +static uint8_t *find_object_buffer(int32_t type, uint32_t tok_id, uint32_t obj_id) +{ + struct obj_hdr *hdr = NODES_TABLE; + uint32_t *tok_obj_stored = NULL; + while ((uintptr_t)hdr < ((uintptr_t)NODES_TABLE + WOLFBOOT_SECTOR_SIZE)) { + if ((hdr->token_id == tok_id) && (hdr->object_id == obj_id) + && (hdr->type == type)) { + tok_obj_stored = (uint32_t *) (vault_base + (2 * WOLFBOOT_SECTOR_SIZE) + (hdr->pos * KEYVAULT_OBJ_SIZE)); + if ((tok_obj_stored[0] != tok_id) || (tok_obj_stored[1] != obj_id)) { + /* Id's don't match. Try backup sector. */ + uint32_t in_sector_off = (hdr->pos * KEYVAULT_OBJ_SIZE) % + WOLFBOOT_SECTOR_SIZE; + uint32_t sector_base = hdr->pos * KEYVAULT_OBJ_SIZE + + 2 * WOLFBOOT_SECTOR_SIZE - in_sector_off; + tok_obj_stored = (uint32_t *)((BACKUP_SECTOR_ADDRESS + in_sector_off)); + if ((tok_obj_stored[0] == tok_id) && (tok_obj_stored[1] == obj_id)) { + /* Found backup! restoring... */ + restore_backup(sector_base); + } else { + delete_object(type, tok_id, obj_id); + return NULL; /* Cannot recover object payload */ + } + } + /* Object is now OK */ + return vault_base + 2 * WOLFBOOT_SECTOR_SIZE + hdr->pos * KEYVAULT_OBJ_SIZE; + } + hdr++; + } + return NULL; /* object not found */ +} + +static struct obj_hdr *find_object_header(int32_t type, uint32_t tok_id, + uint32_t obj_id) +{ + struct obj_hdr *hdr = NODES_TABLE; + while ((uintptr_t)hdr < ((uintptr_t)NODES_TABLE + WOLFBOOT_SECTOR_SIZE)) { + if ((hdr->token_id == tok_id) && (hdr->object_id == obj_id) + && (hdr->type == type)) { + return hdr; + } + hdr++; + } + return NULL; +} + +static struct obj_hdr *create_object(int32_t type, uint32_t tok_id, uint32_t obj_id) +{ + struct obj_hdr *hdr = NULL; + uint32_t *tok_obj_id; + /* Refuse to create an object that's already in store */ + if (find_object_buffer(type, tok_id, obj_id) != NULL) { + return NULL; + } + + /* Caching sector 0 */ + memcpy(cached_sector, vault_base , WOLFBOOT_SECTOR_SIZE); + hdr = (struct obj_hdr *)(cached_sector + STORE_PRIV_HDR_OFFSET); + while ((uintptr_t)hdr < ((uintptr_t)cached_sector + WOLFBOOT_SECTOR_SIZE)) { + if (hdr->token_id == WOLFPSA_INVALID_ID) { + uint32_t sector_base, in_sector_off; + int pos = bitmap_find_free_pos(); + if (pos < 0) { + return NULL; + } + hdr->pos = (unsigned)pos; + in_sector_off = (hdr->pos * KEYVAULT_OBJ_SIZE) % + WOLFBOOT_SECTOR_SIZE; + sector_base = hdr->pos * KEYVAULT_OBJ_SIZE + + 2 * WOLFBOOT_SECTOR_SIZE - in_sector_off; + /* Claim the spot in the table */ + hdr->token_id = tok_id; + hdr->object_id = obj_id; + hdr->type = type; + /* Set vault initial size to eight bytes (this includes the + * tok/obj id at the beginning of the buffer, before the + * payload). When an object is opened, the initial 'in_buffer_offset' + * is set to 8 as well. + */ + hdr->size = 2 * sizeof(uint32_t); + /* Set the bit to claim the position in flash */ + bitmap_put(hdr->pos, 1); + cache_commit(0); + /* Mark the beginning of the object in the sector, + * write the tok/obj ids + */ + memcpy(cached_sector, vault_base + sector_base, + WOLFBOOT_SECTOR_SIZE); + tok_obj_id = (void*)(cached_sector + in_sector_off); + tok_obj_id[0] = tok_id; + tok_obj_id[1] = obj_id; + cache_commit(sector_base); + /* Return the address of the header in flash */ + return (struct obj_hdr *)(vault_base + ((uint8_t *)hdr - (uint8_t *)cached_sector)); + } + hdr++; + } + return NULL; /* No space left in the nodes table */ +} + +static void update_store_size(struct obj_hdr *hdr, uint32_t size) +{ + uint32_t off; + struct obj_hdr *hdr_mem; + if (((uint8_t *)hdr) < vault_base || + ((uint8_t *)hdr > vault_base + WOLFBOOT_SECTOR_SIZE)) + return; + check_vault(); + off = (uintptr_t)hdr - (uintptr_t)vault_base; + memcpy(cached_sector, vault_base, WOLFBOOT_SECTOR_SIZE); + hdr_mem = (struct obj_hdr *)(cached_sector + off); + hdr_mem->size = size; + cache_commit(0); +} + +/* Find a free handle in openstores_handles[] array + * to manage the interaction with the API. + * + * A maximum of MAX_OPEN_STORES objects can be opened + * at the same time. + */ +static struct store_handle *find_free_handle(void) +{ + int i; + for (i = 0; i < MAX_OPEN_STORES; i++) { + if ((openstores_handles[i].flags & STORE_FLAGS_OPEN) == 0) + return &openstores_handles[i]; + } + return NULL; +} + +int wolfPSA_Store_Open(int type, unsigned long id1, unsigned long id2, int read, + void** store) +{ + struct store_handle *handle; + uint8_t *buf; + + /* Check if there is one handle available to open the slot */ + handle = find_free_handle(); + if (!handle) { + *store = NULL; + return SESSION_COUNT_E; + } + + /* Check if the target object exists */ + check_vault(); + buf = find_object_buffer(type, id1, id2); + if ((buf == NULL) && read) { + *store = NULL; + return NOT_AVAILABLE_E; + } + + if ((buf == NULL) && (!read)) { + handle->hdr = create_object(type, id1, id2); + if (handle->hdr == NULL) { + *store = NULL; + return FIND_FULL_E; + + } + buf = find_object_buffer(type, id1, id2); + if (!buf) { + *store = NULL; + return NOT_AVAILABLE_E; + } + } else { /* buf != NULL, readonly */ + handle->hdr = find_object_header(type, id1, id2); + if (!handle->hdr) { + *store = NULL; + return NOT_AVAILABLE_E; + } + } + + /* Set the position of the buffer in the handle */ + handle->buffer = buf; + handle->pos = (((uintptr_t)buf) - (uintptr_t)vault_base) / KEYVAULT_OBJ_SIZE; + /* Set the 'open' flag */ + handle->flags |= STORE_FLAGS_OPEN; + + /* Set the 'readonly' flag in this handle if open with 'r' */ + if (read) + handle->flags |= STORE_FLAGS_READONLY; + else { + handle->flags &= ~STORE_FLAGS_READONLY; + /* Truncate the slot when opening in write mode */ + update_store_size(handle->hdr, 2 * sizeof(uint32_t)); + } + + + /* Set start of the buffer after the tok/obj id fields */ + handle->in_buffer_offset = (2 * sizeof(uint32_t)); + *store = handle; + return 0; +} + +int wolfPSA_Store_OpenSz(int type, unsigned long id1, unsigned long id2, int read, + int variableSz, void** store) +{ + (void)variableSz; + return wolfPSA_Store_Open(type, id1, id2, read, store); +} + +void wolfPSA_Store_Close(void* store) +{ + struct store_handle *handle = store; + /* This removes all flags (including STORE_FLAGS_OPEN) */ + handle->flags = 0; + handle->hdr = NULL; +} + +int wolfPSA_Store_Read(void* store, unsigned char* buffer, int len) +{ + struct store_handle *handle = store; + uint32_t obj_size = 0; + if ((handle == NULL) || (handle->hdr == NULL) || (handle->buffer == NULL)) + return -1; + + obj_size = handle->hdr->size; + if (obj_size > KEYVAULT_OBJ_SIZE) + return -1; + + if (handle->in_buffer_offset >= obj_size) + return 0; /* "EOF" */ + + /* Truncate len to actual available bytes */ + if (handle->in_buffer_offset + len > obj_size) + len = (obj_size - handle->in_buffer_offset); + + if (len > 0) { + memcpy(buffer, (uint8_t *)(handle->buffer) + handle->in_buffer_offset, len); + handle->in_buffer_offset += len; + } + return len; +} + +int wolfPSA_Store_Write(void* store, unsigned char* buffer, int len) +{ + struct store_handle *handle = store; + uint32_t obj_size = 0; + uint32_t in_sector_offset = 0; + uint32_t in_sector_len = 0; + uint32_t sector_base = 0; + int written = 0; + + + if ((handle == NULL) || (handle->hdr == NULL) || (handle->buffer == NULL)) + return -1; + if ((handle->flags & STORE_FLAGS_READONLY) != 0) + return -1; + + obj_size = handle->hdr->size; + if (obj_size > KEYVAULT_OBJ_SIZE) + return -1; + + if (len + handle->in_buffer_offset > KEYVAULT_OBJ_SIZE) + len = KEYVAULT_OBJ_SIZE - handle->in_buffer_offset; + + if (len < 0) + return -1; + + + while (written < len) { + in_sector_offset = ((uintptr_t)(handle->buffer) + handle->in_buffer_offset) + % WOLFBOOT_SECTOR_SIZE; + sector_base = (uintptr_t)handle->buffer + handle->in_buffer_offset - in_sector_offset; + in_sector_len = WOLFBOOT_SECTOR_SIZE - in_sector_offset; + if (in_sector_len > (uint32_t)len) + in_sector_len = len; + + /* Cache the corresponding sector */ + memcpy(cached_sector, (void *)(uintptr_t)sector_base, WOLFBOOT_SECTOR_SIZE); + /* Write content into cache */ + memcpy(cached_sector + in_sector_offset, buffer + written, in_sector_len); + /* Adjust in_buffer position for the handle accordingly */ + handle->in_buffer_offset += in_sector_len; + written += in_sector_len; + /* Write sector to flash */ + cache_commit((uintptr_t)sector_base - (uintptr_t)vault_base); + } + obj_size += written; + update_store_size(handle->hdr, obj_size); + return len; +} + +int wolfPSA_Store_Remove(int type, unsigned long id1, unsigned long id2) +{ + uint8_t* buf; + + check_vault(); + buf = find_object_buffer((int32_t)type, (uint32_t)id1, (uint32_t)id2); + if (buf == NULL) + return NOT_AVAILABLE_E; + + delete_object((int32_t)type, (uint32_t)id1, (uint32_t)id2); + return 0; +} + +#endif /* WOLFCRYPT_TZ_PSA */ diff --git a/tools/config.mk b/tools/config.mk index 86942d22fc..a4fd9dc1a8 100644 --- a/tools/config.mk +++ b/tools/config.mk @@ -61,6 +61,7 @@ ifeq ($(ARCH),) TZEN?=0 WOLFCRYPT_TZ?=0 WOLFCRYPT_TZ_PKCS11?=0 + WOLFCRYPT_TZ_PSA?=0 WOLFBOOT_PARTITION_SIZE?=0x20000 WOLFBOOT_SECTOR_SIZE?=0x20000 WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x08020000 @@ -93,6 +94,7 @@ CONFIG_VARS:= ARCH TARGET SIGN HASH MCUXSDK MCUXPRESSO MCUXPRESSO_CPU MCUXPRESSO SPMATH SPMATHALL RAM_CODE DUALBANK_SWAP IMAGE_HEADER_SIZE PKA TZEN PSOC6_CRYPTO \ WOLFTPM WOLFBOOT_TPM_VERIFY MEASURED_BOOT WOLFBOOT_TPM_SEAL WOLFBOOT_TPM_KEYSTORE \ WOLFCRYPT_TZ WOLFCRYPT_TZ_PKCS11 \ + WOLFCRYPT_TZ_PSA \ WOLFBOOT_PARTITION_SIZE WOLFBOOT_SECTOR_SIZE \ WOLFBOOT_PARTITION_BOOT_ADDRESS WOLFBOOT_PARTITION_UPDATE_ADDRESS \ WOLFBOOT_PARTITION_SWAP_ADDRESS WOLFBOOT_LOAD_ADDRESS \ diff --git a/tools/scripts/boot_status.py b/tools/scripts/boot_status.py index df40cd9314..6347949ed7 100755 --- a/tools/scripts/boot_status.py +++ b/tools/scripts/boot_status.py @@ -205,6 +205,46 @@ def hash_name_for_len(n: int): if n == 64: return "sha512" return None +def resolve_header_size(args) -> int: + """Resolve header size from args or optional .config.""" + if args.header_size is not None: + return args.header_size + if getattr(args, "config", None): + cfg = read_config(args.config) + if "IMAGE_HEADER_SIZE" in cfg: + try: + return int(cfg["IMAGE_HEADER_SIZE"], 0) + except ValueError: + pass + return 0x100 + +def auto_detect_header_size_for_hash(data: bytes) -> int | None: + """Try common header sizes and return the one that verifies the hash.""" + for header_size in (0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000): + if len(data) < header_size + 8: + continue + try: + hdr = parse_header(data, header_size=header_size) + except Exception: + continue + sha_info = find_tlv(data, header_size, 0x0003) + if sha_info is None: + continue + sha_val_off, sha_len, sha_tlv_hdr = sha_info + hash_bytes = data[sha_val_off:sha_val_off + sha_len] + header_prefix_end = sha_tlv_hdr + header_prefix = data[0:header_prefix_end] + payload = data[header_size: header_size + hdr["size"]] + hname = hash_name_for_len(sha_len) + if not hname: + continue + h = hashlib.new(hname) + h.update(header_prefix) + h.update(payload) + if h.digest() == hash_bytes: + return header_size + return None + def try_load_public_key(pubkey_path: Path): """Load a public key from PEM or DER format.""" try: @@ -273,7 +313,8 @@ def cmd_image_inspect(args): sys.exit(1) data = img_path.read_bytes() - hdr = parse_header(data, header_size=args.header_size) + header_size = resolve_header_size(args) + hdr = parse_header(data, header_size=header_size) magic = hdr["magic"]; size = hdr["size"]; header_size = hdr["header_size"]; tlist = hdr["tlvs"] d = tlv_dict(tlist) @@ -324,13 +365,20 @@ def cmd_image_verify(args): sys.exit(1) data = img_path.read_bytes() - hdr = parse_header(data, header_size=args.header_size) + header_size = resolve_header_size(args) + if args.header_size is None and args.verify_hash and not getattr(args, "config", None): + auto_size = auto_detect_header_size_for_hash(data) + if auto_size is not None: + header_size = auto_size + print(f"[HASH] Auto-detected header size: 0x{header_size:x}") + hdr = parse_header(data, header_size=header_size) magic = hdr["magic"]; size = hdr["size"]; header_size = hdr["header_size"]; tlist = hdr["tlvs"] d = tlv_dict(tlist) # Show basic info print(f"Magic: {magic.decode('ascii', 'replace')} (raw: {magic.hex()})") print(f"Payload size: {size} (0x{size:08X})") + print(f"Header size: {header_size} (0x{header_size:X})") hash_bytes = d.get(0x0003, [(None, None)])[0][1] sig = d.get(0x0020, [(None, None)])[0][1] @@ -392,7 +440,8 @@ def cmd_image_dump(args): sys.exit(1) data = img_path.read_bytes() - hdr = parse_header(data, header_size=args.header_size) + header_size = resolve_header_size(args) + hdr = parse_header(data, header_size=header_size) size = hdr["size"] header_size = hdr["header_size"] @@ -552,14 +601,16 @@ def main(): # image inspect img_inspect = image_subparsers.add_parser("inspect", help="Inspect image header and metadata") img_inspect.add_argument("image", help="Signed image file") - img_inspect.add_argument("--header-size", type=lambda x: int(x, 0), default=0x100, - help="Header size (default 0x100)") + img_inspect.add_argument("--header-size", type=lambda x: int(x, 0), default=None, + help="Header size (default 0x100 if not provided)") + img_inspect.add_argument("--config", help="Path to wolfBoot .config (for IMAGE_HEADER_SIZE)") # image verify img_verify = image_subparsers.add_parser("verify", help="Verify image hash and signature") img_verify.add_argument("image", help="Signed image file") - img_verify.add_argument("--header-size", type=lambda x: int(x, 0), default=0x100, - help="Header size (default 0x100)") + img_verify.add_argument("--header-size", type=lambda x: int(x, 0), default=None, + help="Header size (default 0x100 if not provided)") + img_verify.add_argument("--config", help="Path to wolfBoot .config (for IMAGE_HEADER_SIZE)") img_verify.add_argument("--pubkey", metavar="KEY", help="Public key file (PEM/DER) for signature verification") img_verify.add_argument("--alg", choices=["ecdsa-p256", "ed25519"], help="Signature algorithm (auto-detect if omitted)") img_verify.add_argument("--verify-hash", action="store_true", help="Verify payload hash") @@ -568,8 +619,9 @@ def main(): img_dump = image_subparsers.add_parser("dump", help="Dump payload to file") img_dump.add_argument("image", help="Signed image file") img_dump.add_argument("output", help="Output file for payload") - img_dump.add_argument("--header-size", type=lambda x: int(x, 0), default=0x100, - help="Header size (default 0x100)") + img_dump.add_argument("--header-size", type=lambda x: int(x, 0), default=None, + help="Header size (default 0x100 if not provided)") + img_dump.add_argument("--config", help="Path to wolfBoot .config (for IMAGE_HEADER_SIZE)") # ======================================================================== # KEYSTORE SUBCOMMAND diff --git a/zephyr/CMakeLists.txt b/zephyr/CMakeLists.txt new file mode 100644 index 0000000000..b2c7b9d204 --- /dev/null +++ b/zephyr/CMakeLists.txt @@ -0,0 +1,40 @@ +# CMakeLists.txt +# +# Copyright (C) 2026 wolfSSL Inc. +# +# This file is part of wolfBoot. +# +# wolfBoot is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. + +if(NOT CONFIG_WOLFBOOT_TEE) + return() +endif() + +set(WOLFBOOT_MODULE_DIR ${CMAKE_CURRENT_LIST_DIR}/.. CACHE PATH "wolfBoot module root") + +zephyr_library() + +zephyr_library_sources( + ${CMAKE_CURRENT_LIST_DIR}/src/arm_tee_ns_interface.c + ${CMAKE_CURRENT_LIST_DIR}/src/arm_tee_crypto_api.c + ${CMAKE_CURRENT_LIST_DIR}/src/arm_tee_ps_api.c + ${CMAKE_CURRENT_LIST_DIR}/src/arm_tee_attest_api.c + ${CMAKE_CURRENT_LIST_DIR}/src/wolfboot_psa_ns_api.c + ${CMAKE_CURRENT_LIST_DIR}/src/wolfboot_veneers_stub.c +) + +zephyr_library_include_directories( + ${CMAKE_CURRENT_LIST_DIR}/include + ${CMAKE_CURRENT_LIST_DIR}/include/crypto_keys + ${WOLFBOOT_MODULE_DIR}/include +) + +set(WOLFBOOT_CMSE_VENEERS ${WOLFBOOT_MODULE_DIR}/src/wc_secure_calls.o) +if(EXISTS ${WOLFBOOT_CMSE_VENEERS}) + zephyr_library_link_libraries(${WOLFBOOT_CMSE_VENEERS}) +else() + message(WARNING "wolfBoot CMSE veneers not found at ${WOLFBOOT_CMSE_VENEERS}; PSA calls will fall back to stubs.") +endif() diff --git a/zephyr/Kconfig b/zephyr/Kconfig new file mode 100644 index 0000000000..87d4d7b289 --- /dev/null +++ b/zephyr/Kconfig @@ -0,0 +1,54 @@ +# Kconfig +# +# Copyright (C) 2026 wolfSSL Inc. +# +# This file is part of wolfBoot. +# +# wolfBoot is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. + +menuconfig WOLFBOOT + bool "wolfBoot integration" + help + Enable wolfBoot integration helpers (PSA CMSE client glue, signing). + +if WOLFBOOT + +config WOLFBOOT_SIGN_TOOL + string "Path to wolfBoot signing tool" + default "" + help + Optional override for the wolfBoot signing tool path. If empty, the + integration uses wolfBoot's tools/keytools/sign (C tool). + +config WOLFBOOT_SIGNATURE_KEY_FILE + string "wolfBoot signing key file" + default "" + help + Signing key used by the wolfBoot signing tool. If empty, the integration + falls back to CONFIG_MCUBOOT_SIGNATURE_KEY_FILE. + +config WOLFBOOT_SIGN_ALG + string "wolfBoot signing algorithm" + default "ecc256" + help + Signing algorithm passed to wolfBoot sign tool. Supported values depend + on wolfBoot (e.g. ecc256, ecc384, ed25519, rsa2048). + +config WOLFBOOT_SIGN_HASH + string "wolfBoot signing hash" + default "sha256" + help + Hash algorithm passed to wolfBoot sign tool (e.g. sha256, sha384, sha3). + +config WOLFBOOT_SIGN_VERSION + int "wolfBoot image version" + default 1 + help + Integer version passed to wolfBoot sign tool. If left at the default, + the integration will attempt to derive a version from + CONFIG_MCUBOOT_IMGTOOL_SIGN_VERSION when possible. + +endif # WOLFBOOT diff --git a/zephyr/README.md b/zephyr/README.md new file mode 100644 index 0000000000..cc0d2d109f --- /dev/null +++ b/zephyr/README.md @@ -0,0 +1,108 @@ +# wolfBoot Zephyr Integration (ARM TEE Replacement) + +This guide explains how to replace the default TEE provider with wolfBoot as the PSA/TEE provider in a vanilla Zephyr workspace. + +It assumes: +- You have this wolfBoot repo on disk (this workspace). +- You want Zephyr to build and run the PSA crypto sample using wolfBoot as the TEE. + +## 1) Create a fresh Zephyr workspace + +From the workspace root: + +```sh +mkdir -p zephyrproject +cd zephyrproject +python3 -m venv .venv +./.venv/bin/pip install --upgrade pip west jsonschema pyelftools + +./.venv/bin/west init -m https://github.com/zephyrproject-rtos/zephyr +./.venv/bin/west update +``` + +You now have a vanilla Zephyr tree under `zephyrproject/zephyr`. + +## 2) Patch Zephyr for wolfBoot integration + +From the Zephyr base (`zephyrproject/zephyr`), apply the patches in order: + +```sh +cd /path/to/your/workspace/zephyrproject/zephyr + +git apply /path/to/your/workspace/wolfboot/zephyr/patches/*.patch +``` + +These patches add: +- wolfBoot TEE driver hooks (`drivers/tee/wolfboot` + Kconfig/CMake wiring). +- Device-tree binding for the wolfBoot TEE and the `wolfssl` vendor prefix. +- `samples/wolfboot_integration/psa_crypto`. +- STM32H5 NS board variants used by the sample (Nucleo H563ZI and H573I-DK). +- Kconfig tweaks so PSA crypto client is enabled with `WOLFBOOT_TEE`, and the legacy TEE dependency isn’t forced when not configured. + +## 3) Build wolfBoot (secure side) first + +wolfBoot provides the secure-side PSA service and CMSE veneers. Build it before the Zephyr app: + +```sh +cd /path/to/your/workspace/wolfboot +cp config/examples/stm32h5-tz-psa.config .config +make clean wolfboot.bin +``` + +This also produces `src/wc_secure_calls.o`, which Zephyr links for CMSE veneers. + +## 4) Build the PSA crypto sample with wolfBoot as an extra module + +Use the Zephyr sample in-tree, and point `ZEPHYR_EXTRA_MODULES` to the wolfBoot repo: + +```sh +cd /path/to/your/workspace/zephyrproject/zephyr + +./../.venv/bin/west build -p auto \ + -b nucleo_h563zi/stm32h563xx/ns \ + -d ./build \ + ./samples/wolfboot_integration/psa_crypto \ + -- -DZEPHYR_EXTRA_MODULES=/path/to/your/workspace/wolfboot +``` + +Notes: +- The wolfBoot module provides the PSA client veneers, PSA IPC glue, and minimal PSA API wrappers. +- The sample overlay uses `compatible = "wolfssl,tee"` and the vendor prefix is registered. + +## 5) Flash on STM32H5 Nucleo (TrustZone enabled) + +Follow the STM32H5 target guide in `wolfboot/docs/Targets.md` and program the board with +STM32_Programmer_CLI. The key steps are: + +```sh +# Enable TrustZone +STM32_Programmer_CLI -c port=swd -ob TZEN=0xB4 + +# Secure window (first 384KB) + remainder non-secure +STM32_Programmer_CLI -c port=swd -ob SECWM1_STRT=0x0 SECWM1_END=0x2F SECWM2_STRT=0x0 SECWM2_END=0x7F + +# Secure wolfBoot image +STM32_Programmer_CLI -c port=swd -d wolfboot.bin 0x0C000000 + +# Non-secure Zephyr payload +STM32_Programmer_CLI -c port=swd -d zephyrproject/zephyr/build/zephyr/zephyr.payload_v1_signed.bin 0x08060000 +``` + +For the full option-byte list and related notes, see `wolfboot/docs/STM32-TZ.md`. + +## Troubleshooting + +### Missing syscall stubs when building wolfBoot +If you see link errors for `_read/_write/_close/_lseek/_fstat/_isatty`, wolfBoot includes weak stubs in: + +``` +wolfboot/src/arm_tee_psa_ipc.c +``` + +### PSA symbols missing during Zephyr build +Make sure: +- You built wolfBoot first (for `wc_secure_calls.o`). +- You passed `-DZEPHYR_EXTRA_MODULES=/path/to/wolfboot`. + +### Kconfig warnings about WOLFBOOT_* symbols +The wolfBoot module supplies its own Kconfig (`wolfboot/zephyr/Kconfig`) and will generate `wolfboot.conf` during the sample build when `ZEPHYR_EXTRA_MODULES` is set correctly. diff --git a/zephyr/include/arm_tee_attest_defs.h b/zephyr/include/arm_tee_attest_defs.h new file mode 100644 index 0000000000..e53116d116 --- /dev/null +++ b/zephyr/include/arm_tee_attest_defs.h @@ -0,0 +1,30 @@ +/* arm_tee_attest_defs.h + * + * ARM TEE attestation message IDs. + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + */ + +#ifndef WOLFBOOT_ARM_TEE_ATTEST_DEFS_H_ +#define WOLFBOOT_ARM_TEE_ATTEST_DEFS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Initial Attestation message types that distinguish Attest services. */ +#define ARM_TEE_ATTEST_GET_TOKEN 1001 +#define ARM_TEE_ATTEST_GET_TOKEN_SIZE 1002 + +#ifdef __cplusplus +} +#endif + +#endif /* WOLFBOOT_ARM_TEE_ATTEST_DEFS_H_ */ diff --git a/zephyr/include/arm_tee_builtin_key_ids.h b/zephyr/include/arm_tee_builtin_key_ids.h new file mode 100644 index 0000000000..7cb9e787c1 --- /dev/null +++ b/zephyr/include/arm_tee_builtin_key_ids.h @@ -0,0 +1,32 @@ +/* arm_tee_builtin_key_ids.h + * + * ARM TEE builtin key identifiers. + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + */ + +#ifndef WOLFBOOT_ARM_TEE_BUILTIN_KEY_IDS_H_ +#define WOLFBOOT_ARM_TEE_BUILTIN_KEY_IDS_H_ + +/** + * \brief The persistent key identifiers for builtin keys. + */ +enum arm_tee_builtin_key_id_t { + ARM_TEE_BUILTIN_KEY_ID_MIN = 0x7FFF815Bu, + ARM_TEE_BUILTIN_KEY_ID_HUK, + ARM_TEE_BUILTIN_KEY_ID_IAK, +#ifdef ARM_TEE_PARTITION_DELEGATED_ATTESTATION + ARM_TEE_BUILTIN_KEY_ID_DAK_SEED, +#endif + ARM_TEE_BUILTIN_KEY_ID_PLAT_SPECIFIC_MIN = 0x7FFF816Bu, + ARM_TEE_BUILTIN_KEY_ID_MAX = 0x7FFF817Bu, +}; + +#endif /* WOLFBOOT_ARM_TEE_BUILTIN_KEY_IDS_H_ */ diff --git a/zephyr/include/arm_tee_crypto_defs.h b/zephyr/include/arm_tee_crypto_defs.h new file mode 100644 index 0000000000..7c7e049c15 --- /dev/null +++ b/zephyr/include/arm_tee_crypto_defs.h @@ -0,0 +1,76 @@ +/* arm_tee_crypto_defs.h + * + * ARM TEE crypto pack definitions for PSA IPC dispatch. + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + */ + +#ifndef WOLFBOOT_ARM_TEE_CRYPTO_DEFS_H_ +#define WOLFBOOT_ARM_TEE_CRYPTO_DEFS_H_ + +#include +#include "psa/crypto.h" +#include "psa_manifest/sid.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef PLATFORM_DEFAULT_CRYPTO_KEYS +#include "crypto_keys/arm_tee_builtin_key_ids.h" +#else +#include "arm_tee_builtin_key_ids.h" +#endif + +#define ARM_TEE_CRYPTO_MAX_NONCE_LENGTH (16u) + +struct arm_tee_crypto_aead_pack_input { + uint8_t nonce[ARM_TEE_CRYPTO_MAX_NONCE_LENGTH]; + uint32_t nonce_length; +}; + +struct arm_tee_crypto_pack_iovec { + psa_key_id_t key_id; + psa_algorithm_t alg; + uint32_t op_handle; + uint32_t ad_length; + uint32_t plaintext_length; + struct arm_tee_crypto_aead_pack_input aead_in; + uint16_t function_id; + uint16_t step; + union { + uint32_t capacity; + uint64_t value; + }; +}; + +/* Function ID values aligned with ARM TEE service IDs. */ +#define ARM_TEE_CRYPTO_GENERATE_RANDOM_SID (0x0100U) +#define ARM_TEE_CRYPTO_GET_KEY_ATTRIBUTES_SID (0x0200U) +#define ARM_TEE_CRYPTO_OPEN_KEY_SID (0x0201U) +#define ARM_TEE_CRYPTO_CLOSE_KEY_SID (0x0202U) +#define ARM_TEE_CRYPTO_IMPORT_KEY_SID (0x0203U) +#define ARM_TEE_CRYPTO_DESTROY_KEY_SID (0x0204U) +#define ARM_TEE_CRYPTO_EXPORT_KEY_SID (0x0205U) +#define ARM_TEE_CRYPTO_EXPORT_PUBLIC_KEY_SID (0x0206U) +#define ARM_TEE_CRYPTO_GENERATE_KEY_SID (0x0209U) +#define ARM_TEE_CRYPTO_HASH_COMPUTE_SID (0x0300U) +#define ARM_TEE_CRYPTO_HASH_SETUP_SID (0x0302U) +#define ARM_TEE_CRYPTO_HASH_UPDATE_SID (0x0303U) +#define ARM_TEE_CRYPTO_HASH_FINISH_SID (0x0305U) +#define ARM_TEE_CRYPTO_HASH_ABORT_SID (0x0307U) +#define ARM_TEE_CRYPTO_ASYMMETRIC_SIGN_HASH_SID (0x0702U) +#define ARM_TEE_CRYPTO_ASYMMETRIC_VERIFY_HASH_SID (0x0703U) + +#ifdef __cplusplus +} +#endif + +#endif /* WOLFBOOT_ARM_TEE_CRYPTO_DEFS_H_ */ diff --git a/zephyr/include/arm_tee_ns_interface.h b/zephyr/include/arm_tee_ns_interface.h new file mode 100644 index 0000000000..b13873bbc3 --- /dev/null +++ b/zephyr/include/arm_tee_ns_interface.h @@ -0,0 +1,36 @@ +/* arm_tee_ns_interface.h + * + * ARM TEE NS interface helpers for PSA client dispatch. + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + */ + +#ifndef WOLFBOOT_ARM_TEE_NS_INTERFACE_H_ +#define WOLFBOOT_ARM_TEE_NS_INTERFACE_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int32_t (*arm_tee_veneer_fn)(uint32_t arg0, uint32_t arg1, + uint32_t arg2, uint32_t arg3); + +int32_t arm_tee_ns_interface_dispatch(arm_tee_veneer_fn fn, + uint32_t arg0, uint32_t arg1, uint32_t arg2, uint32_t arg3); + +uint32_t arm_tee_ns_interface_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* WOLFBOOT_ARM_TEE_NS_INTERFACE_H_ */ diff --git a/zephyr/include/arm_tee_ps_defs.h b/zephyr/include/arm_tee_ps_defs.h new file mode 100644 index 0000000000..b1d40be21a --- /dev/null +++ b/zephyr/include/arm_tee_ps_defs.h @@ -0,0 +1,33 @@ +/* arm_tee_ps_defs.h + * + * ARM TEE protected storage message IDs. + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + */ + +#ifndef WOLFBOOT_ARM_TEE_PS_DEFS_H_ +#define WOLFBOOT_ARM_TEE_PS_DEFS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Protected Storage message types that distinguish PS services. */ +#define ARM_TEE_PS_SET 1001 +#define ARM_TEE_PS_GET 1002 +#define ARM_TEE_PS_GET_INFO 1003 +#define ARM_TEE_PS_REMOVE 1004 +#define ARM_TEE_PS_GET_SUPPORT 1005 + +#ifdef __cplusplus +} +#endif + +#endif /* WOLFBOOT_ARM_TEE_PS_DEFS_H_ */ diff --git a/zephyr/include/arm_tee_psa_call_pack.h b/zephyr/include/arm_tee_psa_call_pack.h new file mode 100644 index 0000000000..7d1de6b46c --- /dev/null +++ b/zephyr/include/arm_tee_psa_call_pack.h @@ -0,0 +1,23 @@ +/* arm_tee_psa_call_pack.h + * + * Packing helper for PSA call parameters. + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + */ + +#ifndef WOLFBOOT_ARM_TEE_PSA_CALL_PACK_H_ +#define WOLFBOOT_ARM_TEE_PSA_CALL_PACK_H_ + +#include + +#define PARAM_PACK(type, in_len, out_len) \ + WOLFBOOT_ARM_TEE_PARAM_PACK(type, in_len, out_len) + +#endif /* WOLFBOOT_ARM_TEE_PSA_CALL_PACK_H_ */ diff --git a/zephyr/include/arm_tee_veneers.h b/zephyr/include/arm_tee_veneers.h new file mode 100644 index 0000000000..7e9b15e227 --- /dev/null +++ b/zephyr/include/arm_tee_veneers.h @@ -0,0 +1,38 @@ +/* arm_tee_veneers.h + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + */ + +#ifndef WOLFBOOT_ARM_TEE_VENEERS_H_ +#define WOLFBOOT_ARM_TEE_VENEERS_H_ + +#include +#include "psa/client.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* wolfBoot CMSE veneers (ARM TEE compatible names). */ +uint32_t arm_tee_psa_framework_version_veneer(void); +uint32_t arm_tee_psa_version_veneer(uint32_t sid); +psa_handle_t arm_tee_psa_connect_veneer(uint32_t sid, uint32_t version); +psa_status_t arm_tee_psa_call_veneer(psa_handle_t handle, + uint32_t ctrl_param, + const psa_invec *in_vec, + psa_outvec *out_vec); +void arm_tee_psa_close_veneer(psa_handle_t handle); + +#ifdef __cplusplus +} +#endif + +#endif /* WOLFBOOT_ARM_TEE_VENEERS_H_ */ diff --git a/zephyr/include/crypto_keys/arm_tee_builtin_key_ids.h b/zephyr/include/crypto_keys/arm_tee_builtin_key_ids.h new file mode 100644 index 0000000000..ee9ec36f71 --- /dev/null +++ b/zephyr/include/crypto_keys/arm_tee_builtin_key_ids.h @@ -0,0 +1,20 @@ +/* arm_tee_builtin_key_ids.h + * + * ARM TEE builtin key identifiers (crypto_keys include path). + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + */ + +#ifndef WOLFBOOT_ARM_TEE_BUILTIN_KEY_IDS_CRYPTO_KEYS_H_ +#define WOLFBOOT_ARM_TEE_BUILTIN_KEY_IDS_CRYPTO_KEYS_H_ + +#include "../arm_tee_builtin_key_ids.h" + +#endif /* WOLFBOOT_ARM_TEE_BUILTIN_KEY_IDS_CRYPTO_KEYS_H_ */ diff --git a/zephyr/include/psa/client.h b/zephyr/include/psa/client.h new file mode 100644 index 0000000000..e34489515e --- /dev/null +++ b/zephyr/include/psa/client.h @@ -0,0 +1,66 @@ +/* client.h + * + * Minimal PSA client definitions for wolfBoot Zephyr integration. + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + */ + +#ifndef WOLFBOOT_PSA_CLIENT_H_ +#define WOLFBOOT_PSA_CLIENT_H_ + +#include +#include +#include "psa/error.h" + +#ifndef __PSA_CLIENT_H__ +#define __PSA_CLIENT_H__ +#endif + +typedef int32_t psa_handle_t; +typedef uint32_t rot_size_t; + +#ifndef ROT_SIZE_MAX +#define ROT_SIZE_MAX UINT32_MAX +#endif + +typedef struct psa_invec { + const void *base; + size_t len; +} psa_invec; + +typedef struct psa_outvec { + void *base; + size_t len; +} psa_outvec; + +#ifndef PSA_IPC_CALL +#define PSA_IPC_CALL ((int32_t)1) +#endif + +#ifndef PSA_CALL_TYPE_MIN +#define PSA_CALL_TYPE_MIN (PSA_IPC_CALL) +#endif +#ifndef PSA_CALL_TYPE_MAX +#define PSA_CALL_TYPE_MAX (PSA_IPC_CALL) +#endif + +#ifndef PSA_MAX_IOVEC +#define PSA_MAX_IOVEC 4 +#endif + +#define IOVEC_LEN(x) (sizeof(x) / sizeof((x)[0])) + +psa_handle_t psa_connect(uint32_t sid, uint32_t version); +void psa_close(psa_handle_t handle); +psa_status_t psa_call(psa_handle_t handle, int32_t type, + const psa_invec *in_vec, size_t in_len, + psa_outvec *out_vec, size_t out_len); + +#endif /* WOLFBOOT_PSA_CLIENT_H_ */ diff --git a/zephyr/include/psa/error.h b/zephyr/include/psa/error.h new file mode 100644 index 0000000000..7e897a22e9 --- /dev/null +++ b/zephyr/include/psa/error.h @@ -0,0 +1,83 @@ +/* error.h + * + * Minimal PSA error definitions for wolfBoot Zephyr integration. + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + */ + +#ifndef WOLFBOOT_PSA_ERROR_H_ +#define WOLFBOOT_PSA_ERROR_H_ + +#include + +typedef int32_t psa_status_t; + +#ifndef PSA_SUCCESS +#define PSA_SUCCESS ((psa_status_t)0) +#endif +#ifndef PSA_ERROR_PROGRAMMER_ERROR +#define PSA_ERROR_PROGRAMMER_ERROR ((psa_status_t)-129) +#endif +#ifndef PSA_ERROR_CONNECTION_REFUSED +#define PSA_ERROR_CONNECTION_REFUSED ((psa_status_t)-130) +#endif +#ifndef PSA_ERROR_CONNECTION_BUSY +#define PSA_ERROR_CONNECTION_BUSY ((psa_status_t)-131) +#endif +#ifndef PSA_ERROR_INVALID_ARGUMENT +#define PSA_ERROR_INVALID_ARGUMENT ((psa_status_t)-135) +#endif +#ifndef PSA_ERROR_INVALID_HANDLE +#define PSA_ERROR_INVALID_HANDLE ((psa_status_t)-136) +#endif +#ifndef PSA_ERROR_BAD_STATE +#define PSA_ERROR_BAD_STATE ((psa_status_t)-137) +#endif +#ifndef PSA_ERROR_BUFFER_TOO_SMALL +#define PSA_ERROR_BUFFER_TOO_SMALL ((psa_status_t)-138) +#endif +#ifndef PSA_ERROR_ALREADY_EXISTS +#define PSA_ERROR_ALREADY_EXISTS ((psa_status_t)-139) +#endif +#ifndef PSA_ERROR_DOES_NOT_EXIST +#define PSA_ERROR_DOES_NOT_EXIST ((psa_status_t)-140) +#endif +#ifndef PSA_ERROR_INSUFFICIENT_MEMORY +#define PSA_ERROR_INSUFFICIENT_MEMORY ((psa_status_t)-141) +#endif +#ifndef PSA_ERROR_INSUFFICIENT_STORAGE +#define PSA_ERROR_INSUFFICIENT_STORAGE ((psa_status_t)-142) +#endif +#ifndef PSA_ERROR_INSUFFICIENT_DATA +#define PSA_ERROR_INSUFFICIENT_DATA ((psa_status_t)-143) +#endif +#ifndef PSA_ERROR_SERVICE_FAILURE +#define PSA_ERROR_SERVICE_FAILURE ((psa_status_t)-144) +#endif +#ifndef PSA_ERROR_COMMUNICATION_FAILURE +#define PSA_ERROR_COMMUNICATION_FAILURE ((psa_status_t)-145) +#endif +#ifndef PSA_ERROR_STORAGE_FAILURE +#define PSA_ERROR_STORAGE_FAILURE ((psa_status_t)-146) +#endif +#ifndef PSA_ERROR_HARDWARE_FAILURE +#define PSA_ERROR_HARDWARE_FAILURE ((psa_status_t)-147) +#endif +#ifndef PSA_ERROR_INVALID_SIGNATURE +#define PSA_ERROR_INVALID_SIGNATURE ((psa_status_t)-149) +#endif +#ifndef PSA_ERROR_NOT_SUPPORTED +#define PSA_ERROR_NOT_SUPPORTED ((psa_status_t)-134) +#endif +#ifndef PSA_ERROR_GENERIC_ERROR +#define PSA_ERROR_GENERIC_ERROR ((psa_status_t)-132) +#endif + +#endif /* WOLFBOOT_PSA_ERROR_H_ */ diff --git a/zephyr/include/psa/initial_attestation.h b/zephyr/include/psa/initial_attestation.h new file mode 100644 index 0000000000..d6ecd9941f --- /dev/null +++ b/zephyr/include/psa/initial_attestation.h @@ -0,0 +1,45 @@ +/* initial_attestation.h + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + */ + +#ifndef PSA_INITIAL_ATTESTATION_H +#define PSA_INITIAL_ATTESTATION_H + +#include +#include + +#include "psa/error.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define PSA_INITIAL_ATTEST_API_VERSION_MAJOR (1) +#define PSA_INITIAL_ATTEST_API_VERSION_MINOR (0) + +#define PSA_INITIAL_ATTEST_CHALLENGE_SIZE_32 (32u) +#define PSA_INITIAL_ATTEST_CHALLENGE_SIZE_48 (48u) +#define PSA_INITIAL_ATTEST_CHALLENGE_SIZE_64 (64u) + +psa_status_t psa_initial_attest_get_token(const uint8_t *auth_challenge, + size_t challenge_size, + uint8_t *token_buf, + size_t token_buf_size, + size_t *token_size); + +psa_status_t psa_initial_attest_get_token_size(size_t challenge_size, + size_t *token_size); + +#ifdef __cplusplus +} +#endif + +#endif /* PSA_INITIAL_ATTESTATION_H */ diff --git a/zephyr/include/psa/protected_storage.h b/zephyr/include/psa/protected_storage.h new file mode 100644 index 0000000000..4412c58e1a --- /dev/null +++ b/zephyr/include/psa/protected_storage.h @@ -0,0 +1,49 @@ +/* protected_storage.h + * + * Minimal PSA protected storage definitions for wolfBoot Zephyr integration. + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + */ + +#ifndef WOLFBOOT_PSA_PROTECTED_STORAGE_H_ +#define WOLFBOOT_PSA_PROTECTED_STORAGE_H_ + +#include +#include +#include "psa/error.h" + +typedef uint64_t psa_storage_uid_t; +typedef uint32_t psa_storage_create_flags_t; + +struct psa_storage_info_t { + size_t capacity; + size_t size; + psa_storage_create_flags_t flags; +}; + +#ifndef PSA_STORAGE_FLAG_WRITE_ONCE +#define PSA_STORAGE_FLAG_WRITE_ONCE ((psa_storage_create_flags_t)0x00000001) +#endif + +psa_status_t psa_ps_set(psa_storage_uid_t uid, + size_t data_length, + const void *p_data, + psa_storage_create_flags_t create_flags); +psa_status_t psa_ps_get(psa_storage_uid_t uid, + size_t data_offset, + size_t data_size, + void *p_data, + size_t *p_data_length); +psa_status_t psa_ps_get_info(psa_storage_uid_t uid, + struct psa_storage_info_t *p_info); +psa_status_t psa_ps_remove(psa_storage_uid_t uid); +uint32_t psa_ps_get_support(void); + +#endif /* WOLFBOOT_PSA_PROTECTED_STORAGE_H_ */ diff --git a/zephyr/include/psa_manifest/sid.h b/zephyr/include/psa_manifest/sid.h new file mode 100644 index 0000000000..eab6ece839 --- /dev/null +++ b/zephyr/include/psa_manifest/sid.h @@ -0,0 +1,45 @@ +/* sid.h + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + */ + +#ifndef PSA_MANIFEST_SID_H_ +#define PSA_MANIFEST_SID_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Service IDs and handles aligned with ARM TEE defaults. */ +#define ARM_TEE_ATTESTATION_SERVICE_SID (0x00000020U) +#define ARM_TEE_ATTESTATION_SERVICE_VERSION (1U) +#define ARM_TEE_ATTESTATION_SERVICE_HANDLE (4U) + +#define ARM_TEE_PLATFORM_SERVICE_SID (0x00000040U) +#define ARM_TEE_PLATFORM_SERVICE_VERSION (1U) +#define ARM_TEE_PLATFORM_SERVICE_HANDLE (6U) + +#define ARM_TEE_PROTECTED_STORAGE_SERVICE_SID (0x00000060U) +#define ARM_TEE_PROTECTED_STORAGE_SERVICE_VERSION (1U) +#define ARM_TEE_PROTECTED_STORAGE_SERVICE_HANDLE (2U) + +#define ARM_TEE_INTERNAL_TRUSTED_STORAGE_SERVICE_SID (0x00000070U) +#define ARM_TEE_INTERNAL_TRUSTED_STORAGE_SERVICE_VERSION (1U) +#define ARM_TEE_INTERNAL_TRUSTED_STORAGE_SERVICE_HANDLE (3U) + +#define ARM_TEE_CRYPTO_SID (0x00000080U) +#define ARM_TEE_CRYPTO_VERSION (1U) +#define ARM_TEE_CRYPTO_HANDLE (1U) + +#ifdef __cplusplus +} +#endif + +#endif /* PSA_MANIFEST_SID_H_ */ diff --git a/zephyr/module.yml b/zephyr/module.yml new file mode 100644 index 0000000000..d42db2e104 --- /dev/null +++ b/zephyr/module.yml @@ -0,0 +1,5 @@ +name: wolfboot + +build: + cmake: zephyr + kconfig: zephyr/Kconfig diff --git a/zephyr/patches/0001-wolfboot-tee-driver.patch b/zephyr/patches/0001-wolfboot-tee-driver.patch new file mode 100644 index 0000000000..1562ab887c --- /dev/null +++ b/zephyr/patches/0001-wolfboot-tee-driver.patch @@ -0,0 +1,378 @@ +diff --git a/drivers/tee/CMakeLists.txt b/drivers/tee/CMakeLists.txt +index aaf8924b096..15ca3a06782 100644 +--- a/drivers/tee/CMakeLists.txt ++++ b/drivers/tee/CMakeLists.txt +@@ -1,6 +1,7 @@ + # SPDX-License-Identifier: Apache-2.0 + + add_subdirectory_ifdef(CONFIG_OPTEE optee) ++add_subdirectory_ifdef(CONFIG_WOLFBOOT_TEE wolfboot) + + if(CONFIG_TEE) + zephyr_library() +diff --git a/drivers/tee/Kconfig b/drivers/tee/Kconfig +index dd5acce4e00..24447494d01 100644 +--- a/drivers/tee/Kconfig ++++ b/drivers/tee/Kconfig +@@ -14,5 +14,6 @@ module-str = tee + comment "Device Drivers" + + source "drivers/tee/optee/Kconfig" ++source "drivers/tee/wolfboot/Kconfig" + + endif # TEE +diff --git a/drivers/tee/wolfboot/CMakeLists.txt b/drivers/tee/wolfboot/CMakeLists.txt +new file mode 100644 +index 00000000000..812499ea603 +--- /dev/null ++++ b/drivers/tee/wolfboot/CMakeLists.txt +@@ -0,0 +1,9 @@ ++# SPDX-License-Identifier: GPL-3.0 OR LicenceRef-wolfssl.com-Commercial ++ ++zephyr_library() ++zephyr_library_sources_ifdef(CONFIG_WOLFBOOT_TEE wolfboot.c) ++ ++if(CONFIG_WOLFBOOT_TEE) ++ set_target_properties(zephyr_property_target PROPERTIES SIGNING_SCRIPT ++ ${CMAKE_CURRENT_LIST_DIR}/wolfboot_sign.cmake) ++endif() +diff --git a/drivers/tee/wolfboot/Kconfig b/drivers/tee/wolfboot/Kconfig +new file mode 100644 +index 00000000000..d32faaf8e9b +--- /dev/null ++++ b/drivers/tee/wolfboot/Kconfig +@@ -0,0 +1,9 @@ ++# SPDX-License-Identifier: GPL-3.0 OR LicenceRef-wolfssl.com-Commercial ++ ++config WOLFBOOT_TEE ++ bool "wolfBoot TEE driver" ++ depends on TEE ++ select PSA_CRYPTO_CLIENT ++ select WOLFBOOT ++ help ++ Enable the wolfBoot TEE driver and PSA CMSE glue. +diff --git a/drivers/tee/wolfboot/wolfboot.c b/drivers/tee/wolfboot/wolfboot.c +new file mode 100644 +index 00000000000..ae69692422c +--- /dev/null ++++ b/drivers/tee/wolfboot/wolfboot.c +@@ -0,0 +1,41 @@ ++/* SPDX-License-Identifier: GPL-3.0 OR LicenceRef-wolfssl.com-Commercial */ ++ ++#include ++#include ++#include ++ ++#define DT_DRV_COMPAT wolfboot_tee ++ ++#define TEE_IMPL_ID_WOLFBOOT 0x57424F4F /* "WBOO" */ ++ ++static int wolfboot_get_version(const struct device *dev, struct tee_version_info *info) ++{ ++ ARG_UNUSED(dev); ++ ++ if (!info) { ++ return -EINVAL; ++ } ++ ++ info->impl_id = TEE_IMPL_ID_WOLFBOOT; ++ info->impl_caps = 0; ++ info->gen_caps = TEE_GEN_CAP_GP; ++ ++ return 0; ++} ++ ++static const struct tee_driver_api wolfboot_tee_api = { ++ .get_version = wolfboot_get_version, ++}; ++ ++static int wolfboot_tee_init(const struct device *dev) ++{ ++ ARG_UNUSED(dev); ++ return 0; ++} ++ ++#define WOLFBOOT_TEE_INIT(inst) \ ++ DEVICE_DT_INST_DEFINE(inst, wolfboot_tee_init, NULL, NULL, NULL, \ ++ POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \ ++ &wolfboot_tee_api); ++ ++DT_INST_FOREACH_STATUS_OKAY(WOLFBOOT_TEE_INIT) +diff --git a/drivers/tee/wolfboot/wolfboot_config.py b/drivers/tee/wolfboot/wolfboot_config.py +new file mode 100644 +index 00000000000..b5110089d78 +--- /dev/null ++++ b/drivers/tee/wolfboot/wolfboot_config.py +@@ -0,0 +1,69 @@ ++#!/usr/bin/env python3 ++# SPDX-License-Identifier: GPL-3.0 OR LicenceRef-wolfssl.com-Commercial ++ ++"""Import select values from a wolfBoot .config file.""" ++ ++from __future__ import annotations ++ ++import argparse ++import re ++from pathlib import Path ++ ++ ++_CONFIG_RE = re.compile(r"^([A-Za-z0-9_]+)\??=\s*(.+)$") ++ ++ ++def _parse_value(raw: str) -> int: ++ val = raw.strip() ++ if val.lower().startswith("0x"): ++ return int(val, 16) ++ return int(val, 10) ++ ++ ++def parse_config(path: Path) -> dict[str, str]: ++ data: dict[str, str] = {} ++ for line in path.read_text(encoding="utf-8").splitlines(): ++ line = line.strip() ++ if not line or line.startswith("#"): ++ continue ++ match = _CONFIG_RE.match(line) ++ if not match: ++ continue ++ key, value = match.group(1), match.group(2) ++ data[key] = value.strip() ++ return data ++ ++ ++def emit_conf(path: Path, header_size: int) -> None: ++ content = f"CONFIG_ROM_START_OFFSET=0x{header_size:x}\n" ++ path.write_text(content, encoding="utf-8") ++ ++ ++def main() -> int: ++ parser = argparse.ArgumentParser() ++ parser.add_argument("--config", required=True, type=Path) ++ parser.add_argument("--emit-conf", type=Path) ++ parser.add_argument("--print-header-size", action="store_true") ++ args = parser.parse_args() ++ ++ cfg_path: Path = args.config ++ if not cfg_path.exists(): ++ raise SystemExit(f"wolfBoot config not found: {cfg_path}") ++ ++ cfg = parse_config(cfg_path) ++ if "IMAGE_HEADER_SIZE" not in cfg: ++ raise SystemExit("IMAGE_HEADER_SIZE not found in wolfBoot config") ++ ++ header_size = _parse_value(cfg["IMAGE_HEADER_SIZE"]) ++ ++ if args.emit_conf: ++ emit_conf(args.emit_conf, header_size) ++ ++ if args.print_header_size: ++ print(header_size) ++ ++ return 0 ++ ++ ++if __name__ == "__main__": ++ raise SystemExit(main()) +diff --git a/drivers/tee/wolfboot/wolfboot_sign.cmake b/drivers/tee/wolfboot/wolfboot_sign.cmake +new file mode 100644 +index 00000000000..c5f0017cbea +--- /dev/null ++++ b/drivers/tee/wolfboot/wolfboot_sign.cmake +@@ -0,0 +1,164 @@ ++# SPDX-License-Identifier: GPL-3.0 OR LicenceRef-wolfssl.com-Commercial ++ ++function(wolfboot_parse_version version_string out_var) ++ if(version_string STREQUAL "") ++ set(${out_var} 0 PARENT_SCOPE) ++ return() ++ endif() ++ ++ string(REGEX MATCH "^([0-9]+)\\.([0-9]+)\\.([0-9]+)" _match "${version_string}") ++ if(NOT _match STREQUAL "") ++ string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+).*" "\\1;\\2;\\3" _parts "${version_string}") ++ list(GET _parts 0 _maj) ++ list(GET _parts 1 _min) ++ list(GET _parts 2 _pat) ++ math(EXPR _ver "${_maj} * 10000 + ${_min} * 100 + ${_pat}") ++ set(${out_var} ${_ver} PARENT_SCOPE) ++ return() ++ endif() ++ ++ set(${out_var} 0 PARENT_SCOPE) ++endfunction() ++ ++function(zephyr_wolfboot_tasks) ++ if(NOT DEFINED WOLFBOOT_MODULE_DIR AND DEFINED ZEPHYR_WOLFBOOT_MODULE_DIR) ++ set(WOLFBOOT_MODULE_DIR ${ZEPHYR_WOLFBOOT_MODULE_DIR}) ++ endif() ++ ++ if(NOT CONFIG_BUILD_OUTPUT_BIN) ++ message(FATAL_ERROR "Can't sign images for wolfBoot: CONFIG_BUILD_OUTPUT_BIN is required.") ++ endif() ++ ++ set(keyfile "${CONFIG_WOLFBOOT_SIGNATURE_KEY_FILE}") ++ if("${keyfile}" STREQUAL "") ++ set(keyfile "${CONFIG_MCUBOOT_SIGNATURE_KEY_FILE}") ++ endif() ++ ++ if("${keyfile}" STREQUAL "") ++ message(WARNING "No signing key configured; wolfBoot signing skipped.") ++ return() ++ endif() ++ ++ if(NOT IS_ABSOLUTE "${keyfile}") ++ if(EXISTS "${APPLICATION_CONFIG_DIR}/${keyfile}") ++ set(keyfile "${APPLICATION_CONFIG_DIR}/${keyfile}") ++ elseif(DEFINED WEST_TOPDIR AND EXISTS "${WEST_TOPDIR}/${keyfile}") ++ set(keyfile "${WEST_TOPDIR}/${keyfile}") ++ elseif(DEFINED WOLFBOOT_MODULE_DIR AND EXISTS "${WOLFBOOT_MODULE_DIR}/${keyfile}") ++ set(keyfile "${WOLFBOOT_MODULE_DIR}/${keyfile}") ++ endif() ++ endif() ++ ++ if(NOT EXISTS "${keyfile}") ++ message(FATAL_ERROR "Can't sign images for wolfBoot: can't find key ${keyfile}") ++ endif() ++ set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${keyfile}) ++ ++ if("${CONFIG_WOLFBOOT_SIGN_TOOL}" STREQUAL "") ++ if(DEFINED WOLFBOOT_MODULE_DIR) ++ set(sign_tool "${WOLFBOOT_MODULE_DIR}/tools/keytools/sign") ++ else() ++ message(FATAL_ERROR "Can't sign images for wolfBoot: WOLFBOOT_MODULE_DIR not set.") ++ endif() ++ else() ++ set(sign_tool "${CONFIG_WOLFBOOT_SIGN_TOOL}") ++ endif() ++ ++ if(NOT IS_ABSOLUTE "${sign_tool}") ++ if(DEFINED WOLFBOOT_MODULE_DIR AND EXISTS "${WOLFBOOT_MODULE_DIR}/${sign_tool}") ++ set(sign_tool "${WOLFBOOT_MODULE_DIR}/${sign_tool}") ++ elseif(DEFINED WEST_TOPDIR AND EXISTS "${WEST_TOPDIR}/${sign_tool}") ++ set(sign_tool "${WEST_TOPDIR}/${sign_tool}") ++ endif() ++ endif() ++ ++ if(NOT EXISTS "${sign_tool}") ++ message(FATAL_ERROR "Can't sign images for wolfBoot: can't find sign tool ${sign_tool}") ++ endif() ++ ++ set(wolfboot_header_size "") ++ if(DEFINED WOLFBOOT_MODULE_DIR) ++ set(wolfboot_config "${WOLFBOOT_MODULE_DIR}/.config") ++ if(EXISTS "${wolfboot_config}") ++ set(wolfboot_cfg_tool ++ "${CMAKE_CURRENT_LIST_DIR}/wolfboot_config.py") ++ execute_process( ++ COMMAND ${PYTHON_EXECUTABLE} ${wolfboot_cfg_tool} ++ --config ${wolfboot_config} ++ --emit-conf ${ZEPHYR_BINARY_DIR}/wolfboot.conf ++ --print-header-size ++ OUTPUT_VARIABLE wolfboot_header_size ++ OUTPUT_STRIP_TRAILING_WHITESPACE ++ RESULT_VARIABLE wolfboot_cfg_result ++ ) ++ if(NOT wolfboot_cfg_result EQUAL 0) ++ message(FATAL_ERROR "Failed to parse wolfBoot config at ${wolfboot_config}") ++ endif() ++ set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ++ ${wolfboot_config}) ++ set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ++ ${wolfboot_cfg_tool}) ++ else() ++ message(WARNING "wolfBoot config not found at ${wolfboot_config}; using Zephyr defaults.") ++ endif() ++ endif() ++ ++ set(ver_int "${CONFIG_WOLFBOOT_SIGN_VERSION}") ++ if("${ver_int}" STREQUAL "1" AND NOT "${CONFIG_MCUBOOT_IMGTOOL_SIGN_VERSION}" STREQUAL "") ++ wolfboot_parse_version("${CONFIG_MCUBOOT_IMGTOOL_SIGN_VERSION}" ver_int) ++ if(ver_int EQUAL 0) ++ set(ver_int "${CONFIG_WOLFBOOT_SIGN_VERSION}") ++ endif() ++ endif() ++ ++ set(output ${ZEPHYR_BINARY_DIR}/${KERNEL_NAME}) ++ set(signed_bin ${output}.signed.bin) ++ set(signed_hex ${output}.signed.hex) ++ set(payload_bin ${output}.payload.bin) ++ set(sign_input ${output}.bin) ++ set(signed_base ${output}) ++ set(sign_input ${output}.bin) ++ ++ set(sign_args "--${CONFIG_WOLFBOOT_SIGN_ALG}" "--${CONFIG_WOLFBOOT_SIGN_HASH}") ++ ++ set(sign_env_cmd ${CMAKE_COMMAND} -E env) ++ if(NOT "${wolfboot_header_size}" STREQUAL "") ++ list(APPEND sign_env_cmd IMAGE_HEADER_SIZE=${wolfboot_header_size}) ++ endif() ++ ++ if(DEFINED CONFIG_ROM_START_OFFSET AND NOT "${CONFIG_ROM_START_OFFSET}" STREQUAL "0") ++ set(strip_tool "${CMAKE_CURRENT_LIST_DIR}/wolfboot_strip.py") ++ set(sign_input ${payload_bin}) ++ set(signed_base ${output}.payload) ++ set_property(GLOBAL APPEND PROPERTY extra_post_build_commands ++ COMMAND ${PYTHON_EXECUTABLE} ${strip_tool} ++ --input ${output}.bin ++ --output ${payload_bin} ++ --strip ${CONFIG_ROM_START_OFFSET}) ++ set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ++ ${strip_tool}) ++ endif() ++ ++ set_property(GLOBAL APPEND PROPERTY extra_post_build_commands ++ COMMAND ${sign_env_cmd} ${sign_tool} ${sign_args} ++ ${sign_input} ${keyfile} ${ver_int} ++ COMMAND ${CMAKE_COMMAND} -E copy ++ ${signed_base}_v${ver_int}_signed.bin ${signed_bin}) ++ ++ if(CONFIG_BUILD_OUTPUT_BIN) ++ set(BYPRODUCT_KERNEL_SIGNED_BIN_NAME "${signed_bin}" ++ CACHE FILEPATH "Signed kernel bin file" FORCE) ++ set_property(GLOBAL APPEND PROPERTY extra_post_build_byproducts ${signed_bin}) ++ endif() ++ ++ if(CONFIG_BUILD_OUTPUT_HEX) ++ set(BYPRODUCT_KERNEL_SIGNED_HEX_NAME "${signed_hex}" ++ CACHE FILEPATH "Signed kernel hex file" FORCE) ++ set_property(GLOBAL APPEND PROPERTY extra_post_build_commands ++ COMMAND ${CMAKE_OBJCOPY} -I binary -O ihex ++ ${signed_bin} ${signed_hex}) ++ set_property(GLOBAL APPEND PROPERTY extra_post_build_byproducts ${signed_hex}) ++ endif() ++endfunction() ++ ++zephyr_wolfboot_tasks() +diff --git a/drivers/tee/wolfboot/wolfboot_strip.py b/drivers/tee/wolfboot/wolfboot_strip.py +new file mode 100644 +index 00000000000..6411412767c +--- /dev/null ++++ b/drivers/tee/wolfboot/wolfboot_strip.py +@@ -0,0 +1,27 @@ ++#!/usr/bin/env python3 ++# SPDX-License-Identifier: GPL-3.0 OR LicenceRef-wolfssl.com-Commercial ++ ++"""Strip a fixed number of bytes from the start of a binary image.""" ++ ++from __future__ import annotations ++ ++import argparse ++from pathlib import Path ++ ++ ++def main() -> int: ++ parser = argparse.ArgumentParser() ++ parser.add_argument("--input", required=True, type=Path) ++ parser.add_argument("--output", required=True, type=Path) ++ parser.add_argument("--strip", required=True, type=lambda x: int(x, 0)) ++ args = parser.parse_args() ++ ++ data = args.input.read_bytes() ++ if args.strip < 0 or args.strip > len(data): ++ raise SystemExit(f"strip size {args.strip} exceeds input size {len(data)}") ++ args.output.write_bytes(data[args.strip:]) ++ return 0 ++ ++ ++if __name__ == "__main__": ++ raise SystemExit(main()) diff --git a/zephyr/patches/0002-wolfboot-tee-dt-binding.patch b/zephyr/patches/0002-wolfboot-tee-dt-binding.patch new file mode 100644 index 0000000000..ffb2886e06 --- /dev/null +++ b/zephyr/patches/0002-wolfboot-tee-dt-binding.patch @@ -0,0 +1,12 @@ +diff --git a/dts/bindings/vendor-prefixes.txt b/dts/bindings/vendor-prefixes.txt +index 7e7d27b5331..c36bbcd36e5 100644 +--- a/dts/bindings/vendor-prefixes.txt ++++ b/dts/bindings/vendor-prefixes.txt +@@ -769,6 +769,7 @@ wits Shenzhen Merrii Technology Co., Ltd. (WITS) + witte Witte Technology + wiznet WIZnet Co., Ltd. + wlf Wolfson Microelectronics ++wolfssl wolfSSL Inc. + wm Wondermedia Technologies, Inc. + wnc Wistron NeWeb Corporation + wobo Wobo diff --git a/zephyr/patches/0003-wolfboot-sample.patch b/zephyr/patches/0003-wolfboot-sample.patch new file mode 100644 index 0000000000..b52fab7e94 --- /dev/null +++ b/zephyr/patches/0003-wolfboot-sample.patch @@ -0,0 +1,2153 @@ +diff --git a/samples/wolfboot_integration/psa_crypto/app.overlay b/samples/wolfboot_integration/psa_crypto/app.overlay +new file mode 100644 +index 00000000000..1329dd33c4d +--- /dev/null ++++ b/samples/wolfboot_integration/psa_crypto/app.overlay +@@ -0,0 +1,10 @@ ++/* ++ * SPDX-License-Identifier: GPL-3.0 OR LicenceRef-wolfssl.com-Commercial ++ */ ++ ++/ { ++ wolfboot_tee: wolfboot_tee { ++ compatible = "wolfssl,tee"; ++ status = "okay"; ++ }; ++}; +diff --git a/samples/wolfboot_integration/psa_crypto/boards/max32657evkit_max32657_ns.conf b/samples/wolfboot_integration/psa_crypto/boards/max32657evkit_max32657_ns.conf +new file mode 100644 +index 00000000000..85ae2978c4c +--- /dev/null ++++ b/samples/wolfboot_integration/psa_crypto/boards/max32657evkit_max32657_ns.conf +@@ -0,0 +1,3 @@ ++# SPDX-License-Identifier: GPL-3.0 OR LicenceRef-wolfssl.com-Commercial ++# ++# Board-specific wolfBoot settings can be added here if needed. +diff --git a/samples/wolfboot_integration/psa_crypto/boards/nrf9160dk_nrf9160_ns.overlay b/samples/wolfboot_integration/psa_crypto/boards/nrf9160dk_nrf9160_ns.overlay +new file mode 100644 +index 00000000000..f1cc3115fe8 +--- /dev/null ++++ b/samples/wolfboot_integration/psa_crypto/boards/nrf9160dk_nrf9160_ns.overlay +@@ -0,0 +1,5 @@ ++/* ++ * SPDX-License-Identifier: GPL-3.0 OR LicenceRef-wolfssl.com-Commercial ++ */ ++ ++/* Board-specific wolfBoot overrides can be added here if needed. */ +diff --git a/samples/wolfboot_integration/psa_crypto/CMakeLists.txt b/samples/wolfboot_integration/psa_crypto/CMakeLists.txt +new file mode 100644 +index 00000000000..41f26064a0b +--- /dev/null ++++ b/samples/wolfboot_integration/psa_crypto/CMakeLists.txt +@@ -0,0 +1,83 @@ ++# SPDX-License-Identifier: GPL-3.0 OR LicenceRef-wolfssl.com-Commercial ++ ++cmake_minimum_required(VERSION 3.20.0) ++ ++if(DEFINED ZEPHYR_EXTRA_MODULES) ++ foreach(extra_module ${ZEPHYR_EXTRA_MODULES}) ++ if(EXISTS "${extra_module}/.config") ++ set(WOLFBOOT_MODULE_DIR "${extra_module}") ++ break() ++ endif() ++ endforeach() ++endif() ++ ++if(DEFINED WOLFBOOT_MODULE_DIR AND EXISTS "${WOLFBOOT_MODULE_DIR}/.config") ++ if(DEFINED WEST_PYTHON) ++ set(WOLFBOOT_PYTHON "${WEST_PYTHON}") ++ set(Python3_EXECUTABLE "${WOLFBOOT_PYTHON}" CACHE FILEPATH "" FORCE) ++ elseif(DEFINED ZEPHYR_PYTHON) ++ set(WOLFBOOT_PYTHON "${ZEPHYR_PYTHON}") ++ set(Python3_EXECUTABLE "${WOLFBOOT_PYTHON}" CACHE FILEPATH "" FORCE) ++ elseif(DEFINED ENV{WEST_PYTHON}) ++ set(WOLFBOOT_PYTHON "$ENV{WEST_PYTHON}") ++ set(Python3_EXECUTABLE "${WOLFBOOT_PYTHON}" CACHE FILEPATH "" FORCE) ++ elseif(DEFINED ENV{ZEPHYR_PYTHON}) ++ set(WOLFBOOT_PYTHON "$ENV{ZEPHYR_PYTHON}") ++ set(Python3_EXECUTABLE "${WOLFBOOT_PYTHON}" CACHE FILEPATH "" FORCE) ++ else() ++ find_package(Python3 REQUIRED COMPONENTS Interpreter) ++ set(WOLFBOOT_PYTHON "${Python3_EXECUTABLE}") ++ endif() ++ get_filename_component(wolfboot_cfg_tool ++ "${CMAKE_CURRENT_LIST_DIR}/../../../drivers/tee/wolfboot/wolfboot_config.py" ++ REALPATH) ++ set(wolfboot_conf "${CMAKE_BINARY_DIR}/wolfboot.conf") ++ execute_process( ++ COMMAND ${WOLFBOOT_PYTHON} ${wolfboot_cfg_tool} ++ --config ${WOLFBOOT_MODULE_DIR}/.config ++ --emit-conf ${wolfboot_conf} ++ RESULT_VARIABLE wolfboot_cfg_result ++ ) ++ if(NOT wolfboot_cfg_result EQUAL 0) ++ message(FATAL_ERROR "Failed to parse wolfBoot config at ${WOLFBOOT_MODULE_DIR}/.config") ++ endif() ++ set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ++ ${WOLFBOOT_MODULE_DIR}/.config ${wolfboot_cfg_tool}) ++ if(DEFINED EXTRA_CONF_FILE AND NOT "${EXTRA_CONF_FILE}" STREQUAL "") ++ set(EXTRA_CONF_FILE "${EXTRA_CONF_FILE};${wolfboot_conf}" CACHE STRING "" FORCE) ++ else() ++ set(EXTRA_CONF_FILE "${wolfboot_conf}" CACHE STRING "" FORCE) ++ endif() ++endif() ++ ++find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) ++ ++project(wolfboot_psa_crypto) ++ ++# Source files in this sample ++target_sources(app PRIVATE src/main.c) ++target_sources(app PRIVATE src/psa_attestation.c) ++target_sources(app PRIVATE src/psa_crypto.c) ++target_sources(app PRIVATE src/shell.c) ++target_sources(app PRIVATE src/util_app_cfg.c) ++target_sources(app PRIVATE src/util_app_log.c) ++target_sources(app PRIVATE src/util_sformat.c) ++ ++if(DEFINED ZEPHYR_TRUSTED_FIRMWARE_M_MODULE_DIR) ++ target_include_directories(app PRIVATE ++ ${ZEPHYR_TRUSTED_FIRMWARE_M_MODULE_DIR}/interface/include ++ ) ++endif() ++ ++if(DEFINED ZEPHYR_EXTRA_MODULES) ++ foreach(extra_module ${ZEPHYR_EXTRA_MODULES}) ++ if(EXISTS "${extra_module}/zephyr/include/psa/initial_attestation.h") ++ target_include_directories(app PRIVATE ++ ${extra_module}/zephyr/include ++ ) ++ break() ++ endif() ++ endforeach() ++endif() ++ ++zephyr_include_directories(${APPLICATION_SOURCE_DIR}/src/configs) +diff --git a/samples/wolfboot_integration/psa_crypto/Kconfig b/samples/wolfboot_integration/psa_crypto/Kconfig +new file mode 100644 +index 00000000000..c9035821c22 +--- /dev/null ++++ b/samples/wolfboot_integration/psa_crypto/Kconfig +@@ -0,0 +1,49 @@ ++# Private config options for PSA Crypto application ++ ++# Copyright (c) 2023 Linaro ++# SPDX-License-Identifier: GPL-3.0 OR LicenceRef-wolfssl.com-Commercial ++ ++mainmenu "wolfBoot PSA Crypto sample application" ++ ++menu "Application configuration" ++ ++module = PSA ++module-str = psa ++source "subsys/logging/Kconfig.template.log_config" ++ ++endmenu ++ ++config PSA_SHELL ++ bool "The 'psa' shell command" ++ depends on SHELL ++ help ++ Enabling this option will make the 'psa' shell command available. ++ ++config PSA_IMPORT_KEY ++ bool "Support for importing private key data" ++ help ++ Enable support for importing a pre-generated or randomly generated ++ private key using PSA APIs and PRIVATE_KEY_STATIC or ++ PRIVATE_KEY_RANDOM. ++ ++choice ++ prompt "Private Key" ++ default PRIVATE_KEY_RANDOM ++ ++config PRIVATE_KEY_STATIC ++ bool "Static" ++ depends on PSA_IMPORT_KEY ++ help ++ A static key value will be used for the elliptic curve 'secp256r1' ++ private key. ++ ++config PRIVATE_KEY_RANDOM ++ bool "Random" ++ depends on PSA_IMPORT_KEY ++ help ++ A randomly generated value will be used for the elliptic curve ++ 'secp256r1' private key. ++ ++endchoice ++ ++source "Kconfig.zephyr" +diff --git a/samples/wolfboot_integration/psa_crypto/prj.conf b/samples/wolfboot_integration/psa_crypto/prj.conf +new file mode 100644 +index 00000000000..93bab465806 +--- /dev/null ++++ b/samples/wolfboot_integration/psa_crypto/prj.conf +@@ -0,0 +1,64 @@ ++CONFIG_LOG=y ++CONFIG_LOG_RUNTIME_FILTERING=y ++CONFIG_LOG_BUFFER_SIZE=2048 ++CONFIG_LOG_PROCESS_TRIGGER_THRESHOLD=0 ++CONFIG_LOG_DEFAULT_LEVEL=3 ++CONFIG_NO_OPTIMIZATIONS=y ++CONFIG_BUILD_OUTPUT_BIN=y ++CONFIG_FPU=n ++ ++ ++#CONFIG_SHELL=n ++#CONFIG_SHELL_HISTORY=y ++#CONFIG_SHELL_VT100_COLORS=y ++#CONFIG_SHELL_CMDS=n ++#CONFIG_PSA_SHELL=y ++ ++CONFIG_TEE=y ++CONFIG_WOLFBOOT_TEE=y ++CONFIG_BUILD_WITH_TFM=n ++CONFIG_PSA_CRYPTO_PROVIDER_CUSTOM=y ++ ++# The Zephyr CMSIS emulation assumes that ticks are ms, currently ++CONFIG_SYS_CLOCK_TICKS_PER_SEC=1000 ++ ++CONFIG_MAIN_STACK_SIZE=8192 ++CONFIG_HEAP_MEM_POOL_SIZE=4096 ++ ++# Mbed TLS ++CONFIG_MBEDTLS=y ++CONFIG_MBEDTLS_BUILTIN=y ++CONFIG_MBEDTLS_ENABLE_HEAP=y ++CONFIG_MBEDTLS_HEAP_SIZE=32768 ++CONFIG_MBEDTLS_USER_CONFIG_ENABLE=y ++CONFIG_MBEDTLS_USER_CONFIG_FILE="config_mbedtls.h" ++CONFIG_MBEDTLS_USE_PSA_CRYPTO=y ++ ++# Added because build was failing ++# Needed for PK + PSA and CSR/write paths ++CONFIG_MBEDTLS_ASN1_PARSE_C=y ++ ++CONFIG_PSA_CRYPTO=y ++CONFIG_PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY=y ++CONFIG_PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_IMPORT=y ++CONFIG_PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_EXPORT=y ++CONFIG_PSA_WANT_ECC_SECP_R1_256=y ++CONFIG_PSA_WANT_ALG_ECDSA=y ++CONFIG_PSA_WANT_KEY_TYPE_RSA_PUBLIC_KEY=n ++CONFIG_PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_IMPORT=n ++CONFIG_PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_EXPORT=n ++CONFIG_PSA_WANT_ALG_RSA_PKCS1V15_SIGN=n ++CONFIG_PSA_WANT_ALG_RSA_PSS=y ++CONFIG_PSA_WANT_ALG_SHA_256=y ++CONFIG_MBEDTLS_PK_WRITE_C=y ++CONFIG_MBEDTLS_KEY_EXCHANGE_RSA_ENABLED=n ++ ++# JSON ++CONFIG_JSON_LIBRARY=y ++ ++CONFIG_WOLFBOOT_SIGN_ALG="ecc256" ++CONFIG_WOLFBOOT_SIGN_HASH="sha256" ++CONFIG_WOLFBOOT_SIGN_VERSION=1 ++CONFIG_WOLFBOOT_SIGNATURE_KEY_FILE="wolfboot_signing_private_key.der" ++ ++CONFIG_REQUIRES_FULL_LIBC=y +diff --git a/samples/wolfboot_integration/psa_crypto/README.rst b/samples/wolfboot_integration/psa_crypto/README.rst +new file mode 100644 +index 00000000000..684430b717a +--- /dev/null ++++ b/samples/wolfboot_integration/psa_crypto/README.rst +@@ -0,0 +1,73 @@ ++.. zephyr:code-sample:: wolfboot_psa_crypto ++ :name: wolfBoot PSA crypto ++ ++ Use the PSA Crypto API for cryptography and device certificate signing requests via wolfBoot CMSE gateways. ++ ++Overview ++******** ++This wolfBoot integration example demonstrates how to use the PSA crypto API in ++Zephyr for cryptography and device certificate signing request. The secure ++processing environment is provided by wolfBoot + wolfPSA, with Zephyr running ++in the non-secure processing environment. PSA calls are routed through CMSE ++gateways compatible with the TF-M client API. ++ ++Key Files ++********* ++ ++``psa_crypto.c`` ++================ ++ ++Demonstrates hash, sign/verify workflow: ++ ++- Generate/import a persistent key: secp256r1 (usage: ecdsa-with-SHA256) ++- Display the public key based on the private key data above ++- Calculate the SHA256 hash of a payload ++- Sign the hash with the persistent key ++- Verify the signature using the public key ++- Destroy the key ++ ++Also demonstrates device certificate signing request (CSR) workflow: ++ ++- Generate/import a persistent key: secp256r1 (usage: ecdsa-with-SHA256) ++- Set subject name in device CSR ++- Generate device CSR in PEM format ++- Encode device CSR as JSON ++ ++Importing/generating the persistent key is based on config option ++``PSA_IMPORT_KEY``. When ``PSA_IMPORT_KEY`` is enabled, ++the key data can be static if ``PRIVATE_KEY_STATIC`` is set or key data ++is generated using ``psa_generate_random`` if ``PRIVATE_KEY_RANDOM`` ++is set. ++ ++``psa_attestation.c`` ++===================== ++ ++Demonstrates how to request an initial attestation token (IAT) from the secure ++processing environment if supported by wolfBoot. ++ ++Building and Running ++******************** ++ ++This project outputs startup status and info to the console. Build it for a ++non-secure target and make sure wolfBoot is available as an extra module. ++ ++Using ``west`` with ZEPHYR_EXTRA_MODULES: ++ ++.. code-block:: bash ++ ++ cd ++ west build -p -b /ns samples/wolfboot_integration/psa_crypto \ ++ -- -DZEPHYR_EXTRA_MODULES=/wolfboot ++ ++The non-secure application expects CMSE gateways exported by the secure image. ++Build and flash the wolfBoot secure image for your target before running this ++sample. ++ ++Signing ++======= ++ ++When ``CONFIG_WOLFBOOT_TEE`` is enabled, Zephyr's signing step uses wolfBoot's ++sign tool (C implementation). Configure the signing key with ++``CONFIG_WOLFBOOT_SIGNATURE_KEY_FILE`` (or reuse ++``CONFIG_MCUBOOT_SIGNATURE_KEY_FILE``) and set algorithm/hash via ++``CONFIG_WOLFBOOT_SIGN_ALG`` and ``CONFIG_WOLFBOOT_SIGN_HASH``. +diff --git a/samples/wolfboot_integration/psa_crypto/sample.yaml b/samples/wolfboot_integration/psa_crypto/sample.yaml +new file mode 100644 +index 00000000000..874fa555f80 +--- /dev/null ++++ b/samples/wolfboot_integration/psa_crypto/sample.yaml +@@ -0,0 +1,35 @@ ++sample: ++ description: PSA crypto example using wolfBoot CMSE gateways for PSA calls. ++ name: wolfBoot PSA crypto example ++tests: ++ sample.wolfboot_psa_crypto: ++ tags: ++ - introduction ++ - wolfboot ++ - crypto ++ - csr ++ platform_allow: ++ - mps2/an521/cpu0/ns ++ - v2m_musca_s1/musca_s1/ns ++ - nrf5340dk/nrf5340/cpuapp/ns ++ - nrf9160dk/nrf9160/ns ++ - stm32l562e_dk/stm32l562xx/ns ++ - bl5340_dvk/nrf5340/cpuapp/ns ++ - max32657evkit/max32657/ns ++ - stm32h573i_dk/stm32h573xx/ns ++ integration_platforms: ++ - mps2/an521/cpu0/ns ++ harness: console ++ harness_config: ++ type: multi_line ++ regex: ++ - "System IAT size is: (.*)" ++ - "Requesting IAT with (.*) byte challenge." ++ - "IAT data received: (.*)" ++ - "Retrieving public key for key #1" ++ - "Signature verified" ++ - "Destroyed persistent key #1" ++ - "Generating 256 bytes of random data." ++ - "Create device Certificate Signing Request completed" ++ - "BEGIN CERTIFICATE REQUEST" ++ - "END CERTIFICATE REQUEST" +diff --git a/samples/wolfboot_integration/psa_crypto/src/configs/config_mbedtls.h b/samples/wolfboot_integration/psa_crypto/src/configs/config_mbedtls.h +new file mode 100644 +index 00000000000..17f1abfe7e5 +--- /dev/null ++++ b/samples/wolfboot_integration/psa_crypto/src/configs/config_mbedtls.h +@@ -0,0 +1,17 @@ ++/* ++ * Copyright (c) 2023 Linaro Limited ++ * ++ * SPDX-License-Identifier: GPL-3.0 OR LicenceRef-wolfssl.com-Commercial ++ */ ++ ++#define MBEDTLS_X509_CSR_WRITE_C ++#define MBEDTLS_X509_CREATE_C ++#define MBEDTLS_PEM_WRITE_C ++#define MBEDTLS_BASE64_C ++ ++#define MBEDTLS_ASN1_WRITE_C ++#define MBEDTLS_OID_C ++#define MBEDTLS_PK_PARSE_C ++#define MBEDTLS_ASN1_PARSE_C ++ ++#define MCUBOOT_LOG_LEVEL 0 +diff --git a/samples/wolfboot_integration/psa_crypto/src/configs/config_tfm.h b/samples/wolfboot_integration/psa_crypto/src/configs/config_tfm.h +new file mode 100644 +index 00000000000..38b24d6e78c +--- /dev/null ++++ b/samples/wolfboot_integration/psa_crypto/src/configs/config_tfm.h +@@ -0,0 +1,10 @@ ++/* ++ * Copyright (c) 2024 Nordic Semiconductor ++ * ++ * SPDX-License-Identifier: Apache-2.0 ++ */ ++ ++/* In TF-M the default value of CRYPTO_ENGINE_BUF_SIZE is 0x2080. ++ * It causes insufficient memory failure while verifying signature. ++ */ ++#define CRYPTO_ENGINE_BUF_SIZE 0x2400 +diff --git a/samples/wolfboot_integration/psa_crypto/src/main.c b/samples/wolfboot_integration/psa_crypto/src/main.c +new file mode 100644 +index 00000000000..1fd0888d8ce +--- /dev/null ++++ b/samples/wolfboot_integration/psa_crypto/src/main.c +@@ -0,0 +1,50 @@ ++/* ++ * Copyright (c) 2019,2020 Linaro Limited ++ * ++ * SPDX-License-Identifier: Apache-2.0 ++ */ ++ ++#include ++#include ++#include ++ ++#include "tfm_ns_interface.h" ++#include "psa_attestation.h" ++#include "psa_crypto.h" ++#include "util_app_cfg.h" ++#include "util_app_log.h" ++#include "util_sformat.h" ++ ++/** Declare a reference to the application logging interface. */ ++LOG_MODULE_DECLARE(app, CONFIG_LOG_DEFAULT_LEVEL); ++ ++/* Create an instance of the system config struct for the application. */ ++static struct cfg_data cfg; ++ ++int main(void) ++{ ++ /* Initialise the logger subsys and dump the current buffer. */ ++ log_init(); ++ ++ /* Load app config struct from secure storage (create if missing). */ ++ if (cfg_load_data(&cfg)) { ++ LOG_ERR("Error loading/generating app config data in SS."); ++ } ++ ++ /* Get the entity attestation token (requires ~1kB stack memory!). */ ++ att_test(); ++ ++ /* Crypto tests */ ++ crp_test(); ++ crp_test_rng(); ++ ++ /* Generate Certificate Signing Request using Mbed TLS */ ++ crp_generate_csr(); ++ ++ /* Dump any queued log messages, and wait for system events. */ ++ al_dump_log(); ++ ++ LOG_INF("Done."); ++ ++ return 0; ++} +diff --git a/samples/wolfboot_integration/psa_crypto/src/psa_attestation.c b/samples/wolfboot_integration/psa_crypto/src/psa_attestation.c +new file mode 100644 +index 00000000000..0bc40ded4ec +--- /dev/null ++++ b/samples/wolfboot_integration/psa_crypto/src/psa_attestation.c +@@ -0,0 +1,144 @@ ++/* ++ * Copyright (c) 2019,2020 Linaro Limited ++ * ++ * SPDX-License-Identifier: Apache-2.0 ++ */ ++ ++#include ++#include ++#include ++ ++#include "psa/initial_attestation.h" ++#include "psa_attestation.h" ++#include "util_app_log.h" ++#include "util_sformat.h" ++ ++LOG_MODULE_DECLARE(app, CONFIG_LOG_DEFAULT_LEVEL); ++ ++psa_status_t att_get_pub_key(void) ++{ ++ psa_status_t err = PSA_SUCCESS; ++ ++ /* TODO: How to retrieve this?!? */ ++ ++ /* Log any eventual errors via app_log */ ++ return err ? al_psa_status(err, __func__) : err; ++} ++ ++psa_status_t att_get_iat(uint8_t *ch_buffer, uint32_t ch_sz, ++ uint8_t *token_buffer, uint32_t *token_sz) ++{ ++ psa_status_t err = PSA_SUCCESS; ++ uint32_t sys_token_sz; ++ size_t token_buf_size = ATT_MAX_TOKEN_SIZE; ++ ++ ++ /* Call with bigger challenge object than allowed */ ++ ++ /* ++ * First determine how large the token is on this system. ++ * We don't need to compare with the size of ATT_MAX_TOKEN_SIZE here ++ * since a check will be made in 'psa_initial_attest_get_token' and the ++ * error return code will indicate a mismatch. ++ */ ++ switch (ch_sz) { ++ case 32: ++ err = psa_initial_attest_get_token( ++ ch_buffer, ++ PSA_INITIAL_ATTEST_CHALLENGE_SIZE_32, ++ token_buffer, ++ token_buf_size, ++ &sys_token_sz); ++ break; ++ case 48: ++ err = psa_initial_attest_get_token( ++ ch_buffer, ++ PSA_INITIAL_ATTEST_CHALLENGE_SIZE_48, ++ token_buffer, ++ token_buf_size, ++ &sys_token_sz); ++ break; ++ case 64: ++ err = psa_initial_attest_get_token( ++ ch_buffer, ++ PSA_INITIAL_ATTEST_CHALLENGE_SIZE_64, ++ token_buffer, ++ token_buf_size, ++ &sys_token_sz); ++ break; ++ default: ++ err = -EINVAL; ++ break; ++ } ++ if (err) { ++ goto err; ++ } ++ ++ LOG_INF("att: System IAT size is: %u bytes.", sys_token_sz); ++ ++ /* Request the initial attestation token w/the challenge data. */ ++ LOG_INF("att: Requesting IAT with %u byte challenge.", ch_sz); ++ err = psa_initial_attest_get_token( ++ ch_buffer, /* Challenge/nonce input buffer. */ ++ ch_sz, /* Challenge size (32, 48 or 64). */ ++ token_buffer, /* Token output buffer. */ ++ token_buf_size, ++ token_sz /* Post exec output token size. */ ++ ); ++ LOG_INF("att: IAT data received: %u bytes.", *token_sz); ++ ++err: ++ /* Log any eventual errors via app_log */ ++ return err ? al_psa_status(err, __func__) : err; ++} ++ ++psa_status_t att_test(void) ++{ ++ psa_status_t err = PSA_SUCCESS; ++ ++ /* 64-byte nonce/challenge, encrypted using the default public key; ++ * ++ * 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF ++ * 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF ++ * 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF ++ * 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF ++ */ ++ uint32_t nonce_sz = 64; ++ uint8_t nonce_buf[ATT_MAX_TOKEN_SIZE] = { ++ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, ++ 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, ++ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, ++ 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, ++ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, ++ 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, ++ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, ++ 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, ++ 0 ++ }; ++ ++ /* IAT response buffer. */ ++ uint32_t iat_sz = ATT_MAX_TOKEN_SIZE; ++ uint8_t iat_buf[ATT_MAX_TOKEN_SIZE] = { 0 }; ++ ++ /* String format output config. */ ++ struct sf_hex_tbl_fmt fmt = { ++ .ascii = true, ++ .addr_label = true, ++ .addr = 0 ++ }; ++ ++ /* Request the IAT from the initial attestation service. */ ++ err = att_get_iat(nonce_buf, nonce_sz, iat_buf, &iat_sz); ++ if (err) { ++ goto err; ++ } ++ ++ /* Display queued log messages before dumping the IAT. */ ++ al_dump_log(); ++ ++ /* Dump the IAT for debug purposes. */ ++ sf_hex_tabulate_16(&fmt, iat_buf, (size_t)iat_sz); ++ ++err: ++ return err; ++} +diff --git a/samples/wolfboot_integration/psa_crypto/src/psa_attestation.h b/samples/wolfboot_integration/psa_crypto/src/psa_attestation.h +new file mode 100644 +index 00000000000..1164dda8ad4 +--- /dev/null ++++ b/samples/wolfboot_integration/psa_crypto/src/psa_attestation.h +@@ -0,0 +1,65 @@ ++/* ++ * Copyright (c) 2019,2020 Linaro Limited ++ * ++ * SPDX-License-Identifier: Apache-2.0 ++ */ ++ ++#include ++ ++#include "psa/error.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/** Maximum buffer size for an initial attestation token instance. */ ++#define ATT_MAX_TOKEN_SIZE (0x240) ++ ++/** ++ * @brief Gets the public key portion of the attestation service's securely ++ * stored key pair. This public key can be provided to external ++ * verification services for device verification purposes. ++ * ++ * @return Returns error code as specified in \ref psa_status_t ++ */ ++psa_status_t att_get_pub_key(void); ++ ++/** ++ * @brief Gets an initial attestation token (IAT) from the TF-M secure ++ * processing environment (SPE). This data will be provided in CBOR ++ * format and is encrypted using the private key held on the SPE. ++ * ++ * The initial attestation token (IAT) is composed of a series of 'claims' or ++ * data points used to uniquely identify this device to an external ++ * verification entity (the IAT consumer). ++ * ++ * The generated IAT should be cryptographically verifiable by the IAT consumer. ++ * ++ * For details on IAT see https://tools.ietf.org/html/draft-mandyam-eat-01 ++ * ++ * @param ch_buffer Pointer to the buffer containing the nonce or ++ * challenge data to be validated with the private key. ++ * @param ch_sz The number of bytes in the challenge. 32, 48 or 64. ++ * @param token_buffer Pointer to the buffer where the IAT will be written. ++ * Must be equal in size to the system IAT output, which ++ * can be determined via a call to ++ * 'psa_initial_attest_get_token_size'. ++ * @param token_sz Pointer to the size of token_buffer, this value will be ++ * updated in this function to contain the number of bytes ++ * actually retrieved during the IAT request. ++ * ++ * @return Returns error code as specified in \ref psa_status_t ++ */ ++psa_status_t att_get_iat(uint8_t *ch_buffer, uint32_t ch_sz, ++ uint8_t *token_buffer, uint32_t *token_sz); ++ ++/** ++ * @brief TODO! ++ * ++ * @return Returns error code as specified in \ref psa_status_t ++ */ ++psa_status_t att_test(void); ++ ++#ifdef __cplusplus ++} ++#endif +diff --git a/samples/wolfboot_integration/psa_crypto/src/psa_crypto.c b/samples/wolfboot_integration/psa_crypto/src/psa_crypto.c +new file mode 100644 +index 00000000000..6a934c1c15e +--- /dev/null ++++ b/samples/wolfboot_integration/psa_crypto/src/psa_crypto.c +@@ -0,0 +1,871 @@ ++/* ++ * Copyright (c) 2019,2020 Linaro Limited ++ * ++ * SPDX-License-Identifier: Apache-2.0 ++ */ ++ ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "mbedtls/pk.h" ++#include "mbedtls/x509.h" ++#include "mbedtls/x509_csr.h" ++ ++#include "psa_crypto.h" ++#include "util_app_log.h" ++#include "util_sformat.h" ++ ++/** Declare a reference to the application logging interface. */ ++LOG_MODULE_DECLARE(app, CONFIG_LOG_DEFAULT_LEVEL); ++ ++/* Formatting details for displaying hex dumps. */ ++struct sf_hex_tbl_fmt crp_fmt = { ++ .ascii = true, ++ .addr_label = true, ++ .addr = 0 ++}; ++ ++struct csr_json_struct { ++ const char *CSR; ++}; ++ ++static const struct json_obj_descr csr_json_descr[] = { ++ JSON_OBJ_DESCR_PRIM(struct csr_json_struct, CSR, JSON_TOK_STRING) ++}; ++ ++/** ++ * @brief Extracts the public key from the specified persistent key id. ++ * ++ * @param key_id The permanent identifier for the generated key. ++ * @param key Pointer to the buffer where the public key data ++ * will be written. ++ * @param key_buf_size Size of key buffer in bytes. ++ * @param key_len Number of bytes written into key by this function. ++ */ ++static psa_status_t crp_get_pub_key(psa_key_id_t key_id, ++ uint8_t *key, size_t key_buf_size, ++ size_t *key_len) ++{ ++ psa_status_t status; ++ psa_key_handle_t key_handle; ++ ++ LOG_INF("Retrieving public key for key #%d", key_id); ++ al_dump_log(); ++ ++ /* Now try to re-open the persisted key based on the key ID. */ ++ status = al_psa_status( ++ psa_open_key(key_id, &key_handle), ++ __func__); ++ if (status != PSA_SUCCESS) { ++ LOG_ERR("Failed to open persistent key #%d", key_id); ++ goto err; ++ } ++ ++ /* Export the persistent key's public key part. */ ++ status = al_psa_status( ++ psa_export_public_key(key_handle, key, key_buf_size, key_len), ++ __func__); ++ if (status != PSA_SUCCESS) { ++ LOG_ERR("Failed to export public key."); ++ goto err; ++ } ++ ++ /* Display the binary key data for debug purposes. */ ++ sf_hex_tabulate_16(&crp_fmt, key, *key_len); ++ ++ /* Close the key to free up the volatile slot. */ ++ status = al_psa_status( ++ psa_close_key(key_handle), ++ __func__); ++ if (status != PSA_SUCCESS) { ++ LOG_ERR("Failed to close persistent key."); ++ goto err; ++ } ++ ++ return status; ++err: ++ al_dump_log(); ++ return status; ++} ++ ++#if CONFIG_PSA_IMPORT_KEY ++/** ++ * @brief Stores a new persistent secp256r1 key (usage: ecdsa-with-SHA256) ++ * in ITS, associating it with the specified unique key identifier. ++ * ++ * This function will store a new persistent secp256r1 key in internal trusted ++ * storage. Cryptographic operations can then be performed using the key ++ * identifier (key_id) associated with this persistent key. Only the 32-byte ++ * private key needs to be supplied, the public key can be derived using ++ * the supplied private key value. ++ * ++ * @param key_id The permament identifier for the generated key. ++ * @param key_usage The usage policy for the key. ++ * @param key_data Pointer to the 32-byte private key data. ++ */ ++static psa_status_t crp_imp_key_secp256r1(psa_key_id_t key_id, ++ psa_key_usage_t key_usage, ++ uint8_t *key_data) ++{ ++ psa_status_t status = PSA_SUCCESS; ++ psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT; ++ psa_key_type_t key_type = ++ PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1); ++ psa_algorithm_t alg = PSA_ALG_ECDSA(PSA_ALG_SHA_256); ++ psa_key_handle_t key_handle; ++ size_t key_len = 32; ++ size_t data_len; ++ uint8_t data_out[65] = { 0 }; /* ECDSA public key = 65 bytes. */ ++ int comp_result; ++ ++ LOG_INF("Persisting SECP256R1 key as #%d", (uint32_t)key_id); ++ al_dump_log(); ++ ++ /* Setup the key's attributes before the creation request. */ ++ psa_set_key_id(&key_attributes, key_id); ++ psa_set_key_usage_flags(&key_attributes, key_usage); ++ psa_set_key_lifetime(&key_attributes, PSA_KEY_LIFETIME_PERSISTENT); ++ psa_set_key_algorithm(&key_attributes, alg); ++ psa_set_key_type(&key_attributes, key_type); ++ ++ /* Import the private key, creating the persistent key on success */ ++ status = al_psa_status( ++ psa_import_key(&key_attributes, key_data, key_len, &key_handle), ++ __func__); ++ if (status != PSA_SUCCESS) { ++ LOG_ERR("Failed to import key."); ++ goto err; ++ } ++ ++ /* Close the key to free up the volatile slot. */ ++ status = al_psa_status( ++ psa_close_key(key_handle), ++ __func__); ++ if (status != PSA_SUCCESS) { ++ LOG_ERR("Failed to close persistent key."); ++ goto err; ++ } ++ ++ /* Try to retrieve the public key. */ ++ status = crp_get_pub_key(key_id, data_out, sizeof(data_out), &data_len); ++ ++ /* Export the private key if usage includes PSA_KEY_USAGE_EXPORT. */ ++ if (key_usage & PSA_KEY_USAGE_EXPORT) { ++ /* Re-open the persisted key based on the key ID. */ ++ status = al_psa_status( ++ psa_open_key(key_id, &key_handle), ++ __func__); ++ if (status != PSA_SUCCESS) { ++ LOG_ERR("Failed to open persistent key #%d", key_id); ++ goto err; ++ } ++ ++ /* Read the original (private) key data back. */ ++ status = al_psa_status( ++ psa_export_key(key_handle, data_out, ++ sizeof(data_out), &data_len), ++ __func__); ++ if (status != PSA_SUCCESS) { ++ LOG_ERR("Failed to export key."); ++ goto err; ++ } ++ ++ /* Check key len. */ ++ if (data_len != key_len) { ++ LOG_ERR("Unexpected number of bytes in exported key."); ++ goto err; ++ } ++ ++ /* Verify that the exported private key matches input data. */ ++ comp_result = memcmp(data_out, key_data, key_len); ++ if (comp_result != 0) { ++ LOG_ERR("Imported/exported private key mismatch."); ++ goto err; ++ } ++ ++ /* Display the private key. */ ++ LOG_INF("Private key data:"); ++ al_dump_log(); ++ sf_hex_tabulate_16(&crp_fmt, data_out, data_len); ++ ++ /* Close the key to free up the volatile slot. */ ++ status = al_psa_status( ++ psa_close_key(key_handle), ++ __func__); ++ if (status != PSA_SUCCESS) { ++ LOG_ERR("Failed to close persistent key."); ++ goto err; ++ } ++ } ++ ++ return status; ++err: ++ al_dump_log(); ++ return status; ++} ++ ++#else /* !CONFIG_PSA_IMPORT_KEY */ ++/** ++ * @brief Generates a new permanent, persistent prime256v1 (ecdsa-with-SHA256) ++ * key in ITS, associating it with the specified unique key identifier. ++ * ++ * This function will generate a new permanent prime256v1 key in internal trusted ++ * storage. Cryptographic operations can then be performed using the key ++ * identifier (key_id) associated with this persistent key. ++ * ++ * @param key_id The permanent identifier for the generated key. ++ * @param key_usage The usage policy for the key. ++ */ ++static psa_status_t crp_gen_key_secp256r1(psa_key_id_t key_id, ++ psa_key_usage_t key_usage) ++{ ++ psa_status_t status = PSA_SUCCESS; ++ psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT; ++ psa_key_type_t key_type = ++ PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1); ++ psa_algorithm_t alg = PSA_ALG_ECDSA(PSA_ALG_SHA_256); ++ psa_key_handle_t key_handle; ++ size_t key_len = 32; ++ size_t data_len; ++ uint8_t data_out[65] = { 0 }; /* ECDSA public key = 65 bytes. */ ++ ++ LOG_INF("Persisting SECP256R1 key as #%d", (uint32_t)key_id); ++ al_dump_log(); ++ ++ /* Setup the key's attributes before the creation request. */ ++ psa_set_key_id(&key_attributes, key_id); ++ psa_set_key_usage_flags(&key_attributes, key_usage); ++ psa_set_key_lifetime(&key_attributes, PSA_KEY_LIFETIME_PERSISTENT); ++ psa_set_key_algorithm(&key_attributes, alg); ++ psa_set_key_type(&key_attributes, key_type); ++ psa_set_key_bits(&key_attributes, 256); ++ ++ /* Generate the private key, creating the persistent key on success */ ++ status = al_psa_status( ++ psa_generate_key(&key_attributes, &key_handle), ++ __func__); ++ if (status != PSA_SUCCESS) { ++ LOG_ERR("Failed to generate key."); ++ goto err; ++ } ++ ++ /* Close the key to free up the volatile slot. */ ++ status = al_psa_status( ++ psa_close_key(key_handle), ++ __func__); ++ if (status != PSA_SUCCESS) { ++ LOG_ERR("Failed to close persistent key."); ++ goto err; ++ } ++ ++ /* Try to retrieve the public key. */ ++ status = crp_get_pub_key(key_id, data_out, sizeof(data_out), &data_len); ++ ++ /* Export the private key if usage includes PSA_KEY_USAGE_EXPORT. */ ++ if (key_usage & PSA_KEY_USAGE_EXPORT) { ++ /* Re-open the persisted key based on the key ID. */ ++ status = al_psa_status( ++ psa_open_key(key_id, &key_handle), ++ __func__); ++ if (status != PSA_SUCCESS) { ++ LOG_ERR("Failed to open persistent key #%d", key_id); ++ goto err; ++ } ++ ++ /* Read the original (private) key data back. */ ++ status = al_psa_status( ++ psa_export_key(key_handle, data_out, ++ sizeof(data_out), &data_len), ++ __func__); ++ if (status != PSA_SUCCESS) { ++ LOG_ERR("Failed to export key."); ++ goto err; ++ } ++ ++ /* Check key len. */ ++ if (data_len != key_len) { ++ LOG_ERR("Unexpected number of bytes in exported key."); ++ goto err; ++ } ++ ++ /* Display the private key. */ ++ LOG_INF("Private key data:"); ++ al_dump_log(); ++ ++ sf_hex_tabulate_16(&crp_fmt, data_out, data_len); ++ ++ /* Close the key to free up the volatile slot. */ ++ status = al_psa_status( ++ psa_close_key(key_handle), ++ __func__); ++ if (status != PSA_SUCCESS) { ++ LOG_ERR("Failed to close persistent key."); ++ goto err; ++ } ++ } ++ ++ return status; ++err: ++ al_dump_log(); ++ return status; ++} ++#endif /* CONFIG_PSA_IMPORT_KEY */ ++ ++/** ++ * @brief PSA Random number generator wrapper for Mbed TLS ++ */ ++static int psa_rng_for_mbedtls(void *p_rng, ++ unsigned char *output, size_t output_len) ++{ ++ (void)p_rng; ++ ++ return psa_generate_random(output, output_len); ++} ++ ++/** ++ * @brief Generates device certificate signing request (CSR) using Mbed TLS ++ * X.509 and TF-M crypto service. ++ */ ++void crp_generate_csr(void) ++{ ++ psa_status_t status; ++ psa_key_id_t key_slot = 1; ++ psa_key_handle_t key_handle; ++ ++ unsigned char output_buf[1024]; ++ unsigned char json_encoded_buf[1024]; ++ ++ mbedtls_pk_context pk_key_container; ++ mbedtls_x509write_csr req; ++ ++ struct csr_json_struct csr_json = { ++ .CSR = output_buf ++ }; ++ ++ /* Initialize Mbed TLS structures. */ ++ mbedtls_x509write_csr_init(&req); ++ mbedtls_pk_init(&pk_key_container); ++ memset(output_buf, 0, sizeof(output_buf)); ++ ++ /* Initialize crypto API. */ ++ LOG_INF("Initialising PSA crypto"); ++ al_dump_log(); ++ ++ status = al_psa_status(psa_crypto_init(), __func__); ++ if (status != PSA_SUCCESS) { ++ LOG_ERR("Crypto init failed."); ++ goto err; ++ } ++ ++ LOG_INF("PSA crypto init completed"); ++ al_dump_log(); ++ ++ /* prime256v1 (ecdsa-with-SHA256) private key. */ ++#if CONFIG_PSA_IMPORT_KEY ++#if CONFIG_PRIVATE_KEY_STATIC ++ /* This value is based on the private key in user.pem, ++ * which can be viewed viw the following command: ++ * ++ * $ openssl ec -in user.pem -text -noout ++ */ ++ uint8_t priv_key_data[32] = { ++ 0x14, 0xbc, 0xb9, 0x53, 0xa4, 0xee, 0xed, 0x50, ++ 0x09, 0x36, 0x92, 0x07, 0x1d, 0xdb, 0x24, 0x2c, ++ 0xef, 0xf9, 0x57, 0x92, 0x40, 0x4f, 0x49, 0xaa, ++ 0xd0, 0x7c, 0x5b, 0x3f, 0x26, 0xa7, 0x80, 0x48 ++ }; ++#else /* !CONFIG_PRIVATE_KEY_STATIC */ ++ /* Randomly generate the private key. */ ++ uint8_t priv_key_data[32] = { 0 }; ++ ++ LOG_INF("Generate rnadom data for private key"); ++ al_dump_log(); ++ ++ psa_generate_random(priv_key_data, sizeof(priv_key_data)); ++ LOG_INF("Random data generation for private key completed"); ++ al_dump_log(); ++ ++#endif /* CONFIG_PRIVATE_KEY_STATIC */ ++ ++ /* Generate persistent prime256v1 (ecdsa-with-SHA256) key w/ID #1. */ ++ /* PSA_KEY_USAGE_EXPORT can be added for debug purposes. */ ++ status = al_psa_status( ++ crp_imp_key_secp256r1(key_slot, ++ PSA_KEY_USAGE_SIGN_HASH | ++ PSA_KEY_USAGE_VERIFY_HASH, ++ priv_key_data), ++ __func__); ++ if (status != PSA_SUCCESS) { ++ LOG_ERR("Failed to create persistent key #%d", key_slot); ++ goto err; ++ } ++#else /* !CONFIG_PSA_IMPORT_KEY */ ++ ++ /* NOTE: The certificate signing request (CSR) can be generated using ++ * openssl by using following commands: ++ * ++ * Generate a new key: ++ * ++ * $ openssl ecparam -name secp256k1 -genkey -out USER.key ++ * ++ * Generate a certificate signing request, containing the user public key ++ * and required details to be inserted into the user certificate. ++ * openssl req -new -key USER.key -out USER.csr \ ++ * -subj "/O=Linaro/CN=$(uuidgen | tr '[:upper:]' '[:lower:]')" ++ * ++ */ ++ ++ /* Generate persistent prime256v1 (ecdsa-with-SHA256) key w/ID #1. */ ++ /* PSA_KEY_USAGE_EXPORT can be added for debug purposes. */ ++ status = al_psa_status( ++ crp_gen_key_secp256r1(key_slot, ++ PSA_KEY_USAGE_SIGN_HASH | ++ PSA_KEY_USAGE_VERIFY_HASH), ++ __func__); ++ if (status != PSA_SUCCESS) { ++ LOG_ERR("Failed to create persistent key #%d", key_slot); ++ goto err; ++ } ++#endif /* CONFIG_PSA_IMPORT_KEY */ ++ ++ status = al_psa_status( ++ psa_open_key(key_slot, &key_handle), ++ __func__); ++ if (status != PSA_SUCCESS) { ++ LOG_ERR("Failed to open persistent key #%d", key_slot); ++ goto err; ++ } ++ ++ psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; ++ ++ psa_get_key_attributes(key_handle, &attributes); ++ mbedtls_x509write_csr_set_md_alg(&req, MBEDTLS_MD_SHA256); ++ ++ LOG_INF("Adding subject name to CSR"); ++ al_dump_log(); ++ ++ status = mbedtls_x509write_csr_set_subject_name(&req, "O=Linaro,CN=Device Certificate"); ++ if (status != 0) { ++ LOG_ERR("failed! mbedtls_x509write_csr_set_subject_name returned %d", status); ++ goto err; ++ } ++ ++ LOG_INF("Adding subject name to CSR completed"); ++ al_dump_log(); ++ ++ LOG_INF("Adding EC key to PK container"); ++ al_dump_log(); ++ ++ status = mbedtls_pk_setup_opaque(&pk_key_container, key_handle); ++ if (status != 0) { ++ LOG_ERR("failed! mbedtls_pk_setup_opaque returned -0x%04x", (unsigned int) -status); ++ goto err; ++ } ++ ++ LOG_INF("Adding EC key to PK container completed"); ++ al_dump_log(); ++ ++ mbedtls_x509write_csr_set_key(&req, &pk_key_container); ++ ++ LOG_INF("Create device Certificate Signing Request"); ++ al_dump_log(); ++ ++ status = mbedtls_x509write_csr_pem(&req, output_buf, sizeof(output_buf), ++ psa_rng_for_mbedtls, NULL); ++ if (status < 0) { ++ LOG_ERR("failed! mbedtls_x509write_csr_pem returned -0x%04x", ++ (unsigned int) -status); ++ goto err; ++ } ++ ++ LOG_INF("Create device Certificate Signing Request completed"); ++ al_dump_log(); ++ ++ LOG_INF("Certificate Signing Request:\n"); ++ al_dump_log(); ++ ++ printf("%s\n", output_buf); ++ ++ /* ++ * 1.3. Encoding CSR as JSON ++ */ ++ LOG_INF("Encoding CSR as json"); ++ al_dump_log(); ++ ++ status = json_obj_encode_buf(csr_json_descr, ARRAY_SIZE(csr_json_descr), ++ &csr_json, json_encoded_buf, sizeof(json_encoded_buf)); ++ ++ if (status != 0) { ++ LOG_ERR("failed! json_obj_encode_buf returned 0x%04x", status); ++ goto err; ++ } ++ ++ LOG_INF("Encoding CSR as json completed"); ++ al_dump_log(); ++ ++ LOG_INF("Certificate Signing Request in JSON:\n"); ++ al_dump_log(); ++ ++ printf("%s\n\n", json_encoded_buf); ++ ++ /* Close the key to free up the volatile slot. */ ++ status = al_psa_status( ++ psa_close_key(key_handle), ++ __func__); ++ if (status != PSA_SUCCESS) { ++ LOG_ERR("Failed to close persistent key."); ++ goto err; ++ } ++ ++err: ++ al_dump_log(); ++ mbedtls_x509write_csr_free(&req); ++ mbedtls_pk_free(&pk_key_container); ++} ++ ++/** ++ * @brief Calculates the SHA256 hash for the supplied message. ++ * ++ * @param msg Pointer to the buffer to read when generating the hash. ++ * @param msg_len Number of bytes in msg. ++ * @param hash Pointer to the buffer where the hash should be written. ++ * @param hash_buf_size Size of hash in bytes. ++ * @param hash_len Placeholder for the number of hash bytes written. ++ */ ++static psa_status_t crp_hash_payload(uint8_t *msg, size_t msg_len, ++ uint8_t *hash, size_t hash_buf_size, ++ size_t *hash_len) ++{ ++ psa_status_t status; ++ psa_hash_operation_t hash_handle = psa_hash_operation_init(); ++ psa_algorithm_t alg = PSA_ALG_SHA_256; ++ ++ LOG_INF("Calculating SHA-256 hash of value"); ++ al_dump_log(); ++ ++ /* Display the input message */ ++ sf_hex_tabulate_16(&crp_fmt, msg, msg_len); ++ ++ /* Setup the hash object. */ ++ status = al_psa_status(psa_hash_setup(&hash_handle, alg), ++ __func__); ++ if (status != PSA_SUCCESS) { ++ LOG_ERR("Failed to setup hash op."); ++ goto err; ++ } ++ ++ /* Update object with all the message chunks. */ ++ /* For the moment, the message is passed in a single operation, */ ++ /* but this can be broken up in chunks for larger messages. */ ++ status = al_psa_status(psa_hash_update(&hash_handle, msg, msg_len), ++ __func__); ++ if (status != PSA_SUCCESS) { ++ LOG_ERR("Failed to update hash."); ++ goto err; ++ } ++ ++ /* Finalize the hash calculation. */ ++ status = al_psa_status(psa_hash_finish(&hash_handle, ++ hash, hash_buf_size, hash_len), ++ __func__); ++ if (status != PSA_SUCCESS) { ++ LOG_ERR("Failed to finalize hash op."); ++ goto err; ++ } ++ ++ /* Display the SHA-256 hash for debug purposes */ ++ sf_hex_tabulate_16(&crp_fmt, hash, (size_t)(PSA_HASH_MAX_SIZE)); ++ ++ return status; ++err: ++ psa_hash_abort(&hash_handle); ++ al_dump_log(); ++ return status; ++} ++ ++/** ++ * @brief Signs the supplied hash using the specified persistent key. ++ * ++ * @param key_id The identifier of the key to use when signing. ++ * @param hash Pointer to the buffer where the hash should be written. ++ * @param hash_buf_size Size of hash in bytes. ++ * @param sig Pointer to the buffer to read when generating the sig. ++ * @param sig_buf_size Size of sig buffer in bytes. ++ * @param sig_len Number of bytes written to sig. ++ */ ++static psa_status_t crp_sign_hash(psa_key_id_t key_id, ++ uint8_t *hash, size_t hash_buf_size, ++ uint8_t *sig, size_t sig_buf_size, ++ size_t *sig_len) ++{ ++ psa_status_t status; ++ psa_key_handle_t key_handle; ++ ++ LOG_INF("Signing SHA-256 hash"); ++ al_dump_log(); ++ ++ /* Try to open the persisted key based on the key ID. */ ++ status = al_psa_status( ++ psa_open_key(key_id, &key_handle), ++ __func__); ++ if (status != PSA_SUCCESS) { ++ LOG_ERR("Failed to open persistent key #%d", key_id); ++ goto err; ++ } ++ ++ /* Sign using psa_sign_hash. */ ++ status = al_psa_status( ++ psa_sign_hash(key_handle, ++ PSA_ALG_ECDSA(PSA_ALG_SHA_256), ++ hash, hash_buf_size, ++ sig, sig_buf_size, sig_len), ++ __func__); ++ if (status != PSA_SUCCESS) { ++ LOG_ERR("Failed to sign hash w/persistent key #%d", key_id); ++ goto err; ++ } ++ ++ /* Display the ECDSA signature for debug purposes */ ++ sf_hex_tabulate_16(&crp_fmt, sig, *sig_len); ++ ++ /* You can test this same operation with openssl as follows: ++ * ++ * $ openssl dgst -sha256 -sign ++ */ ++ ++ /* Close the key to free up the volatile slot. */ ++ status = al_psa_status( ++ psa_close_key(key_handle), ++ __func__); ++ if (status != PSA_SUCCESS) { ++ LOG_ERR("Failed to close persistent key."); ++ goto err; ++ } ++ ++ return status; ++err: ++ al_dump_log(); ++ return status; ++} ++ ++/** ++ * @brief Verifies the hash signature using the public key associated ++ * with key_id. ++ * ++ * @param key_id The identifier for the persistent key. ++ * @param hash Pointer to the hash data to verify. ++ * @param hash_len Size of the hash buffer in bytes. ++ * @param sig Pointer to the signature buffer. ++ * @param sig_len Size of the signature buffer in bytes. ++ */ ++static psa_status_t crp_verify_sign(psa_key_id_t key_id, ++ uint8_t *hash, size_t hash_len, ++ uint8_t *sig, size_t sig_len) ++{ ++ psa_status_t status; ++ psa_key_handle_t key_handle; ++ ++ LOG_INF("Verifying signature for SHA-256 hash"); ++ al_dump_log(); ++ ++ /* Try to open the persisted key based on the key ID. */ ++ status = al_psa_status( ++ psa_open_key(key_id, &key_handle), ++ __func__); ++ if (status != PSA_SUCCESS) { ++ LOG_ERR("Failed to open persistent key #%d", key_id); ++ goto err; ++ } ++ ++ /* Verify the hash signature. */ ++ status = al_psa_status( ++ psa_verify_hash(key_handle, ++ PSA_ALG_ECDSA(PSA_ALG_SHA_256), ++ hash, hash_len, ++ sig, sig_len), ++ __func__); ++ if (status != PSA_SUCCESS) { ++ LOG_ERR("Signature verification failed!"); ++ goto err; ++ } ++ ++ LOG_INF("Signature verified."); ++ al_dump_log(); ++ ++ /* Close the key to free up the volatile slot. */ ++ status = al_psa_status( ++ psa_close_key(key_handle), ++ __func__); ++ if (status != PSA_SUCCESS) { ++ LOG_ERR("Failed to close persistent key."); ++ goto err; ++ } ++ ++ return status; ++err: ++ al_dump_log(); ++ return status; ++} ++ ++/** ++ * @brief Destroys the specified persistent key. ++ * ++ * @param key_id The identifier for the persistent key. ++ */ ++static psa_status_t crp_dest_key(psa_key_id_t key_id) ++{ ++ psa_status_t status; ++ psa_key_handle_t key_handle; ++ ++ /* Try to open the persisted key based on the key ID. */ ++ status = al_psa_status( ++ psa_open_key(key_id, &key_handle), ++ __func__); ++ if (status != PSA_SUCCESS) { ++ LOG_ERR("Failed to open persistent key #%d", key_id); ++ goto err; ++ } ++ ++ /* Destroy the persistent key */ ++ status = al_psa_status( ++ psa_destroy_key(key_handle), ++ __func__); ++ if (status != PSA_SUCCESS) { ++ LOG_ERR("Failed to destroy a persistent key"); ++ goto err; ++ } ++ ++ LOG_INF("Destroyed persistent key #%d", (uint32_t)key_id); ++ al_dump_log(); ++ ++ return status; ++err: ++ al_dump_log(); ++ return status; ++} ++ ++void crp_test(void) ++{ ++ psa_status_t status; ++ uint8_t msg[] = "Please hash and sign this message."; ++ uint8_t hash[PSA_HASH_MAX_SIZE] = { 0 }; ++ size_t hash_len; ++ uint8_t sig[PSA_VENDOR_ECDSA_SIGNATURE_MAX_SIZE] = { 0 }; ++ size_t sig_len; ++ ++ /* secp256r1 private key. */ ++#if CONFIG_PSA_IMPORT_KEY ++#if CONFIG_PRIVATE_KEY_STATIC ++ /* This value is based on the private key in user.pem, ++ * which can be viewed viw the following command: ++ * ++ * $ openssl ec -in user.pem -text -noout ++ */ ++ uint8_t priv_key_data[32] = { ++ 0x14, 0xbc, 0xb9, 0x53, 0xa4, 0xee, 0xed, 0x50, ++ 0x09, 0x36, 0x92, 0x07, 0x1d, 0xdb, 0x24, 0x2c, ++ 0xef, 0xf9, 0x57, 0x92, 0x40, 0x4f, 0x49, 0xaa, ++ 0xd0, 0x7c, 0x5b, 0x3f, 0x26, 0xa7, 0x80, 0x48 ++ }; ++#else /* !CONFIG_PRIVATE_KEY_STATIC */ ++ /* Randomly generate the private key. */ ++ uint8_t priv_key_data[32] = { 0 }; ++ ++ psa_generate_random(priv_key_data, sizeof(priv_key_data)); ++#endif /* CONFIG_PRIVATE_KEY_STATIC */ ++#endif /* CONFIG_PSA_IMPORT_KEY */ ++ ++ /* Initialize crypto API. */ ++ status = al_psa_status(psa_crypto_init(), __func__); ++ if (status != PSA_SUCCESS) { ++ LOG_ERR("Crypto init failed."); ++ return; ++ } ++ ++ /* NOTE: The same key generation, SHA256 hash, sign, and verify ++ * operations performed in this file can be also performed with ++ * openssl using the commands described below. ++ * ++ * Generate a new key: ++ * ++ * The curve `prime256v1` is same as `secp256r1` in OpenSSL ++ * (https://github.com/openssl/openssl/blob/master/apps/ecparam.c#L216) ++ * $ openssl ecparam -name prime256v1 -genkey -out user.pem ++ * ++ * Display the public and private keys in hexadecimal format: ++ * ++ * $ openssl ec -in user.pem -text -noout ++ * ++ * Update the private key value in priv_key_data with the hexadecimal ++ * values from "priv:" to be able to compare the PSA API and openssl ++ * output. ++ * ++ * Generate a PEM file with the public key (which will be used to ++ * verify any data signed with the private key): ++ * ++ * $ openssl ec -in user.pem -pubout -out user_pub.pem ++ * ++ * Hash the message with SHA256, and sign it with the private key: ++ * ++ * $ echo "Please hash and sign this message." > message.txt ++ * $ openssl dgst -sha256 -sign user.pem message.txt > signature.der ++ * ++ * Verify the signature using the public key and message file: ++ * ++ * $ openssl dgst -sha256 -verify user_pub.pem \ ++ * -signature signature.der message.txt ++ * ++ * If everything ws OK you should see "Verified OK". ++ */ ++ ++ /* Generate persistent secp256r1 key w/ID #1. */ ++ /* PSA_KEY_USAGE_EXPORT can be added for debug purposes. */ ++#if CONFIG_PSA_IMPORT_KEY ++ status = crp_imp_key_secp256r1(1, ++ PSA_KEY_USAGE_SIGN_HASH | ++ PSA_KEY_USAGE_VERIFY_HASH, ++ priv_key_data); ++#else /* !CONFIG_PSA_IMPORT_KEY */ ++ status = crp_gen_key_secp256r1(1, ++ PSA_KEY_USAGE_SIGN_HASH | ++ PSA_KEY_USAGE_VERIFY_HASH); ++#endif ++ ++ /* Hash some data with the key using SHA256. */ ++ status = crp_hash_payload(msg, strlen(msg), ++ hash, sizeof(hash), &hash_len); ++ ++ /* Sign the hash using key #1. */ ++ status = crp_sign_hash(1, ++ hash, hash_len, ++ sig, sizeof(sig), &sig_len); ++ ++ /* Verify the hash signature using the public key. */ ++ status = crp_verify_sign(1, hash, hash_len, sig, sig_len); ++ ++ /* Destroy the key. */ ++ status = crp_dest_key(1); ++} ++ ++/** ++ * @brief Generates random values using the TF-M crypto service. ++ */ ++void crp_test_rng(void) ++{ ++ psa_status_t status; ++ uint8_t outbuf[256] = { 0 }; ++ struct sf_hex_tbl_fmt fmt = { ++ .ascii = true, ++ .addr_label = true, ++ .addr = 0 ++ }; ++ ++ status = al_psa_status(psa_generate_random(outbuf, 256), __func__); ++ LOG_INF("Generating 256 bytes of random data."); ++ al_dump_log(); ++ sf_hex_tabulate_16(&fmt, outbuf, 256); ++} +diff --git a/samples/wolfboot_integration/psa_crypto/src/psa_crypto.h b/samples/wolfboot_integration/psa_crypto/src/psa_crypto.h +new file mode 100644 +index 00000000000..1998e9ceec1 +--- /dev/null ++++ b/samples/wolfboot_integration/psa_crypto/src/psa_crypto.h +@@ -0,0 +1,34 @@ ++/* ++ * Copyright (c) 2019,2020 Linaro Limited ++ * ++ * SPDX-License-Identifier: Apache-2.0 ++ */ ++ ++#include ++ ++#include "psa/crypto.h" ++#include "psa/error.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/** ++ * @brief Generates random values using the TF-M crypto service. ++ */ ++void crp_test_rng(void); ++ ++/** ++ * @brief Runs a series of PSA Cryptography API test functions. ++ */ ++void crp_test(void); ++ ++/** ++ * @brief Generates device certificate signing request (CSR) using Mbed TLS ++ * X.509 and TF-M crypto service. ++ */ ++void crp_generate_csr(void); ++ ++#ifdef __cplusplus ++} ++#endif +diff --git a/samples/wolfboot_integration/psa_crypto/src/shell.c b/samples/wolfboot_integration/psa_crypto/src/shell.c +new file mode 100644 +index 00000000000..62b39451abe +--- /dev/null ++++ b/samples/wolfboot_integration/psa_crypto/src/shell.c +@@ -0,0 +1,40 @@ ++/* ++ * Copyright (c) 2019,2020 Linaro Limited ++ * ++ * SPDX-License-Identifier: Apache-2.0 ++ */ ++ ++#include ++#include ++#include ++ ++#if CONFIG_PSA_SHELL ++ ++static int ++psa_shell_invalid_arg(const struct shell *sh, char *arg_name) ++{ ++ shell_print(sh, "Error: invalid argument \"%s\"\n", arg_name); ++ ++ return -EINVAL; ++} ++ ++static int ++psa_shell_cmd_version(const struct shell *sh, size_t argc, char **argv) ++{ ++ shell_print(sh, "%s", "0.0.0"); ++ ++ return 0; ++} ++ ++/* Subcommand array for "psa" (level 1). */ ++SHELL_STATIC_SUBCMD_SET_CREATE(sub_psa, ++ /* 'version' command handler. */ ++ SHELL_CMD(version, NULL, "app version", psa_shell_cmd_version), ++ /* Array terminator. */ ++ SHELL_SUBCMD_SET_END ++); ++ ++/* Root command "psa" (level 0). */ ++SHELL_CMD_REGISTER(psa, &sub_psa, "PSA commands", NULL); ++ ++#endif +diff --git a/samples/wolfboot_integration/psa_crypto/src/util_app_cfg.c b/samples/wolfboot_integration/psa_crypto/src/util_app_cfg.c +new file mode 100644 +index 00000000000..fdec12f9aaa +--- /dev/null ++++ b/samples/wolfboot_integration/psa_crypto/src/util_app_cfg.c +@@ -0,0 +1,76 @@ ++/* ++ * Copyright (c) 2019,2020 Linaro Limited ++ * ++ * SPDX-License-Identifier: Apache-2.0 ++ */ ++ ++#include ++#include ++ ++#include "psa/error.h" ++#include "psa/protected_storage.h" ++#include "util_app_cfg.h" ++#include "util_app_log.h" ++ ++/** The 64-bit UID associated with the config record in secure storage. */ ++static psa_storage_uid_t cfg_data_uid = 0x0000000055CFDA7A; ++ ++LOG_MODULE_DECLARE(app, CONFIG_LOG_DEFAULT_LEVEL); ++ ++/** ++ * @brief Default config settings. These settings will be used when a new ++ * config record is created, or when the config record is reset. ++ */ ++static struct cfg_data cfg_data_dflt = { ++ .magic = 0x55CFDA7A, ++ .version = 1, ++ .scratch = { 0 } ++}; ++ ++psa_status_t cfg_create_data(void) ++{ ++ psa_status_t status; ++ ++ LOG_INF("app_cfg: Creating new config file with UID 0x%llX", ++ (uint64_t)cfg_data_uid); ++ ++ /* ++ * psa_ps_create can also be used here, which enables to the use of ++ * the psa_ps_set_extended function, but this requires us to set a ++ * maximum file size for resource allocation. Since the upper limit ++ * isn't known at present, we opt here for the simpler psa_ps_set ++ * call which also creates the secure storage record if necessary, ++ * but precludes the use of psa_ps_set_extended. ++ */ ++ status = psa_ps_set(cfg_data_uid, sizeof(cfg_data_dflt), ++ (void *)&cfg_data_dflt, 0); ++ if (status) { ++ goto err; ++ } ++ ++err: ++ return (status ? al_psa_status(status, __func__) : status); ++} ++ ++psa_status_t cfg_load_data(struct cfg_data *p_cfg_data) ++{ ++ psa_status_t status; ++ struct psa_storage_info_t p_info; ++ ++ memset(&p_info, 0, sizeof(p_info)); ++ ++ /* Check if the config record exists, if not create it. */ ++ status = psa_ps_get_info(cfg_data_uid, &p_info); ++ if (status == PSA_ERROR_DOES_NOT_EXIST) { ++ /* Create a new config file. */ ++ status = cfg_create_data(); ++ /* Copy default values to the cfg_data placeholder. */ ++ memcpy(p_cfg_data, &cfg_data_dflt, sizeof(cfg_data_dflt)); ++ } ++ if (status) { ++ goto err; ++ } ++ ++err: ++ return (status ? al_psa_status(status, __func__) : status); ++} +diff --git a/samples/wolfboot_integration/psa_crypto/src/util_app_cfg.h b/samples/wolfboot_integration/psa_crypto/src/util_app_cfg.h +new file mode 100644 +index 00000000000..2b3e312b264 +--- /dev/null ++++ b/samples/wolfboot_integration/psa_crypto/src/util_app_cfg.h +@@ -0,0 +1,63 @@ ++/* ++ * Copyright (c) 2019,2020 Linaro Limited ++ * ++ * SPDX-License-Identifier: Apache-2.0 ++ */ ++ ++#include ++ ++#include "psa/error.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/** ++ * @brief The struct used to persist config data to secure storage. ++ * ++ * The first 6 bytes of this struct should remain consistent in any future ++ * firmware updates, since they can be used to identify to layout of the rest ++ * of the struct in cases where config data version management becomes ++ * a necessity. ++ */ ++struct cfg_data { ++ /** ++ * @brief Magic number for config data payloads (0x55CFDA7A). ++ */ ++ uint32_t magic; ++ ++ /** ++ * @brief The version number for the stored config record. ++ * ++ * This number should be incremented any time the config_data struct ++ * definition changes to allow version management of config data at ++ * the application level. ++ */ ++ uint16_t version; ++ ++ /** @brief 256-byte debug scratch area. */ ++ uint8_t scratch[256]; ++}; ++ ++/** ++ * @brief Creates a new config record in secure storage. ++ * ++ * @return #PSA_SUCCESS on success, otherwise a appropriate psa_status_t code. ++ */ ++psa_status_t cfg_create_data(void); ++ ++/** ++ * @brief Attempts to load the config record from secure storage. If the ++ * record is not found in secure storage, a new record will be created ++ * using default config settings. ++ * ++ * @param p_cfg_data Pointer to the cfg_data struct where the config data ++ * should be assigned once loaded. ++ * ++ * @return #PSA_SUCCESS on success, otherwise a appropriate psa_status_t code. ++ */ ++psa_status_t cfg_load_data(struct cfg_data *p_cfg_data); ++ ++#ifdef __cplusplus ++} ++#endif +diff --git a/samples/wolfboot_integration/psa_crypto/src/util_app_log.c b/samples/wolfboot_integration/psa_crypto/src/util_app_log.c +new file mode 100644 +index 00000000000..52f766c81cc +--- /dev/null ++++ b/samples/wolfboot_integration/psa_crypto/src/util_app_log.c +@@ -0,0 +1,110 @@ ++/* ++ * Copyright (c) 2019,2020 Linaro Limited ++ * ++ * SPDX-License-Identifier: Apache-2.0 ++ */ ++#include ++#include ++#include ++ ++#include "psa/crypto.h" ++#include "util_app_log.h" ++ ++LOG_MODULE_REGISTER(app, CONFIG_LOG_DEFAULT_LEVEL); ++ ++psa_status_t al_psa_status(psa_status_t status, const char *func_name) ++{ ++ switch (status) { ++ case PSA_SUCCESS: ++ break; ++ ++ /* Generic PSA errors (psa/error.h). */ ++ case PSA_ERROR_PROGRAMMER_ERROR: ++ LOG_ERR("Programmer error"); ++ break; ++ case PSA_ERROR_CONNECTION_REFUSED: ++ LOG_ERR("Connection refused"); ++ break; ++ case PSA_ERROR_CONNECTION_BUSY: ++ LOG_ERR("Connection busy"); ++ break; ++ case PSA_ERROR_GENERIC_ERROR: ++ LOG_ERR("Generic error"); ++ break; ++ case PSA_ERROR_NOT_PERMITTED: ++ LOG_ERR("Not permitted"); ++ break; ++ case PSA_ERROR_NOT_SUPPORTED: ++ LOG_ERR("Unsupported operation"); ++ break; ++ case PSA_ERROR_INVALID_ARGUMENT: ++ LOG_ERR("Invalid argument"); ++ break; ++ case PSA_ERROR_INVALID_HANDLE: ++ LOG_ERR("Invalid handle"); ++ break; ++ case PSA_ERROR_BAD_STATE: ++ LOG_ERR("Bad state"); ++ break; ++ case PSA_ERROR_BUFFER_TOO_SMALL: ++ LOG_ERR("Buffer too small"); ++ break; ++ case PSA_ERROR_ALREADY_EXISTS: ++ LOG_ERR("Already exists"); ++ break; ++ case PSA_ERROR_DOES_NOT_EXIST: ++ LOG_ERR("Does not exist"); ++ break; ++ case PSA_ERROR_INSUFFICIENT_MEMORY: ++ LOG_ERR("Insufficient memory"); ++ break; ++ case PSA_ERROR_INSUFFICIENT_STORAGE: ++ LOG_ERR("Insufficient storage"); ++ break; ++ case PSA_ERROR_INSUFFICIENT_DATA: ++ LOG_ERR("Insufficient memory data"); ++ break; ++ case PSA_ERROR_SERVICE_FAILURE: ++ LOG_ERR("Service failure"); ++ break; ++ case PSA_ERROR_COMMUNICATION_FAILURE: ++ LOG_ERR("Communication failure"); ++ break; ++ case PSA_ERROR_STORAGE_FAILURE: ++ LOG_ERR("Storage failure"); ++ break; ++ case PSA_ERROR_HARDWARE_FAILURE: ++ LOG_ERR("Hardware failure"); ++ break; ++ case PSA_ERROR_INVALID_SIGNATURE: ++ LOG_ERR("Invalid signature"); ++ break; ++ ++ /* PSA crypto errors (psa/crypto_values.h). */ ++ case PSA_ERROR_INSUFFICIENT_ENTROPY: ++ LOG_ERR("CRYPTO: Insufficient entropy"); ++ break; ++ case PSA_ERROR_CORRUPTION_DETECTED: ++ LOG_ERR("CRYPTO: Tampering detected"); ++ break; ++ ++ /* Catch-all error handler. */ ++ default: ++ LOG_ERR("Unhandled status response: %d", status); ++ break; ++ } ++ ++ /* Display the calling function name for debug purposes. */ ++ if (status != PSA_SUCCESS) { ++ LOG_ERR("Function: '%s'", func_name); ++ } ++ ++ return status; ++} ++ ++void al_dump_log(void) ++{ ++ while (log_process()) { ++ ++ } ++} +diff --git a/samples/wolfboot_integration/psa_crypto/src/util_app_log.h b/samples/wolfboot_integration/psa_crypto/src/util_app_log.h +new file mode 100644 +index 00000000000..af935163e08 +--- /dev/null ++++ b/samples/wolfboot_integration/psa_crypto/src/util_app_log.h +@@ -0,0 +1,35 @@ ++/* ++ * Copyright (c) 2019,2020 Linaro Limited ++ * ++ * SPDX-License-Identifier: Apache-2.0 ++ */ ++ ++#include ++ ++#include "psa/error.h" ++#include "psa/initial_attestation.h" ++#include "psa/protected_storage.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/** ++ * @brief Logs PSA response messages other than PSA_SUCCESS for debugging ++ * purposes. ++ * ++ * @param status The psa_status_t value to log. ++ * @param func_name The name of the function that made this function call. ++ * ++ * @return Returns the psa_status_t value passed into the function. ++ */ ++psa_status_t al_psa_status(psa_status_t status, const char *func_name); ++ ++/** ++ * @brief Calls 'log_process' in Zephyr to dump any queued log messages. ++ */ ++void al_dump_log(void); ++ ++#ifdef __cplusplus ++} ++#endif +diff --git a/samples/wolfboot_integration/psa_crypto/src/util_sformat.c b/samples/wolfboot_integration/psa_crypto/src/util_sformat.c +new file mode 100644 +index 00000000000..bd46aec54fc +--- /dev/null ++++ b/samples/wolfboot_integration/psa_crypto/src/util_sformat.c +@@ -0,0 +1,115 @@ ++/* ++ * Copyright (c) 2019,2020 Linaro Limited ++ * ++ * SPDX-License-Identifier: Apache-2.0 ++ */ ++ ++#include ++#include ++#include "util_sformat.h" ++ ++static void sf_hex_ascii(unsigned char *data, size_t len, unsigned char nonvis) ++{ ++ uint32_t idx; ++ ++ /* Render printable characters. */ ++ idx = 0; ++ while (len) { ++ printf("%c", isprint(data[idx]) ? data[idx] : nonvis); ++ idx++; ++ len--; ++ } ++} ++ ++void sf_hex_tabulate_16(struct sf_hex_tbl_fmt *fmt, unsigned char *data, ++ size_t len) ++{ ++ uint32_t idx; ++ uint32_t cpos; /* Current position. */ ++ uint32_t ca; /* Current address. */ ++ uint32_t ea; /* End address. */ ++ ++ if (!len) { ++ return; ++ } ++ ++ /* Set the end address (since we modify len in the write loop). */ ++ ea = fmt->addr + len; ++ ++ /* Check if we need to render the top address bar. */ ++ if (fmt->addr_label) { ++ /* Render the top address bar. */ ++ printf("\n"); ++ printf(" "); ++ printf("0 1 2 3 4 5 6 7 8 9 "); ++ printf("A B C D E F\n"); ++ printf("%08X ", fmt->addr - (fmt->addr % 16)); ++ } ++ ++ /* Insert offset padding for first row if necessary. */ ++ cpos = fmt->addr % 16; ++ if (cpos != 0) { ++ for (idx = 0; idx < cpos; idx++) { ++ printf(" "); ++ } ++ } ++ ++ /* Print data row by row. */ ++ idx = 0; ++ ca = fmt->addr; ++ while (len) { ++ /* Print the current value. */ ++ printf("%02X ", data[idx++]); ++ cpos++; ++ ca++; ++ ++ /* Wrap around to the next line if necessary. */ ++ if (cpos == 16 || ca == ea) { ++ /* Render ASCII equiv at end of row if requested. */ ++ if (fmt->ascii) { ++ if (ca == ea) { ++ /* Handle last/single row. */ ++ if (ca % 16) { ++ /* PARTIAL row (< 16 vals). */ ++ printf("%.*s", ++ (16 - ca % 16) * 3, ++ " " ++ " " ++ " "); ++ sf_hex_ascii( ++ &data[idx - (ca % 16)], ++ ca - fmt->addr < 16 ? ++ idx % 16 : ca % 16, ++ '.'); ++ } else { ++ /* FULL row. */ ++ sf_hex_ascii( ++ &data[idx - 16], ++ 16, '.'); ++ } ++ } else if (ca < fmt->addr + 15) { ++ /* Handle first row. */ ++ printf("%.*s", fmt->addr % 16, ++ " "); ++ sf_hex_ascii(data, ++ 16 - fmt->addr % 16, '.'); ++ } else { ++ /* Full row. */ ++ sf_hex_ascii(&data[idx - 16], 16, '.'); ++ } ++ } ++ ++ /* Wrap around if this isn't the last row. */ ++ printf("\n"); ++ if (ca != ea) { ++ /* Render the next base row addr. */ ++ if (fmt->addr_label) { ++ printf("%08X ", ca); ++ } ++ } ++ cpos = 0; ++ } ++ len--; ++ } ++ printf("\n"); ++} +diff --git a/samples/wolfboot_integration/psa_crypto/src/util_sformat.h b/samples/wolfboot_integration/psa_crypto/src/util_sformat.h +new file mode 100644 +index 00000000000..b76d2aa5b91 +--- /dev/null ++++ b/samples/wolfboot_integration/psa_crypto/src/util_sformat.h +@@ -0,0 +1,45 @@ ++/* ++ * Copyright (c) 2019,2020 Linaro Limited ++ * ++ * SPDX-License-Identifier: Apache-2.0 ++ */ ++ ++#ifndef _SFORMAT_H_ ++#define _SFORMAT_H_ ++ ++#include ++#include ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/** ++ * @brief Indicates how hex data should be rendered in tabular format. ++ */ ++struct sf_hex_tbl_fmt { ++ /** Whether or not to render ASCII equivalents. */ ++ uint8_t ascii : 1; ++ /** Whether or not to add address labels to the output. */ ++ uint8_t addr_label : 1; ++ /** The starting value for the address labels. */ ++ uint32_t addr; ++} __packed; ++ ++/** ++ * @brief Prints a 16-value wide tabular rendering of 8-bit hex data, with ++ * optional ascii equivalents and address labels. ++ * ++ * @param fmt Pointer to thee sf_hex_tbl_fmt struct indicating how the ++ * table should be rendered. ++ * @param data Pointer to the data to render. ++ * @param len The number of bytes to render from data. ++ */ ++void sf_hex_tabulate_16(struct sf_hex_tbl_fmt *fmt, unsigned char *data, ++ size_t len); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* _SFORMAT_H_ */ +diff --git a/samples/wolfboot_integration/psa_crypto/user.pem b/samples/wolfboot_integration/psa_crypto/user.pem +new file mode 100644 +index 00000000000..9be207fb091 +--- /dev/null ++++ b/samples/wolfboot_integration/psa_crypto/user.pem +@@ -0,0 +1,8 @@ ++-----BEGIN EC PARAMETERS----- ++BggqhkjOPQMBBw== ++-----END EC PARAMETERS----- ++-----BEGIN EC PRIVATE KEY----- ++MHcCAQEEIBS8uVOk7u1QCTaSBx3bJCzv+VeSQE9JqtB8Wz8mp4BIoAoGCCqGSM49 ++AwEHoUQDQgAER+qu2dZtLh1lBfUE/swhmb5eWlZrTx4MQ+Jbzht9BtezceIKPEft ++hJ9lDtv5PdIHu4Gmc+Y7FpUZrAECyxz1NQ== ++-----END EC PRIVATE KEY----- +diff --git a/samples/wolfboot_integration/psa_crypto/user_pub.pem b/samples/wolfboot_integration/psa_crypto/user_pub.pem +new file mode 100644 +index 00000000000..e37f7142a5f +--- /dev/null ++++ b/samples/wolfboot_integration/psa_crypto/user_pub.pem +@@ -0,0 +1,4 @@ ++-----BEGIN PUBLIC KEY----- ++MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAER+qu2dZtLh1lBfUE/swhmb5eWlZr ++Tx4MQ+Jbzht9BtezceIKPEfthJ9lDtv5PdIHu4Gmc+Y7FpUZrAECyxz1NQ== ++-----END PUBLIC KEY----- diff --git a/zephyr/patches/0004-stm32h5-ns-board-support.patch b/zephyr/patches/0004-stm32h5-ns-board-support.patch new file mode 100644 index 0000000000..1beaa8e7a9 --- /dev/null +++ b/zephyr/patches/0004-stm32h5-ns-board-support.patch @@ -0,0 +1,219 @@ +diff --git a/boards/st/nucleo_h563zi/Kconfig.defconfig b/boards/st/nucleo_h563zi/Kconfig.defconfig +index e14e20d30a4..824a007d004 100644 +--- a/boards/st/nucleo_h563zi/Kconfig.defconfig ++++ b/boards/st/nucleo_h563zi/Kconfig.defconfig +@@ -8,4 +8,7 @@ if BOARD_NUCLEO_H563ZI + configdefault NET_L2_ETHERNET + default y + ++config BUILD_OUTPUT_ADJUST_LMA ++ default "0x400" if TRUSTED_EXECUTION_NONSECURE ++ + endif # BOARD_NUCLEO_H563ZI +diff --git a/boards/st/nucleo_h563zi/board.yml b/boards/st/nucleo_h563zi/board.yml +index fec89e6d37b..b0814b9397b 100644 +--- a/boards/st/nucleo_h563zi/board.yml ++++ b/boards/st/nucleo_h563zi/board.yml +@@ -4,3 +4,5 @@ board: + vendor: st + socs: + - name: stm32h563xx ++ variants: ++ - name: ns +diff --git a/boards/st/stm32h573i_dk/Kconfig.defconfig b/boards/st/stm32h573i_dk/Kconfig.defconfig +index b24ccf7f106..f0a1fbfea3f 100644 +--- a/boards/st/stm32h573i_dk/Kconfig.defconfig ++++ b/boards/st/stm32h573i_dk/Kconfig.defconfig +@@ -13,6 +13,9 @@ configdefault NET_L2_ETHERNET + configdefault SDMMC_STM32_CLOCK_CHECK + default n + ++config BUILD_OUTPUT_ADJUST_LMA ++ default "0x400" if TRUSTED_EXECUTION_NONSECURE ++ + if DISPLAY + + choice ST7789V_PIXEL_FORMAT +diff --git a/boards/st/stm32h573i_dk/stm32h573i_dk_stm32h573xx_ns.dts b/boards/st/stm32h573i_dk/stm32h573i_dk_stm32h573xx_ns.dts +index 46357c2e2d5..66583781c3c 100644 +--- a/boards/st/stm32h573i_dk/stm32h573i_dk_stm32h573xx_ns.dts ++++ b/boards/st/stm32h573i_dk/stm32h573i_dk_stm32h573xx_ns.dts +@@ -16,12 +16,13 @@ + zephyr,flash = &flash0; + zephyr,flash-controller = &flash; + zephyr,code-partition = &slot0_ns_partition; ++ zephyr,sram = &sram3; + }; + }; + +-/* Last 64kB of SRAM1 are owned by TF-M */ ++/* Use SRAM3 for non-secure, keep SRAM1/2 secure */ + &sram1 { +- reg = <0x20000000 DT_SIZE_K(256 - 64)>; ++ status = "disabled"; + }; + + /* SRAM2 is owned by TF-M */ +@@ -29,6 +30,10 @@ + status = "disabled"; + }; + ++&sram3 { ++ reg = <0x20050000 DT_SIZE_K(256)>; ++}; ++ + &flash0 { + partitions { + compatible = "fixed-partitions"; +diff --git a/boards/st/stm32h573i_dk/stm32h573i_dk_stm32h573xx_ns_defconfig b/boards/st/stm32h573i_dk/stm32h573i_dk_stm32h573xx_ns_defconfig +index ecccf647305..0d19316aaf5 100644 +--- a/boards/st/stm32h573i_dk/stm32h573i_dk_stm32h573xx_ns_defconfig ++++ b/boards/st/stm32h573i_dk/stm32h573i_dk_stm32h573xx_ns_defconfig +@@ -22,3 +22,7 @@ CONFIG_GPIO=y + CONFIG_TRUSTED_EXECUTION_NONSECURE=y + CONFIG_RUNTIME_NMI=y + CONFIG_TFM_MCUBOOT_SIGNATURE_TYPE="RSA-3072" ++ ++# Use SRAM3 as non-secure RAM (aligned with wolfBoot TZ layout) ++CONFIG_SRAM_BASE_ADDRESS=0x20050000 ++CONFIG_SRAM_SIZE=256 +diff --git a/boards/st/nucleo_h563zi/nucleo_h563zi_stm32h563xx_ns.dts b/boards/st/nucleo_h563zi/nucleo_h563zi_stm32h563xx_ns.dts +new file mode 100644 +index 00000000000..db6964e7659 +--- /dev/null ++++ b/boards/st/nucleo_h563zi/nucleo_h563zi_stm32h563xx_ns.dts +@@ -0,0 +1,84 @@ ++/* ++ * Copyright (c) 2026 wolfSSL Inc. ++ * ++ * SPDX-License-Identifier: Apache-2.0 ++ */ ++ ++/dts-v1/; ++#include "nucleo_h563zi-common.dtsi" ++ ++/ { ++ model = "STMicroelectronics STM32H563ZI-NUCLEO board (Non-Secure)"; ++ compatible = "st,stm32h563zi-nucleo"; ++ ++ chosen { ++ zephyr,console = &usart3; ++ zephyr,shell-uart = &usart3; ++ zephyr,sram = &sram3; ++ zephyr,flash = &flash0; ++ zephyr,code-partition = &slot0_ns_partition; ++ zephyr,canbus = &fdcan1; ++ }; ++ ++ aliases { ++ led0 = &green_led_1; ++ led1 = &yellow_led_1; ++ sw0 = &user_button; ++ watchdog0 = &iwdg; ++ pwm-led0 = &pwm_led_1; ++ volt-sensor0 = &vref; ++ volt-sensor1 = &vbat; ++ }; ++}; ++ ++/* Use SRAM3 for non-secure, keep SRAM1/2 secure */ ++&sram1 { ++ status = "disabled"; ++}; ++ ++&sram2 { ++ status = "disabled"; ++}; ++ ++&sram3 { ++ reg = <0x20050000 DT_SIZE_K(256)>; ++}; ++ ++&flash0 { ++ partitions { ++ compatible = "fixed-partitions"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ /* Layout inspired by wolfBoot STM32H5 TZ PSA config */ ++ boot_partition: partition@0 { ++ label = "wolfboot"; ++ reg = <0x00000000 DT_SIZE_K(64)>; ++ }; ++ ++ slot0_partition: partition@10000 { ++ label = "image-0"; ++ reg = <0x00010000 DT_SIZE_K(320)>; ++ }; ++ ++ slot0_ns_partition: partition@60000 { ++ label = "image-0-nonsecure"; ++ reg = <0x00060000 DT_SIZE_K(640)>; ++ }; ++ ++ slot1_partition: partition@100000 { ++ label = "image-1"; ++ reg = <0x00100000 DT_SIZE_K(320)>; ++ }; ++ ++ slot1_ns_partition: partition@150000 { ++ label = "image-1-nonsecure"; ++ reg = <0x00150000 DT_SIZE_K(640)>; ++ }; ++ ++ storage_partition: partition@1f0000 { ++ label = "storage"; ++ reg = <0x001f0000 DT_SIZE_K(64)>; ++ }; ++ }; ++}; +diff --git a/boards/st/nucleo_h563zi/nucleo_h563zi_stm32h563xx_ns.yaml b/boards/st/nucleo_h563zi/nucleo_h563zi_stm32h563xx_ns.yaml +new file mode 100644 +index 00000000000..27a5200fd18 +--- /dev/null ++++ b/boards/st/nucleo_h563zi/nucleo_h563zi_stm32h563xx_ns.yaml +@@ -0,0 +1,9 @@ ++identifier: nucleo_h563zi/stm32h563xx/ns ++name: ST Nucleo H563ZI non-secure ++type: mcu ++arch: arm ++toolchain: ++ - zephyr ++ram: 192 ++flash: 639 # size in kB of 1 app slot minus wolfBoot header size (1KB) ++vendor: st +diff --git a/boards/st/nucleo_h563zi/nucleo_h563zi_stm32h563xx_ns_defconfig b/boards/st/nucleo_h563zi/nucleo_h563zi_stm32h563xx_ns_defconfig +new file mode 100644 +index 00000000000..4fd9c2bbdcf +--- /dev/null ++++ b/boards/st/nucleo_h563zi/nucleo_h563zi_stm32h563xx_ns_defconfig +@@ -0,0 +1,29 @@ ++# Copyright (c) 2026 wolfSSL Inc. ++# SPDX-License-Identifier: Apache-2.0 ++ ++# Enable MPU ++CONFIG_ARM_MPU=y ++ ++# Enable HW stack protection ++CONFIG_HW_STACK_PROTECTION=y ++ ++# Enable UART driver ++CONFIG_SERIAL=y ++ ++# Enable console ++CONFIG_CONSOLE=y ++CONFIG_UART_CONSOLE=y ++ ++# Enable GPIO ++CONFIG_GPIO=y ++ ++# Enable TZ non-secure configuration ++CONFIG_TRUSTED_EXECUTION_NONSECURE=y ++CONFIG_RUNTIME_NMI=y ++ ++# Do not build TF-M by default for this /ns variant ++CONFIG_BUILD_WITH_TFM=n ++ ++# Use SRAM3 as non-secure RAM (aligned with wolfBoot TZ layout) ++CONFIG_SRAM_BASE_ADDRESS=0x20050000 ++CONFIG_SRAM_SIZE=256 diff --git a/zephyr/patches/0005-wolfboot-psa-kconfig.patch b/zephyr/patches/0005-wolfboot-psa-kconfig.patch new file mode 100644 index 0000000000..39fc6e03ee --- /dev/null +++ b/zephyr/patches/0005-wolfboot-psa-kconfig.patch @@ -0,0 +1,26 @@ +diff --git a/modules/mbedtls/Kconfig.mbedtls b/modules/mbedtls/Kconfig.mbedtls +index c8065fa758a..ab68e1fc398 100644 +--- a/modules/mbedtls/Kconfig.mbedtls ++++ b/modules/mbedtls/Kconfig.mbedtls +@@ -595,7 +595,7 @@ config MBEDTLS_USE_PSA_CRYPTO + config MBEDTLS_PSA_CRYPTO_CLIENT + bool + default y +- depends on BUILD_WITH_TFM || MBEDTLS_PSA_CRYPTO_C ++ depends on BUILD_WITH_TFM || MBEDTLS_PSA_CRYPTO_C || WOLFBOOT_TEE + select PSA_CRYPTO_CLIENT + + config MBEDTLS_LMS_C +diff --git a/soc/st/stm32/Kconfig.defconfig b/soc/st/stm32/Kconfig.defconfig +index e1e234b2465..77d27eb1b56 100644 +--- a/soc/st/stm32/Kconfig.defconfig ++++ b/soc/st/stm32/Kconfig.defconfig +@@ -97,7 +97,7 @@ config USE_DT_CODE_PARTITION + default y if TRUSTED_EXECUTION_NONSECURE + + config BUILD_WITH_TFM +- default y if TRUSTED_EXECUTION_NONSECURE ++ default y if TRUSTED_EXECUTION_NONSECURE && TFM_BOARD != "" + + config FLASH_BASE_ADDRESS + default $(dt_node_reg_addr_hex,$(DT_CHOSEN_FLASH_PARENT),1) \ diff --git a/zephyr/src/arm_tee_attest_api.c b/zephyr/src/arm_tee_attest_api.c new file mode 100644 index 0000000000..f28adca29f --- /dev/null +++ b/zephyr/src/arm_tee_attest_api.c @@ -0,0 +1,80 @@ +/* arm_tee_attest_api.c + * + * ARM TEE attestation NS API wrappers. + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + */ + +#include "psa/initial_attestation.h" +#include "psa/client.h" +#include "psa_manifest/sid.h" +#include "arm_tee_attest_defs.h" + +psa_status_t +psa_initial_attest_get_token(const uint8_t *auth_challenge, + size_t challenge_size, + uint8_t *token_buf, + size_t token_buf_size, + size_t *token_size) +{ + psa_status_t status; + + psa_invec in_vec[] = { + { auth_challenge, challenge_size } + }; + psa_outvec out_vec[] = { + { token_buf, token_buf_size } + }; + + status = psa_call(ARM_TEE_ATTESTATION_SERVICE_HANDLE, + ARM_TEE_ATTEST_GET_TOKEN, + in_vec, IOVEC_LEN(in_vec), + out_vec, IOVEC_LEN(out_vec)); + + if (status == PSA_SUCCESS && token_size != NULL) { + *token_size = out_vec[0].len; + } + + return status; +} + +psa_status_t +psa_initial_attest_get_token_size(size_t challenge_size, + size_t *token_size) +{ + psa_status_t status; + rot_size_t challenge_size_param; + rot_size_t token_size_param = 0; + + psa_invec in_vec[] = { + { &challenge_size_param, sizeof(challenge_size_param) } + }; + psa_outvec out_vec[] = { + { &token_size_param, sizeof(token_size_param) } + }; + + if (challenge_size > ROT_SIZE_MAX) { + return PSA_ERROR_INVALID_ARGUMENT; + } + challenge_size_param = (rot_size_t)challenge_size; + + if (token_size == NULL) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + status = psa_call(ARM_TEE_ATTESTATION_SERVICE_HANDLE, + ARM_TEE_ATTEST_GET_TOKEN_SIZE, + in_vec, IOVEC_LEN(in_vec), + out_vec, IOVEC_LEN(out_vec)); + + *token_size = token_size_param; + + return status; +} diff --git a/zephyr/src/arm_tee_crypto_api.c b/zephyr/src/arm_tee_crypto_api.c new file mode 100644 index 0000000000..37a7cb6860 --- /dev/null +++ b/zephyr/src/arm_tee_crypto_api.c @@ -0,0 +1,347 @@ +/* arm_tee_crypto_api.c + * + * ARM TEE PSA Crypto NS API wrappers. + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include + +#include "psa/client.h" +#include "arm_tee_crypto_defs.h" + +#define API_DISPATCH(in_vec, out_vec) \ + psa_call(ARM_TEE_CRYPTO_HANDLE, PSA_IPC_CALL, \ + in_vec, IOVEC_LEN(in_vec), \ + out_vec, IOVEC_LEN(out_vec)) + +#define API_DISPATCH_NO_OUTVEC(in_vec) \ + psa_call(ARM_TEE_CRYPTO_HANDLE, PSA_IPC_CALL, \ + in_vec, IOVEC_LEN(in_vec), \ + (psa_outvec *)NULL, 0) + +psa_status_t psa_crypto_init(void) +{ + return PSA_SUCCESS; +} + +psa_status_t psa_generate_random(uint8_t *output, size_t output_size) +{ + const struct arm_tee_crypto_pack_iovec iov = { + .function_id = ARM_TEE_CRYPTO_GENERATE_RANDOM_SID, + }; + psa_invec in_vec[] = { + {.base = &iov, .len = sizeof(struct arm_tee_crypto_pack_iovec)}, + }; + psa_outvec out_vec[] = { + {.base = output, .len = output_size}, + }; + + return API_DISPATCH(in_vec, out_vec); +} + +psa_status_t psa_open_key(psa_key_id_t id, psa_key_id_t *key) +{ + const struct arm_tee_crypto_pack_iovec iov = { + .function_id = ARM_TEE_CRYPTO_OPEN_KEY_SID, + .key_id = id, + }; + psa_invec in_vec[] = { + {.base = &iov, .len = sizeof(struct arm_tee_crypto_pack_iovec)}, + }; + psa_outvec out_vec[] = { + {.base = key, .len = sizeof(psa_key_id_t)}, + }; + + return API_DISPATCH(in_vec, out_vec); +} + +psa_status_t psa_close_key(psa_key_id_t key) +{ + const struct arm_tee_crypto_pack_iovec iov = { + .function_id = ARM_TEE_CRYPTO_CLOSE_KEY_SID, + .key_id = key, + }; + psa_invec in_vec[] = { + {.base = &iov, .len = sizeof(struct arm_tee_crypto_pack_iovec)}, + }; + + return API_DISPATCH_NO_OUTVEC(in_vec); +} + +psa_status_t psa_import_key(const psa_key_attributes_t *attributes, + const uint8_t *data, + size_t data_length, + psa_key_id_t *key) +{ + struct arm_tee_crypto_pack_iovec iov = { + .function_id = ARM_TEE_CRYPTO_IMPORT_KEY_SID, + }; + psa_invec in_vec[] = { + {.base = &iov, .len = sizeof(struct arm_tee_crypto_pack_iovec)}, + {.base = attributes, .len = sizeof(psa_key_attributes_t)}, + {.base = data, .len = data_length}, + }; + psa_outvec out_vec[] = { + {.base = key, .len = sizeof(psa_key_id_t)}, + }; + + return API_DISPATCH(in_vec, out_vec); +} + +psa_status_t psa_destroy_key(psa_key_id_t key) +{ + struct arm_tee_crypto_pack_iovec iov = { + .function_id = ARM_TEE_CRYPTO_DESTROY_KEY_SID, + .key_id = key, + }; + psa_invec in_vec[] = { + {.base = &iov, .len = sizeof(struct arm_tee_crypto_pack_iovec)}, + }; + + return API_DISPATCH_NO_OUTVEC(in_vec); +} + +psa_status_t psa_get_key_attributes(psa_key_id_t key, + psa_key_attributes_t *attributes) +{ + struct arm_tee_crypto_pack_iovec iov = { + .function_id = ARM_TEE_CRYPTO_GET_KEY_ATTRIBUTES_SID, + .key_id = key, + }; + psa_invec in_vec[] = { + {.base = &iov, .len = sizeof(struct arm_tee_crypto_pack_iovec)}, + }; + psa_outvec out_vec[] = { + {.base = attributes, .len = sizeof(psa_key_attributes_t)}, + }; + + return API_DISPATCH(in_vec, out_vec); +} + +void psa_reset_key_attributes(psa_key_attributes_t *attributes) +{ + if (attributes != NULL) { + memset(attributes, 0, sizeof(*attributes)); + } +} + +psa_status_t psa_export_key(psa_key_id_t key, + uint8_t *data, + size_t data_size, + size_t *data_length) +{ + psa_status_t status; + struct arm_tee_crypto_pack_iovec iov = { + .function_id = ARM_TEE_CRYPTO_EXPORT_KEY_SID, + .key_id = key, + }; + psa_invec in_vec[] = { + {.base = &iov, .len = sizeof(struct arm_tee_crypto_pack_iovec)}, + }; + psa_outvec out_vec[] = { + {.base = data, .len = data_size}, + }; + + status = API_DISPATCH(in_vec, out_vec); + *data_length = out_vec[0].len; + + return status; +} + +psa_status_t psa_export_public_key(psa_key_id_t key, + uint8_t *data, + size_t data_size, + size_t *data_length) +{ + psa_status_t status; + struct arm_tee_crypto_pack_iovec iov = { + .function_id = ARM_TEE_CRYPTO_EXPORT_PUBLIC_KEY_SID, + .key_id = key, + }; + psa_invec in_vec[] = { + {.base = &iov, .len = sizeof(struct arm_tee_crypto_pack_iovec)}, + }; + psa_outvec out_vec[] = { + {.base = data, .len = data_size}, + }; + + status = API_DISPATCH(in_vec, out_vec); + *data_length = out_vec[0].len; + + return status; +} + +psa_status_t psa_generate_key(const psa_key_attributes_t *attributes, + psa_key_id_t *key) +{ + struct arm_tee_crypto_pack_iovec iov = { + .function_id = ARM_TEE_CRYPTO_GENERATE_KEY_SID, + }; + psa_invec in_vec[] = { + {.base = &iov, .len = sizeof(struct arm_tee_crypto_pack_iovec)}, + {.base = attributes, .len = sizeof(psa_key_attributes_t)}, + }; + psa_outvec out_vec[] = { + {.base = key, .len = sizeof(psa_key_id_t)}, + }; + + return API_DISPATCH(in_vec, out_vec); +} + +psa_status_t psa_hash_compute(psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + uint8_t *hash, + size_t hash_size, + size_t *hash_length) +{ + psa_status_t status; + struct arm_tee_crypto_pack_iovec iov = { + .function_id = ARM_TEE_CRYPTO_HASH_COMPUTE_SID, + .alg = alg, + }; + psa_invec in_vec[] = { + {.base = &iov, .len = sizeof(struct arm_tee_crypto_pack_iovec)}, + {.base = input, .len = input_length}, + }; + psa_outvec out_vec[] = { + {.base = hash, .len = hash_size}, + }; + + status = API_DISPATCH(in_vec, out_vec); + *hash_length = out_vec[0].len; + + return status; +} + +psa_status_t psa_hash_setup(psa_hash_operation_t *operation, psa_algorithm_t alg) +{ + struct arm_tee_crypto_pack_iovec iov = { + .function_id = ARM_TEE_CRYPTO_HASH_SETUP_SID, + .alg = alg, + .op_handle = operation->handle, + }; + psa_invec in_vec[] = { + {.base = &iov, .len = sizeof(struct arm_tee_crypto_pack_iovec)}, + }; + psa_outvec out_vec[] = { + {.base = &(operation->handle), .len = sizeof(uint32_t)}, + }; + + return API_DISPATCH(in_vec, out_vec); +} + +psa_status_t psa_hash_update(psa_hash_operation_t *operation, + const uint8_t *input, + size_t input_length) +{ + struct arm_tee_crypto_pack_iovec iov = { + .function_id = ARM_TEE_CRYPTO_HASH_UPDATE_SID, + .op_handle = operation->handle, + }; + psa_invec in_vec[] = { + {.base = &iov, .len = sizeof(struct arm_tee_crypto_pack_iovec)}, + {.base = input, .len = input_length}, + }; + + return API_DISPATCH_NO_OUTVEC(in_vec); +} + +psa_status_t psa_hash_finish(psa_hash_operation_t *operation, + uint8_t *hash, + size_t hash_size, + size_t *hash_length) +{ + psa_status_t status; + struct arm_tee_crypto_pack_iovec iov = { + .function_id = ARM_TEE_CRYPTO_HASH_FINISH_SID, + .op_handle = operation->handle, + }; + psa_invec in_vec[] = { + {.base = &iov, .len = sizeof(struct arm_tee_crypto_pack_iovec)}, + }; + psa_outvec out_vec[] = { + {.base = &(operation->handle), .len = sizeof(uint32_t)}, + {.base = hash, .len = hash_size}, + }; + + status = API_DISPATCH(in_vec, out_vec); + *hash_length = out_vec[1].len; + + return status; +} + +psa_status_t psa_hash_abort(psa_hash_operation_t *operation) +{ + struct arm_tee_crypto_pack_iovec iov = { + .function_id = ARM_TEE_CRYPTO_HASH_ABORT_SID, + .op_handle = operation->handle, + }; + psa_invec in_vec[] = { + {.base = &iov, .len = sizeof(struct arm_tee_crypto_pack_iovec)}, + }; + psa_outvec out_vec[] = { + {.base = &(operation->handle), .len = sizeof(uint32_t)}, + }; + + return API_DISPATCH(in_vec, out_vec); +} + +psa_status_t psa_sign_hash(psa_key_id_t key, + psa_algorithm_t alg, + const uint8_t *hash, + size_t hash_length, + uint8_t *signature, + size_t signature_size, + size_t *signature_length) +{ + psa_status_t status; + struct arm_tee_crypto_pack_iovec iov = { + .function_id = ARM_TEE_CRYPTO_ASYMMETRIC_SIGN_HASH_SID, + .key_id = key, + .alg = alg, + }; + psa_invec in_vec[] = { + {.base = &iov, .len = sizeof(struct arm_tee_crypto_pack_iovec)}, + {.base = hash, .len = hash_length}, + }; + psa_outvec out_vec[] = { + {.base = signature, .len = signature_size}, + }; + + status = API_DISPATCH(in_vec, out_vec); + *signature_length = out_vec[0].len; + + return status; +} + +psa_status_t psa_verify_hash(psa_key_id_t key, + psa_algorithm_t alg, + const uint8_t *hash, + size_t hash_length, + const uint8_t *signature, + size_t signature_length) +{ + struct arm_tee_crypto_pack_iovec iov = { + .function_id = ARM_TEE_CRYPTO_ASYMMETRIC_VERIFY_HASH_SID, + .key_id = key, + .alg = alg, + }; + psa_invec in_vec[] = { + {.base = &iov, .len = sizeof(struct arm_tee_crypto_pack_iovec)}, + {.base = hash, .len = hash_length}, + {.base = signature, .len = signature_length}, + }; + + return API_DISPATCH_NO_OUTVEC(in_vec); +} diff --git a/zephyr/src/arm_tee_ns_interface.c b/zephyr/src/arm_tee_ns_interface.c new file mode 100644 index 0000000000..6efc791c4a --- /dev/null +++ b/zephyr/src/arm_tee_ns_interface.c @@ -0,0 +1,65 @@ +/* arm_tee_ns_interface.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + */ + +#include +#include + +#include "arm_tee_ns_interface.h" +#include "psa/client.h" + +/* Global mutex used by the PSA dispatcher. */ +K_MUTEX_DEFINE(arm_tee_mutex); + +int32_t arm_tee_ns_interface_dispatch(arm_tee_veneer_fn fn, + uint32_t arg0, uint32_t arg1, + uint32_t arg2, uint32_t arg3) +{ + bool isr_mode = k_is_in_isr() || k_is_pre_kernel(); + int saved_prio = 0; + int32_t result; + + if (!isr_mode) { + if (k_mutex_lock(&arm_tee_mutex, K_FOREVER) != 0) { + return (int32_t)PSA_ERROR_GENERIC_ERROR; + } + +#if !defined(CONFIG_ARM_NONSECURE_PREEMPTIBLE_SECURE_CALLS) + saved_prio = k_thread_priority_get(k_current_get()); + k_thread_priority_set(k_current_get(), K_HIGHEST_THREAD_PRIO); +#endif + } + +#if defined(CONFIG_FPU_SHARING) + struct fpu_ctx_full context_buffer; + z_arm_save_fp_context(&context_buffer); +#endif + + result = fn(arg0, arg1, arg2, arg3); + +#if defined(CONFIG_FPU_SHARING) + z_arm_restore_fp_context(&context_buffer); +#endif + + if (!isr_mode) { +#if !defined(CONFIG_ARM_NONSECURE_PREEMPTIBLE_SECURE_CALLS) + k_thread_priority_set(k_current_get(), saved_prio); +#endif + k_mutex_unlock(&arm_tee_mutex); + } + + return result; +} + +uint32_t arm_tee_ns_interface_init(void) +{ + return PSA_SUCCESS; +} diff --git a/zephyr/src/arm_tee_ps_api.c b/zephyr/src/arm_tee_ps_api.c new file mode 100644 index 0000000000..2d2971a630 --- /dev/null +++ b/zephyr/src/arm_tee_ps_api.c @@ -0,0 +1,128 @@ +/* arm_tee_ps_api.c + * + * ARM TEE Protected Storage NS API wrappers. + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + */ + +#include "psa/client.h" +#include "psa/protected_storage.h" +#include "psa_manifest/sid.h" +#include "arm_tee_ps_defs.h" + +struct rot_psa_ps_storage_info_t { + rot_size_t capacity; + rot_size_t size; + psa_storage_create_flags_t flags; +}; + +psa_status_t psa_ps_set(psa_storage_uid_t uid, + size_t data_length, + const void *p_data, + psa_storage_create_flags_t create_flags) +{ + psa_invec in_vec[] = { + { .base = &uid, .len = sizeof(uid) }, + { .base = p_data, .len = data_length }, + { .base = &create_flags, .len = sizeof(create_flags) } + }; + + return psa_call(ARM_TEE_PROTECTED_STORAGE_SERVICE_HANDLE, + ARM_TEE_PS_SET, in_vec, IOVEC_LEN(in_vec), NULL, 0); +} + +psa_status_t psa_ps_get(psa_storage_uid_t uid, + size_t data_offset, + size_t data_size, + void *p_data, + size_t *p_data_length) +{ + psa_status_t status; + rot_size_t data_offset_param; + + psa_invec in_vec[] = { + { .base = &uid, .len = sizeof(uid) }, + { .base = &data_offset_param, .len = sizeof(data_offset_param) } + }; + + psa_outvec out_vec[] = { + { .base = p_data, .len = data_size } + }; + + if (data_offset > ROT_SIZE_MAX) { + return PSA_ERROR_INVALID_ARGUMENT; + } + data_offset_param = (rot_size_t)data_offset; + + if (p_data_length == NULL) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + status = psa_call(ARM_TEE_PROTECTED_STORAGE_SERVICE_HANDLE, + ARM_TEE_PS_GET, in_vec, IOVEC_LEN(in_vec), + out_vec, IOVEC_LEN(out_vec)); + + *p_data_length = out_vec[0].len; + + return status; +} + +psa_status_t psa_ps_get_info(psa_storage_uid_t uid, + struct psa_storage_info_t *p_info) +{ + psa_status_t status; + struct rot_psa_ps_storage_info_t info_param = {0}; + + psa_invec in_vec[] = { + { .base = &uid, .len = sizeof(uid) } + }; + + psa_outvec out_vec[] = { + { .base = &info_param, .len = sizeof(info_param) } + }; + + if (p_info == NULL) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + status = psa_call(ARM_TEE_PROTECTED_STORAGE_SERVICE_HANDLE, + ARM_TEE_PS_GET_INFO, in_vec, IOVEC_LEN(in_vec), + out_vec, IOVEC_LEN(out_vec)); + + p_info->capacity = info_param.capacity; + p_info->size = info_param.size; + p_info->flags = info_param.flags; + + return status; +} + +psa_status_t psa_ps_remove(psa_storage_uid_t uid) +{ + psa_invec in_vec[] = { + { .base = &uid, .len = sizeof(uid) } + }; + + return psa_call(ARM_TEE_PROTECTED_STORAGE_SERVICE_HANDLE, + ARM_TEE_PS_REMOVE, in_vec, IOVEC_LEN(in_vec), NULL, 0); +} + +uint32_t psa_ps_get_support(void) +{ + uint32_t support_flags = 0; + psa_outvec out_vec[] = { + { .base = &support_flags, .len = sizeof(support_flags) } + }; + + (void)psa_call(ARM_TEE_PROTECTED_STORAGE_SERVICE_HANDLE, + ARM_TEE_PS_GET_SUPPORT, NULL, 0, + out_vec, IOVEC_LEN(out_vec)); + + return support_flags; +} diff --git a/zephyr/src/wolfboot_psa_ns_api.c b/zephyr/src/wolfboot_psa_ns_api.c new file mode 100644 index 0000000000..f43b5b9c9e --- /dev/null +++ b/zephyr/src/wolfboot_psa_ns_api.c @@ -0,0 +1,58 @@ +/* wolfboot_psa_ns_api.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + */ + +#include "psa/client.h" +#include "arm_tee_ns_interface.h" +#include "arm_tee_psa_call_pack.h" +#include + +uint32_t psa_framework_version(void) +{ + return arm_tee_ns_interface_dispatch( + (arm_tee_veneer_fn)arm_tee_psa_framework_version_veneer, 0, 0, 0, 0); +} + +uint32_t psa_version(uint32_t sid) +{ + return arm_tee_ns_interface_dispatch( + (arm_tee_veneer_fn)arm_tee_psa_version_veneer, sid, 0, 0, 0); +} + +psa_status_t psa_call(psa_handle_t handle, int32_t type, + const psa_invec *in_vec, size_t in_len, + psa_outvec *out_vec, size_t out_len) +{ + if ((in_len > PSA_MAX_IOVEC) || (out_len > PSA_MAX_IOVEC)) { + return PSA_ERROR_PROGRAMMER_ERROR; + } + + uint32_t ctrl_param = PARAM_PACK(type, in_len, out_len); + + return arm_tee_ns_interface_dispatch( + (arm_tee_veneer_fn)arm_tee_psa_call_veneer, + (uint32_t)handle, + ctrl_param, + (uint32_t)(uintptr_t)in_vec, + (uint32_t)(uintptr_t)out_vec); +} + +psa_handle_t psa_connect(uint32_t sid, uint32_t version) +{ + return arm_tee_ns_interface_dispatch( + (arm_tee_veneer_fn)arm_tee_psa_connect_veneer, sid, version, 0, 0); +} + +void psa_close(psa_handle_t handle) +{ + (void)arm_tee_ns_interface_dispatch( + (arm_tee_veneer_fn)arm_tee_psa_close_veneer, (uint32_t)handle, 0, 0, 0); +} diff --git a/zephyr/src/wolfboot_veneers_stub.c b/zephyr/src/wolfboot_veneers_stub.c new file mode 100644 index 0000000000..87cd69bb8a --- /dev/null +++ b/zephyr/src/wolfboot_veneers_stub.c @@ -0,0 +1,55 @@ +/* wolfboot_veneers_stub.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + */ + +#include "psa/client.h" +#include + +#if defined(__GNUC__) +#define WOLFBOOT_WEAK __attribute__((weak)) +#else +#define WOLFBOOT_WEAK +#endif + +WOLFBOOT_WEAK uint32_t arm_tee_psa_framework_version_veneer(void) +{ + return 0; +} + +WOLFBOOT_WEAK uint32_t arm_tee_psa_version_veneer(uint32_t sid) +{ + (void)sid; + return 0; +} + +WOLFBOOT_WEAK psa_handle_t arm_tee_psa_connect_veneer(uint32_t sid, uint32_t version) +{ + (void)sid; + (void)version; + return (psa_handle_t)PSA_ERROR_CONNECTION_REFUSED; +} + +WOLFBOOT_WEAK psa_status_t arm_tee_psa_call_veneer(psa_handle_t handle, + uint32_t ctrl_param, + const psa_invec *in_vec, + psa_outvec *out_vec) +{ + (void)handle; + (void)ctrl_param; + (void)in_vec; + (void)out_vec; + return PSA_ERROR_NOT_SUPPORTED; +} + +WOLFBOOT_WEAK void arm_tee_psa_close_veneer(psa_handle_t handle) +{ + (void)handle; +}