diff --git a/.github/workflows/test-configs.yml b/.github/workflows/test-configs.yml index 2260582b5e..3b5b94f08d 100644 --- a/.github/workflows/test-configs.yml +++ b/.github/workflows/test-configs.yml @@ -498,7 +498,37 @@ jobs: uses: ./.github/workflows/test-build.yml with: arch: host - config-file: ./config/examples/sim-wolfHSM-client.config + config-file: ./config/examples/sim-wolfHSM-client-ecc.config + + sim_wolfhsm_client_mldsa_test: + uses: ./.github/workflows/test-build.yml + with: + arch: host + config-file: ./config/examples/sim-wolfHSM-client-mldsa.config + + sim_wolfhsm_client_certchain_ecc_test: + uses: ./.github/workflows/test-build.yml + with: + arch: host + config-file: ./config/examples/sim-wolfHSM-client-certchain-ecc.config + + sim_wolfhsm_client_certchain_rsa4096_test: + uses: ./.github/workflows/test-build.yml + with: + arch: host + config-file: ./config/examples/sim-wolfHSM-client-certchain-rsa4096.config + + sim_wolfhsm_server_certchain_ecc_test: + uses: ./.github/workflows/test-build.yml + with: + arch: host + config-file: ./config/examples/sim-wolfHSM-server-certchain-ecc.config + + sim_wolfhsm_server_certchain_rsa4096_test: + uses: ./.github/workflows/test-build.yml + with: + arch: host + config-file: ./config/examples/sim-wolfHSM-server-certchain-rsa4096.config rp2350_test: uses: ./.github/workflows/test-build-pico-sdk.yml diff --git a/.github/workflows/test-external-library-paths.yml b/.github/workflows/test-external-library-paths.yml index 9bf221ead9..7d11c471ba 100644 --- a/.github/workflows/test-external-library-paths.yml +++ b/.github/workflows/test-external-library-paths.yml @@ -24,7 +24,7 @@ jobs: build-tpm-tools: true - name: "external wolfHSM" - config: "config/examples/sim-wolfHSM-client.config" + config: "config/examples/sim-wolfHSM-client-ecc.config" - name: "Unit tests" config: "" diff --git a/.github/workflows/test-wolfhsm-simulator.yml b/.github/workflows/test-wolfhsm-simulator.yml index be55426e0a..afbc47a07f 100644 --- a/.github/workflows/test-wolfhsm-simulator.yml +++ b/.github/workflows/test-wolfhsm-simulator.yml @@ -14,14 +14,36 @@ jobs: strategy: matrix: config: - - name: "Standard wolfHSM" - file: "config/examples/sim-wolfHSM-client.config" - - name: "wolfHSM ML-DSA" + - name: "wolfHSM client ECC" + file: "config/examples/sim-wolfHSM-client-ecc.config" + needs_posix_server: true + posix_server_nvminit: false + needs_nvm_image: false + - name: "wolfHSM client ML-DSA" file: "config/examples/sim-wolfHSM-client-mldsa.config" - - name: "wolfHSM cert chain verify" - file: "config/examples/sim-wolfHSM-client-certchain.config" - - name: "wolfHSM server cert chain verify" - file: "config/examples/sim-wolfHSM-server-certchain.config" + needs_posix_server: true + posix_server_nvminit: false + needs_nvm_image: false + - name: "wolfHSM client cert chain verify ECC" + file: "config/examples/sim-wolfHSM-client-certchain-ecc.config" + needs_posix_server: true + posix_server_nvminit: true + needs_nvm_image: false + - name: "wolfHSM client cert chain verify RSA4096" + file: "config/examples/sim-wolfHSM-client-certchain-rsa4096.config" + needs_posix_server: true + posix_server_nvminit: true + needs_nvm_image: false + - name: "wolfHSM server cert chain verify ECC" + file: "config/examples/sim-wolfHSM-server-certchain-ecc.config" + needs_posix_server: false + posix_server_nvminit: false + needs_nvm_image: true + - name: "wolfHSM server cert chain verify RSA4096" + file: "config/examples/sim-wolfHSM-server-certchain-rsa4096.config" + needs_posix_server: false + posix_server_nvminit: false + needs_nvm_image: true fail-fast: false @@ -98,15 +120,15 @@ jobs: make clean && make test-sim-internal-flash-with-update - name: Build example POSIX TCP server - if: matrix.config.name != 'wolfHSM server cert chain verify' + if: matrix.config.needs_posix_server run: cd lib/wolfHSM/examples/posix/wh_posix_server && make WOLFSSL_DIR=../../../../wolfssl # Start the server in the background - name: Run POSIX TCP server - if: matrix.config.name != 'wolfHSM server cert chain verify' + if: matrix.config.needs_posix_server run: | cd lib/wolfHSM/examples/posix/wh_posix_server - if [ "${{ matrix.config.name }}" = "wolfHSM cert chain verify" ]; then + if [ "${{ matrix.config.posix_server_nvminit }}" = "true" ]; then tmpfile=$(mktemp) echo "obj 1 0xFFFF 0x0000 \"cert CA\" ../../../../../test-dummy-ca/root-cert.der" >> $tmpfile ./Build/wh_posix_server.elf --type tcp --nvminit $tmpfile & @@ -120,7 +142,7 @@ jobs: # For testing the wolfHSM server cert chain verify feature, we need to create an NVM image containing our root CA that # the internal wolfHSM server can load. - name: Create NVM image for wolfHSM server cert chain verify - if: matrix.config.name == 'wolfHSM server cert chain verify' + if: matrix.config.needs_nvm_image run: | make -C lib/wolfHSM/tools/whnvmtool tmpfile=$(mktemp) @@ -134,6 +156,6 @@ jobs: # Kill the server if it is still running - name: Kill POSIX TCP server - if: always() && matrix.config.name != 'wolfHSM server cert chain verify' + if: always() && matrix.config.needs_posix_server run: | kill $TCP_SERVER_PID || true diff --git a/Makefile b/Makefile index 1bb259a2d0..61fa0fe083 100644 --- a/Makefile +++ b/Makefile @@ -40,16 +40,84 @@ ifneq ($(TARGET),library) OBJS+=./hal/$(TARGET).o endif +# User-provided key configuration +# - USER_PRIVATE_KEY: Path to user's private key (DER format) +# - USER_PUBLIC_KEY: Path to user's public key (DER format) +# - USER_CERT_CHAIN: Path to user's certificate chain (DER format) +# All must be provided together, or none at all + +# Validate USER_PRIVATE_KEY and USER_PUBLIC_KEY are used together +ifneq ($(USER_PRIVATE_KEY),) + ifeq ($(USER_PUBLIC_KEY),) + $(error USER_PRIVATE_KEY requires USER_PUBLIC_KEY to also be set) + endif + ifeq ($(wildcard $(USER_PRIVATE_KEY)),) + $(error USER_PRIVATE_KEY file not found: $(USER_PRIVATE_KEY)) + endif +endif + +ifneq ($(USER_PUBLIC_KEY),) + ifeq ($(USER_PRIVATE_KEY),) + $(error USER_PUBLIC_KEY requires USER_PRIVATE_KEY to also be set) + endif + ifeq ($(wildcard $(USER_PUBLIC_KEY)),) + $(error USER_PUBLIC_KEY file not found: $(USER_PUBLIC_KEY)) + endif +endif + +# Validate USER_CERT_CHAIN requires USER_PRIVATE_KEY and USER_PUBLIC_KEY +ifneq ($(USER_CERT_CHAIN),) + ifeq ($(USER_PRIVATE_KEY),) + $(error USER_CERT_CHAIN requires USER_PRIVATE_KEY to also be set) + endif + ifeq ($(USER_PUBLIC_KEY),) + $(error USER_CERT_CHAIN requires USER_PUBLIC_KEY to also be set) + endif + ifeq ($(wildcard $(USER_CERT_CHAIN)),) + $(error USER_CERT_CHAIN file not found: $(USER_CERT_CHAIN)) + endif +endif + +# Validate USER_NVM_INIT if provided +# - USER_NVM_INIT: Path to user's NVM init file for wolfHSM NVM image generation +ifneq ($(USER_NVM_INIT),) + ifeq ($(wildcard $(USER_NVM_INIT)),) + $(error USER_NVM_INIT file not found: $(USER_NVM_INIT)) + endif +endif + +# Helper variable to detect if user-provided keys are being used +# This is used to skip auto-generated NVM images when users provide their own keys +ifneq ($(USER_PRIVATE_KEY),) + _USER_PROVIDED_KEYS:=1 +else ifneq ($(USER_PUBLIC_KEY),) + _USER_PROVIDED_KEYS:=1 +else ifneq ($(USER_CERT_CHAIN),) + _USER_PROVIDED_KEYS:=1 +endif + +# USER_NVM_INIT overrides default NVM_CONFIG when provided +ifneq ($(USER_NVM_INIT),) + NVM_CONFIG:=$(USER_NVM_INIT) +endif + ifeq ($(SIGN),NONE) PRIVATE_KEY= else - # Key selection logic: - # - Without CERT_CHAIN_GEN: Single key (wolfboot_signing_private_key.der) signs everything - # - With CERT_CHAIN_GEN: Generate cert chain, use leaf key (test-dummy-ca/leaf-prvkey.der) for signing - ifneq ($(CERT_CHAIN_GEN),) - PRIVATE_KEY=test-dummy-ca/leaf-prvkey.der + # Private Key selection logic: + # 1. User-provided private keys take precedence (USER_PRIVATE_KEY) + # 2. Otherwise, if CERT_CHAIN_VERIFY, use generated dummy cert chain leaf key + # 3. Otherwise use standard generated private key + ifneq ($(USER_PRIVATE_KEY),) + PRIVATE_KEY=$(USER_PRIVATE_KEY) else - PRIVATE_KEY=wolfboot_signing_private_key.der + ifneq ($(CERT_CHAIN_VERIFY),) + # Auto-generate cert chain mode - use leaf key + PRIVATE_KEY?=test-dummy-ca/leaf-prvkey.der + else + # No cert chain verification - standard single key mode + PRIVATE_KEY?=wolfboot_signing_private_key.der + endif endif ifeq ($(FLASH_OTP_KEYSTORE),1) OBJS+=./src/flash_otp_keystore.o @@ -268,21 +336,31 @@ hal/$(TARGET).o: keytools_check: keytools -# Generate the initial signing key -# - Always creates wolfboot_signing_private_key.der -# - If CERT_CHAIN_GEN is set, also generates cert chain with leaf key +# Generate the initial signing key (only if not using user-provided keys) +# - Creates wolfboot_signing_private_key.der when USER_PRIVATE_KEY is not set +# - If CERT_CHAIN_VERIFY is enabled and USER_CERT_CHAIN not provided, also generates cert chain with leaf key wolfboot_signing_private_key.der: +ifeq ($(USER_PRIVATE_KEY),) $(Q)$(MAKE) keytools_check $(Q)(test $(SIGN) = NONE) || ($(SIGN_ENV) "$(KEYGEN_TOOL)" $(KEYGEN_OPTIONS) -g wolfboot_signing_private_key.der) || true $(Q)(test $(SIGN) = NONE) && (echo "// SIGN=NONE" > src/keystore.c) || true $(Q)(test "$(FLASH_OTP_KEYSTORE)" = "1") && (make -C tools/keytools/otp) || true - $(Q)(test $(SIGN) = NONE) || (test "$(CERT_CHAIN_VERIFY)" = "") || (test "$(CERT_CHAIN_GEN)" = "") || (tools/scripts/sim-gen-dummy-chain.sh --algo $(CERT_CHAIN_GEN_ALGO) --leaf wolfboot_signing_private_key.der) + $(Q)(test $(SIGN) = NONE) || (test "$(CERT_CHAIN_VERIFY)" = "") || (test "$(USER_CERT_CHAIN)" != "") || (tools/scripts/sim-gen-dummy-chain.sh --algo $(CERT_CHAIN_GEN_ALGO) --leaf wolfboot_signing_private_key.der) +else + @echo "Using user-provided private key: $(USER_PRIVATE_KEY)" +endif -# CERT_CHAIN_GEN only: Ensure leaf key exists after cert chain generation -ifneq ($(CERT_CHAIN_GEN),) +# Auto-generate cert chain mode: Ensure leaf key exists after cert chain generation +# Only applies when CERT_CHAIN_VERIFY is enabled and USER_CERT_CHAIN not provided +# Skip this when using user-provided keys +ifeq ($(USER_PRIVATE_KEY),) +ifneq ($(CERT_CHAIN_VERIFY),) +ifeq ($(USER_CERT_CHAIN),) $(PRIVATE_KEY): wolfboot_signing_private_key.der @test -f $(PRIVATE_KEY) || (echo "Error: $(PRIVATE_KEY) not found" && exit 1) endif +endif +endif $(SECONDARY_PRIVATE_KEY): $(PRIVATE_KEY) keystore.der $(Q)$(MAKE) keytools_check @@ -320,6 +398,13 @@ endif ifeq ($(WOLFHSM_SERVER),1) _DO_WH_NVMTOOL:=1 endif +# Disable NVM image generation if user-provided keys without explicit USER_NVM_INIT +# (providing USER_NVM_INIT allows users to supply keys and still generate a custom NVM image) +ifeq ($(_USER_PROVIDED_KEYS),1) + ifeq ($(USER_NVM_INIT),) + _DO_WH_NVMTOOL:= + endif +endif ifeq ($(_DO_WH_NVMTOOL),1) whnvmtool: @echo "Building wolfHSM NVM tool" @@ -363,9 +448,7 @@ internal_flash.dd: $(BINASSEMBLE) wolfboot.bin $(BOOT_IMG) $(PRIVATE_KEY) test-a $(Q)dd if=/dev/zero bs=1 count=$$(($(WOLFBOOT_SECTOR_SIZE))) > /tmp/swap make assemble_internal_flash.dd -ifeq ($(WOLFHSM_CLIENT),1) -factory.bin: $(BINASSEMBLE) wolfboot.bin $(BOOT_IMG) $(PRIVATE_KEY) test-app/image_v1_signed.bin nvm-image -else ifeq ($(WOLFHSM_SERVER),1) +ifeq ($(_DO_WH_NVMTOOL),1) factory.bin: $(BINASSEMBLE) wolfboot.bin $(BOOT_IMG) $(PRIVATE_KEY) test-app/image_v1_signed.bin nvm-image else factory.bin: $(BINASSEMBLE) wolfboot.bin $(BOOT_IMG) $(PRIVATE_KEY) test-app/image_v1_signed.bin @@ -435,7 +518,15 @@ srec: wolfboot.srec @echo "\t[ELF2SREC] $@" @$(OBJCOPY) -O srec $^ $@ +# Keystore generation: use user-provided public key if available +ifneq ($(USER_PUBLIC_KEY),) +src/keystore.c: $(USER_PUBLIC_KEY) + @echo "Generating keystore from user-provided public key: $(USER_PUBLIC_KEY)" + $(Q)$(MAKE) keytools_check + $(Q)$(SIGN_ENV) "$(KEYGEN_TOOL)" $(KEYGEN_OPTIONS) --force -i $(USER_PUBLIC_KEY) +else src/keystore.c: $(PRIVATE_KEY) +endif flash_keystore: src/flash_otp_keystore.o @@ -479,7 +570,7 @@ utilsclean: clean keysclean: clean $(Q)rm -f *.pem *.der tags ./src/*_pub_key.c ./src/keystore.c include/target.h - $(Q)(test "$(CERT_CHAIN_GEN)" = "") || rm -rf test-dummy-ca || true + $(Q)(test "$(CERT_CHAIN_VERIFY)" = "" || test "$(USER_CERT_CHAIN)" != "") || rm -rf test-dummy-ca || true distclean: clean keysclean utilsclean $(Q)rm -f *.bin *.elf diff --git a/arch.mk b/arch.mk index cde222483c..73d5b17bd9 100644 --- a/arch.mk +++ b/arch.mk @@ -1160,6 +1160,14 @@ ifeq ($(ARCH),sim) $(WOLFBOOT_LIB_WOLFHSM)/src/wh_transport_mem.o endif + # wolfHSM NVM image generation support for simulator + # User must provide NVM_CONFIG for their specific setup + ifneq ($(filter 1,$(WOLFHSM_CLIENT) $(WOLFHSM_SERVER)),) + WH_NVM_BIN ?= whNvmImage.bin + WH_NVM_HEX ?= whNvmImage.hex + WH_NVM_PART_SIZE ?= 16384 # must match partition size in hal/sim.c + WH_NVM_BASE_ADDRESS ?= 0x0 + endif endif # Infineon AURIX Tricore @@ -1201,10 +1209,11 @@ ifeq ($(ARCH), AURIX_TC3) WH_NVM_BASE_ADDRESS ?= 0xAFC00000 # Select config file based on certificate chain verification + # Use ?= to allow user override via command line (e.g., for offline cert chain) ifneq ($(CERT_CHAIN_VERIFY),) - NVM_CONFIG = tools/scripts/tc3xx/wolfBoot-wolfHSM-dummy-certchain.nvminit + NVM_CONFIG ?= tools/scripts/tc3xx/wolfBoot-wolfHSM-dummy-certchain.nvminit else - NVM_CONFIG = tools/scripts/tc3xx/wolfBoot-wolfHSM-keys.nvminit + NVM_CONFIG ?= tools/scripts/tc3xx/wolfBoot-wolfHSM-keys.nvminit endif endif diff --git a/config/examples/aurix-tc375.config b/config/examples/aurix-tc375-ecc.config similarity index 100% rename from config/examples/aurix-tc375.config rename to config/examples/aurix-tc375-ecc.config diff --git a/config/examples/aurix-tc375-elf.config b/config/examples/aurix-tc375-elf-ecc.config similarity index 100% rename from config/examples/aurix-tc375-elf.config rename to config/examples/aurix-tc375-elf-ecc.config diff --git a/config/examples/aurix-tc375-elf-wolfHSM-certs.config b/config/examples/aurix-tc375-elf-wolfHSM-certs-ecc.config similarity index 94% rename from config/examples/aurix-tc375-elf-wolfHSM-certs.config rename to config/examples/aurix-tc375-elf-wolfHSM-certs-ecc.config index ad5adab8c6..7be4b5bb12 100644 --- a/config/examples/aurix-tc375-elf-wolfHSM-certs.config +++ b/config/examples/aurix-tc375-elf-wolfHSM-certs-ecc.config @@ -26,17 +26,11 @@ ELF_FLASH_SCATTER=1 # Cert chain options CERT_CHAIN_VERIFY=1 -CERT_CHAIN_GEN=1 # Ensure header is large enough to hold the cert chain (check sign tool output) # for actual length IMAGE_HEADER_SIZE=2048 -# If SIGN=RSA4096, use the below options -#WOLFBOOT_HUGE_STACK=1 -#IMAGE_HEADER_SIZE=4096 - - ARCH_FLASH_OFFSET=0x800A0000 WOLFBOOT_SECTOR_SIZE=0x4000 diff --git a/config/examples/aurix-tc375-elf-wolfHSM-certs-rsa4096.config b/config/examples/aurix-tc375-elf-wolfHSM-certs-rsa4096.config new file mode 100644 index 0000000000..a5b25ef6f8 --- /dev/null +++ b/config/examples/aurix-tc375-elf-wolfHSM-certs-rsa4096.config @@ -0,0 +1,57 @@ +ARCH?=AURIX_TC3 +TARGET?=aurix_tc3xx +SIGN?=RSA4096 +HASH?=SHA256 +DEBUG?=0 +NO_ASM?=1 +WOLFBOOT_VERSION?=1 +V?=0 +SPMATH?=1 +RAM_CODE?=1 +EXT_FLASH?=1 +EXT_BOOT=1 +EXT_UPDATE=1 +EXT_SWAP=1 +FLAGS_INVERT=1 +FLASH_MULTI_SECTOR_ERASE=1 +DEBUG_UART=1 +PRINTF_ENABLED=1 + +# wolfHSM options +WOLFHSM_CLIENT=1 + +# ELF loading specific configuration +ELF=1 +ELF_FLASH_SCATTER=1 + +# Cert chain options +CERT_CHAIN_VERIFY=1 + +# RSA4096 cert chains need the larger header and stack +WOLFBOOT_HUGE_STACK=1 +IMAGE_HEADER_SIZE=4096 + +ARCH_FLASH_OFFSET=0x800A0000 +WOLFBOOT_SECTOR_SIZE=0x4000 + +# ELF memory partitioning (same PFLASH1 space as standard wolfBoot): +# Standard wolfBoot uses 0x80300000-0x80600000 (3MB) for BOOT+UPDATE+SWAP +# ELF mode splits this same space into EXEC+BOOT+UPDATE+SWAP: +# - Execution space: 0x80300000 (~1.5MB) - where app runs after scatter loading +# - BOOT partition: 0x8047C000 (~0.75MB) - where signed ELF file is stored +# - UPDATE partition: 0x8053C000 (~0.75MB) - where update ELF file is stored +# - SWAP sector: 0x805FC000 (16KB) - for atomic updates + +# ELF storage partitions (where signed ELF files are stored) +WOLFBOOT_PARTITION_BOOT_ADDRESS=0x8047C000 +WOLFBOOT_PARTITION_UPDATE_ADDRESS=0x8053C000 +WOLFBOOT_PARTITION_SWAP_ADDRESS=0x805FC000 +WOLFBOOT_PARTITION_SIZE=0xC0000 + +# ELF execution space (where test app runs after scatter loading) +# Uses the same space that would be the BOOT partition in standard mode +# This is only needed to configure the memory regions in the test app linker file +# (see test-app/tc3tc_app.ld). For custom user apps with a different memory layout +# and linker file this is not necessary. +WOLFBOOT_ELF_EXEC_ADDRESS=0x80300000 +WOLFBOOT_ELF_EXEC_SIZE=0x17C000 diff --git a/config/examples/aurix-tc375-elf-wolfHSM.config b/config/examples/aurix-tc375-elf-wolfHSM-ecc.config similarity index 100% rename from config/examples/aurix-tc375-elf-wolfHSM.config rename to config/examples/aurix-tc375-elf-wolfHSM-ecc.config diff --git a/config/examples/aurix-tc375-hsm.config b/config/examples/aurix-tc375-hsm-ecc.config similarity index 100% rename from config/examples/aurix-tc375-hsm.config rename to config/examples/aurix-tc375-hsm-ecc.config diff --git a/config/examples/aurix-tc375-hsm-wolfHSM-certs.config b/config/examples/aurix-tc375-hsm-wolfHSM-certs-ecc.config similarity index 86% rename from config/examples/aurix-tc375-hsm-wolfHSM-certs.config rename to config/examples/aurix-tc375-hsm-wolfHSM-certs-ecc.config index c14d54c9ec..b4f4fe56bf 100644 --- a/config/examples/aurix-tc375-hsm-wolfHSM-certs.config +++ b/config/examples/aurix-tc375-hsm-wolfHSM-certs-ecc.config @@ -23,17 +23,11 @@ WOLFHSM_SERVER=1 # Cert chain options CERT_CHAIN_VERIFY=1 -CERT_CHAIN_GEN=1 # Ensure header is large enough to hold the cert chain (check sign tool output) # for actual length IMAGE_HEADER_SIZE=2048 -# If SIGN=RSA4096, use the below options -#WOLFBOOT_HUGE_STACK=1 -#IMAGE_HEADER_SIZE=4096 - - ARCH_FLASH_OFFSET=0x80028000 WOLFBOOT_SECTOR_SIZE=0x4000 WOLFBOOT_PARTITION_SIZE=0x30000 diff --git a/config/examples/aurix-tc375-hsm-wolfHSM-certs-rsa4096.config b/config/examples/aurix-tc375-hsm-wolfHSM-certs-rsa4096.config new file mode 100644 index 0000000000..86e4992801 --- /dev/null +++ b/config/examples/aurix-tc375-hsm-wolfHSM-certs-rsa4096.config @@ -0,0 +1,36 @@ +ARCH?=AURIX_TC3 +TARGET?=aurix_tc3xx +AURIX_TC3_HSM=1 +SIGN?=RSA4096 +HASH?=SHA256 +DEBUG?=0 +NO_ASM?=1 +WOLFBOOT_VERSION?=1 +V?=0 +SPMATH?=1 +RAM_CODE?=1 +EXT_FLASH?=1 +EXT_BOOT=1 +EXT_UPDATE=1 +EXT_SWAP=1 +FLAGS_INVERT=1 +FLASH_MULTI_SECTOR_ERASE=1 +DEBUG_UART=1 +PRINTF_ENABLED=1 + +# wolfHSM options +WOLFHSM_SERVER=1 + +# Cert chain options +CERT_CHAIN_VERIFY=1 + +# RSA4096 cert chains need the larger header and stack +WOLFBOOT_HUGE_STACK=1 +IMAGE_HEADER_SIZE=4096 + +ARCH_FLASH_OFFSET=0x80028000 +WOLFBOOT_SECTOR_SIZE=0x4000 +WOLFBOOT_PARTITION_SIZE=0x30000 +WOLFBOOT_PARTITION_BOOT_ADDRESS=0x80038000 +WOLFBOOT_PARTITION_UPDATE_ADDRESS=0x80068000 +WOLFBOOT_PARTITION_SWAP_ADDRESS=0x80098000 diff --git a/config/examples/sim-wolfHSM-client-certchain.config b/config/examples/sim-wolfHSM-client-certchain-ecc.config similarity index 88% rename from config/examples/sim-wolfHSM-client-certchain.config rename to config/examples/sim-wolfHSM-client-certchain-ecc.config index 14639088f1..818fb8136b 100644 --- a/config/examples/sim-wolfHSM-client-certchain.config +++ b/config/examples/sim-wolfHSM-client-certchain-ecc.config @@ -9,16 +9,11 @@ SPMATH=1 # Cert chain options CERT_CHAIN_VERIFY=1 -CERT_CHAIN_GEN=1 # Ensure header is large enough to hold the cert chain (check sign tool output) # for actual length IMAGE_HEADER_SIZE=2048 -# If SIGN=RSA4096, use the below options -#WOLFBOOT_HUGE_STACK=1 -#IMAGE_HEADER_SIZE=4096 - # wolfHSM options WOLFHSM_CLIENT=1 diff --git a/config/examples/sim-wolfHSM-client-certchain-rsa4096.config b/config/examples/sim-wolfHSM-client-certchain-rsa4096.config new file mode 100644 index 0000000000..9e837ce2de --- /dev/null +++ b/config/examples/sim-wolfHSM-client-certchain-rsa4096.config @@ -0,0 +1,35 @@ +ARCH=sim +TARGET=sim +SIGN?=RSA4096 +HASH?=SHA256 +WOLFBOOT_SMALL_STACK?=0 +SPI_FLASH=0 +DEBUG=0 +SPMATH=1 + +# Cert chain options +CERT_CHAIN_VERIFY=1 + +# RSA4096 cert chains need the larger header and stack +WOLFBOOT_HUGE_STACK=1 +IMAGE_HEADER_SIZE=4096 + +# wolfHSM options +WOLFHSM_CLIENT=1 + +# sizes should be multiple of system page size +#WOLFBOOT_PARTITION_SIZE=0x40000 +WOLFBOOT_PARTITION_SIZE=0x100000 +WOLFBOOT_SECTOR_SIZE=0x1000 +WOLFBOOT_PARTITION_BOOT_ADDRESS=0x80000 +# if on external flash, it should be multiple of system page size +#WOLFBOOT_PARTITION_UPDATE_ADDRESS=0x100000 +#WOLFBOOT_PARTITION_SWAP_ADDRESS=0x180000 +WOLFBOOT_PARTITION_UPDATE_ADDRESS=0x180000 +WOLFBOOT_PARTITION_SWAP_ADDRESS=0x280000 + +# required for keytools +WOLFBOOT_FIXED_PARTITIONS=1 + +# For debugging XMALLOC/XFREE +#CFLAGS_EXTRA+=-DWOLFBOOT_DEBUG_MALLOC diff --git a/config/examples/sim-wolfHSM-client.config b/config/examples/sim-wolfHSM-client-ecc.config similarity index 100% rename from config/examples/sim-wolfHSM-client.config rename to config/examples/sim-wolfHSM-client-ecc.config diff --git a/config/examples/sim-wolfHSM-server-certchain.config b/config/examples/sim-wolfHSM-server-certchain-ecc.config similarity index 87% rename from config/examples/sim-wolfHSM-server-certchain.config rename to config/examples/sim-wolfHSM-server-certchain-ecc.config index 8ca5cfbc46..806726913d 100644 --- a/config/examples/sim-wolfHSM-server-certchain.config +++ b/config/examples/sim-wolfHSM-server-certchain-ecc.config @@ -9,16 +9,11 @@ SPMATH=1 # Cert chain options CERT_CHAIN_VERIFY=1 -CERT_CHAIN_GEN=1 # Ensure header is large enough to hold the cert chain (check sign tool output) # for actual length IMAGE_HEADER_SIZE=2048 -# If SIGN=RSA4096, use the below options -#WOLFBOOT_HUGE_STACK=1 -#IMAGE_HEADER_SIZE=4096 - # wolfHSM options WOLFHSM_SERVER=1 diff --git a/config/examples/sim-wolfHSM-server-certchain-rsa4096.config b/config/examples/sim-wolfHSM-server-certchain-rsa4096.config new file mode 100644 index 0000000000..c7f416749b --- /dev/null +++ b/config/examples/sim-wolfHSM-server-certchain-rsa4096.config @@ -0,0 +1,32 @@ +ARCH=sim +TARGET=sim +SIGN?=RSA4096 +HASH?=SHA256 +WOLFBOOT_SMALL_STACK?=0 +SPI_FLASH=0 +DEBUG=0 +SPMATH=1 + +# Cert chain options +CERT_CHAIN_VERIFY=1 + +# RSA4096 cert chains need the larger header and stack +WOLFBOOT_HUGE_STACK=1 +IMAGE_HEADER_SIZE=4096 + +# wolfHSM options +WOLFHSM_SERVER=1 + +# sizes should be multiple of system page size +WOLFBOOT_PARTITION_SIZE=0x200000 +WOLFBOOT_SECTOR_SIZE=0x1000 +WOLFBOOT_PARTITION_BOOT_ADDRESS=0x80000 +# if on external flash, it should be multiple of system page size +WOLFBOOT_PARTITION_UPDATE_ADDRESS=0x280000 +WOLFBOOT_PARTITION_SWAP_ADDRESS=0x480000 + +# required for keytools +WOLFBOOT_FIXED_PARTITIONS=1 + +# For debugging XMALLOC/XFREE +#CFLAGS_EXTRA+=-DWOLFBOOT_DEBUG_MALLOC diff --git a/docs/Signing.md b/docs/Signing.md index 5c27818908..bed298f243 100644 --- a/docs/Signing.md +++ b/docs/Signing.md @@ -128,6 +128,28 @@ wolfBoot also supports verifying firmware images using certificate chains instea To generate an image for use with this mode, pass the `--cert-chain CERT_CHAIN.der` option to the sign tool, where `CERT_CHAIN.der` is a der encoded certificate chain containing one or more certificates in SSL order (leaf/signer cert last). Note that the sign tool still expects a signing private key to be provided as described above, and assumes that the public key of the signer cert in the chain corresponds to the signing private key. +When building wolfBoot and the test app with the Makefile, the `USER_*` variables provide a convenience for using your own locally-managed keys and certificate chain, avoiding manual `keygen -i` and file placement steps: + +- `CERT_CHAIN_VERIFY=1`: Enables certificate chain verification mode +- `USER_PRIVATE_KEY`: Path to your leaf signing key (DER format) +- `USER_PUBLIC_KEY`: Path to your leaf public key (DER format) +- `USER_CERT_CHAIN`: Path to your certificate chain (DER format) + +Example: + +```sh +make CERT_CHAIN_VERIFY=1 \ + USER_PRIVATE_KEY=my-leaf-private-key.der \ + USER_PUBLIC_KEY=my-leaf-pubkey.der \ + USER_CERT_CHAIN=my-cert-chain.der +``` + +Note that `USER_PUBLIC_KEY` and `USER_PRIVATE_KEY` must correspond to the leaf certificate identity in the chain. + +If `USER_CERT_CHAIN` is not provided when `CERT_CHAIN_VERIFY=1`, a dummy certificate hierarchy is auto-generated for testing. See the [Compiling wolfBoot](compile.md#pre-existing-local-keys-for-test-app-builds) documentation for full details on these options. + +**Note:** If your private key is managed by a third party and you only have access to the public key, use `keygen -i` to import it instead. See the [Keygen tool](#keygen-tool) section above. + Certificate chain verification of images is currently limited to use in conjunction with wolfHSM. See [wolfHSM.md](wolfHSM.md) for more details. #### Target partition id (Multiple partition images, "self-update" feature) diff --git a/docs/compile.md b/docs/compile.md index a7aeb5668a..6639b430a5 100644 --- a/docs/compile.md +++ b/docs/compile.md @@ -355,3 +355,100 @@ Available overrides: - `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_WOLFHSM`: Path to the [wolfHSM](https://github.com/wolfSSL/wolfHSM) library source code + +## Key Generation and Signing + +### Default Key Behavior + +When building wolfBoot for the first time, the build system automatically generates the cryptographic keys needed for firmware signing and verification. + +- **Private Key**: `wolfboot_signing_private_key.der` - Used to sign firmware images +- **Keystore**: `src/keystore.c` - Contains the public key embedded in the bootloader + +The key algorithm is determined by the `SIGN` variable (e.g., `SIGN=ECC256`, `SIGN=RSA2048`). + +For most targets, the makefile also builds the wolfBoot test app and signs it with the aforementioned key. + +### Pre-existing Local Keys for Test App Builds + +The `USER_*` Makefile variables provide a convenience for building the test app with your own locally-managed keys, avoiding the need to manually run `keygen -i` and place key files before building. + +**Note:** If your private key is managed by a third party (e.g., HSM-as-a-service, Azure KeyVault) and you only have access to the public key, use the `keygen -i` option instead. See [Signing.md](Signing.md#keygen-tool) and [Manual Key Management](#manual-key-management) below. + +The following variables are available: + +- `USER_PRIVATE_KEY`: Path to your private signing key (DER format) +- `USER_PUBLIC_KEY`: Path to your public key (DER format) + +**Usage:** + +```sh +make USER_PRIVATE_KEY=/path/to/my-signing-key.der \ + USER_PUBLIC_KEY=/path/to/my-public-key.der +``` + +#### Requirements + +- Both `USER_PRIVATE_KEY` and `USER_PUBLIC_KEY` must be provided together +- Keys must be in DER format appropriate for the selected `SIGN` algorithm + +When these variables are specified, the build: + +1. Skips auto-generation of `wolfboot_signing_private_key.der` +2. Generates the keystore (`src/keystore.c`) from your public key via `keygen -i` +3. Uses your private key to sign the test app + +This is primarily useful when you want a single `make` invocation to build wolfBoot and a signed test app using keys you've generated externally. For wolfBoot-only builds (without the test app), the main benefit is automating the `keygen -i` step for simple single-key keystores. If you need multiple keys in the keystore then you must invoke `keygen -i` manually before building wolfBoot. + +### Pre-existing Certificate Chain for Test App Builds + +When building the test app using certificate chain verification (`CERT_CHAIN_VERIFY=1`), you can provide your own certificate chain: + +- `USER_CERT_CHAIN`: Path to your certificate chain (DER format, leaf cert last) + +**Usage:** + +```sh +make CERT_CHAIN_VERIFY=1 \ + USER_PRIVATE_KEY=/path/to/leaf-signing-key.der \ + USER_PUBLIC_KEY=/path/to/leaf-public-key.der \ + USER_CERT_CHAIN=/path/to/my-cert-chain.der +``` + +**Requirements:** + +- `USER_CERT_CHAIN` requires both `USER_PRIVATE_KEY` and `USER_PUBLIC_KEY` +- The private and public keys must correspond to the leaf certificate identity in the chain + +When `CERT_CHAIN_VERIFY=1` is set without `USER_CERT_CHAIN`, the build auto-generates a dummy 3-tier certificate hierarchy in `test-dummy-ca/` for testing. This also applies to wolfHSM NVM image generation when applicable. + +### Manual Key Management + +For advanced scenarios (multiple keys, mixed algorithms, partition-restricted keys, or third-party managed private keys), use the `keygen` tool directly instead of the `USER_*` variables. + +**Importing a public key (when private key is externally managed):** + +```sh +./tools/keytools/keygen --ecc256 -i my-public-key.der +``` + +This creates `src/keystore.c` from your public key. Signing must then be performed in two steps following the steps outlined in [Signing.md](./Signing.md#signing-firmware-with-external-private-key-hsm) + + +**Using locally-managed keys without `USER_XXX` variables:** + +1. Import your public key to generate the keystore: + ```sh + ./tools/keytools/keygen --ecc256 -i my-public-key.der + ``` + +2. Place your signing key at the expected location: + ```sh + cp my-private-key.der wolfboot_signing_private_key.der + ``` + +Now the build system detects existing files and skips auto-generation when building wolfBoot and the test app. + +**Multiple keys and advanced keystores:** + +The `keygen` tool supports multiple `-g` (generate) and `-i` (import) arguments, mixed key algorithms, and partition ID restrictions. See [keystore.md](keystore.md) for full details on keystore capabilities. When using these advanced features, image signing via the `sign` tool must also be performed manually. diff --git a/docs/keystore.md b/docs/keystore.md index b21d447b3f..a61f85bcd0 100644 --- a/docs/keystore.md +++ b/docs/keystore.md @@ -221,3 +221,27 @@ Returns the permissions mask, as a 32-bit word, for the public key stored in the wolfBoot supports certain platforms that contain connected HSMs (Hardware Security Modules) that can provide cryptographic services using keys that are not stored in the device NVM or readable by wolfBoot, for example, wolfHSM. In these scenarios, wolfBoot key tools should be used to generate the keys, which can then be manually loaded into the HSM (see [--exportpubkey](#exporting-the-public-key-to-a-file)). At runtime, wolfBoot will still use the keystore to obtain information about the public keys, specifically the size of the key and the key type, but does not need access to the actual key material. To support this mode of operation, the `keygen` tool supports the `--nolocalkeys` option, which instructs the tool to generate a keystore entry with a zeroed key material. It still generates the `.der` files for private and public keys, so the wolfBoot key tools can sign images, but the `keystore.c` file that is linked into wolfBoot will contain all zeros in the `pubkey` field. Because the key material isn't present in the keystore, the keypair used to sign the image and stored on the HSM for verification can be updated in the field without needing to rebuild wolfBoot against a new `keystore.c`, as long as the signature algorithm and key size does not change. Most targets that use this option will automatically add it to the key generation options or explicitly mention this step in the build documentation. + +## Build System Integration + +By default, when running `make` to build the default target (`factory.bin`) for the first time, wolfBoot automatically generates a signing keypair and creates a single-key keystore as a "demonstration". This is distinct from using `keygen` directly with `-g` or `-i` options, which provides full control over keystore creation. + +### Default `make` Behavior + +Running `make` without creating the expected pre-existing keys automatically: +- Generates a signing keypair at `wolfboot_signing_private_key.der` +- Creates the keystore at `src/keystore.c` with the corresponding public key + +Note that supplying either of these dependencies manually will cause the build system to skip the generation step + +### Pre-existing Local Keys for Test App Builds + +The `USER_PRIVATE_KEY` and `USER_PUBLIC_KEY` Makefile variables provide a convenience for building the test app with your own locally-managed keys, avoiding manual `keygen -i` invocation: + +```sh +make USER_PRIVATE_KEY=/path/to/my-key.der USER_PUBLIC_KEY=/path/to/my-pubkey.der +``` + +This is primarily useful for test app builds where you wish to use your own PKI as a test. For wolfBoot-only builds, the main benefit is automating the `keygen -i` step for simple single-key keystores. + +**Note:** If your private key is managed by a third party (e.g., HSM-as-a-service) and you only have access to the public key, use `keygen -i` directly instead. See [Signing.md](./Signing.md#signing-firmware-with-external-private-key-hsm) and [compile.md](./compile.md#pre-existing-local-keys-for-test-app-builds) for full details regarding this use case. diff --git a/include/user_settings.h b/include/user_settings.h index b21ff4c5c4..ec2cc236cd 100644 --- a/include/user_settings.h +++ b/include/user_settings.h @@ -214,7 +214,9 @@ extern int tolower(int c); # define WC_RSA_DIRECT # define RSA_LOW_MEM # define WC_ASN_HASH_SHA256 -# if !defined(WOLFBOOT_TPM) && !defined(WOLFCRYPT_SECURE_MODE) +# if !defined(WOLFBOOT_TPM) && !defined(WOLFCRYPT_SECURE_MODE) && \ + !defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT) && \ + !defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER) # define WOLFSSL_RSA_VERIFY_INLINE # define WOLFSSL_RSA_VERIFY_ONLY # define WOLFSSL_RSA_PUBLIC_ONLY @@ -503,20 +505,20 @@ extern int tolower(int c); # define WOLFSSL_SP_NO_DYN_STACK #endif -#if !defined(WOLFBOOT_SMALL_STACK) && !defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER) +#if defined(WOLFBOOT_SMALL_STACK) +# if defined(WOLFBOOT_HUGE_STACK) +# error "Cannot use SMALL_STACK=1 with HUGE_STACK=1" +# endif +# define WOLFSSL_SMALL_STACK +#else # if defined(WOLFSSL_SP_MATH) || defined(WOLFSSL_SP_MATH_ALL) # define WOLFSSL_SP_NO_MALLOC # define WOLFSSL_SP_NO_DYN_STACK # endif -# if !defined(SECURE_PKCS11) +# if !defined(SECURE_PKCS11) && !defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER) # define NO_WOLFSSL_MEMORY # define WOLFSSL_NO_MALLOC # endif -#else -# if defined(WOLFBOOT_HUGE_STACK) -# error "Cannot use SMALL_STACK=1 with HUGE_STACK=1" -# endif -# define WOLFSSL_SMALL_STACK #endif @@ -585,6 +587,7 @@ extern int tolower(int c); # define WOLFSSL_USER_IO # define WOLFSSL_SP_MUL_D # define WOLFSSL_PEM_TO_DER +# define WOLFSSL_ALLOW_NO_SUITES #endif #ifdef WOLFSSL_STM32_PKA diff --git a/lib/wolfHSM b/lib/wolfHSM index 76da7ddfa9..ad9ec84347 160000 --- a/lib/wolfHSM +++ b/lib/wolfHSM @@ -1 +1 @@ -Subproject commit 76da7ddfa9e8657587180a94f3a60eacbd3a6d58 +Subproject commit ad9ec843474ae45fe0ea36030f0eb8f3f625b9b4 diff --git a/options.mk b/options.mk index b55395e128..75d5e5ec20 100644 --- a/options.mk +++ b/options.mk @@ -992,11 +992,15 @@ ifneq ($(CERT_CHAIN_VERIFY),) CFLAGS += -DWOLFBOOT_CERT_CHAIN_VERIFY # export the private key in DER format so it can be used with certificates KEYGEN_OPTIONS += --der - ifneq ($(CERT_CHAIN_GEN),) - # Use dummy cert chain file if not provided (needs to be generated when keys are generated) + + # User-provided cert chain takes precedence + ifneq ($(USER_CERT_CHAIN),) + CERT_CHAIN_FILE = $(USER_CERT_CHAIN) + else + # Auto-generate dummy cert chain (when USER_CERT_CHAIN not provided) CERT_CHAIN_FILE = test-dummy-ca/raw-chain.der - # Set appropriate cert gen options based on sigalg + # Set appropriate cert gen algo based on signature algorithm ifeq ($(SIGN),ECC256) CERT_CHAIN_GEN_ALGO+=ecc256 endif @@ -1005,10 +1009,8 @@ ifneq ($(CERT_CHAIN_VERIFY),) endif ifeq ($(SIGN),RSA4096) CERT_CHAIN_GEN_ALGO+=rsa4096 - endif - else - ifeq ($(CERT_CHAIN_FILE),) - $(error CERT_CHAIN_FILE must be specified when CERT_CHAIN_VERIFY is enabled and not using CERT_CHAIN_GEN) + # Reasonably large default + CFLAGS += -DWOLFHSM_CFG_MAX_CERT_SIZE=4096 endif endif SIGN_OPTIONS += --cert-chain $(CERT_CHAIN_FILE) diff --git a/src/image.c b/src/image.c index 998c62027f..96c8d564ca 100644 --- a/src/image.c +++ b/src/image.c @@ -420,6 +420,8 @@ static void wolfBoot_verify_signature_rsa(uint8_t key_slot, word32 inOutIdx = 0; struct RsaKey rsa; + (void)inOutIdx; + #if (!defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT) && \ !defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER)) || \ (defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT) && \ @@ -500,7 +502,8 @@ static void wolfBoot_verify_signature_rsa(uint8_t key_slot, XMEMCPY(output, sig, RSA_IMAGE_SIGNATURE_SIZE); RSA_VERIFY_FN(ret, wc_RsaSSL_VerifyInline, output, RSA_IMAGE_SIGNATURE_SIZE, &digest_out, &rsa); -#if !defined(WOLFBOOT_USE_WOLFHSM_PUBKEY_ID) +#if defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT) && \ + !defined(WOLFBOOT_USE_WOLFHSM_PUBKEY_ID) /* evict the key after use, since we aren't using the RSA import API */ if (WH_ERROR_OK != wh_Client_KeyEvict(&hsmClientCtx, hsmKeyId)) { return; diff --git a/test-app/Makefile b/test-app/Makefile index ac8f2dd4b0..af707119bc 100644 --- a/test-app/Makefile +++ b/test-app/Makefile @@ -21,13 +21,6 @@ BOOTLOADER_PARTITION_SIZE?=$$(( $(WOLFBOOT_PARTITION_BOOT_ADDRESS) - $(ARCH_FLAS # For test-applications we should only use user_settings.h CFLAGS+=-DWOLFSSL_USER_SETTINGS -DWOLFTPM_USER_SETTINGS -ifeq ($(SIGN),RSA2048) - IMAGE_HEADER_SIZE:=512 -endif - -ifeq ($(SIGN),RSA4096) - IMAGE_HEADER_SIZE:=1024 -endif ifeq ($(HASH),SHA256) WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha256.o CFLAGS+=-D"WOLFBOOT_HASH_SHA256" diff --git a/tools/scripts/tc3xx/wolfBoot-wolfHSM-dummy-certchain.nvminit b/tools/scripts/tc3xx/wolfBoot-wolfHSM-dummy-certchain.nvminit index ddf361e2d2..8c4661434c 100644 --- a/tools/scripts/tc3xx/wolfBoot-wolfHSM-dummy-certchain.nvminit +++ b/tools/scripts/tc3xx/wolfBoot-wolfHSM-dummy-certchain.nvminit @@ -1,2 +1,2 @@ # NVM config file for wolfHSM whnvmtool to create NVM image based on generated keys -obj 1 0xFFFF 0x0000 "cert CA" test-dummy-ca/root-cert.der +obj 1 0xFFFF 0x0100 "cert CA" test-dummy-ca/root-cert.der