mirror of https://github.com/wolfSSL/wolfBoot.git
Add wolfHSM cert chain verification for ECC and RSA
parent
2b996f8280
commit
7660bf66f8
|
@ -18,6 +18,8 @@ jobs:
|
||||||
file: "config/examples/sim-wolfHSM.config"
|
file: "config/examples/sim-wolfHSM.config"
|
||||||
- name: "wolfHSM ML-DSA"
|
- name: "wolfHSM ML-DSA"
|
||||||
file: "config/examples/sim-wolfHSM-mldsa.config"
|
file: "config/examples/sim-wolfHSM-mldsa.config"
|
||||||
|
- name: "wolfHSM cert chain verify"
|
||||||
|
file: "config/examples/sim-wolfHSM-certchain.config"
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
@ -54,8 +56,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
repository: wolfssl/wolfHSM-examples
|
repository: wolfssl/wolfHSM-examples
|
||||||
# Make sure to update this when the wolfHSM submodule is updated!
|
# Make sure to update this when the wolfHSM submodule is updated!
|
||||||
#ref: wolfHSM-v1.1.0
|
ref: wolfHSM-examples-v1.2.0
|
||||||
ref: 3e03bd4d4a8439ed4a8a9577823c89e4c37eb9be
|
|
||||||
path: wolfHSM-examples
|
path: wolfHSM-examples
|
||||||
|
|
||||||
- name: Build example POSIX TCP server
|
- name: Build example POSIX TCP server
|
||||||
|
@ -65,7 +66,13 @@ jobs:
|
||||||
- name: Run POSIX TCP server
|
- name: Run POSIX TCP server
|
||||||
run: |
|
run: |
|
||||||
cd wolfHSM-examples/posix/tcp/wh_server_tcp
|
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 --cert ../../../../wolfboot_signing_cert_chain.der &
|
||||||
|
fi
|
||||||
TCP_SERVER_PID=$!
|
TCP_SERVER_PID=$!
|
||||||
echo "TCP_SERVER_PID=$TCP_SERVER_PID" >> $GITHUB_ENV
|
echo "TCP_SERVER_PID=$TCP_SERVER_PID" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
|
|
@ -99,6 +99,7 @@ include/target.h
|
||||||
.wolfboot-partition-size
|
.wolfboot-partition-size
|
||||||
.bootloader-partition-size
|
.bootloader-partition-size
|
||||||
MPLabX/wolfBoot-SAME51.X/.generated_files/
|
MPLabX/wolfBoot-SAME51.X/.generated_files/
|
||||||
|
test-dummy-ca/**
|
||||||
|
|
||||||
# Test tools
|
# Test tools
|
||||||
tools/check_config/check_config
|
tools/check_config/check_config
|
||||||
|
|
2
Makefile
2
Makefile
|
@ -229,6 +229,7 @@ $(PRIVATE_KEY):
|
||||||
$(Q)(test $(SIGN) = NONE) || ($(SIGN_ENV) "$(KEYGEN_TOOL)" $(KEYGEN_OPTIONS) -g $(PRIVATE_KEY)) || true
|
$(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 $(SIGN) = NONE) && (echo "// SIGN=NONE" > src/keystore.c) || true
|
||||||
$(Q)(test "$(FLASH_OTP_KEYSTORE)" = "1") && (make -C tools/keytools/otp) || 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
|
$(SECONDARY_PRIVATE_KEY): $(PRIVATE_KEY) keystore.der
|
||||||
$(Q)$(MAKE) keytools_check
|
$(Q)$(MAKE) keytools_check
|
||||||
|
@ -390,6 +391,7 @@ utilsclean: clean
|
||||||
|
|
||||||
keysclean: clean
|
keysclean: clean
|
||||||
$(Q)rm -f *.pem *.der tags ./src/*_pub_key.c ./src/keystore.c include/target.h
|
$(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
|
distclean: clean keysclean utilsclean
|
||||||
$(Q)rm -f *.bin *.elf
|
$(Q)rm -f *.bin *.elf
|
||||||
|
|
|
@ -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
|
|
@ -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.
|
* `--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 conjuction with wolfHSM. See [wolfHSM.md](wolfHSM.md) for more details.
|
||||||
|
|
||||||
#### Target partition id (Multiple partition images, "self-update" feature)
|
#### Target partition id (Multiple partition images, "self-update" feature)
|
||||||
|
|
||||||
If none of the following is used, "--id=1" is assumed by default. On systems
|
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
|
./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)
|
### Signing Firmware with External Private Key (HSM)
|
||||||
|
|
||||||
|
|
|
@ -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
|
- 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**.
|
- 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).
|
||||||
|
|
|
@ -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.
|
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
|
## 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.
|
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.
|
||||||
|
|
|
@ -100,6 +100,9 @@ const int hsmClientKeyIdPubKey = 0xFF;
|
||||||
const int hsmClientDevIdCrypt = WH_DEV_ID;
|
const int hsmClientDevIdCrypt = WH_DEV_ID;
|
||||||
const int hsmClientKeyIdCrypt = 0xFF;
|
const int hsmClientKeyIdCrypt = 0xFF;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef WOLFBOOT_CERT_CHAIN_VERIFY
|
||||||
|
const int hsmClientNvmIdCertRootCA = 1;
|
||||||
|
#endif
|
||||||
|
|
||||||
int hal_hsm_init_connect(void);
|
int hal_hsm_init_connect(void);
|
||||||
int hal_hsm_disconnect(void);
|
int hal_hsm_disconnect(void);
|
||||||
|
|
|
@ -161,6 +161,10 @@ extern const int hsmClientKeyIdPubKey; /* KeyId for public key operations */
|
||||||
#ifdef EXT_ENCRYPTED
|
#ifdef EXT_ENCRYPTED
|
||||||
extern const int hsmClientKeyIdCrypt; /* KeyId for image (enc/dec)ryption */
|
extern const int hsmClientKeyIdCrypt; /* KeyId for image (enc/dec)ryption */
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef WOLFBOOT_CERT_CHAIN_VERIFY
|
||||||
|
/* NvmId for trusted root CA certificate */
|
||||||
|
extern const whNvmId hsmClientNvmIdCertRootCA;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Implementation of functions provided by HAL */
|
/* Implementation of functions provided by HAL */
|
||||||
int hal_hsm_init_connect(void);
|
int hal_hsm_init_connect(void);
|
||||||
|
|
|
@ -79,6 +79,7 @@ extern "C" {
|
||||||
#define HDR_SIGNATURE 0x20
|
#define HDR_SIGNATURE 0x20
|
||||||
#define HDR_POLICY_SIGNATURE 0x21
|
#define HDR_POLICY_SIGNATURE 0x21
|
||||||
#define HDR_SECONDARY_SIGNATURE 0x22
|
#define HDR_SECONDARY_SIGNATURE 0x22
|
||||||
|
#define HDR_CERT_CHAIN 0x23
|
||||||
#define HDR_PADDING 0xFF
|
#define HDR_PADDING 0xFF
|
||||||
|
|
||||||
/* Auth Key types */
|
/* Auth Key types */
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit ea4c3db1e05b878f39c107b375c4c57ac93ab35a
|
Subproject commit f76701294bb8be47c7a9364a1061483c9ed7b3af
|
|
@ -1 +1 @@
|
||||||
Subproject commit b077c81eb635392e694ccedbab8b644297ec0285
|
Subproject commit 2151a1b8a1f8f81c4dba985429d50b76db7307e5
|
38
options.mk
38
options.mk
|
@ -907,5 +907,43 @@ ifeq ($(WOLFHSM_CLIENT),1)
|
||||||
ifneq ($(WOLFHSM_CLIENT_LOCAL_KEYS),1)
|
ifneq ($(WOLFHSM_CLIENT_LOCAL_KEYS),1)
|
||||||
KEYGEN_OPTIONS += --nolocalkeys
|
KEYGEN_OPTIONS += --nolocalkeys
|
||||||
CFLAGS += -DWOLFBOOT_USE_WOLFHSM_PUBKEY_ID
|
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
|
||||||
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
|
||||||
|
|
106
src/image.c
106
src/image.c
|
@ -55,6 +55,12 @@
|
||||||
/* Globals */
|
/* Globals */
|
||||||
static uint8_t digest[WOLFBOOT_SHA_DIGEST_SIZE];
|
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 */
|
/* TPM based verify */
|
||||||
#if defined(WOLFBOOT_TPM) && defined(WOLFBOOT_TPM_VERIFY)
|
#if defined(WOLFBOOT_TPM) && defined(WOLFBOOT_TPM_VERIFY)
|
||||||
#ifdef ECC_IMAGE_SIGNATURE_SIZE
|
#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;
|
const int point_sz = ECC_IMAGE_SIGNATURE_SIZE / 2;
|
||||||
|
|
||||||
/* Use the public key ID to verify the signature */
|
/* 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);
|
ret = wh_Client_EccSetKeyId(&ecc, hsmClientKeyIdPubKey);
|
||||||
|
#endif
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -273,6 +295,12 @@ static void wolfBoot_verify_signature_ecc(uint8_t key_slot,
|
||||||
img->sha_hash, WOLFBOOT_SHA_DIGEST_SIZE, &verify_res,
|
img->sha_hash, WOLFBOOT_SHA_DIGEST_SIZE, &verify_res,
|
||||||
&ecc);
|
&ecc);
|
||||||
}
|
}
|
||||||
|
#if defined(WOLFBOOT_CERT_CHAIN_VERIFY)
|
||||||
|
if (g_leafKeyIdValid) {
|
||||||
|
(void)wh_Client_KeyEvict(&hsmClientCtx, g_certLeafKeyId);
|
||||||
|
g_leafKeyIdValid = 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#else
|
#else
|
||||||
/* Import public key */
|
/* Import public key */
|
||||||
ret = wc_ecc_import_unsigned(&ecc, pubkey, pubkey + point_sz, NULL,
|
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)
|
#if defined(WOLFBOOT_USE_WOLFHSM_PUBKEY_ID)
|
||||||
(void)key_slot;
|
(void)key_slot;
|
||||||
/* public key is stored on server at hsmClientKeyIdPubKey*/
|
/* 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);
|
ret = wh_Client_RsaSetKeyId(&rsa, hsmClientKeyIdPubKey);
|
||||||
|
#endif
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -426,6 +470,11 @@ static void wolfBoot_verify_signature_rsa(uint8_t key_slot,
|
||||||
if (WH_ERROR_OK != wh_Client_KeyEvict(&hsmClientCtx, hsmKeyId)) {
|
if (WH_ERROR_OK != wh_Client_KeyEvict(&hsmClientCtx, hsmKeyId)) {
|
||||||
return;
|
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 */
|
#endif /* !WOLFBOOT_USE_WOLFHSM_PUBKEY_ID */
|
||||||
#else
|
#else
|
||||||
/* wolfCrypt software RSA verify */
|
/* 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 && \
|
#if defined WOLFBOOT_ENABLE_WOLFHSM_CLIENT && \
|
||||||
defined(WOLFBOOT_USE_WOLFHSM_PUBKEY_ID)
|
defined(WOLFBOOT_USE_WOLFHSM_PUBKEY_ID)
|
||||||
/* Use key slot ID directly with wolfHSM */
|
/* 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);
|
ret = wh_Client_MlDsaSetKeyId(&ml_dsa, hsmClientKeyIdPubKey);
|
||||||
|
#endif
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
wolfBoot_printf("error: wh_Client_MlDsaSetKeyId returned %d\n", ret);
|
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 key_mask = 0U;
|
||||||
uint32_t image_part = 1U;
|
uint32_t image_part = 1U;
|
||||||
int key_slot;
|
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);
|
stored_signature_size = get_header(img, HDR_SIGNATURE, &stored_signature);
|
||||||
pubkey_hint_size = get_header(img, HDR_PUBKEY, &pubkey_hint);
|
pubkey_hint_size = get_header(img, HDR_PUBKEY, &pubkey_hint);
|
||||||
|
@ -1955,6 +2030,37 @@ int wolfBoot_verify_authenticity(struct wolfBoot_image *img)
|
||||||
|
|
||||||
CONFIRM_MASK_VALID(image_part, key_mask);
|
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 */
|
||||||
|
hsm_ret = wh_Client_CertVerifyAndCacheLeafPubKey(
|
||||||
|
&hsmClientCtx, cert_chain, cert_chain_size,
|
||||||
|
hsmClientNvmIdCertRootCA, &g_certLeafKeyId, &cert_verify_result);
|
||||||
|
|
||||||
|
/* 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.
|
/* wolfBoot_verify_signature_ecc() does not return the result directly.
|
||||||
* A call to wolfBoot_image_confirm_signature_ok() is required in order to
|
* A call to wolfBoot_image_confirm_signature_ok() is required in order to
|
||||||
* confirm that the signature verification is OK.
|
* confirm that the signature verification is OK.
|
||||||
|
|
|
@ -158,6 +158,7 @@ static inline int fp_truncate(FILE *f, size_t len)
|
||||||
#define HDR_SIGNATURE 0x20
|
#define HDR_SIGNATURE 0x20
|
||||||
#define HDR_POLICY_SIGNATURE 0x21
|
#define HDR_POLICY_SIGNATURE 0x21
|
||||||
#define HDR_SECONDARY_SIGNATURE 0x22
|
#define HDR_SECONDARY_SIGNATURE 0x22
|
||||||
|
#define HDR_CERT_CHAIN 0x23
|
||||||
|
|
||||||
|
|
||||||
#define HDR_SHA256_LEN 32
|
#define HDR_SHA256_LEN 32
|
||||||
|
@ -265,6 +266,7 @@ struct cmd_options {
|
||||||
const char *policy_file;
|
const char *policy_file;
|
||||||
const char *encrypt_key_file;
|
const char *encrypt_key_file;
|
||||||
const char *delta_base_file;
|
const char *delta_base_file;
|
||||||
|
const char *cert_chain_file;
|
||||||
int no_base_sha;
|
int no_base_sha;
|
||||||
char output_image_file[PATH_MAX];
|
char output_image_file[PATH_MAX];
|
||||||
char output_diff_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_sz = curve_sz * 2;
|
||||||
*pubkey = malloc(*pubkey_sz); /* assume malloc works */
|
*pubkey = malloc(*pubkey_sz); /* assume malloc works */
|
||||||
|
if (*pubkey == NULL) {
|
||||||
|
printf("Pubkey malloc error!\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
initRet = ret = wc_ecc_init(&key.ecc);
|
initRet = ret = wc_ecc_init(&key.ecc);
|
||||||
if (CMD.manual_sign || CMD.sha_only) {
|
if (CMD.manual_sign || CMD.sha_only) {
|
||||||
/* raw (public x + public y) */
|
/* 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) {
|
if (ret != 0 && initRet == 0) {
|
||||||
wc_ecc_free(&key.ecc);
|
wc_ecc_free(&key.ecc);
|
||||||
}
|
}
|
||||||
if (ret != 0)
|
if (ret != 0) {
|
||||||
free(*pubkey);
|
free(*pubkey);
|
||||||
|
*pubkey = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (ret == 0 || CMD.sign != SIGN_AUTO) {
|
if (ret == 0 || CMD.sign != SIGN_AUTO) {
|
||||||
if (CMD.header_sz < header_sz)
|
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;
|
uint32_t keySzOut = 0;
|
||||||
|
|
||||||
if (CMD.manual_sign || CMD.sha_only) {
|
if (CMD.manual_sign || CMD.sha_only) {
|
||||||
/* use public key directly */
|
/* Allocate and copy pubkey instead of using key_buffer directly */
|
||||||
*pubkey = *key_buffer;
|
|
||||||
*pubkey_sz = *key_buffer_sz;
|
*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) {
|
if (*pubkey_sz <= rsa_pubkeysz) {
|
||||||
CMD.header_sz = header_sz;
|
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) {
|
if (ret > 0) {
|
||||||
*pubkey = *key_buffer;
|
/* Allocate and copy pubkey instead of using key_buffer directly */
|
||||||
*pubkey_sz = ret;
|
*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;
|
ret = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -565,6 +588,10 @@ static uint8_t *load_key(uint8_t **key_buffer, uint32_t *key_buffer_sz,
|
||||||
initRet = -1;
|
initRet = -1;
|
||||||
*pubkey_sz = ED25519_PUB_KEY_SIZE;
|
*pubkey_sz = ED25519_PUB_KEY_SIZE;
|
||||||
*pubkey = malloc(*pubkey_sz);
|
*pubkey = malloc(*pubkey_sz);
|
||||||
|
if (*pubkey == NULL) {
|
||||||
|
printf("Pubkey malloc error!\n");
|
||||||
|
goto failure;
|
||||||
|
}
|
||||||
|
|
||||||
if (CMD.manual_sign || CMD.sha_only) {
|
if (CMD.manual_sign || CMD.sha_only) {
|
||||||
/* raw */
|
/* raw */
|
||||||
|
@ -628,6 +655,10 @@ static uint8_t *load_key(uint8_t **key_buffer, uint32_t *key_buffer_sz,
|
||||||
initRet = -1;
|
initRet = -1;
|
||||||
*pubkey_sz = ED448_PUB_KEY_SIZE;
|
*pubkey_sz = ED448_PUB_KEY_SIZE;
|
||||||
*pubkey = malloc(*pubkey_sz);
|
*pubkey = malloc(*pubkey_sz);
|
||||||
|
if (*pubkey == NULL) {
|
||||||
|
printf("Pubkey malloc error!\n");
|
||||||
|
goto failure;
|
||||||
|
}
|
||||||
|
|
||||||
if (CMD.manual_sign || CMD.sha_only) {
|
if (CMD.manual_sign || CMD.sha_only) {
|
||||||
/* raw */
|
/* 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 +
|
if (*key_buffer_sz == (HSS_MAX_PRIVATE_KEY_LEN +
|
||||||
KEYSTORE_PUBKEY_SIZE_LMS)) {
|
KEYSTORE_PUBKEY_SIZE_LMS)) {
|
||||||
/* priv + pub */
|
/* priv + pub */
|
||||||
*pubkey = (*key_buffer) + HSS_MAX_PRIVATE_KEY_LEN;
|
*pubkey_sz = KEYSTORE_PUBKEY_SIZE_LMS;
|
||||||
*pubkey_sz = (*key_buffer_sz) - HSS_MAX_PRIVATE_KEY_LEN;
|
*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;
|
ret = 0;
|
||||||
printf("Found LMS key\n");
|
printf("Found LMS key\n");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (*key_buffer_sz == KEYSTORE_PUBKEY_SIZE_LMS) {
|
else if (*key_buffer_sz == KEYSTORE_PUBKEY_SIZE_LMS) {
|
||||||
/* pub only */
|
/* pub only */
|
||||||
*pubkey = (*key_buffer);
|
|
||||||
*pubkey_sz = KEYSTORE_PUBKEY_SIZE_LMS;
|
*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;
|
ret = 0;
|
||||||
printf("Found LMS public only key\n");
|
printf("Found LMS public only key\n");
|
||||||
break;
|
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)) {
|
if (*key_buffer_sz == (priv_sz + KEYSTORE_PUBKEY_SIZE_XMSS)) {
|
||||||
/* priv + pub */
|
/* priv + pub */
|
||||||
*pubkey = (*key_buffer) + priv_sz;
|
*pubkey_sz = KEYSTORE_PUBKEY_SIZE_XMSS;
|
||||||
*pubkey_sz = (*key_buffer_sz) - priv_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;
|
ret = 0;
|
||||||
printf("Found XMSS key\n");
|
printf("Found XMSS key\n");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (*key_buffer_sz == KEYSTORE_PUBKEY_SIZE_XMSS) {
|
else if (*key_buffer_sz == KEYSTORE_PUBKEY_SIZE_XMSS) {
|
||||||
/* pub only */
|
/* pub only */
|
||||||
*pubkey = (*key_buffer);
|
|
||||||
*pubkey_sz = KEYSTORE_PUBKEY_SIZE_XMSS;
|
*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;
|
ret = 0;
|
||||||
printf("Found XMSS public only key\n");
|
printf("Found XMSS public only key\n");
|
||||||
break;
|
break;
|
||||||
|
@ -844,16 +895,26 @@ static uint8_t *load_key(uint8_t **key_buffer, uint32_t *key_buffer_sz,
|
||||||
/* priv + pub */
|
/* priv + pub */
|
||||||
ret = wc_MlDsaKey_ImportPrivRaw(&key.ml_dsa, *key_buffer,
|
ret = wc_MlDsaKey_ImportPrivRaw(&key.ml_dsa, *key_buffer,
|
||||||
priv_sz);
|
priv_sz);
|
||||||
*pubkey = (*key_buffer) + priv_sz;
|
*pubkey_sz = pub_sz;
|
||||||
*pubkey_sz = (*key_buffer_sz) - priv_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;
|
ret = 0;
|
||||||
printf("Found ml-dsa key\n");
|
printf("Found ml-dsa key\n");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (*key_buffer_sz == pub_sz) {
|
else if (*key_buffer_sz == pub_sz) {
|
||||||
/* pub only */
|
/* pub only */
|
||||||
*pubkey = (*key_buffer);
|
|
||||||
*pubkey_sz = pub_sz;
|
*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;
|
ret = 0;
|
||||||
printf("Found ml-dsa public only key\n");
|
printf("Found ml-dsa public only key\n");
|
||||||
break;
|
break;
|
||||||
|
@ -1066,6 +1127,47 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz,
|
||||||
uint32_t digest_sz = 0;
|
uint32_t digest_sz = 0;
|
||||||
uint32_t image_sz = 0;
|
uint32_t image_sz = 0;
|
||||||
int io_sz;
|
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) {
|
||||||
|
cert_chain_sz = file_stat.st_size;
|
||||||
|
|
||||||
|
/* 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 + cert_chain_sz + 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_idx = 0;
|
||||||
header = malloc(CMD.header_sz);
|
header = malloc(CMD.header_sz);
|
||||||
|
@ -1183,6 +1285,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 */
|
/* 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
|
/* The offset '4' takes into account 2B Tag + 2B Len, so that the Value
|
||||||
* starts at (addr % 8 == 0) position.
|
* starts at (addr % 8 == 0) position.
|
||||||
|
@ -1693,10 +1853,16 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz,
|
||||||
fclose(f2);
|
fclose(f2);
|
||||||
fclose(f);
|
fclose(f);
|
||||||
failure:
|
failure:
|
||||||
|
if (cert_chain)
|
||||||
|
free(cert_chain);
|
||||||
if (policy)
|
if (policy)
|
||||||
free(policy);
|
free(policy);
|
||||||
if (header)
|
if (header)
|
||||||
free(header);
|
free(header);
|
||||||
|
if (signature)
|
||||||
|
free(signature);
|
||||||
|
if (secondary_signature)
|
||||||
|
free(secondary_signature);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2587,6 +2753,13 @@ int main(int argc, char** argv)
|
||||||
CMD.custom_tlvs++;
|
CMD.custom_tlvs++;
|
||||||
i += 2;
|
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 {
|
else {
|
||||||
i--;
|
i--;
|
||||||
break;
|
break;
|
||||||
|
@ -2746,6 +2919,8 @@ int main(int argc, char** argv)
|
||||||
DEBUG_PRINT("Header size: %u\n", CMD.header_sz);
|
DEBUG_PRINT("Header size: %u\n", CMD.header_sz);
|
||||||
if (kbuf2)
|
if (kbuf2)
|
||||||
free(kbuf2);
|
free(kbuf2);
|
||||||
|
if (pubkey2)
|
||||||
|
free(pubkey2);
|
||||||
} else {
|
} else {
|
||||||
make_header(pubkey, pubkey_sz, CMD.image_file, CMD.output_image_file);
|
make_header(pubkey, pubkey_sz, CMD.image_file, CMD.output_image_file);
|
||||||
}
|
}
|
||||||
|
@ -2758,6 +2933,9 @@ int main(int argc, char** argv)
|
||||||
ret = base_diff(CMD.delta_base_file, pubkey, pubkey_sz, 16);
|
ret = base_diff(CMD.delta_base_file, pubkey, pubkey_sz, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Add pubkey cleanup */
|
||||||
|
if (pubkey)
|
||||||
|
free(pubkey);
|
||||||
|
|
||||||
if (kbuf)
|
if (kbuf)
|
||||||
free(kbuf);
|
free(kbuf);
|
||||||
|
|
|
@ -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
|
|
@ -5,7 +5,6 @@ EXPVER_CMD=$(EXPVER) /dev/ttyAMA0
|
||||||
BINASSEMBLE=tools/bin-assemble/bin-assemble
|
BINASSEMBLE=tools/bin-assemble/bin-assemble
|
||||||
SPI_CHIP=SST25VF080B
|
SPI_CHIP=SST25VF080B
|
||||||
SPI_OPTIONS=SPI_FLASH=1 WOLFBOOT_PARTITION_SIZE=0x80000 WOLFBOOT_PARTITION_UPDATE_ADDRESS=0x00000 WOLFBOOT_PARTITION_SWAP_ADDRESS=0x80000
|
SPI_OPTIONS=SPI_FLASH=1 WOLFBOOT_PARTITION_SIZE=0x80000 WOLFBOOT_PARTITION_UPDATE_ADDRESS=0x00000 WOLFBOOT_PARTITION_SWAP_ADDRESS=0x80000
|
||||||
SIGN_ARGS=
|
|
||||||
SIGN_ENC_ARGS=
|
SIGN_ENC_ARGS=
|
||||||
DELTA_DATA_SIZE?=2000
|
DELTA_DATA_SIZE?=2000
|
||||||
|
|
||||||
|
@ -21,49 +20,6 @@ else
|
||||||
SIGN_TOOL="$(WOLFBOOT_ROOT)/tools/keytools/sign"
|
SIGN_TOOL="$(WOLFBOOT_ROOT)/tools/keytools/sign"
|
||||||
endif
|
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)
|
ifeq ($(FLAGS_INVERT),1)
|
||||||
INVERSION=
|
INVERSION=
|
||||||
else
|
else
|
||||||
|
@ -138,7 +94,7 @@ test-spi-off: FORCE
|
||||||
|
|
||||||
test-update: test-app/image.bin FORCE
|
test-update: test-app/image.bin FORCE
|
||||||
@dd if=/dev/zero bs=131067 count=1 2>/dev/null $(INVERSION) > test-update.bin
|
@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
|
@dd if=test-app/image_v$(TEST_UPDATE_VERSION)_signed.bin of=test-update.bin bs=1 conv=notrunc
|
||||||
@printf "pBOOT" >> test-update.bin
|
@printf "pBOOT" >> test-update.bin
|
||||||
@make test-reset
|
@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)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)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_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)
|
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=/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
|
$(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)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
|
$(Q)dd if=/dev/urandom of=test-app/image.elf bs=1k count=16 oflag=append conv=notrunc
|
||||||
@printf "0123456789abcdef0123456789abcdef0123456789abcdef" > /tmp/enc_key.der
|
@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)$(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)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)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_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)
|
test-app/image.elf $(PRIVATE_KEY) $(TEST_UPDATE_VERSION)
|
||||||
# Assembling internal flash image
|
# 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
|
$(WOLFBOOT_PARTITION_SIZE) erased_sec.dd
|
||||||
|
|
||||||
test-sim-external-flash-with-enc-delta-update:
|
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"
|
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 \
|
$(Q)$(BINASSEMBLE) external_flash.dd 0 test-app/image_v$(TEST_UPDATE_VERSION)_signed_diff_encrypted.bin \
|
||||||
$(WOLFBOOT_PARTITION_SIZE) erased_sec.dd
|
$(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
|
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)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
|
$(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)$(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)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)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)$(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)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)
|
test-app/image.elf $(PRIVATE_KEY) $(TEST_UPDATE_VERSION)
|
||||||
$(Q)$(BINASSEMBLE) internal_flash.dd \
|
$(Q)$(BINASSEMBLE) internal_flash.dd \
|
||||||
0 wolfboot.bin \
|
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
|
$$(($(WOLFBOOT_PARTITION_SWAP_ADDRESS)-$(ARCH_FLASH_OFFSET))) erased_sec.dd
|
||||||
|
|
||||||
test-sim-internal-flash-with-delta-update:
|
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"
|
make test-sim-internal-flash-with-update DELTA_UPDATE_OPTIONS="--delta test-app/image_v1_signed.bin"
|
||||||
$(Q)$(BINASSEMBLE) internal_flash.dd \
|
$(Q)$(BINASSEMBLE) internal_flash.dd \
|
||||||
0 wolfboot.bin \
|
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
|
$$(($(WOLFBOOT_PARTITION_SWAP_ADDRESS)-$(ARCH_FLASH_OFFSET))) erased_sec.dd
|
||||||
|
|
||||||
test-sim-internal-flash-with-wrong-delta-update:
|
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_v1_signed.bin"
|
||||||
make test-sim-internal-flash-with-update DELTA_UPDATE_OPTIONS="--delta test-app/image_v2_signed.bin" TEST_UPDATE_VERSION=3
|
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 \
|
$(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
|
test-self-update: FORCE
|
||||||
@mv $(PRIVATE_KEY) private_key.old
|
@mv $(PRIVATE_KEY) private_key.old
|
||||||
@make clean factory.bin RAM_CODE=1 WOLFBOOT_VERSION=1 SIGN=$(SIGN)
|
@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 || \
|
@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) || \
|
||||||
(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
|
@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
|
@dd if=wolfboot_v$(WOLFBOOT_VERSION)_signed.bin of=test-self-update.bin bs=1 conv=notrunc
|
||||||
@printf "pBOOT" >> test-self-update.bin
|
@printf "pBOOT" >> test-self-update.bin
|
||||||
@st-flash --reset write test-self-update.bin 0x08040000 || \
|
@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)
|
(make test-reset && sleep 1 && st-flash --reset write test-self-update.bin 0x08040000)
|
||||||
|
|
||||||
test-update-ext: test-app/image.bin FORCE
|
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=/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
|
@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
|
@printf "pBOOT" | dd of=test-update.rom obs=1 seek=524283 count=5 conv=notrunc
|
||||||
|
|
Loading…
Reference in New Issue