Merge pull request #574 from bigbrett/wolfhsm-cert-verify

Add cert chain verification via wolfHSM on sim and TC3xx
pull/573/merge
David Garske 2025-06-06 12:38:59 -07:00 committed by GitHub
commit 0bf65fabb3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 967 additions and 127 deletions

View File

@ -18,6 +18,8 @@ jobs:
file: "config/examples/sim-wolfHSM.config"
- name: "wolfHSM ML-DSA"
file: "config/examples/sim-wolfHSM-mldsa.config"
- name: "wolfHSM cert chain verify"
file: "config/examples/sim-wolfHSM-certchain.config"
fail-fast: false
runs-on: ubuntu-latest
@ -54,8 +56,7 @@ jobs:
with:
repository: wolfssl/wolfHSM-examples
# Make sure to update this when the wolfHSM submodule is updated!
#ref: wolfHSM-v1.1.0
ref: 3e03bd4d4a8439ed4a8a9577823c89e4c37eb9be
ref: wolfHSM-examples-v1.2.0
path: wolfHSM-examples
- name: Build example POSIX TCP server
@ -65,7 +66,13 @@ jobs:
- name: Run POSIX TCP server
run: |
cd wolfHSM-examples/posix/tcp/wh_server_tcp
./Build/wh_server_tcp.elf --client 12 --id 255 --key ../../../../wolfboot_signing_private_key_pub.der &
if [ "${{ matrix.config.name }}" = "wolfHSM cert chain verify" ]; then
tmpfile=$(mktemp)
echo "obj 1 0xFFFF 0x0000 \"cert CA\" ../../../../test-dummy-ca/root-cert.der" >> $tmpfile
./Build/wh_server_tcp.elf --nvminit $tmpfile &
else
./Build/wh_server_tcp.elf --client 12 --id 255 --key ../../../../wolfboot_signing_private_key_pub.der &
fi
TCP_SERVER_PID=$!
echo "TCP_SERVER_PID=$TCP_SERVER_PID" >> $GITHUB_ENV

1
.gitignore vendored
View File

@ -99,6 +99,7 @@ include/target.h
.wolfboot-partition-size
.bootloader-partition-size
MPLabX/wolfBoot-SAME51.X/.generated_files/
test-dummy-ca/**
# Test tools
tools/check_config/check_config

View File

@ -10,6 +10,7 @@ This example demonstrates using wolfBoot on the Infineon AURIX TC3xx family of m
- [Flash Partitioning](#flash-partitioning)
- [Standard wolfBoot images](#standard-wolfboot-images)
- [ELF files](#elf-files)
- [Cert Chain Verification](#cert-chain-verification)
- [Configuration and the wolfBoot AURIX tool (wbaurixtool.sh)](#configuration-and-the-wolfboot-aurix-tool-wbaurixtoolsh)
- [Building and running the wolfBoot demo](#building-and-running-the-wolfboot-demo)
- [Prerequisites](#prerequisites)
@ -23,6 +24,9 @@ This example demonstrates using wolfBoot on the Infineon AURIX TC3xx family of m
- [Load and run the wolfBoot demo in TRACE32](#load-and-run-the-wolfboot-demo-in-trace32)
- [wolfHSM Compatibility](#wolfhsm-compatibility)
- [Building wolfBoot with wolfHSM](#building-wolfboot-with-wolfhsm)
- [Building wolfBoot with wolfHSM and cert chain verification](#building-wolfboot-with-wolfhsm-and-cert-chain-verification)
- [Custom Certificate Chain](#custom-certificate-chain)
- [Dummy Certificate Chain](#dummy-certificate-chain)
- [Building: Command Sequence](#building-command-sequence)
- [Troubleshooting](#troubleshooting)
- [WSL "bad interpreter" error](#wsl-bad-interpreter-error)
@ -95,6 +99,10 @@ Different linker script templates are used to configure the memory layout via `w
- [test-app (standard wolfBoot images)](test-app/Lcf_Gnu_Tricore_Tc.lsl.in)
- [test-app (ELF files)](test-app/Lcf_Gnu_Tricore_elf.lsl.in)
### Cert Chain Verification
wolfBoot on AURIX supports verifying firmware images using certificate chains. For more information on how this wolfBoot feature works, refer to [Signing.md](../../docs/Signing.md), [firmware_update.md](../docs/firmware_update.md), and [wolfBoot-wolfHSM](../../IDE/AURIX/wolfBoot-wolfHSM). Currently this feature can only be used in conjunction with wolfHSM. Instructions for using this feature are detailed below in the [Building wolfBoot with wolfHSM](#building-wolfboot-with-wolfhsm) section.
## Configuration and the wolfBoot AURIX tool (wbaurixtool.sh)
wolfBoot relies extensively on its build system in order to properly set configuration macros, linker addresses, and other target-specific items. Because wolfBoot on AURIX uses the AURIX studio IDE to build, changing things like the signature algorithm would require manually editing IDE settings, linker scripts, etc. which is error prone and tedious. The `wbaurixtool.sh` script provides a single tool that automates the generation of all configurable items required for building wolfBoot and the test application on AURIX given the chosen signature and hashing algorithms, including managing all configuration macros, linker scripts, as well as handling the actual key generation and image signing process. `wbaurixtool.sh` can also generate wolfHSM NVM images containing the generated image signing key when used in conjunction with wolfHSM.
@ -320,6 +328,27 @@ IDE/AURIX/wolfHSM-infineon-tc3xx/
3. Provide the `--hsm` global option to the `wbaurixtool.sh` script when invoking it so that the wolfHSM projects are used instead of the standard wolfBoot projects
4. If using the default build options in [wolfBoot-tc3xx-wolfHSM](./wolfBoot-tc3xx-wolfHSM/), wolfBoot will expect the public key for image verification to be stored at a specific keyId for the wolfBoot client ID. You can use [whnvmtool](https://github.com/wolfSSL/wolfHSM/tree/main/tools/whnvmtool) to generate a loadable NVM image that contains the required keys automatically via `wbaurixtool.sh` through the `nvm` subcommand. This generates an NVM image containing the generated image signing key based on the [wolfBoot-wolfHSM-keys.nvminit](../../tools/scripts/tc3xx/wolfBoot-wolfHSM-keys.nvminit) configuration file, which can then be loaded to the device via a flash programming tool. Before using the `nvm` subcommand of `wbaurixtool.sh`, first compile `whnvmtool` by running `make` in the `lib/wolfHSM/tools/whnvmtool` directory. See the `whnvmtool` documentation and the documentation included in your wolfHSM AURIX release for more details. Note: if you want to use the standard wolfBoot keystore functionality in conjunction with wolfHSM for testing purposes (doesn't require pre-loading keys on the HSM) you can configure wolfBoot to send the keys to the HSM on-the-fly as ephemeral keys. To do this, ensure `WOLFBOOT_USE_WOLFHSM_PUBKEY_ID` is **NOT** defined, and add the `--localkeys` argument to then `./wbaurixtool.sh keygen` command, which invokes the `keygen` tool without the default `--nolocalkeys` option.
### Building wolfBoot with wolfHSM and cert chain verification
wolfBoot with wolfHSM supports certificate chain verification for firmware images. This feature allows wolfBoot to verify that firmware images are signed with certificates that can be traced back to a trusted root certificate authority through a certificate chain.
The `wbaurixtool.sh` script provides two options for applicable commands that enable automation of configuring the build for certificate chain verification:
- `--certchain <file>`: Use a user-provided certificate chain file
- `--dummy-certchain`: Use a "dummy" certificate chain that is created from the key pair generated by the wolfBoot keytools for development/testing purposes.
Both options require the `--hsm` global option and can be used with the `keygen`, `sign`, `macros`, `lcf`, and `nvm` subcommands.
### Custom Certificate Chain
If you want to use a custom certificate chain for verification, you can provide the `--certchain <certchain file>` option to the `macros`, `lcf`, and `sign` subcommands. This ensures that your certificate chain will be included in the generated firmware image and that wolfBoot will verify the chain on boot, then verify the signature of the firmware image using the public key corresponding to the leaf certificate in the chain. Note that the user is responsible for properly constructing the certificate chain and ensuring that the leaf certificate is bound to the firmware signing key pair, as well as properly provisioning the HSM with the root CA certificate at the expected NVM object ID specified by the HAL.
### Dummy Certificate Chain
If you just want to test out the certificate verifcation functionality, `wbaurixtool.sh` can generate and use a dummy certificate chain by providing the `--dummy-certchain` option to the `keygen`, `sign`, `macros`, `lcf`, and `nvm` subcommands. This will cause the keygen step to generate a dummy root CA at `test-dummy-ca/root-ca.der`, well as a dummy certificate chain at `test-dummy-ca/raw-chain.der` using the specified algorithm. The generated chain consists of a single intermediate as well as a leaf (signing) certificate whose identity is bound to the generated firmware signing key pair. The subsequent commands will then ensure this generated chain is used by wolfBoot. Additionally, the `nvm` subcommand will create an NVM image containing the generated root CA that can be loaded into HSM NVM.
**Note: The `--dummy-certchain` option is intended for development and testing. For production use, generate and use your own certificate chain.**
## Building: Command Sequence
The following pseudo command sequence shows a brief overview of the commands needed to build wolfBoot on AURIX (optionally with wolfHSM). The signature and hashing algorithms used in the example are ECC 256 and SHA 256 and specified explicitly for clarity. Note that these algorithms are the default, so do not need to be explicitly specified. Optional arguments are shown in square brackets (e.g. if targeting wolfHSM, the `--hsm` option must be provided as a global option to `wbaurixtool.sh`).

File diff suppressed because one or more lines are too long

View File

@ -7,4 +7,5 @@
@ML_DSA_LEVEL@
@ML_DSA_IMAGE_SIGNATURE_SIZE@
@WOLFBOOT_ELF@
@WOLFBOOT_ELF_FLASH_SCATTER@
@WOLFBOOT_ELF_FLASH_SCATTER@
@WOLFBOOT_CERT_CHAIN_VERIFY@

View File

@ -26,5 +26,6 @@
#define WOLFHSM_CFG_H_
#define WOLFHSM_CFG_DMA
#define WOLFHSM_CFG_CERTIFICATE_MANAGER
#endif /* WOLFHSM_CFG_H_ */

File diff suppressed because one or more lines are too long

View File

@ -229,6 +229,7 @@ $(PRIVATE_KEY):
$(Q)(test $(SIGN) = NONE) || ($(SIGN_ENV) "$(KEYGEN_TOOL)" $(KEYGEN_OPTIONS) -g $(PRIVATE_KEY)) || 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 $(PRIVATE_KEY))
$(SECONDARY_PRIVATE_KEY): $(PRIVATE_KEY) keystore.der
$(Q)$(MAKE) keytools_check
@ -390,6 +391,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
distclean: clean keysclean utilsclean
$(Q)rm -f *.bin *.elf

View File

@ -0,0 +1,39 @@
ARCH=sim
TARGET=sim
SIGN?=ECC256
HASH?=SHA256
WOLFBOOT_SMALL_STACK?=0
SPI_FLASH=0
DEBUG=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
# 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

View File

@ -119,6 +119,14 @@ If none of the following is used, '--sha256' is assumed by default.
* `--sha3` Use sha3-384 for digest calculation on binary images and public keys.
#### Certificate Chain Options
wolfBoot also supports verifying firmware images using certificate chains instead of raw public keys. In this mode of operation, a certificate chain is included in the image manifest header, and the image is signed with the private key corresponding to the leaf certificate identity (signer cert). On boot, wolfBoot verifies the trust of the certificate chain (and therefore the signer cert) against a trusted root CA stored in the wolfHSM server, and if the chain is trusted, verifies the authenticity of the firmware image using the public key from the image signer certificate.
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.
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)
If none of the following is used, "--id=1" is assumed by default. On systems
@ -257,7 +265,7 @@ For a real-life example, see the section below.
./tools/keytools/sign --rsa2048 --sha256 test-app/image.bin wolfboot_signing_private_key.der 1
```
Note: The last argument is the “version” number.
Note: The last argument is the "version" number.
### Signing Firmware with External Private Key (HSM)

View File

@ -221,3 +221,26 @@ Note: When using scattered ELF images, ensure that:
- The ELF file adheres to the ELF file specification and was generated by a toolchain supporting the target architecture
- All section addresses are within valid executable memory regions and **do not overlap with the wolfBoot image, nor the BOOT, UPDATE and SWAP partitions**.
## Certificate Verification
wolfBoot supports authenticating images using certificate chains instead of raw public keys. in this mode of operation, a certificate chain is included in the image manifest header, and the image is signed with the private key corresponding to the leaf certificate identity (signer cert). On boot, wolfBoot verifies the trust of the certificate chain (and therefore the signer cert) against a trusted root CA stored in the wolfHSM server, and if the chain is trusted, verifies the authenticity of the firmware image using the public key from the image signer certificate.
To use this feature:
1. Enable the feature in your wolfBoot configuration by defining `WOLFBOOT_CERT_CHAIN_VERIFY`
2. When signing firmware, include the certificate chain using the `--cert-chain` option:
```sh
./tools/keytools/sign --rsa2048 --sha256 --cert-chain cert_chain.der test-app/image.bin private_key.der 1
```
When verifying firmware, wolfBoot will:
1. Extract the certificate chain from the firmware header
2. Verify the chain using the pre-provisioned root certificate
3. Use the public key from the leaf certificate to verify the firmware signature
This feature is particularly useful in scenarios where you want to rotate signing keys without updating the bootloader, as you can simply resign the image with a new key, create a new certificate chain, then update the certificate chain in the firmware header.
Note: Currently, support for certificate verification is limited to use in conjuction with wolfHSM. Fore more information see [wolfHSM.md](wolfHSM.md).

View File

@ -31,6 +31,34 @@ wolfBoot supports using wolfHSM for the following algorithms:
Encrypted images with wolfHSM is not yet supported in wolfBoot. Note that every HAL target may not support all of these algorithms. Consult the platform-specific wolfBoot documentation for details.
## Additional Features
wolfBoot with wolfHSM also supports the following features:
### Certificate Verification
wolfBoot with wolfHSM supports certificate chain verification for firmware images. In this mode, instead of using raw public keys for signature verification, wolfBoot verifies firmware images using wolfHSM with a public key embedded in a certificate chain that is included in the image manifest header.
The certificate verification process with wolfHSM works as follows:
1. A root CA is created serving as the root of trust for the entire PKI system
2. A signing keypair and corresponding identity certificate is created for signing firmware images
3. The firmware image is signed with the signing private key
4. A certificate chain is created consisting of the signing identity certificate and an optional number of intermediate certificates, where trust is chained back to the root CA.
5. During the signing process, the image is signed with the signer private key and the certificate chain is embedded in the firmware image header.
6. During boot, wolfBoot extracts the certificate chain from the firmware header
7. wolfBoot uses the wolfHSM server to verify the certificate chain against a pre-provisioned root CA certificate stored on the HSM and caches the public key of the leaf certificate if the chain verifies as trusted
8. If the chain is trusted, wolfBoot uses the cached public key from the leaf certificate to verify the firmware signature on the wolfHSM server
To use certificate verification with wolfHSM:
1. Enable `WOLFBOOT_CERT_CHAIN_VERIFY` in your wolfBoot configuration
2. Ensure the wolfHSM server is configured with certificate manager support (`WOLFHSM_CFG_CERTIFICATE_MANAGER`)
3. Pre-provision the root CA certificate on the wolfHSM server at the NVM ID specified by the HAL `hsmClientNvmIdCertRootCA`
4. Sign firmware images with the `--cert-chain` option, providing a DER-encoded certificate chain
To build the simulator using wolfHSM for certificate verification, use [config/examples/sim-wolfHSM-certchain.config](config/examples/sim-wolfHSM-certchain.config).
## Configuration Options
This section describes the configuration options available for wolfHSM client integration. Note that these options should be configured automatically by the build system for each supported platform when wolfHSM support is enabled. Consult the platform-specific documentation for details on enabling wolfHSM support.

View File

@ -665,6 +665,9 @@ const int hsmClientKeyIdPubKey = 0xFF;
const int hsmClientDevIdCrypt = WH_DEV_ID;
const int hsmClientKeyIdCrypt = 0xFF;
#endif
#ifdef WOLFBOOT_CERT_CHAIN_VERIFY
const whNvmId hsmClientNvmIdCertRootCA = 1;
#endif
static int _cancelCb(uint16_t cancelSeq)

View File

@ -100,6 +100,9 @@ const int hsmClientKeyIdPubKey = 0xFF;
const int hsmClientDevIdCrypt = WH_DEV_ID;
const int hsmClientKeyIdCrypt = 0xFF;
#endif
#ifdef WOLFBOOT_CERT_CHAIN_VERIFY
const whNvmId hsmClientNvmIdCertRootCA = 1;
#endif
int hal_hsm_init_connect(void);
int hal_hsm_disconnect(void);

View File

@ -161,6 +161,10 @@ extern const int hsmClientKeyIdPubKey; /* KeyId for public key operations */
#ifdef EXT_ENCRYPTED
extern const int hsmClientKeyIdCrypt; /* KeyId for image (enc/dec)ryption */
#endif
#ifdef WOLFBOOT_CERT_CHAIN_VERIFY
/* NvmId for trusted root CA certificate */
extern const whNvmId hsmClientNvmIdCertRootCA;
#endif
/* Implementation of functions provided by HAL */
int hal_hsm_init_connect(void);

View File

@ -79,6 +79,7 @@ extern "C" {
#define HDR_SIGNATURE 0x20
#define HDR_POLICY_SIGNATURE 0x21
#define HDR_SECONDARY_SIGNATURE 0x22
#define HDR_CERT_CHAIN 0x23
#define HDR_PADDING 0xFF
/* Auth Key types */

@ -1 +1 @@
Subproject commit ea4c3db1e05b878f39c107b375c4c57ac93ab35a
Subproject commit f76701294bb8be47c7a9364a1061483c9ed7b3af

@ -1 +1 @@
Subproject commit b077c81eb635392e694ccedbab8b644297ec0285
Subproject commit 2151a1b8a1f8f81c4dba985429d50b76db7307e5

View File

@ -907,5 +907,43 @@ ifeq ($(WOLFHSM_CLIENT),1)
ifneq ($(WOLFHSM_CLIENT_LOCAL_KEYS),1)
KEYGEN_OPTIONS += --nolocalkeys
CFLAGS += -DWOLFBOOT_USE_WOLFHSM_PUBKEY_ID
# big enough for cert chain
CFLAGS += -DWOLFHSM_CFG_COMM_DATA_LEN=5000
endif
# Ensure wolfHSM is configured to use certificate manager if we are
# doing cert chain verification
ifneq ($(CERT_CHAIN_VERIFY),)
WOLFHSM_CLIENT_OBJS += \
$(LIBDIR)/wolfHSM/src/wh_client_cert.o \
$(LIBDIR)/wolfHSM/src/wh_message_cert.o
CFLAGS += -DWOLFHSM_CFG_CERTIFICATE_MANAGER
endif
endif
# Cert chain verification options
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)
CERT_CHAIN_FILE = test-dummy-ca/raw-chain.der
# Set appropriate cert gen options based on sigalg
ifeq ($(SIGN),ECC256)
CERT_CHAIN_GEN_ALGO+=ecc256
endif
ifeq ($(SIGN),RSA2048)
CERT_CHAIN_GEN_ALGO+=rsa2048
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)
endif
endif
SIGN_OPTIONS += --cert-chain $(CERT_CHAIN_FILE)
endif

View File

@ -55,6 +55,12 @@
/* Globals */
static uint8_t digest[WOLFBOOT_SHA_DIGEST_SIZE];
#if defined(WOLFBOOT_CERT_CHAIN_VERIFY) && \
defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT)
static whKeyId g_certLeafKeyId = WH_KEYID_ERASED;
static int g_leafKeyIdValid = 0;
#endif
/* TPM based verify */
#if defined(WOLFBOOT_TPM) && defined(WOLFBOOT_TPM_VERIFY)
#ifdef ECC_IMAGE_SIGNATURE_SIZE
@ -241,7 +247,23 @@ static void wolfBoot_verify_signature_ecc(uint8_t key_slot,
const int point_sz = ECC_IMAGE_SIGNATURE_SIZE / 2;
/* Use the public key ID to verify the signature */
#if defined(WOLFBOOT_CERT_CHAIN_VERIFY)
/* If using certificate chain verification and we have a verified leaf
* key ID */
if (g_leafKeyIdValid) {
/* Use the leaf key ID from certificate verification */
ret = wh_Client_EccSetKeyId(&ecc, g_certLeafKeyId);
wolfBoot_printf(
"Using leaf cert public key (ID: %08x) for ECC verification\n",
(unsigned int)g_certLeafKeyId);
}
else {
/* Default behavior: use the pre-configured public key ID */
ret = wh_Client_EccSetKeyId(&ecc, hsmClientKeyIdPubKey);
}
#else
ret = wh_Client_EccSetKeyId(&ecc, hsmClientKeyIdPubKey);
#endif
if (ret != 0) {
return;
}
@ -273,6 +295,12 @@ static void wolfBoot_verify_signature_ecc(uint8_t key_slot,
img->sha_hash, WOLFBOOT_SHA_DIGEST_SIZE, &verify_res,
&ecc);
}
#if defined(WOLFBOOT_CERT_CHAIN_VERIFY)
if (g_leafKeyIdValid) {
(void)wh_Client_KeyEvict(&hsmClientCtx, g_certLeafKeyId);
g_leafKeyIdValid = 0;
}
#endif
#else
/* Import public key */
ret = wc_ecc_import_unsigned(&ecc, pubkey, pubkey + point_sz, NULL,
@ -400,7 +428,23 @@ static void wolfBoot_verify_signature_rsa(uint8_t key_slot,
#if defined(WOLFBOOT_USE_WOLFHSM_PUBKEY_ID)
(void)key_slot;
/* public key is stored on server at hsmClientKeyIdPubKey*/
#if defined(WOLFBOOT_CERT_CHAIN_VERIFY)
/* If using certificate chain verification and we have a verified leaf key
* ID */
if (g_leafKeyIdValid) {
/* Use the leaf key ID from certificate verification */
ret = wh_Client_RsaSetKeyId(&rsa, g_certLeafKeyId);
wolfBoot_printf(
"Using leaf cert public key (ID: %08x) for RSA verification\n",
(unsigned int)g_certLeafKeyId);
}
else {
/* Default behavior: use the pre-configured public key ID */
ret = wh_Client_RsaSetKeyId(&rsa, hsmClientKeyIdPubKey);
}
#else
ret = wh_Client_RsaSetKeyId(&rsa, hsmClientKeyIdPubKey);
#endif
if (ret != 0) {
return;
}
@ -426,6 +470,11 @@ static void wolfBoot_verify_signature_rsa(uint8_t key_slot,
if (WH_ERROR_OK != wh_Client_KeyEvict(&hsmClientCtx, hsmKeyId)) {
return;
}
#elif defined(WOLFBOOT_CERT_CHAIN_VERIFY)
if (g_leafKeyIdValid) {
(void)wh_Client_KeyEvict(&hsmClientCtx, g_certLeafKeyId);
g_leafKeyIdValid = 0;
}
#endif /* !WOLFBOOT_USE_WOLFHSM_PUBKEY_ID */
#else
/* wolfCrypt software RSA verify */
@ -644,7 +693,23 @@ static void wolfBoot_verify_signature_ml_dsa(uint8_t key_slot,
#if defined WOLFBOOT_ENABLE_WOLFHSM_CLIENT && \
defined(WOLFBOOT_USE_WOLFHSM_PUBKEY_ID)
/* Use key slot ID directly with wolfHSM */
#if defined(WOLFBOOT_CERT_CHAIN_VERIFY)
/* If using certificate chain verification and we have a verified leaf key
* ID */
if (g_leafKeyIdValid) {
/* Use the leaf key ID from certificate verification */
ret = wh_Client_MlDsaSetKeyId(&ml_dsa, g_certLeafKeyId);
wolfBoot_printf(
"Using leaf cert public key (ID: %08x) for ML-DSA verification\n",
(unsigned int)g_certLeafKeyId);
}
else {
/* Default behavior: use the pre-configured public key ID */
ret = wh_Client_MlDsaSetKeyId(&ml_dsa, hsmClientKeyIdPubKey);
}
#else
ret = wh_Client_MlDsaSetKeyId(&ml_dsa, hsmClientKeyIdPubKey);
#endif
if (ret != 0) {
wolfBoot_printf("error: wh_Client_MlDsaSetKeyId returned %d\n", ret);
}
@ -1901,6 +1966,16 @@ int wolfBoot_verify_authenticity(struct wolfBoot_image *img)
uint32_t key_mask = 0U;
uint32_t image_part = 1U;
int key_slot;
#if defined(WOLFBOOT_CERT_CHAIN_VERIFY) && \
defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT)
uint8_t* cert_chain;
uint16_t cert_chain_size;
int32_t cert_verify_result;
int hsm_ret;
/* Reset certificate chain usage for this verification */
g_leafKeyIdValid = 0;
#endif
stored_signature_size = get_header(img, HDR_SIGNATURE, &stored_signature);
pubkey_hint_size = get_header(img, HDR_PUBKEY, &pubkey_hint);
@ -1955,6 +2030,47 @@ int wolfBoot_verify_authenticity(struct wolfBoot_image *img)
CONFIRM_MASK_VALID(image_part, key_mask);
#if defined(WOLFBOOT_CERT_CHAIN_VERIFY) && \
defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT)
/* Check for certificate chain in the image header */
cert_chain_size = get_header(img, HDR_CERT_CHAIN, &cert_chain);
if (cert_chain_size > 0) {
wolfBoot_printf("Found certificate chain (%d bytes)\n",
cert_chain_size);
/* Verify certificate chain using wolfHSM's verification API. Use DMA if
* available in the wolfHSM configuration */
#if defined(WOLFHSM_CFG_DMA)
wolfBoot_printf(
"verifying cert chain and caching leaf pubkey (using DMA)\n");
hsm_ret = wh_Client_CertVerifyDmaAndCacheLeafPubKey(
&hsmClientCtx, cert_chain, cert_chain_size,
hsmClientNvmIdCertRootCA, &g_certLeafKeyId, &cert_verify_result);
#else
wolfBoot_printf("verifying cert chain and caching leaf pubkey\n");
hsm_ret = wh_Client_CertVerifyAndCacheLeafPubKey(
&hsmClientCtx, cert_chain, cert_chain_size,
hsmClientNvmIdCertRootCA, &g_certLeafKeyId, &cert_verify_result);
#endif
/* Error or verification failure results in standard auth check failure
* path */
if (hsm_ret != 0 || cert_verify_result != 0) {
wolfBoot_printf("Certificate chain verification failed: "
"hsm_ret=%d, verify_result=%d\n",
hsm_ret, cert_verify_result);
return -1;
}
wolfBoot_printf("Certificate chain verified, using leaf key ID: %08x\n",
(unsigned int)g_certLeafKeyId);
/* Set flag to use the leaf certificate's public key for signature
* verification later */
g_leafKeyIdValid = 1;
}
#endif
/* wolfBoot_verify_signature_ecc() does not return the result directly.
* A call to wolfBoot_image_confirm_signature_ok() is required in order to
* confirm that the signature verification is OK.

View File

@ -158,6 +158,7 @@ static inline int fp_truncate(FILE *f, size_t len)
#define HDR_SIGNATURE 0x20
#define HDR_POLICY_SIGNATURE 0x21
#define HDR_SECONDARY_SIGNATURE 0x22
#define HDR_CERT_CHAIN 0x23
#define HDR_SHA256_LEN 32
@ -265,6 +266,7 @@ struct cmd_options {
const char *policy_file;
const char *encrypt_key_file;
const char *delta_base_file;
const char *cert_chain_file;
int no_base_sha;
char output_image_file[PATH_MAX];
char output_diff_file[PATH_MAX];
@ -356,6 +358,10 @@ static int load_key_ecc(int sign_type, uint32_t curve_sz, int curve_id,
*pubkey_sz = curve_sz * 2;
*pubkey = malloc(*pubkey_sz); /* assume malloc works */
if (*pubkey == NULL) {
printf("Pubkey malloc error!\n");
return -1;
}
initRet = ret = wc_ecc_init(&key.ecc);
if (CMD.manual_sign || CMD.sha_only) {
/* raw (public x + public y) */
@ -425,8 +431,10 @@ static int load_key_ecc(int sign_type, uint32_t curve_sz, int curve_id,
if (ret != 0 && initRet == 0) {
wc_ecc_free(&key.ecc);
}
if (ret != 0)
if (ret != 0) {
free(*pubkey);
*pubkey = NULL;
}
if (ret == 0 || CMD.sign != SIGN_AUTO) {
if (CMD.header_sz < header_sz)
@ -455,9 +463,14 @@ static int load_key_rsa(int sign_type, uint32_t rsa_keysz, uint32_t rsa_pubkeysz
uint32_t keySzOut = 0;
if (CMD.manual_sign || CMD.sha_only) {
/* use public key directly */
*pubkey = *key_buffer;
/* Allocate and copy pubkey instead of using key_buffer directly */
*pubkey_sz = *key_buffer_sz;
*pubkey = malloc(*pubkey_sz);
if (*pubkey == NULL) {
printf("Pubkey malloc error!\n");
return -1;
}
memcpy(*pubkey, *key_buffer, *pubkey_sz);
if (*pubkey_sz <= rsa_pubkeysz) {
CMD.header_sz = header_sz;
@ -484,8 +497,18 @@ static int load_key_rsa(int sign_type, uint32_t rsa_keysz, uint32_t rsa_pubkeysz
}
if (ret > 0) {
*pubkey = *key_buffer;
/* Allocate and copy pubkey instead of using key_buffer directly */
*pubkey_sz = ret;
*pubkey = malloc(*pubkey_sz);
if (*pubkey == NULL) {
printf("Pubkey malloc error!\n");
ret = -1;
if (initRet == 0) {
wc_FreeRsaKey(&key.rsa);
}
return -1;
}
memcpy(*pubkey, *key_buffer, *pubkey_sz);
ret = 0;
}
@ -565,6 +588,10 @@ static uint8_t *load_key(uint8_t **key_buffer, uint32_t *key_buffer_sz,
initRet = -1;
*pubkey_sz = ED25519_PUB_KEY_SIZE;
*pubkey = malloc(*pubkey_sz);
if (*pubkey == NULL) {
printf("Pubkey malloc error!\n");
goto failure;
}
if (CMD.manual_sign || CMD.sha_only) {
/* raw */
@ -628,6 +655,10 @@ static uint8_t *load_key(uint8_t **key_buffer, uint32_t *key_buffer_sz,
initRet = -1;
*pubkey_sz = ED448_PUB_KEY_SIZE;
*pubkey = malloc(*pubkey_sz);
if (*pubkey == NULL) {
printf("Pubkey malloc error!\n");
goto failure;
}
if (CMD.manual_sign || CMD.sha_only) {
/* raw */
@ -743,16 +774,26 @@ static uint8_t *load_key(uint8_t **key_buffer, uint32_t *key_buffer_sz,
if (*key_buffer_sz == (HSS_MAX_PRIVATE_KEY_LEN +
KEYSTORE_PUBKEY_SIZE_LMS)) {
/* priv + pub */
*pubkey = (*key_buffer) + HSS_MAX_PRIVATE_KEY_LEN;
*pubkey_sz = (*key_buffer_sz) - HSS_MAX_PRIVATE_KEY_LEN;
*pubkey_sz = KEYSTORE_PUBKEY_SIZE_LMS;
*pubkey = malloc(*pubkey_sz);
if (*pubkey == NULL) {
printf("Pubkey malloc error!\n");
goto failure;
}
memcpy(*pubkey, (*key_buffer) + HSS_MAX_PRIVATE_KEY_LEN, *pubkey_sz);
ret = 0;
printf("Found LMS key\n");
break;
}
else if (*key_buffer_sz == KEYSTORE_PUBKEY_SIZE_LMS) {
/* pub only */
*pubkey = (*key_buffer);
*pubkey_sz = KEYSTORE_PUBKEY_SIZE_LMS;
*pubkey = malloc(*pubkey_sz);
if (*pubkey == NULL) {
printf("Pubkey malloc error!\n");
goto failure;
}
memcpy(*pubkey, *key_buffer, *pubkey_sz);
ret = 0;
printf("Found LMS public only key\n");
break;
@ -791,16 +832,26 @@ static uint8_t *load_key(uint8_t **key_buffer, uint32_t *key_buffer_sz,
if (*key_buffer_sz == (priv_sz + KEYSTORE_PUBKEY_SIZE_XMSS)) {
/* priv + pub */
*pubkey = (*key_buffer) + priv_sz;
*pubkey_sz = (*key_buffer_sz) - priv_sz;
*pubkey_sz = KEYSTORE_PUBKEY_SIZE_XMSS;
*pubkey = malloc(*pubkey_sz);
if (*pubkey == NULL) {
printf("Pubkey malloc error!\n");
goto failure;
}
memcpy(*pubkey, (*key_buffer) + priv_sz, *pubkey_sz);
ret = 0;
printf("Found XMSS key\n");
break;
}
else if (*key_buffer_sz == KEYSTORE_PUBKEY_SIZE_XMSS) {
/* pub only */
*pubkey = (*key_buffer);
*pubkey_sz = KEYSTORE_PUBKEY_SIZE_XMSS;
*pubkey = malloc(*pubkey_sz);
if (*pubkey == NULL) {
printf("Pubkey malloc error!\n");
goto failure;
}
memcpy(*pubkey, *key_buffer, *pubkey_sz);
ret = 0;
printf("Found XMSS public only key\n");
break;
@ -844,16 +895,26 @@ static uint8_t *load_key(uint8_t **key_buffer, uint32_t *key_buffer_sz,
/* priv + pub */
ret = wc_MlDsaKey_ImportPrivRaw(&key.ml_dsa, *key_buffer,
priv_sz);
*pubkey = (*key_buffer) + priv_sz;
*pubkey_sz = (*key_buffer_sz) - priv_sz;
*pubkey_sz = pub_sz;
*pubkey = malloc(*pubkey_sz);
if (*pubkey == NULL) {
printf("Pubkey malloc error!\n");
goto failure;
}
memcpy(*pubkey, (*key_buffer) + priv_sz, *pubkey_sz);
ret = 0;
printf("Found ml-dsa key\n");
break;
}
else if (*key_buffer_sz == pub_sz) {
/* pub only */
*pubkey = (*key_buffer);
*pubkey_sz = pub_sz;
*pubkey = malloc(*pubkey_sz);
if (*pubkey == NULL) {
printf("Pubkey malloc error!\n");
goto failure;
}
memcpy(*pubkey, *key_buffer, *pubkey_sz);
ret = 0;
printf("Found ml-dsa public only key\n");
break;
@ -1066,6 +1127,45 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz,
uint32_t digest_sz = 0;
uint32_t image_sz = 0;
int io_sz;
uint8_t* cert_chain = NULL;
uint32_t cert_chain_sz = 0;
/* Check certificate chain file size before allocating header, and adjust
* header size if needed */
if (CMD.cert_chain_file != NULL) {
struct stat file_stat;
/* Get the file size */
if (stat(CMD.cert_chain_file, &file_stat) == 0) {
/* 2 bytes for tag + 2 bytes for length field */
const uint32_t tag_len_size = 4;
/* Maximum alignment padding that might be needed */
const uint32_t max_alignment = 8;
/* Required space = tag(2) + length(2) + data + potential alignment
* * padding */
const uint32_t required_space =
tag_len_size + file_stat.st_size + max_alignment;
/* If the current header size is too small, increase it */
if (CMD.header_sz < required_space) {
/* Round up to nearest power of 2 that can hold the chain */
const uint32_t min_header_size = 256;
uint32_t new_size = min_header_size;
while (new_size < required_space) {
new_size *= 2;
}
printf("Increasing header size from %u to %u bytes to fit "
"certificate chain\n",
CMD.header_sz, new_size);
CMD.header_sz = new_size;
}
}
else {
printf("Warning: Could not stat certificate chain file %s: %s\n",
CMD.cert_chain_file, strerror(errno));
}
}
header_idx = 0;
header = malloc(CMD.header_sz);
@ -1183,6 +1283,64 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz,
}
}
/* Read certificate chain if provided */
if (CMD.cert_chain_file != NULL) {
const size_t cert_chain_tlv_hdr_sz = 4;
struct stat file_stat;
f = fopen(CMD.cert_chain_file, "rb");
if (f == NULL) {
printf("Open certificate chain file %s failed: %s\n",
CMD.cert_chain_file, strerror(errno));
goto failure;
}
/* Get the file size */
if (stat(CMD.cert_chain_file, &file_stat) != 0) {
printf("Could not get certificate chain file size: %s\n",
strerror(errno));
fclose(f);
goto failure;
}
cert_chain_sz = file_stat.st_size;
/* Verify that the chain will fit in our header */
if (header_idx + cert_chain_tlv_hdr_sz + cert_chain_sz >
CMD.header_sz) {
printf("Error: Certificate chain too large for header (%u bytes "
"needed, %u available)\n",
(unsigned int)(header_idx + cert_chain_tlv_hdr_sz +
cert_chain_sz),
CMD.header_sz);
fclose(f);
goto failure;
}
cert_chain = malloc(cert_chain_sz);
if (cert_chain == NULL) {
printf("Certificate chain buffer malloc error!\n");
fclose(f);
goto failure;
}
/* Read the entire file into the buffer */
io_sz = (int)fread(cert_chain, 1, cert_chain_sz, f);
fclose(f);
if (io_sz != (int)cert_chain_sz) {
printf("Error reading certificate chain file: %s\n",
strerror(errno));
goto failure;
}
/* Append the certificate chain TLV - require 8-byte alignment */
ALIGN_8(header_idx);
header_append_tag(header, &header_idx, HDR_CERT_CHAIN, cert_chain_sz,
cert_chain);
printf("Added certificate chain (%d bytes)\n", cert_chain_sz);
}
/* Add padding bytes. Sha-3 val field requires 8-byte alignment */
/* The offset '4' takes into account 2B Tag + 2B Len, so that the Value
* starts at (addr % 8 == 0) position.
@ -1693,10 +1851,16 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz,
fclose(f2);
fclose(f);
failure:
if (cert_chain)
free(cert_chain);
if (policy)
free(policy);
if (header)
free(header);
if (signature)
free(signature);
if (secondary_signature)
free(secondary_signature);
return ret;
}
@ -2587,6 +2751,13 @@ int main(int argc, char** argv)
CMD.custom_tlvs++;
i += 2;
}
else if (strcmp(argv[i], "--cert-chain") == 0) {
if (argc <= (i + 1)) {
fprintf(stderr, "Missing certificate chain file argument\n");
exit(16);
}
CMD.cert_chain_file = argv[++i];
}
else {
i--;
break;
@ -2746,6 +2917,8 @@ int main(int argc, char** argv)
DEBUG_PRINT("Header size: %u\n", CMD.header_sz);
if (kbuf2)
free(kbuf2);
if (pubkey2)
free(pubkey2);
} else {
make_header(pubkey, pubkey_sz, CMD.image_file, CMD.output_image_file);
}
@ -2758,6 +2931,9 @@ int main(int argc, char** argv)
ret = base_diff(CMD.delta_base_file, pubkey, pubkey_sz, 16);
}
/* Add pubkey cleanup */
if (pubkey)
free(pubkey);
if (kbuf)
free(kbuf);

View File

@ -0,0 +1,267 @@
#!/bin/bash
# Certificate Chain Generation Script (ECC P256 or RSA)
# Creates a certificate chain with root, intermediate, and leaf
# Outputs DER format files plus C arrays for embedding
# Optional: Use existing leaf private key with --leaf <file> argument
set -e # Exit on any error
# Default output directory and algorithm
OUTPUT_DIR="test-dummy-ca"
ALGO="ecc256" # Default to ECC P-256 keys
# Helper functions for key operations
generate_private_key() {
local output_file=$1
if [[ "$ALGO" == "ecc256" ]]; then
openssl ecparam -genkey -name prime256v1 -noout -out "$output_file"
elif [[ "$ALGO" == "rsa2048" ]]; then
openssl genrsa -out "$output_file" 2048
elif [[ "$ALGO" == "rsa4096" ]]; then
openssl genrsa -out "$output_file" 4096
fi
}
convert_key_to_der() {
local input_file=$1
local output_file=$2
if [[ "$ALGO" == "ecc256" ]]; then
openssl ec -in "$input_file" -outform DER -out "$output_file"
elif [[ "$ALGO" == "rsa2048" || "$ALGO" == "rsa4096" ]]; then
openssl rsa -in "$input_file" -outform DER -out "$output_file"
fi
}
extract_public_key() {
local cert_file=$1
local pubkey_pem=$2
local pubkey_der=$3
# Extract public key from certificate (same for both algos)
openssl x509 -in "$cert_file" -pubkey -noout > "$pubkey_pem"
# Convert public key to DER format
if [[ "$ALGO" == "ecc256" ]]; then
openssl ec -pubin -in "$pubkey_pem" -outform DER -out "$pubkey_der"
elif [[ "$ALGO" == "rsa2048" || "$ALGO" == "rsa4096" ]]; then
openssl rsa -pubin -in "$pubkey_pem" -outform DER -out "$pubkey_der"
fi
}
validate_key_format() {
local key_file=$1
if [[ "$ALGO" == "ecc256" ]]; then
openssl ec -in "$key_file" -noout
elif [[ "$ALGO" == "rsa2048" || "$ALGO" == "rsa4096" ]]; then
openssl rsa -in "$key_file" -noout
fi
}
# Parse command line arguments
LEAF_KEY_FILE=""
while [[ $# -gt 0 ]]; do
case $1 in
--leaf)
LEAF_KEY_FILE="$2"
shift 2
;;
--outdir)
OUTPUT_DIR="$2"
shift 2
;;
--algo)
ALGO="$2"
if [[ "$ALGO" != "ecc256" && "$ALGO" != "rsa2048" && "$ALGO" != "rsa4096" ]]; then
echo "Invalid algorithm: $ALGO. Use 'ecc256', 'rsa2048', or 'rsa4096'"
exit 1
fi
shift 2
;;
*)
echo "Unknown option: $1"
echo "Usage: $0 [--leaf <private_key_file>] [--outdir <output_directory>] [--algo <ecc256|rsa2048|rsa4096>]"
exit 1
;;
esac
done
# Configuration
ROOT_SUBJECT="/C=US/ST=California/L=San Francisco/O=MyOrganization/OU=Root CA/CN=My Root CA"
INTERMEDIATE_SUBJECT="/C=US/ST=California/L=San Francisco/O=MyOrganization/OU=Intermediate CA/CN=My Intermediate CA"
LEAF_SUBJECT="/C=US/ST=California/L=San Francisco/O=MyOrganization/OU=Services/CN=service.example.com"
# Create directory structure
echo "Creating directory structure..."
mkdir -p ${OUTPUT_DIR}/temp
##################
# GENERATE CHAIN
##################
echo "Generating Certificate Chain using $ALGO..."
# Step 1: Generate Root key and certificate
echo "Generating Root CA..."
generate_private_key "${OUTPUT_DIR}/temp/root.key.pem"
# Create PEM format root certificate (temporary)
openssl req -new -x509 -days 3650 -sha256 \
-key ${OUTPUT_DIR}/temp/root.key.pem \
-out ${OUTPUT_DIR}/temp/root.crt.pem \
-subj "$ROOT_SUBJECT" \
-addext "basicConstraints=critical,CA:TRUE" \
-addext "keyUsage=critical,keyCertSign,cRLSign,digitalSignature"
# Convert root key and certificate to DER format
convert_key_to_der "${OUTPUT_DIR}/temp/root.key.pem" "${OUTPUT_DIR}/root-prvkey.der"
openssl x509 -in ${OUTPUT_DIR}/temp/root.crt.pem -outform DER -out ${OUTPUT_DIR}/root-cert.der
# Step 2: Generate Intermediate key and CSR
echo "Generating Intermediate CA..."
generate_private_key "${OUTPUT_DIR}/temp/intermediate.key.pem"
openssl req -new -sha256 \
-key ${OUTPUT_DIR}/temp/intermediate.key.pem \
-out ${OUTPUT_DIR}/temp/intermediate.csr \
-subj "$INTERMEDIATE_SUBJECT"
# Step 3: Sign Intermediate certificate with Root
openssl x509 -req -days 1825 -sha256 \
-in ${OUTPUT_DIR}/temp/intermediate.csr \
-out ${OUTPUT_DIR}/temp/intermediate.crt.pem \
-CA ${OUTPUT_DIR}/temp/root.crt.pem \
-CAkey ${OUTPUT_DIR}/temp/root.key.pem \
-CAcreateserial \
-extfile <(printf "basicConstraints=critical,CA:TRUE,pathlen:0\nkeyUsage=critical,keyCertSign,cRLSign,digitalSignature")
# Convert intermediate key and certificate to DER format
convert_key_to_der "${OUTPUT_DIR}/temp/intermediate.key.pem" "${OUTPUT_DIR}/intermediate-prvkey.der"
openssl x509 -in ${OUTPUT_DIR}/temp/intermediate.crt.pem -outform DER -out ${OUTPUT_DIR}/intermediate-cert.der
# Step 4: Handle Leaf key (generate or use existing)
echo "Handling Leaf Certificate..."
if [ -z "$LEAF_KEY_FILE" ]; then
echo "Generating new leaf private key..."
generate_private_key "${OUTPUT_DIR}/temp/leaf.key.pem"
else
echo "Using provided leaf private key: $LEAF_KEY_FILE"
cp "$LEAF_KEY_FILE" ${OUTPUT_DIR}/temp/leaf.key.pem
# Ensure the key file is in the right format
validate_key_format "${OUTPUT_DIR}/temp/leaf.key.pem"
fi
# Create CSR for leaf certificate
openssl req -new -sha256 \
-key ${OUTPUT_DIR}/temp/leaf.key.pem \
-out ${OUTPUT_DIR}/temp/leaf.csr \
-subj "$LEAF_SUBJECT"
# Step 5: Sign Leaf certificate with Intermediate
openssl x509 -req -days 365 -sha256 \
-in ${OUTPUT_DIR}/temp/leaf.csr \
-out ${OUTPUT_DIR}/temp/leaf.crt.pem \
-CA ${OUTPUT_DIR}/temp/intermediate.crt.pem \
-CAkey ${OUTPUT_DIR}/temp/intermediate.key.pem \
-CAcreateserial \
-extfile <(printf "basicConstraints=CA:FALSE\nkeyUsage=critical,digitalSignature,keyEncipherment\nextendedKeyUsage=serverAuth")
# Convert leaf key and certificate to DER format
convert_key_to_der "${OUTPUT_DIR}/temp/leaf.key.pem" "${OUTPUT_DIR}/leaf-prvkey.der"
openssl x509 -in ${OUTPUT_DIR}/temp/leaf.crt.pem -outform DER -out ${OUTPUT_DIR}/leaf-cert.der
# Extract the public key from leaf certificate in DER format
echo "Extracting public key from leaf certificate..."
extract_public_key "${OUTPUT_DIR}/temp/leaf.crt.pem" "${OUTPUT_DIR}/temp/leaf_pubkey.pem" "${OUTPUT_DIR}/leaf-pubkey.der"
# Create raw DER format certificate chain
cat ${OUTPUT_DIR}/intermediate-cert.der ${OUTPUT_DIR}/leaf-cert.der > ${OUTPUT_DIR}/raw-chain.der
##################################
# GENERATE C ARRAYS FOR EMBEDDING
##################################
echo "Generating C arrays for embedding in programs..."
# Create a header file for certificates
HEADER_FILE="${OUTPUT_DIR}/gen_certificates.h"
# Initialize the header file with header guards and includes
cat > "${HEADER_FILE}" << 'EOT'
/*
* Certificate arrays for embedded SSL/TLS applications
* Generated by OpenSSL certificate chain script
*/
#ifndef GEN_CERTIFICATES_H
#define GEN_CERTIFICATES_H
#include <stddef.h>
EOT
# Function to append a certificate array to the header file
append_cert_array() {
local infile=$1
local arrayname=$2
local description=$3
echo "/* ${description} */" >> "${HEADER_FILE}"
echo "const unsigned char ${arrayname}[] = {" >> "${HEADER_FILE}"
# Use xxd instead of hexdump for more reliable output
xxd -i < "${infile}" | grep -v "unsigned char" | grep -v "unsigned int" | \
sed 's/ 0x/0x/g' >> "${HEADER_FILE}"
echo "};" >> "${HEADER_FILE}"
echo "const size_t ${arrayname}_len = sizeof(${arrayname});" >> "${HEADER_FILE}"
echo "" >> "${HEADER_FILE}"
}
### Add certificates to the header file
echo "/* Certificates */" >> "${HEADER_FILE}"
append_cert_array "${OUTPUT_DIR}/root-cert.der" "ROOT_CERT" "Root CA Certificate (DER format)"
append_cert_array "${OUTPUT_DIR}/intermediate-cert.der" "INTERMEDIATE_CERT" "Intermediate CA Certificate (DER format)"
append_cert_array "${OUTPUT_DIR}/leaf-cert.der" "LEAF_CERT" "Leaf/Server Certificate (DER format)"
append_cert_array "${OUTPUT_DIR}/raw-chain.der" "RAW_CERT_CHAIN" "Raw Certificate Chain (Intermediate+Leaf) (DER format)"
# Add leaf certificate public key
append_cert_array "${OUTPUT_DIR}/leaf-pubkey.der" "LEAF_PUBKEY" "Leaf Certificate Public Key (DER format)"
# Close the header guard
echo "#endif /* GEN_CERTIFICATES_H */" >> "${HEADER_FILE}"
echo "Generated C header file with certificate arrays: ${HEADER_FILE}"
# Display verification information
echo ""
echo "=== Certificate Chain Generation Complete ==="
echo ""
# Verify Chain
echo "=== Verifying Certificate Chain ==="
echo "Verifying intermediate certificate against root:"
openssl verify -CAfile ${OUTPUT_DIR}/temp/root.crt.pem ${OUTPUT_DIR}/temp/intermediate.crt.pem
echo ""
echo "Verifying leaf certificate against intermediate and root:"
openssl verify -CAfile ${OUTPUT_DIR}/temp/root.crt.pem -untrusted ${OUTPUT_DIR}/temp/intermediate.crt.pem ${OUTPUT_DIR}/temp/leaf.crt.pem
# Display generated files summary
echo ""
echo "=== Generated Files Summary ==="
echo ""
echo "DER Format (Algorithm: $ALGO):"
echo " Root CA certificate: ${OUTPUT_DIR}/root-cert.der"
echo " Root CA key: ${OUTPUT_DIR}/root-prvkey.der"
echo " Intermediate certificate: ${OUTPUT_DIR}/intermediate-cert.der"
echo " Intermediate key: ${OUTPUT_DIR}/intermediate-prvkey.der"
echo " Leaf certificate: ${OUTPUT_DIR}/leaf-cert.der"
echo " Leaf key: ${OUTPUT_DIR}/leaf-prvkey.der"
echo " Raw chain: ${OUTPUT_DIR}/raw-chain.der"
echo " Leaf public key: ${OUTPUT_DIR}/leaf-pubkey.der"
echo ""
echo "C Header file:"
echo " Certificate arrays: ${OUTPUT_DIR}/gen_certificates.h"
# Clean up temporary files
rm -rf ${OUTPUT_DIR}/temp ${OUTPUT_DIR}/root.srl ${OUTPUT_DIR}/intermediate.srl

View File

@ -27,8 +27,10 @@ PRVKEY_DER="$WOLFBOOT_DIR/priv.der"
PUBKEY_DER="$WOLFBOOT_DIR/priv_pub.der"
TARGET_H="$WOLFBOOT_DIR/include/target.h"
NVM_CONFIG="$WOLFBOOT_DIR/tools/scripts/tc3xx/wolfBoot-wolfHSM-keys.nvminit"
NVM_CONFIG_DUMMY_CERTCHAIN="$WOLFBOOT_DIR/tools/scripts/tc3xx/wolfBoot-wolfHSM-dummy-certchain.nvminit"
NVM_BIN="whNvmImage.bin"
NVM_HEX="whNvmImage.hex"
DUMMY_CERT_CHAIN="$WOLFBOOT_DIR/test-dummy-ca/raw-chain.der"
# Tool paths (relative to project root)
SQUASHELF="$WOLFBOOT_DIR/tools/squashelf/squashelf"
@ -93,30 +95,11 @@ declare -A ML_DSA_HEADER_SIZES=(
[5]=12288
)
# Get the header size based on the selected public key algorithm
get_header_size() {
local algo="$1"
local pq_params="$2"
case "$algo" in
"ml_dsa")
# Default to level 2 for ML-DSA if no params specified
echo "${ML_DSA_HEADER_SIZES[${pq_params:-2}]}"
;;
"ecc256") echo "256" ;;
"ecc384"|"ecc521"|"rsa2048"|"rsa3072") echo "512" ;;
"rsa4096") echo "1024" ;;
"ed25519") echo "256" ;;
"ed448") echo "512" ;;
"lms"|"xmss") echo "0" ;; # currently not supported
"none") echo "256" ;;
*) echo "256" ;; # Default
esac
}
# Add to command options structure
declare -A COMMON_OPTS=(
[sign_pq_params]=""
[certchain_file]=""
[dummy_certchain]=""
)
# Add LCF_OPTS to command options structure
@ -130,6 +113,80 @@ declare -A TARGET_OPTS=(
[use_elf_format]=""
)
# Add NVM_OPTS to command options structure
declare -A NVM_OPTS=(
[dummy_certchain]=""
)
# Get the effective certificate chain file path
get_effective_certchain_file() {
if [[ -n "${COMMON_OPTS[dummy_certchain]}" ]]; then
echo "$DUMMY_CERT_CHAIN"
elif [[ -n "${COMMON_OPTS[certchain_file]}" ]]; then
echo "${COMMON_OPTS[certchain_file]}"
else
echo ""
fi
}
# Get the header size based on the selected public key algorithm
get_header_size() {
local algo="$1"
local pq_params="$2"
local certchain_file="$3"
# Get base header size for the algorithm
local base_size
case "$algo" in
"ml_dsa")
# Default to level 2 for ML-DSA if no params specified
base_size="${ML_DSA_HEADER_SIZES[${pq_params:-2}]}"
;;
"ecc256") base_size="256" ;;
"ecc384"|"ecc521"|"rsa2048"|"rsa3072") base_size="512" ;;
"rsa4096") base_size="1024" ;;
"ed25519") base_size="256" ;;
"ed448") base_size="512" ;;
"lms"|"xmss") base_size="0" ;; # currently not supported
"none") base_size="256" ;;
*) base_size="256" ;; # Default
esac
# If no certificate chain, return base size
if [[ -z "$certchain_file" ]]; then
echo "$base_size"
return
fi
# Check if certificate chain file exists and get its size
if [[ ! -f "$certchain_file" ]]; then
echo "Error: Certificate chain file not found: $certchain_file" >&2
echo "$base_size"
return
fi
local cert_size
cert_size=$(stat -c%s "$certchain_file" 2>/dev/null || stat -f%z "$certchain_file" 2>/dev/null)
if [[ $? -ne 0 ]]; then
echo "Error: Cannot get certificate chain file size: $certchain_file" >&2
echo "$base_size"
return
fi
# Calculate total required space
# cert_size + 4 bytes (TLV header) + 8 bytes (max alignment padding)
local cert_overhead=$((cert_size + 12))
local total_required=$((base_size + cert_overhead))
# Round up to next power of 2 (matching C code behavior)
local final_size=$base_size
while [[ $final_size -lt $total_required ]]; do
final_size=$((final_size * 2))
done
echo "$final_size"
}
# Helper function to display usage
usage() {
echo "Usage: $0 [global-options] COMMAND [command-options] [COMMAND [command-options]]"
@ -142,11 +199,14 @@ usage() {
echo " keygen"
echo " --sign-algo ALGO Signing algorithm (default: ecc256)"
echo " --localkeys Use local keys (only valid with --hsm)"
echo " --dummy-certchain Generate dummy certificate chain after key generation"
echo ""
echo " sign"
echo " --sign-algo ALGO Signing algorithm (inherits from keygen if not specified)"
echo " --hash-algo ALGO Hash algorithm (default: sha256)"
echo " --debug Use debug build (default: release)"
echo " --certchain FILE Certificate chain file to include in header"
echo " --dummy-certchain Use dummy certificate chain in header"
echo ""
echo " target"
echo " No additional options"
@ -157,12 +217,16 @@ usage() {
echo " macros"
echo " --sign-algo ALGO Signing algorithm (inherits from keygen/sign if not specified)"
echo " --hash-algo ALGO Hash algorithm (inherits from sign if not specified)"
echo " --certchain FILE Certificate chain file to include in header"
echo " --dummy-certchain Use dummy certificate chain in header"
echo ""
echo " nvm"
echo " No additional options"
echo " --dummy-certchain Use dummy certificate chain configuration"
echo ""
echo " lcf"
echo " --sign-algo ALGO Signing algorithm (inherits from keygen/sign if not specified)"
echo " --certchain FILE Certificate chain file to include in header"
echo " --dummy-certchain Use dummy certificate chain in header"
echo ""
echo "Examples:"
echo " $0 keygen --sign-algo ecc256"
@ -174,28 +238,29 @@ usage() {
echo " $0 macros"
echo " $0 nvm"
echo " $0 lcf"
echo " $0 sign --certchain /path/to/cert_chain.pem"
echo " $0 macros --certchain /path/to/cert_chain.pem lcf --certchain /path/to/cert_chain.pem"
echo " $0 keygen --sign-algo ecc256 --dummy-certchain"
echo " $0 sign --dummy-certchain"
echo " $0 keygen --sign-algo ecc256 --dummy-certchain sign --dummy-certchain"
echo " $0 nvm --dummy-certchain"
exit 1
}
# Function to generate keys
do_keygen() {
local sign_algo="${KEYGEN_OPTS[sign_algo]:-$DEFAULT_SIGN_ALGO}"
local pq_params="${COMMON_OPTS[sign_pq_params]}"
local header_size
# Get header size for current algorithm
header_size=$(get_header_size "$sign_algo" "$pq_params")
echo "Generating keys with algorithm: $sign_algo"
# Set environment variables for keygen tool
export IMAGE_HEADER_SIZE="$header_size"
if [ "$sign_algo" = "ml_dsa" ]; then
export ML_DSA_LEVEL="${pq_params:-2}" # Default to level 2 if not specified
fi
(cd $WOLFBOOT_DIR && tools/keytools/keygen --"$sign_algo" -g $(basename $PRVKEY_DER) --exportpubkey \
${KEYGEN_OPTS[nolocalkeys]:+--nolocalkeys} --der)
# Generate dummy certificate chain if requested
if [[ -n "${COMMON_OPTS[dummy_certchain]}" ]]; then
echo "Generating dummy certificate chain with algorithm: $sign_algo"
(cd $WOLFBOOT_DIR && tools/scripts/sim-gen-dummy-chain.sh --algo "$sign_algo" --leaf priv.der)
fi
}
# Function to sign binaries
@ -218,7 +283,7 @@ do_sign() {
fi
# Get header size for current algorithm
header_size=$(get_header_size "$sign_algo" "$pq_params")
header_size=$(get_header_size "$sign_algo" "$pq_params" "$(get_effective_certchain_file)")
# Set IMAGE_HEADER_SIZE environment variable for sign tool
export IMAGE_HEADER_SIZE="$header_size"
@ -226,9 +291,17 @@ do_sign() {
echo "Signing binaries with $sign_algo and ${SIGN_OPTS[hash_algo]}"
echo "Using header size: $header_size"
# Build cert-chain argument if specified
local cert_chain_arg=""
if [[ -n "${COMMON_OPTS[certchain_file]}" ]]; then
cert_chain_arg="--cert-chain ${COMMON_OPTS[certchain_file]}"
elif [[ -n "${COMMON_OPTS[dummy_certchain]}" ]]; then
cert_chain_arg="--cert-chain $DUMMY_CERT_CHAIN"
fi
# Sign for both partition 1 and 2
../../keytools/sign --"$sign_algo" --"${SIGN_OPTS[hash_algo]}" "$bin_path" "$PRVKEY_DER" 1
../../keytools/sign --"$sign_algo" --"${SIGN_OPTS[hash_algo]}" "$bin_path" "$PRVKEY_DER" 2
../../keytools/sign --"$sign_algo" --"${SIGN_OPTS[hash_algo]}" $cert_chain_arg "$bin_path" "$PRVKEY_DER" 1
../../keytools/sign --"$sign_algo" --"${SIGN_OPTS[hash_algo]}" $cert_chain_arg "$bin_path" "$PRVKEY_DER" 2
}
# Function to generate target header
@ -303,7 +376,8 @@ do_gen_macros() {
local pq_params="${COMMON_OPTS[sign_pq_params]}"
# Get header size using the new function
local image_header_size=$(get_header_size "$sign_algo" "$pq_params")
local image_header_size=$(get_header_size "$sign_algo" "$pq_params" "$(get_effective_certchain_file)")
echo "generating macros with header size = $image_header_size"
local use_huge_stack=""
local use_wolfhsm_pubkey_id=""
@ -312,6 +386,7 @@ do_gen_macros() {
local ml_dsa_level=""
local use_wolfboot_elf=""
local use_wolfboot_elf_flash_scattered=""
local use_wolfboot_cert_chain_verify=""
# Map algorithms to their macro names
local sign_macro="${SIGN_ALGO_MAP[${sign_algo,,}]:-}"
@ -326,6 +401,13 @@ do_gen_macros() {
exit 1
fi
# Validate certificate chain usage
if [[ -n "${COMMON_OPTS[certchain_file]}" ]]; then
use_wolfboot_cert_chain_verify="-DWOLFBOOT_CERT_CHAIN_VERIFY"
elif [[ -n "${COMMON_OPTS[dummy_certchain]}" ]]; then
use_wolfboot_cert_chain_verify="-DWOLFBOOT_CERT_CHAIN_VERIFY"
fi
# Set huge stack for RSA4096
if [[ "${sign_algo,,}" == "rsa4096" ]]; then
use_huge_stack="-DWOLFBOOT_HUGE_STACK"
@ -373,6 +455,7 @@ do_gen_macros() {
-e "s/@WOLFBOOT_USE_WOLFHSM_PUBKEY_ID@/$use_wolfhsm_pubkey_id/g" \
-e "s/@WOLFBOOT_ELF@/$use_wolfboot_elf/g" \
-e "s/@WOLFBOOT_ELF_FLASH_SCATTER@/$use_wolfboot_elf_flash_scattered/g" \
-e "s/@WOLFBOOT_CERT_CHAIN_VERIFY@/$use_wolfboot_cert_chain_verify/g" \
"$macros_in" > "$macros_out"
# Remove empty lines from the output file, as they cause compiler errors
@ -381,9 +464,16 @@ do_gen_macros() {
# Function to generate a wolfHSM NVM image
do_gen_nvm() {
local nvm_config_file="$NVM_CONFIG"
# Use dummy cert chain config if specified
if [[ -n "${COMMON_OPTS[dummy_certchain]}" ]]; then
nvm_config_file="$NVM_CONFIG_DUMMY_CERTCHAIN"
fi
echo "Generating HSM NVM image"
echo "Running: $WHNVMTOOL --image=$NVM_BIN --size=0x10000 --invert-erased-byte $NVM_CONFIG"
"$WHNVMTOOL" --image="$NVM_BIN" --size=0x10000 --invert-erased-byte "$NVM_CONFIG"
echo "Running: $WHNVMTOOL --image=$NVM_BIN --size=0x10000 --invert-erased-byte $nvm_config_file"
"$WHNVMTOOL" --image="$NVM_BIN" --size=0x10000 --invert-erased-byte "$nvm_config_file"
echo "Converting to Intel HEX format"
echo "Running: objcopy -I binary -O ihex --change-address 0xAFC00000 $NVM_BIN $NVM_HEX"
@ -412,7 +502,7 @@ do_gen_lcf() {
local lcf_template="$base_dir/$app_dir/$template_name"
local lcf_output="$base_dir/$app_dir/Lcf_Gnuc_Tricore_Tc.lsl"
header_size=$(get_header_size "$sign_algo" "$pq_params")
header_size=$(get_header_size "$sign_algo" "$pq_params" "$(get_effective_certchain_file)")
echo "Generating LCF file with header_size=$header_size"
sed -e "s/@LCF_WOLFBOOT_HEADER_OFFSET@/$header_size/g" \
@ -479,7 +569,7 @@ while [[ $# -gt 0 ]]; do
;;
nvm)
OPERATIONS+=("nvm")
CURRENT_OPTS=""
CURRENT_OPTS="NVM_OPTS"
shift
;;
lcf)
@ -546,6 +636,26 @@ while [[ $# -gt 0 ]]; do
COMMON_OPTS[sign_pq_params]="$2"
shift 2
;;
--certchain)
if [[ -z "$CURRENT_OPTS" ]]; then
echo "Error: --certchain must follow a command"
exit 1
fi
if [[ -z "$HSM" ]]; then
echo "Error: --certchain can only be used with --hsm global option"
exit 1
fi
COMMON_OPTS[certchain_file]="$2"
shift 2
;;
--dummy-certchain)
if [[ -z "$CURRENT_OPTS" ]]; then
echo "Error: --dummy-certchain must follow a command"
exit 1
fi
COMMON_OPTS[dummy_certchain]="1"
shift
;;
*)
echo "Unknown option for ${CURRENT_OPTS:-global options}: $1"
usage
@ -559,6 +669,12 @@ if [ ${#OPERATIONS[@]} -eq 0 ]; then
usage
fi
# Validate that --certchain and --dummy-certchain are not both specified
if [[ -n "${COMMON_OPTS[certchain_file]}" && -n "${COMMON_OPTS[dummy_certchain]}" ]]; then
echo "Error: Cannot specify both --certchain and --dummy-certchain"
exit 1
fi
# Execute requested operations in order
for op in "${OPERATIONS[@]}"; do
case $op in

View File

@ -0,0 +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

View File

@ -5,7 +5,6 @@ EXPVER_CMD=$(EXPVER) /dev/ttyAMA0
BINASSEMBLE=tools/bin-assemble/bin-assemble
SPI_CHIP=SST25VF080B
SPI_OPTIONS=SPI_FLASH=1 WOLFBOOT_PARTITION_SIZE=0x80000 WOLFBOOT_PARTITION_UPDATE_ADDRESS=0x00000 WOLFBOOT_PARTITION_SWAP_ADDRESS=0x80000
SIGN_ARGS=
SIGN_ENC_ARGS=
DELTA_DATA_SIZE?=2000
@ -21,49 +20,6 @@ else
SIGN_TOOL="$(WOLFBOOT_ROOT)/tools/keytools/sign"
endif
# Make sign algorithm argument
ifeq ($(SIGN),NONE)
SIGN_ARGS+=--no-sign
endif
ifeq ($(SIGN),ED25519)
SIGN_ARGS+= --ed25519
endif
ifeq ($(SIGN),ED448)
SIGN_ARGS+= --ed448
endif
ifeq ($(SIGN),ECC256)
SIGN_ARGS+= --ecc256
endif
ifeq ($(SIGN),RSA2048)
SIGN_ARGS+= --rsa2048
endif
ifeq ($(SIGN),RSA3072)
SIGN_ARGS+= --rsa3072
endif
ifeq ($(SIGN),RSA4096)
SIGN_ARGS+= --rsa4096
endif
ifeq ($(SIGN),LMS)
SIGN_ARGS+= --lms
endif
ifeq ($(SIGN),XMSS)
SIGN_ARGS+= --xmss
endif
ifeq ($(SIGN),ML_DSA)
SIGN_ARGS+= --ml_dsa
endif
# Make sign hash argument
ifeq ($(HASH),SHA256)
SIGN_ARGS+= --sha256
endif
ifeq ($(HASH),SHA384)
SIGN_ARGS+= --sha384
endif
ifeq ($(HASH),SHA3)
SIGN_ARGS+= --sha3
endif
ifeq ($(FLAGS_INVERT),1)
INVERSION=
else
@ -138,7 +94,7 @@ test-spi-off: FORCE
test-update: test-app/image.bin FORCE
@dd if=/dev/zero bs=131067 count=1 2>/dev/null $(INVERSION) > test-update.bin
@$(SIGN_ENV) $(SIGN_TOOL) $(SIGN_ARGS) test-app/image.bin $(PRIVATE_KEY) $(TEST_UPDATE_VERSION)
@$(SIGN_ENV) $(SIGN_TOOL) $(SIGN_OPTIONS) test-app/image.bin $(PRIVATE_KEY) $(TEST_UPDATE_VERSION)
@dd if=test-app/image_v$(TEST_UPDATE_VERSION)_signed.bin of=test-update.bin bs=1 conv=notrunc
@printf "pBOOT" >> test-update.bin
@make test-reset
@ -174,7 +130,7 @@ test-sim-external-flash-with-enc-delta-update-extradata: wolfboot.bin test-app/i
$(Q)make -C test-app delta-extra-data DELTA_DATA_SIZE=$(DELTA_DATA_SIZE)
$(Q)cp test-app/image_v1_signed.bak test-app/image_v1_signed.bin
$(Q)$(SIGN_ENV) $(SIGN_TOOL) $(SIGN_OPTIONS) $(SIGN_ENC_ARGS) test-app/image.elf $(PRIVATE_KEY) $(TEST_UPDATE_VERSION)
$(Q)$(SIGN_ENV) $(SIGN_TOOL) $(SIGN_ARGS) $(DELTA_UPDATE_OPTIONS) $(SIGN_ENC_ARGS) \
$(Q)$(SIGN_ENV) $(SIGN_TOOL) $(SIGN_OPTIONS) $(DELTA_UPDATE_OPTIONS) $(SIGN_ENC_ARGS) \
test-app/image.elf $(PRIVATE_KEY) $(TEST_UPDATE_VERSION)
$(Q)dd if=/dev/zero bs=$$(($(WOLFBOOT_PARTITION_SIZE))) count=1 2>/dev/null $(INVERSION) > v1_part.dd
$(Q)dd if=test-app/image_v1_signed.bin bs=256 of=v1_part.dd conv=notrunc
@ -192,11 +148,16 @@ test-sim-external-flash-with-enc-update: wolfboot.bin test-app/image.elf FORCE
$(Q)cp test-app/image.elf test-app/image.bak.elf
$(Q)dd if=/dev/urandom of=test-app/image.elf bs=1k count=16 oflag=append conv=notrunc
@printf "0123456789abcdef0123456789abcdef0123456789abcdef" > /tmp/enc_key.der
# First sign command: Create version 1 of the encrypted application (base image)
$(Q)$(SIGN_ENV) $(SIGN_TOOL) $(SIGN_OPTIONS) $(SIGN_ENC_ARGS) test-app/image.elf $(PRIVATE_KEY) 1
$(Q)cp test-app/image.bak.elf test-app/image.elf
$(Q)dd if=/dev/urandom of=test-app/image.elf bs=1k count=16 oflag=append conv=notrunc
# Second sign command: Create a full encrypted update (version 2 by default)
# This produces image_v2_signed_and_encrypted.bin which is needed for the first flash assembly step
$(Q)$(SIGN_ENV) $(SIGN_TOOL) $(SIGN_OPTIONS) $(SIGN_ENC_ARGS) test-app/image.elf $(PRIVATE_KEY) $(TEST_UPDATE_VERSION)
$(Q)$(SIGN_ENV) $(SIGN_TOOL) $(SIGN_ARGS) $(DELTA_UPDATE_OPTIONS) $(SIGN_ENC_ARGS) \
# Third sign command: Create update with delta option (if specified), producing image_v2_signed_diff_encrypted.bin
# This file is used by the test-sim-external-flash-with-enc-delta-update target
$(Q)$(SIGN_ENV) $(SIGN_TOOL) $(SIGN_OPTIONS) $(DELTA_UPDATE_OPTIONS) $(SIGN_ENC_ARGS) \
test-app/image.elf $(PRIVATE_KEY) $(TEST_UPDATE_VERSION)
# Assembling internal flash image
#
@ -210,6 +171,9 @@ test-sim-external-flash-with-enc-update: wolfboot.bin test-app/image.elf FORCE
$(WOLFBOOT_PARTITION_SIZE) erased_sec.dd
test-sim-external-flash-with-enc-delta-update:
# This target first calls test-sim-external-flash-with-enc-update to generate both
# image_v2_signed_and_encrypted.bin (full update) and image_v2_signed_diff_encrypted.bin (delta update)
# Then it rebuilds the external flash image using the delta update version
make test-sim-external-flash-with-enc-update DELTA_UPDATE_OPTIONS="--delta test-app/image_v1_signed.bin"
$(Q)$(BINASSEMBLE) external_flash.dd 0 test-app/image_v$(TEST_UPDATE_VERSION)_signed_diff_encrypted.bin \
$(WOLFBOOT_PARTITION_SIZE) erased_sec.dd
@ -217,12 +181,17 @@ test-sim-external-flash-with-enc-delta-update:
test-sim-internal-flash-with-update: wolfboot.bin test-app/image.elf FORCE
$(Q)cp test-app/image.elf test-app/image.bak.elf
$(Q)dd if=/dev/urandom of=test-app/image.elf bs=1k count=16 oflag=append conv=notrunc
# Create version 1 of the application (base image)
$(Q)$(SIGN_ENV) $(SIGN_TOOL) $(SIGN_OPTIONS) test-app/image.elf $(PRIVATE_KEY) 1
$(Q)cp test-app/image.bak.elf test-app/image.elf
$(Q)dd if=/dev/urandom of=test-app/image.elf bs=1k count=16 oflag=append conv=notrunc
$(Q)$(SIGN_ENV) $(SIGN_TOOL) $(SIGN_OPTIONS) test-app/image.elf $(PRIVATE_KEY) $(TEST_UPDATE_VERSION)
$(Q)dd if=/dev/zero bs=$$(($(WOLFBOOT_SECTOR_SIZE))) count=1 2>/dev/null $(INVERSION) > erased_sec.dd
$(Q)$(SIGN_ENV) $(SIGN_TOOL) $(SIGN_ARGS) $(DELTA_UPDATE_OPTIONS) \
# Sign the update image (version 2 by default)
# This command handles both standard and delta update modes based on DELTA_UPDATE_OPTIONS
# empty DELTA_UPDATE_OPTIONS (Without --delta): Produces image_v2_signed.bin
# DELTA_UPDATE_OPTIONS="--delta test-app/image_v1_signed.bin": Produces image_v2_signed_diff.bin
$(Q)$(SIGN_ENV) $(SIGN_TOOL) $(SIGN_OPTIONS) $(DELTA_UPDATE_OPTIONS) \
test-app/image.elf $(PRIVATE_KEY) $(TEST_UPDATE_VERSION)
$(Q)$(BINASSEMBLE) internal_flash.dd \
0 wolfboot.bin \
@ -231,6 +200,9 @@ test-sim-internal-flash-with-update: wolfboot.bin test-app/image.elf FORCE
$$(($(WOLFBOOT_PARTITION_SWAP_ADDRESS)-$(ARCH_FLASH_OFFSET))) erased_sec.dd
test-sim-internal-flash-with-delta-update:
# This target calls test-sim-internal-flash-with-update with the delta option
# The delta option causes the sign tool to produce image_v2_signed_diff.bin instead of image_v2_signed.bin
# Then it rebuilds the internal flash image using the delta update version
make test-sim-internal-flash-with-update DELTA_UPDATE_OPTIONS="--delta test-app/image_v1_signed.bin"
$(Q)$(BINASSEMBLE) internal_flash.dd \
0 wolfboot.bin \
@ -247,6 +219,9 @@ test-sim-internal-flash-with-delta-update-no-base-sha:
$$(($(WOLFBOOT_PARTITION_SWAP_ADDRESS)-$(ARCH_FLASH_OFFSET))) erased_sec.dd
test-sim-internal-flash-with-wrong-delta-update:
# This target tests the bootloader's ability to reject delta updates with wrong base hashes
# First it creates a delta update based on v1, then creates a different delta update based on v2
# The final image contains v1 as the base image but a delta update that expects v2 as its base
make test-sim-internal-flash-with-update DELTA_UPDATE_OPTIONS="--delta test-app/image_v1_signed.bin"
make test-sim-internal-flash-with-update DELTA_UPDATE_OPTIONS="--delta test-app/image_v2_signed.bin" TEST_UPDATE_VERSION=3
$(Q)$(BINASSEMBLE) internal_flash.dd \
@ -268,12 +243,12 @@ test-sim-rollback-flash: wolfboot.elf test-sim-internal-flash-with-update FORCE
test-self-update: FORCE
@mv $(PRIVATE_KEY) private_key.old
@make clean factory.bin RAM_CODE=1 WOLFBOOT_VERSION=1 SIGN=$(SIGN)
@$(SIGN_ENV) $(SIGN_TOOL) $(SIGN_ARGS) test-app/image.bin $(PRIVATE_KEY) $(TEST_UPDATE_VERSION)
@$(SIGN_ENV) $(SIGN_TOOL) $(SIGN_OPTIONS) test-app/image.bin $(PRIVATE_KEY) $(TEST_UPDATE_VERSION)
@st-flash --reset write test-app/image_v2_signed.bin 0x08020000 || \
(make test-reset && sleep 1 && st-flash --reset write test-app/image_v2_signed.bin 0x08020000) || \
(make test-reset && sleep 1 && st-flash --reset write test-app/image_v2_signed.bin 0x08020000)
@dd if=/dev/zero bs=131067 count=1 2>/dev/null $(INVERSION) > test-self-update.bin
@$(SIGN_ENV) $(SIGN_TOOL) $(SIGN_ARGS) --wolfboot-update wolfboot.bin private_key.old $(WOLFBOOT_VERSION)
@$(SIGN_ENV) $(SIGN_TOOL) $(SIGN_OPTIONS) --wolfboot-update wolfboot.bin private_key.old $(WOLFBOOT_VERSION)
@dd if=wolfboot_v$(WOLFBOOT_VERSION)_signed.bin of=test-self-update.bin bs=1 conv=notrunc
@printf "pBOOT" >> test-self-update.bin
@st-flash --reset write test-self-update.bin 0x08040000 || \
@ -281,7 +256,7 @@ test-self-update: FORCE
(make test-reset && sleep 1 && st-flash --reset write test-self-update.bin 0x08040000)
test-update-ext: test-app/image.bin FORCE
@$(SIGN_ENV) $(SIGN_TOOL) $(SIGN_ARGS) test-app/image.bin $(PRIVATE_KEY) $(TEST_UPDATE_VERSION)
@$(SIGN_ENV) $(SIGN_TOOL) $(SIGN_OPTIONS) test-app/image.bin $(PRIVATE_KEY) $(TEST_UPDATE_VERSION)
@(dd if=/dev/zero bs=1M count=1 | tr '\000' '\377' > test-update.rom)
@dd if=test-app/image_v$(TEST_UPDATE_VERSION)_signed.bin of=test-update.rom bs=1 count=524283 conv=notrunc
@printf "pBOOT" | dd of=test-update.rom obs=1 seek=524283 count=5 conv=notrunc
@ -994,13 +969,13 @@ test-size-all:
make clean
make test-size SIGN=ECC256 NO_ASM=1 LIMIT=13536 NO_ARM_ASM=1
make keysclean
make test-size SIGN=RSA2048 LIMIT=11232 NO_ARM_ASM=1
make test-size SIGN=RSA2048 LIMIT=11272 NO_ARM_ASM=1
make clean
make test-size SIGN=RSA2048 NO_ASM=1 LIMIT=11808 NO_ARM_ASM=1
make test-size SIGN=RSA2048 NO_ASM=1 LIMIT=11840 NO_ARM_ASM=1
make keysclean
make test-size SIGN=RSA4096 LIMIT=11520 NO_ARM_ASM=1
make test-size SIGN=RSA4096 LIMIT=11556 NO_ARM_ASM=1
make clean
make test-size SIGN=RSA4096 NO_ASM=1 LIMIT=12096 NO_ARM_ASM=1
make test-size SIGN=RSA4096 NO_ASM=1 LIMIT=12128 NO_ARM_ASM=1
make keysclean
make test-size SIGN=ECC384 LIMIT=17556 NO_ARM_ASM=1
make clean
@ -1008,9 +983,9 @@ test-size-all:
make keysclean
make test-size SIGN=ED448 LIMIT=13464 NO_ARM_ASM=1
make keysclean
make test-size SIGN=RSA3072 LIMIT=11372 NO_ARM_ASM=1
make test-size SIGN=RSA3072 LIMIT=11408 NO_ARM_ASM=1
make clean
make test-size SIGN=RSA3072 NO_ASM=1 LIMIT=11912 NO_ARM_ASM=1
make test-size SIGN=RSA3072 NO_ASM=1 LIMIT=11944 NO_ARM_ASM=1
make keysclean
make test-size SIGN=LMS LMS_LEVELS=2 LMS_HEIGHT=5 LMS_WINTERNITZ=8 \
WOLFBOOT_SMALL_STACK=0 IMAGE_SIGNATURE_SIZE=2644 \