Compare commits

...

39 Commits

Author SHA1 Message Date
David Garske dea8b4e347 Don't use XALIGNED_STACK on static. 2025-06-16 19:05:49 +02:00
David Garske dd03cf19c4 Disabled `wolfBoot_swap_and_final_erase` with `CUSTOM_PARTITION_TRAILER` (not compatible). Added a few more `uint8_t` alignments. 2025-06-16 19:05:49 +02:00
David Garske 79cef5f605 Peer review fixes (thank you Copilot). 2025-06-16 19:05:49 +02:00
David Garske b79be86a56 Fix for `NO_SWAP_EXT=1` with encryption enabled. Peer review fixes. 2025-06-16 19:05:49 +02:00
David Garske bd179ed339 Added TSIP support to the set_key, get_key and erase_key API's. Finished full encrypted update testing on Renesas RX with TSIP. Fixed issue with `No rule to make target `NONE’` using encrypted AES. 2025-06-16 19:05:49 +02:00
David Garske 566f3b99cb Cleanup duplicate code in `aes_init`. 2025-06-16 19:05:49 +02:00
David Garske 708b797c8f Fix for Renesas RX TSIP AES CTR to make sure the wolfCrypt_Init() is called before trying to setup the crypto callback. 2025-06-16 19:05:49 +02:00
David Garske 3d9beb2642 Cleanup the AES CTR IV. 2025-06-16 19:05:49 +02:00
David Garske 2e5bbfe273 Make sure keySize is set for RX crypto callback. 2025-06-16 19:05:49 +02:00
David Garske b07d99b57e Fix RX TSIP IV nonce. Adjust location of XALIGNED in declarations. 2025-06-16 19:05:49 +02:00
David Garske 7bc947dd06 Fix logic on `ext_flash_check_read` return code (it is supposed to return size read or decrypted). 2025-06-16 19:05:49 +02:00
David Garske 73fc193931 Portability fixes with include < vs ". Added `NO_SWAP_EXT` to allow support for onboard flash swap sector. Added `WOLFSSL_NO_CT_OPS` for ECDSA verify only. Added `WC_NO_DEFAULT_DEVID` to help with code size reduction. 2025-06-16 19:05:49 +02:00
David Garske 844196981b Cleanup includes. 2025-06-16 19:05:49 +02:00
David Garske 8e6b0af105 Added Renesas RX TSIP encrypted updates support using AES CTR. Requires https://github.com/wolfSSL/wolfssl/pull/8854 2025-06-16 19:05:49 +02:00
David Garske 476d1c834b Gramar fix `partitions` -> `partition`. 2025-06-16 19:05:49 +02:00
David Garske 9451b47628 Fix for Renesas TSIP key types. Fix for Renesas RX .keystore location in linker script. Fix for wolfBoot as library with Renesas to make sure crypto hardware is initialized and setup. Add forced alignment on additional buffers used for flash read/write. 2025-06-16 19:05:49 +02:00
David Garske 4787d70cd8 Fix to force alignment on the flash header copy (hdr_cpy). Caused issues using wolfBoot as static library on Renesas RX. 2025-06-16 19:05:49 +02:00
David Garske 487efc606d Improvements for portability using older gcc 4.8.2. Make sure wolfboot.h includes the `wolfBoot_verify_*` API's. Fixed issue with parsing headers when `#include` is `# include`. 2025-06-16 19:05:49 +02:00
David Garske 77ede65739 Fixes for grammar. 2025-06-09 16:29:31 +02:00
David Garske 28bf060488 Fixed PIC32 CI test branches. Update Targets.md with space between CK and CZ targets. Added note about power cycle after programming. 2025-06-09 16:29:31 +02:00
Marco Oliverio c90c01ac2d pic32c-app: use symbolic name for pins offset 2025-06-09 16:29:31 +02:00
Marco Oliverio b62ec9da33 fix grammar errors 2025-06-09 16:29:31 +02:00
Marco Oliverio 6e8870156e address reviewer comments 2025-06-09 16:29:31 +02:00
Marco Oliverio e3c7f6c663 hal: clean hal tests and PIC32C*.ld linker script 2025-06-09 16:29:31 +02:00
Marco Oliverio 1b9ec09b1a fix reviewer's comments 2025-06-09 16:29:31 +02:00
Marco Oliverio 16c160146f hal: add additional flash hal tests 2025-06-09 16:29:31 +02:00
Marco Oliverio 2d6cf95c20 add support for PIC32CZ and PIC32CK 2025-06-09 16:29:31 +02:00
Marco Oliverio 4154057649 hal: add missing include in hal.h 2025-06-09 16:29:31 +02:00
David Garske 0bf65fabb3
Merge pull request #574 from bigbrett/wolfhsm-cert-verify
Add cert chain verification via wolfHSM on sim and TC3xx
2025-06-06 12:38:59 -07:00
Brett Nicholas 6abf550754 review feedback: tabs to spaces 2025-06-03 13:45:09 -06:00
Brett Nicholas ac0eb6dd9f remove erroneous unused argument to example server 2025-05-30 11:24:57 -06:00
Brett Nicholas d689656a71 Add support for wolfHSM cert chain verification on TC3xx 2025-05-30 11:15:56 -06:00
Brett Nicholas 23cf367c38 review feedback: remove use of unused intermediate variable 2025-05-28 12:24:26 -06:00
Brett Nicholas be9b21dddc Fix test sizes for wolfSSL updates 2025-05-28 11:38:28 -06:00
Brett Nicholas 38d1441627 copilot review feedback 2025-05-28 09:37:14 -06:00
Brett Nicholas 7660bf66f8 Add wolfHSM cert chain verification for ECC and RSA 2025-05-27 15:42:59 -06:00
David Garske 2b996f8280
Merge pull request #569 from bigbrett/update-flash-elf-tc3xx-integration
ELF scatter-loading fixes and TC3xx support
2025-05-12 13:11:33 -07:00
Brett Nicholas 16e5b94e9d review comments 2025-05-09 19:06:33 -06:00
Brett Nicholas d49e507926 - Support external flash for elf scatter-loading
- Support ELF scatter-loading on TC3xx
- Modify TC3xx HAL flash erase to support arbitrary offsets and sizes
- Realign partition size and addresses to support more efficient sector mass erase on update
2025-05-08 08:50:26 -06:00
79 changed files with 8576 additions and 865 deletions

View File

@ -0,0 +1,21 @@
name: Test PIC32CZ and PIC32CK Build
on:
push:
branches: [ 'master', 'main', 'release/**' ]
pull_request:
branches: [ '*' ]
jobs:
build-pic32cz:
uses: ./.github/workflows/test-build.yml
with:
arch: ARM
config-file: config/examples/pic32cz.config
make-args: "TARGET=pic32cz"
build-pic32ck:
uses: ./.github/workflows/test-build.yml
with:
arch: ARM
config-file: config/examples/pic32ck.config
make-args: "TARGET=pic32ck"

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

14
.gitignore vendored
View File

@ -61,8 +61,8 @@ src/ecc512_pub_key.c
src/rsa2048_pub_key.c
src/rsa4096_pub_key.c
# Renesas key data files
include/key_data.c
include/key_data.h
include/key_data.*
include/enckey_data.*
# keygen binaries
tools/keytools/sign
@ -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
@ -118,7 +119,13 @@ tools/tpm/policy_sign
config/*.ld
test-lib
# Generated configuration file
# Elf preprocessing tools
tools/squashelf/**
!tools/squashelf/squashelf.c
!tools/squashelf/Makefile
!tools/squashelf/README.md
# Generated configuration files
.config
.vs
*.filters
@ -247,3 +254,4 @@ lib/r_tsip_rx
Debug/
Release/
language.settings.xml

View File

@ -8,9 +8,13 @@ This example demonstrates using wolfBoot on the Infineon AURIX TC3xx family of m
- [Overview](#overview)
- [Important notes](#important-notes)
- [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)
- [Important notes](#important-notes-1)
- [Clone wolfBoot](#clone-wolfboot)
- [Build wolfBoot keytools and generate keys](#build-wolfboot-keytools-and-generate-keys)
- [Install the Infineon TC3xx SDK into the wolfBoot project](#install-the-infineon-tc3xx-sdk-into-the-wolfboot-project)
@ -20,13 +24,18 @@ 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)
- [Post Quantum: ML-DSA](#post-quantum-ml-dsa)
- [ML-DSA Keytools](#ml-dsa-keytools)
The example contains two projects: `wolfBoot-tc3xx` and `test-app`. The `wolfBoot-tc3xx` project contains the wolfBoot bootloader, and the `test-app` project contains a simple firmware application that will be loaded and executed by wolfBoot. The `test-app` project is a simple blinky application that blinks LED2 on the TC375 Lite-Kit V2 once per second when running the base image, and rapidly (~3x/sec) when running the update image. The test app determines if it is a base or update image by inspecting the firmware version (obtained through the wolfBoot API). The firmware version is set in the image header by the wolfBoot keytools when signing the test app binaries. The same test app binary is used for both the base and update images, with the only difference being the firmware version set by the keytools.
The example contains two projects: `wolfBoot-tc3xx` and `test-app`. The `wolfBoot-tc3xx` project contains the wolfBoot bootloader, and the `test-app` project contains a simple firmware application that will be loaded and executed by wolfBoot. The `test-app` project is a simple blinky application that blinks LED1 on the TC375 Lite-Kit V2 once per second when running the base image, and rapidly (~3x/sec) when running the update image. The test app determines if it is a base or update image by inspecting the firmware version (obtained through the wolfBoot API). The firmware version is set in the image header by the wolfBoot keytools when signing the test app binaries. The same test app binary is used for both the base and update images, with the only difference being the firmware version set by the keytools.
This example supports loading the test application firmware as standard wolfBoot images or as an ELF file. Refer to the [firmware_update.md](../docs/firmware_update.md) documentation for more information on how the wolfBoot flash loader supports loading ELF files.
## Important notes
@ -34,10 +43,15 @@ The example contains two projects: `wolfBoot-tc3xx` and `test-app`. The `wolfBoo
- Because TC3xx PFLASH ECC prevents reading from erased flash, the `EXT_FLASH` option is used to redirect flash reads to the `ext_flash_read()` HAL API, where the flash pages requested to be read can be blank-checked by hardware before reading.
- TC3xx PFLASH is write-once (`NVM_FLASH_WRITEONCE`), however wolfBoot `NVM_FLASH_WRITEONCE` does not support `EXT_FLASH`. Therefore the write-once functionality is re-implemented in the `HAL` layer.
- This demo app is only compatible with the GCC toolchain build configurations shipped with the AURIX IDE. The TASKING compiler build configurations are not yet supported.
- When detailing commands to be run, square brackets `[]` are used to indicate optional arguments. Do not include the square brackets when running the commands.
## Flash Partitioning
The TC3xx AURIX port of wolfBoot places all images in PFLASH, and uses both PFLASH0 and PFLASH1 banks. The wolfBoot executable code and the image swap sector are located in PFLASH0, with the remainder available for use. PFLASH1 is divided in half, with the first half holding the BOOT partition and the second half holding the UPDATE partition. User firmware images are directly executed in place from the BOOT partition in PFLASH1, and so must be linked to execute within this address space, with an offset of `IMAGE_HEADER_SIZE` to account for the wolfBoot image header.
The TC3xx AURIX port of wolfBoot places all images in PFLASH, and uses both PFLASH0 and PFLASH1 banks. The wolfBoot executable code and the image swap sector are located in PFLASH0, with the remainder available for use. The layout of PFLASH1 depends on whether the test application is being loaded as a standard wolfBoot image or an ELF file.
### Standard wolfBoot images
When configured to load standard wolfBoot images, the demo application divides PFLASH1 approximately in half, with the first half holding the BOOT partition and the second half holding the UPDATE partition. User firmware images are directly executed in place from the BOOT partition in PFLASH1, and so must be linked to execute within this address space, with an offset of `IMAGE_HEADER_SIZE` to account for the wolfBoot image header. The last sector of PFLASH1 is reserved for the SWAP area.
```
+==========+
@ -61,11 +75,37 @@ The TC3xx AURIX port of wolfBoot places all images in PFLASH, and uses both PFLA
+----------+ <-- 0x8060_0000
```
Please refer to the [wolfBoot](wolfBoot-tc3xx/Lcf_Gnu_Tricore_Tc.lsl.in) and [test-app](test-app/Lcf_Gnu_Tricore_Tc.lsl.in) linker script templates for the exact memory configuration.
### ELF files
When loading the test app as an ELF file, PFLASH1 is divided into three sections. Approximately the first half of PFLASH1 is reserved for application use. This is the region that the application should be linked to execute from, and should contain all loadable segments in the ELF file. The second half of PFLASH1 is divided equally between the BOOT and UPDATE partitions as with standard wolfBoot images, with the last sector reserved for SWAP.
```
+==========+
| PFLASH1 |
+==========+ <-- 0x8030_0000
| APP | ~1.5M (0x17_C000)
+----------+ <-- 0x8047_C000
| BOOT | ~0.75M (0xC_0000)
+----------+ <-- 0x8053_C000
| UPDATE | ~0.75M (0xC_0000)
+----------+ <-- 0x805F_C000
| SWAP | 16K (0x4000)
+----------+ <-- 0x8060_0000
```
Different linker script templates are used to configure the memory layout via `wbaurixtool.sh` (see next section) depending on whether standard wolfBoot images or ELF files are being loaded. Please refer to the following linker script templates for the exact memory configuration:
- [wolfBoot](wolfBoot-tc3xx/Lcf_Gnu_Tricore_Tc.lsl.in)
- [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.
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.
The general usage of `wbaurixtool.sh` is as follows:
@ -73,7 +113,12 @@ The general usage of `wbaurixtool.sh` is as follows:
$ ./wbaurixtool.sh [global options] <subcommand> [subcommand options]
```
where `<subcommand>` is one of the following:
where `[global options]` are:
- `--hsm`: Use the wolfHSM AURIX projects instead of the standard projects (e.g. `wolfBoot-tc3xx-wolfHSM` and `test-app-wolfHSM`)
- `--elf`: loads the test application as an ELF file instead of the standard wolfBoot image. If you wish to load the test application as an ELF file, all invocations of `wbaurixtool.sh` must use the `--elf` option.
and where `<subcommand>` is one of the following:
- `keygen`: Generate a new signing key pair
- `sign`: Sign a firmware image
@ -99,6 +144,10 @@ For more information on the `wbaurixtool.sh` script, run `./wbaurixtool.sh --hel
- A WSL2 distro (tested on Ubuntu 22.04) with the `build-essential` package installed (`sudo apt install build-essential`)
- A TC375 AURIX Lite-Kit V2
### Important notes
- If you wish to load the test application as an ELF file, all invocations of `wbaurixtool.sh` must use the global `--elf` option before any subcommand. Otherwise, the invocation to `wbaurixtool.sh` will execute its commands as if the test application was a standard wolfBoot image. Mixing and matching `--elf` invocations with non-`--elf` invocations to `wbaurixtool.sh` will result in difficult to diagnose build or runtime errors. When in doubt, clean all build artifacts and start fresh.
### Clone wolfBoot
1. Clone the wolfBoot repository and initialize the repository submodules (`git submodule update --init`)
@ -109,10 +158,10 @@ For more information on the `wbaurixtool.sh` script, run `./wbaurixtool.sh --hel
2. Compile the keytools by running `make keytools`
3. Use the helper script to generate a new signing key pair using ECC 256
1. Navigate to `wolfBoot/tools/scripts/tc3xx`
2. Run `./wbaurixtool.sh keygen --sign-algo ecc256 macros lcf`. This:
2. Run `./wbaurixtool.sh [--elf] keygen --sign-algo ecc256 macros lcf`. This:
- Generates the signing private key `wolfBoot/priv.der` and adds the public key to the wolfBoot keystore (see [keygen](https://github.com/wolfSSL/wolfBoot/blob/aurix-tc3xx-support/docs/Signing.md) for more information)
- Generates the `wolfBoot_macros.txt` file from the `wolfBoot_macros.in` template in the `wolfBoot-tc3xx` directory, which sets the appropriate wolfBoot preprocessor macros based on the hash and signature algorithms. The `wolfBoot_macros.txt` file is then passed to the compiler in the AURIX project
- Generates the `Lcf_Gnu_Tricore_Tc.lsl` file from the `Lcf_Gnu_Tricore_Tc.lcf.in` template in the `test-app` directory, which sets appropriate values for Linker addresses (e.g. wolfBoot header size)based on the selected signature algorithm. The `Lcf_Gnu_Tricore_Tc.lsl` file is then passed to the linker in the AURIX project
- Generates the `Lcf_Gnu_Tricore_Tc.lsl` file from either the `Lcf_Gnu_Tricore_Tc.lcf.in` template in the `test-app` directory or the `Lcf_Gnu_Tricore_elf.lcf.in` template in the `test-app` directory, which sets appropriate values for Linker addresses (e.g. wolfBoot header size) based on the selected signature algorithm. The `Lcf_Gnu_Tricore_Tc.lsl` file is then passed to the linker in the AURIX project
- Note that if you already have generated keys, you can use `./wbaurixtool.sh clean` to remove them first
```
@ -164,7 +213,7 @@ wolfBoot/IDE/AURIX/Configurations/
### Build wolfBoot
1. Generate the 'target.h` header file for the tc375 flash configuration
1. Open a WSL terminal and navigate to `wolfBoot/tools/scripts/tc3xx`
2. Run `./wbaurixtool.sh target`
2. Run `./wbaurixtool.sh [--elf] target`
2. Open the AURIX IDE and create a new workspace directory, if you do not already have a workspace you wish to use
3. Import the wolfBoot project
1. Click "File" -> Open Projects From File System"
@ -176,9 +225,10 @@ wolfBoot/IDE/AURIX/Configurations/
3. Click the hammer icon to build the active project. This will compile wolfBoot.
5. Import the test-app project using the same procedure as in step (3), except using `wolfBoot/IDE/AURIX/test-app` as the directory
6. Build the test-app project using the same procedure as in step (4), except choosing the `test-app` eclipse project. Note that the build process contains a custom post-build step that converts the application `elf` file into a `.bin` file using `tricore-elf-objcopy`, which can then be signed by the wolfBoot key tools in the following step
7. Sign the generated test-app binary using the wolfBoot keytools
7. If intending to build and load elf files, compile the squashelf tool by running `make` in the `tools/squashelf` directory. This step can be skipped if you only wish to build and load standard wolfBoot images.
8. Sign the generated test-app binary using the wolfBoot keytools
1. Open a WSL terminal and navigate to `wolfBoot/tools/scripts/tc3xx`
2. Run `./wbaurixtool.sh sign --debug` or `./wbaurixtool.sh sign` to sign either the debug or release build, respectively. This creates the signed image files `test-app_v1_signed.bin` and `test-app_v2_signed.bin` in the test-app output build directory. The v1 image is the initial image that will be loaded to the `BOOT` partition, and the v2 image is the update image that will be loaded to the `UPDATE` partition.
2. Run `./wbaurixtool.sh [--elf] sign --debug` or `./wbaurixtool.sh [--elf] sign` to sign either the debug or release build, respectively. This creates the signed image files `test-app_v1_signed.bin` and `test-app_v2_signed.bin` in the test-app output build directory. The v1 image is the initial image that will be loaded to the `BOOT` partition, and the v2 image is the update image that will be loaded to the `UPDATE` partition. If `--elf` is specified, `wbaurixtool.sh` will first preprocess the test-app ELF file with [squashelf](../../tools/squashelf) and then sign the resulting ELF file which contains only loadable segments.
```
$ ./wbaurixtool.sh sign
@ -236,7 +286,7 @@ The device is now configured to boot from `0xA00A0000` out of reset.
We can now load wolfBoot and the firmware application images to the tc3xx device using Trace32 and a Lauterbach probe
1. Click "File" -> "ChangeDir and Run Script" and choose the `wolfBoot/tools/scripts/tc3xx/wolfBoot-loadAll-$BUILD.cmm` script, where $BUILD should be either "debug" or "release" depending on your build type in (4) and (6).
1. Click "File" -> "ChangeDir and Run Script" and choose the `wolfBoot/tools/scripts/tc3xx/wolfBoot-loadAll-$BUILD.cmm` script, where $BUILD should be either "debug" or "release" depending on your build type from earlier. There are also corresponding ELF load scripts for loading the test app as an ELF image.
wolfBoot and the demo applications are now loaded into flash, and core0 will be halted at the wolfBoot entry point (`core0_main()`).
@ -275,8 +325,29 @@ IDE/AURIX/wolfHSM-infineon-tc3xx/
4. Follow all of the steps in [Building and Running the wolfBoot Demo](#building-and-running-the-wolfboot-demo) for the non-HSM enabled case, but with the following key differences:
1. The [wolfBoot-tc3xx-wolfHSM](./wolfBoot-tc3xx-wolfHSM/) AURIX Studio project should be used instead of `wolfBoot-tc3xx`
2. Use the `wolfBoot-wolfHSM-loadAll-XXX.cmm` lauterbach scripts instead of `wolfBoot-loadAll-XXX.cmm` to load the wolfBoot and test-app images in the TRACE32 GUI
3. Provide the `--hsm` global option to the `wbaurixtool.sh` script when invoking it, so 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. 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.
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
@ -309,7 +380,7 @@ cd $SCRIPTS_DIR
# Generate keys, as well as configuration macros and linker script based on the selected signature algorithm
./wbaurixtool.sh [--hsm] keygen --sign-algo ecc256 --hash-algo sha256 macros lcf
# If using wolfHSM, generate key NVM image
# If using wolfHSM, generate key NVM image (make sure you have compiled whnvmtool before running this command)
./wbaurixtool.sh nvm
# Load NVM image hexfile to the device
# ...

View File

@ -453,6 +453,9 @@
<option id="com.infineon.aurix.buildsystem.managed.tool.c.link.option.ldflags.708051953" name="Linker flags" superClass="com.infineon.aurix.buildsystem.managed.tool.c.link.option.ldflags" useByScannerDiscovery="false" value="-mtc162 -Wl,--gc-sections -Wl,-Map,output.map" valueType="string"/>
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="com.infineon.aurix.buildsystem.managed.tool.c.link.option.userobjs.1601921229" name="Other objects" superClass="com.infineon.aurix.buildsystem.managed.tool.c.link.option.userobjs" useByScannerDiscovery="false" valueType="userObjs">
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/wolfBoot-tc3xx-wolfHSM/TriCore Debug (GCC)/wolfBoot/hal/aurix_tc3xx.o}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/wolfBoot-tc3xx-wolfHSM/TriCore Debug (GCC)/Libraries/iLLD/TC37A/Tricore/Asclin/Asc/IfxAsclin_Asc.o}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/wolfBoot-tc3xx-wolfHSM/TriCore Debug (GCC)/Libraries/iLLD/TC37A/Tricore/Asclin/Std/IfxAsclin.o}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/wolfBoot-tc3xx-wolfHSM/TriCore Debug (GCC)/wolfBoot/src/string.o}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/wolfBoot-tc3xx-wolfHSM/TriCore Debug (GCC)/wolfBoot/src/libwolfboot.o}&quot;"/>
</option>
<inputType id="com.infineon.aurix.buildsystem.managed.c.linker.inputType.1147881076" superClass="com.infineon.aurix.buildsystem.managed.c.linker.inputType"/>
@ -543,7 +546,7 @@
<listOptionValue builtIn="false" value="PART_SWAP_EXT"/>
<listOptionValue builtIn="false" value="RAM_CODE"/>
</option>
<option id="com.infineon.aurix.buildsystem.managed.tool.c.compiler.option.misc.other.1199404591" superClass="com.infineon.aurix.buildsystem.managed.tool.c.compiler.option.misc.other" value="-mtc162 -c -fmessage-length=0" valueType="string"/>
<option id="com.infineon.aurix.buildsystem.managed.tool.c.compiler.option.misc.other.1199404591" name="Other flags" superClass="com.infineon.aurix.buildsystem.managed.tool.c.compiler.option.misc.other" useByScannerDiscovery="false" value="-mtc162 -c -fmessage-length=0" valueType="string"/>
<inputType id="com.infineon.aurix.buildsystem.managed.tool.c.compiler.inputType.1990995811" superClass="com.infineon.aurix.buildsystem.managed.tool.c.compiler.inputType"/>
</tool>
<tool id="com.infineon.aurix.buildsystem.managed.tool.cpp.compiler.669528986" name="AURIX G++ Compiler" superClass="com.infineon.aurix.buildsystem.managed.tool.cpp.compiler">

View File

@ -121,10 +121,10 @@ MEMORY
pfls1_hdr (rx!p): org = LCF_WOLFBOOT_BOOT_PART_BASEADDR, len = LCF_WOLFBOOT_HEADER_OFFSET
/* pfls1 is the remainder of the wolfBoot BOOT partition. Everything goes here */
pfls1 (rx!p): org = LCF_CODE_BASE_ADDR, len = (0x17E000 - LCF_WOLFBOOT_HEADER_OFFSET)
pfls1 (rx!p): org = LCF_CODE_BASE_ADDR, len = (0x17C000 - LCF_WOLFBOOT_HEADER_OFFSET)
/* reserved for wolfBoot UPDATE partition */
pfls1_update (rwx!p): org = 0xA047E000, len = 0x17E000 /* ~1.5MiB */
pfls1_update (rwx!p): org = 0xA047C000, len = 0x17C000 /* ~1.5MiB */
/* SWAP sector for wolfBoot image update */
pfls1_swap (rwx!p): org = 0xA05FC000, len = 16K /* last sector of PFLASH1 */

File diff suppressed because it is too large Load Diff

View File

@ -440,6 +440,9 @@
<listOptionValue builtIn="false" value="PART_SWAP_EXT"/>
</option>
<option id="com.infineon.aurix.buildsystem.managed.tool.c.compiler.option.misc.other.739113076" name="Other flags" superClass="com.infineon.aurix.buildsystem.managed.tool.c.compiler.option.misc.other" useByScannerDiscovery="false" value="-c -fmessage-length=0 -mtc162" valueType="string"/>
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="com.infineon.aurix.buildsystem.managed.tool.c.compiler.option.include.files.884786319" name="Include files (-include)" superClass="com.infineon.aurix.buildsystem.managed.tool.c.compiler.option.include.files" useByScannerDiscovery="false" valueType="includeFiles">
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/wolfBoot-tc3xx/wolfBoot/include/image.h}&quot;"/>
</option>
<inputType id="com.infineon.aurix.buildsystem.managed.tool.c.compiler.inputType.1480422611" superClass="com.infineon.aurix.buildsystem.managed.tool.c.compiler.inputType"/>
</tool>
<tool id="com.infineon.aurix.buildsystem.managed.tool.cpp.compiler.366043271" name="AURIX G++ Compiler" superClass="com.infineon.aurix.buildsystem.managed.tool.cpp.compiler">
@ -453,6 +456,9 @@
<option id="com.infineon.aurix.buildsystem.managed.tool.c.link.option.ldflags.708051953" name="Linker flags" superClass="com.infineon.aurix.buildsystem.managed.tool.c.link.option.ldflags" useByScannerDiscovery="false" value="-mtc162 -Wl,--gc-sections -Wl,-Map,output.map" valueType="string"/>
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="com.infineon.aurix.buildsystem.managed.tool.c.link.option.userobjs.1601921229" name="Other objects" superClass="com.infineon.aurix.buildsystem.managed.tool.c.link.option.userobjs" useByScannerDiscovery="false" valueType="userObjs">
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/wolfBoot-tc3xx/TriCore Debug (GCC)/wolfBoot/hal/aurix_tc3xx.o}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/wolfBoot-tc3xx/TriCore Debug (GCC)/Libraries/iLLD/TC37A/Tricore/Asclin/Std/IfxAsclin.o}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/wolfBoot-tc3xx/TriCore Debug (GCC)/Libraries/iLLD/TC37A/Tricore/Asclin/Asc/IfxAsclin_Asc.o}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/wolfBoot-tc3xx/TriCore Debug (GCC)/wolfBoot/src/string.o}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/wolfBoot-tc3xx/TriCore Debug (GCC)/wolfBoot/src/libwolfboot.o}&quot;"/>
</option>
<inputType id="com.infineon.aurix.buildsystem.managed.c.linker.inputType.1147881076" superClass="com.infineon.aurix.buildsystem.managed.c.linker.inputType"/>
@ -469,6 +475,7 @@
</configuration>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
<storageModule moduleId="com.infineon.aurix.buildsystem.build.autodiscovery.settings"/>
</cconfiguration>
<cconfiguration id="com.infineon.aurix.buildsystem.managed.external.gcc.builtin.configuration.release.856945873">
<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="com.infineon.aurix.buildsystem.managed.external.gcc.builtin.configuration.release.856945873" moduleId="org.eclipse.cdt.core.settings" name="TriCore Release (GCC)">

View File

@ -1,41 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project>
<configuration id="com.infineon.aurix.buildsystem.managed.configuration.binary.1914762785" name="TriCore Debug (TASKING)">
<extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
<provider-reference id="com.infineon.aurix.buildsystem.managed.TaskingBuiltintSpecsDetector" ref="shared-provider"/>
</extension>
</configuration>
<configuration id="com.infineon.aurix.buildsystem.managed.configuration.binary.697039770" name="TriCore Release (TASKING)">
<extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
<provider-reference id="com.infineon.aurix.buildsystem.managed.TaskingBuiltintSpecsDetector" ref="shared-provider"/>
</extension>
</configuration>
<configuration id="com.infineon.aurix.buildsystem.managed.external.gcc.builtin.configuration.debug.1333370870" name="TriCore Debug (GCC)">
<extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
<provider class="com.infineon.aurix.buildsystem.managed.gcc.AURIXGCC11BuiltinSpecsDetector" console="false" env-hash="1122385352849106138" id="com.infineon.aurix.buildsystem.managed.CrossGCC11BuiltinSpecsDetector" keep-relative-paths="false" name="AURIXCrossGCC11compilerSpecsDetector" parameter="${COMMAND} ${FLAGS} -E -P -v -dD &quot;${INPUTS}&quot;" prefer-non-shared="true">
<language-scope id="org.eclipse.cdt.core.gcc"/>
<language-scope id="org.eclipse.cdt.core.g++"/>
</provider>
</extension>
</configuration>
<configuration id="com.infineon.aurix.buildsystem.managed.external.gcc.builtin.configuration.release.856945873" name="TriCore Release (GCC)">
<extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
<provider class="com.infineon.aurix.buildsystem.managed.gcc.AURIXGCC11BuiltinSpecsDetector" console="false" env-hash="1122385352849106138" id="com.infineon.aurix.buildsystem.managed.CrossGCC11BuiltinSpecsDetector" keep-relative-paths="false" name="AURIXCrossGCC11compilerSpecsDetector" parameter="${COMMAND} ${FLAGS} -E -P -v -dD &quot;${INPUTS}&quot;" prefer-non-shared="true">
<language-scope id="org.eclipse.cdt.core.gcc"/>
<language-scope id="org.eclipse.cdt.core.g++"/>
</provider>
</extension>
</configuration>
</project>

View File

@ -121,10 +121,10 @@ MEMORY
pfls1_hdr (rx!p): org = LCF_WOLFBOOT_BOOT_PART_BASEADDR, len = LCF_WOLFBOOT_HEADER_OFFSET
/* pfls1 is the remainder of the wolfBoot BOOT partition. Everything goes here */
pfls1 (rx!p): org = LCF_CODE_BASE_ADDR, len = (0x17E000 - LCF_WOLFBOOT_HEADER_OFFSET)
pfls1 (rx!p): org = LCF_CODE_BASE_ADDR, len = (0x17C000 - LCF_WOLFBOOT_HEADER_OFFSET)
/* reserved for wolfBoot UPDATE partition */
pfls1_update (rwx!p): org = 0xA047E000, len = 0x17E000 /* ~1.5MiB */
pfls1_update (rwx!p): org = 0xA047C000, len = 0x17C000 /* ~1.5MiB */
/* SWAP sector for wolfBoot image update */
pfls1_swap (rwx!p): org = 0xA05FC000, len = 16K /* last sector of PFLASH1 */

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -123,18 +123,11 @@ MEMORY
/* wolfBoot program code - all of this project's executable code goes here */
pfls0_nc (rx!p): org = 0xA00A8000, len = 0x23000 /* 140K total (incl 32K for startup) */
/* reserved for wolfBoot BOOT partition */
pfls1_boot (rwx!p): org = 0x80300000, len = 0x17E000 /* ~1.5MiB */
pfls1_boot_nc (rwx!p): org = 0xA0300000, len = 0x17E000 /* ~1.5MiB */
/* reserved for BOOT, UPDATE, and SWAP partitions */
pfls1(rwx!p): org = 0x80300000, len = 0x300000 /* 3MiB */
pfls1_nc(rwx!p): org = 0xA0300000, len = 0x300000 /* 3MiB */
/* reserved for wolfBoot UPDATE partition */
pfls1_update (rwx!p): org = 0x8047E000, len = 0x17E000 /* ~1.5MiB */
pfls1_update_nc (rwx!p): org = 0xA047E000, len = 0x17E000 /* ~1.5MiB */
/* SWAP sector for wolfBoot image update */
pfls1_swap (rwx!p): org = 0x805FC000, len = 16K /* last sector of PFLASH1 */
dfls0 (rx!p): org = 0xaf000000, len = 256K
fls0 (rx!p): org = 0xaf000000, len = 256K
ucb (rx!p): org = 0xaf400000, len = 24K

View File

@ -6,3 +6,6 @@
@WOLFBOOT_USE_WOLFHSM_PUBKEY_ID@
@ML_DSA_LEVEL@
@ML_DSA_IMAGE_SIGNATURE_SIZE@
@WOLFBOOT_ELF@
@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

@ -1,41 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project>
<configuration id="com.infineon.aurix.buildsystem.managed.configuration.binary.1019631072" name="TriCore Debug (TASKING)">
<extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
<provider-reference id="com.infineon.aurix.buildsystem.managed.TaskingBuiltintSpecsDetector" ref="shared-provider"/>
</extension>
</configuration>
<configuration id="com.infineon.aurix.buildsystem.managed.configuration.binary.394981185" name="TriCore Release (TASKING)">
<extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
<provider-reference id="com.infineon.aurix.buildsystem.managed.TaskingBuiltintSpecsDetector" ref="shared-provider"/>
</extension>
</configuration>
<configuration id="com.infineon.aurix.buildsystem.managed.external.gcc.builtin.configuration.debug.401330509" name="TriCore Debug (GCC)">
<extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
<provider class="com.infineon.aurix.buildsystem.managed.gcc.AURIXGCC11BuiltinSpecsDetector" console="false" env-hash="1122385352849106138" id="com.infineon.aurix.buildsystem.managed.CrossGCC11BuiltinSpecsDetector" keep-relative-paths="false" name="AURIXCrossGCC11compilerSpecsDetector" parameter="${COMMAND} ${FLAGS} -E -P -v -dD &quot;${INPUTS}&quot;" prefer-non-shared="true">
<language-scope id="org.eclipse.cdt.core.gcc"/>
<language-scope id="org.eclipse.cdt.core.g++"/>
</provider>
</extension>
</configuration>
<configuration id="com.infineon.aurix.buildsystem.managed.external.gcc.builtin.configuration.release.1853938077" name="TriCore Release (GCC)">
<extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
<provider class="com.infineon.aurix.buildsystem.managed.gcc.AURIXGCC11BuiltinSpecsDetector" console="false" env-hash="1122385352849106138" id="com.infineon.aurix.buildsystem.managed.CrossGCC11BuiltinSpecsDetector" keep-relative-paths="false" name="AURIXCrossGCC11compilerSpecsDetector" parameter="${COMMAND} ${FLAGS} -E -P -v -dD &quot;${INPUTS}&quot;" prefer-non-shared="true">
<language-scope id="org.eclipse.cdt.core.gcc"/>
<language-scope id="org.eclipse.cdt.core.g++"/>
</provider>
</extension>
</configuration>
</project>

View File

@ -123,16 +123,9 @@ MEMORY
/* wolfBoot program code - all of this project's executable code goes here */
pfls0_nc (rx!p): org = 0xA00A8000, len = 0x23000 /* 140K total (incl 32K for startup) */
/* reserved for wolfBoot BOOT partition */
pfls1_boot (rwx!p): org = 0x80300000, len = 0x17E000 /* ~1.5MiB */
pfls1_boot_nc (rwx!p): org = 0xA0300000, len = 0x17E000 /* ~1.5MiB */
/* reserved for wolfBoot UPDATE partition */
pfls1_update (rwx!p): org = 0x8047E000, len = 0x17E000 /* ~1.5MiB */
pfls1_update_nc (rwx!p): org = 0xA047E000, len = 0x17E000 /* ~1.5MiB */
/* SWAP sector for wolfBoot image update */
pfls1_swap (rwx!p): org = 0x805FC000, len = 16K /* last sector of PFLASH1 */
/* reserved for BOOT, UPDATE, and SWAP partitions */
pfls1(rwx!p): org = 0x80300000, len = 0x300000 /* 3MiB */
pfls1_nc(rwx!p): org = 0xA0300000, len = 0x300000 /* 3MiB */
dfls0 (rx!p): org = 0xaf000000, len = 256K

View File

@ -6,3 +6,5 @@
@WOLFBOOT_USE_WOLFHSM_PUBKEY_ID@
@ML_DSA_LEVEL@
@ML_DSA_IMAGE_SIGNATURE_SIZE@
@WOLFBOOT_ELF@
@WOLFBOOT_ELF_FLASH_SCATTER@

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
@ -243,6 +244,12 @@ keytools:
@echo "Building key tools"
@$(MAKE) -C tools/keytools -j
squashelf:
@echo "Building squashelf tool"
@$(MAKE) -C tools/squashelf -j
squashelf_check: squashelf
tpmtools: include/target.h keys
@echo "Building TPM tools"
@$(MAKE) -C tools/tpm -s clean
@ -267,6 +274,10 @@ test-app/image.elf: wolfboot.elf
$(Q)$(MAKE) -C test-app WOLFBOOT_ROOT="$(WOLFBOOT_ROOT)" image.elf
$(Q)$(SIZE) test-app/image.elf
ifeq ($(ELF_FLASH_SCATTER),1)
test-app/image.elf: squashelf
endif
assemble_internal_flash.dd: FORCE
$(Q)$(BINASSEMBLE) internal_flash.dd \
0 wolfboot.bin \
@ -376,9 +387,11 @@ utilsclean: clean
$(Q)$(MAKE) -C tools/uart-flash-server -s clean
$(Q)$(MAKE) -C tools/unit-tests -s clean
$(Q)$(MAKE) -C tools/keytools/otp -s clean
$(Q)$(MAKE) -C tools/squashelf -s 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
@ -467,4 +480,4 @@ pico-sdk-info: FORCE
FORCE:
.PHONY: FORCE clean keytool_check
.PHONY: FORCE clean keytool_check squashelf_check

17
arch.mk
View File

@ -156,6 +156,18 @@ ifeq ($(ARCH),ARM)
SPI_TARGET=stm32
endif
ifeq ($(TARGET),pic32cz)
ARCH_FLASH_OFFSET=0x08000000
CORTEX_M7=1
OBJS+=hal/pic32c.o
endif
ifeq ($(TARGET),pic32ck)
ARCH_FLASH_OFFSET=0x08000000
CORTEX_M33=1
OBJS+=hal/pic32c.o
endif
ifeq ($(TARGET),stm32l4)
SPI_TARGET=stm32
ARCH_FLASH_OFFSET=0x08000000
@ -458,7 +470,8 @@ ifeq ($(ARCH),RENESAS_RX)
OBJS+=./lib/wolfssl/wolfcrypt/src/cryptocb.o \
./lib/wolfssl/wolfcrypt/src/port/Renesas/renesas_common.o \
./lib/wolfssl/wolfcrypt/src/port/Renesas/renesas_tsip_util.o
./lib/wolfssl/wolfcrypt/src/port/Renesas/renesas_tsip_util.o \
./lib/wolfssl/wolfcrypt/src/port/Renesas/renesas_tsip_aes.o
# RX TSIP uses pre-compiled .a library by default
ifneq ($(RX_TSIP_SRC),1)
@ -1105,7 +1118,7 @@ ifeq ($(ARCH),sim)
LD_END_GROUP=
BOOT_IMG=test-app/image.elf
CFLAGS+=-DARCH_SIM
ifneq ($(ELF_SCATTERED),1)
ifneq ($(ELF_FLASH_SCATTER),1)
CFLAGS+=-DWOLFBOOT_USE_STDLIBC
endif
ifeq ($(FORCE_32BIT),1)

View File

@ -0,0 +1,14 @@
ARCH=ARM
TARGET=pic32ck
SIGN?=ED25519
HASH?=SHA256
VTOR?=1
SPMATH?=1
NO_MPU=1
WOLFBOOT_PARTITION_SIZE=0x10000
BOOTLOADER_PARTITION_SIZE=0x20000
WOLFBOOT_SECTOR_SIZE=0x1000
WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x0c000000
WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x0c07f000
WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x0c0fe000
TZEN=0

View File

@ -0,0 +1,13 @@
ARCH=ARM
TARGET=pic32cz
SIGN?=ED25519
HASH?=SHA256
VTOR?=1
SPMATH?=1
NO_MPU=1
WOLFBOOT_PARTITION_SIZE=0x10000
WOLFBOOT_SECTOR_SIZE=0x1000
WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x0c000000
WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x0c200000
WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x0c030000
#DUALBANK_SWAP=0

View File

@ -5,7 +5,7 @@ HASH?=SHA256
WOLFBOOT_SMALL_STACK?=0
SPI_FLASH=0
DEBUG=1
ELF_SCATTERED=1
ELF_FLASH_SCATTER=1
ELF=1
@ -15,7 +15,7 @@ WOLFBOOT_SECTOR_SIZE=0x1000
WOLFBOOT_PARTITION_BOOT_ADDRESS=0x80000
# if on external flash, it should be multiple of system page size
# Address from 0x100000 to 0x1FFFFF is reserved for ELF_SCATTERED
# Address from 0x100000 to 0x1FFFFF is reserved for ELF_FLASH_SCATTER
WOLFBOOT_PARTITION_UPDATE_ADDRESS=0x200000
WOLFBOOT_PARTITION_SWAP_ADDRESS=0x280000

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

@ -7,7 +7,7 @@ SPI_FLASH=0
DEBUG=1
FORCE_32BIT=1
ELF=1
ELF_SCATTERED=1
ELF_FLASH_SCATTER=1
# sizes should be multiple of system page size
WOLFBOOT_PARTITION_SIZE=0x40000

View File

@ -17,7 +17,7 @@ Platforms Supported:
All of the Renesas examples support using e2Studio.
The Renesas RX parts support using wolfBoot Makefile's with the rx-elf-gcc cross-compiler and example .config files.
### Security Key Management Tool (SKMT) Key Wrapping
## Security Key Management Tool (SKMT) Key Wrapping
1) Setup a Renesas KeyWrap account and do the PGP key exchange.
https://dlm.renesas.com/keywrap
@ -34,12 +34,13 @@ Use GPG4Win and the Sign/Encrypt option. Sign with your own GPG key and encrypt
It will use the Hidden Root Key (HRK) that both Renesas and the RX TSIP have pre-provisioned from Renesas Factory.
Result is `sample.key_enc.key`. Example: `00000001 6CCB9A1C 8AA58883 B1CB02DE 6C37DA60 54FB94E2 06EAE720 4D9CCF4C 6EEB288C`
### RX TSIP
## RX TSIP
1) Build key tools for Renesas
```sh
# Build keytools for Renesas RX (TSIP)
# Use RENESAS_KEY=2 for TSIP
$ make keytools RENESAS_KEY=2
```
@ -163,8 +164,36 @@ Output image(s) successfully created.
Download files to flash using Renesas flash programmer.
## RX TSIP AES Encryption (optional)
#### RX TSIP Benchmarks
Create a wrapped AES key for encrypting/decrypting the update
Example key: `fwenc.key`: e07227e477450b1ca266078e217a3c89cbae827a7bb117ff851bc25300163575
Note: `.config` must include `ENCRYPT=1` and `ENCRYPT_WITH_AES256=1`
```sh
$ C:\Renesas\SecurityKeyManagementTool\cli\skmt.exe -genkey -ufpk file=./sample.key -wufpk file=./sample.key_enc.key -key file=./fwenc.key -mcu RX-TSIP -keytype AES-256 -output include/enckey_data.c -filetype csource -keyname wrap_enc_key -iv A8B14B0F5F09D73F31D4777FC0103FB4
Output File: C:\CPG_Controls\wolfboot\include\enckey_data.h
Output File: C:\CPG_Controls\wolfboot\include\enckey_data.c
UFPK: B94A2B961C75510174F0C967ECFC20B377C7FB256DB627B1BFFADEE05EE98AC4
W-UFPK: 000000016CCB9A1C8AA58883B1CB02DE6C37DA6054FB94E206EAE7204D9CCF4C6EEB288C
IV: A8B14B0F5F09D73F31D4777FC0103FB4
Encrypted key: 3C39BE75E9CA5CB9D2D0BBDE111CABC894A2B13F857399B05E7B140518F35D05CD97D8DF20817CEEBA2F207CC90BAF2C
$ C:\Renesas\SecurityKeyManagementTool\cli\skmt.exe -genkey -ufpk file=./sample.key -wufpk file=./sample.key_enc.key -key file=./fwenc.key -mcu RX-TSIP -keytype AES-256 -output fwenc.srec -filetype "mot" -address FFFF0100 -iv A8B14B0F5F09D73F31D4777FC0103FB4
Output File: C:\CPG_Controls\wolfboot\fwenc.srec
UFPK: B94A2B961C75510174F0C967ECFC20B377C7FB256DB627B1BFFADEE05EE98AC4
W-UFPK: 000000016CCB9A1C8AA58883B1CB02DE6C37DA6054FB94E206EAE7204D9CCF4C6EEB288C
IV: A8B14B0F5F09D73F31D4777FC0103FB4
Encrypted key: 3C39BE75E9CA5CB9D2D0BBDE111CABC894A2B13F857399B05E7B140518F35D05CD97D8DF20817CEEBA2F207CC90BAF2C
```
The offset for the wrapped AES key is determined by `RENESAS_TSIP_INSTALLEDENCKEY_ADDR` and defaults to `RENESAS_TSIP_INSTALLEDKEY_ADDR` + 0x100
The key needed for the firmware signing tool is the 32 byte AES Key + 16 byte IV.
`echo "e07227e477450b1ca266078e217a3c89cbae827a7bb117ff851bc25300163575A8B14B0F5F09D73F31D4777FC0103FB4" | xxd -r -p - > fwkey.bin`
### RX TSIP Benchmarks
| Hardware | Clock | Algorithm | RX TSIP | Debug | Release (-Os) | Release (-O2) |
| -------- | ------ | ----------------- | -------- | -------- | ------------- | ------------- |
@ -172,3 +201,16 @@ Download files to flash using Renesas flash programmer.
| RX72N | 240MHz | ECDSA Verify P256 | 2.73 ms | 469 ms | 135 ms | 107 ms |
| RX65N | 120MHz | ECDSA Verify P384 | 18.57 ms | 4213 ms | 2179 ms | 1831 ms |
| RX65N | 120MHz | ECDSA Verify P256 | 2.95 ms | 1208 ms | 602 ms | 517 ms |
## RX Production Protection (recommendations)
1) Lockdown external serial programmer `SPCC.SPE = 0`
2) Flash Access Window Setting Register (FAW)
* BTFLG: Start-up Area Select FAW.BTFLG (1=FFFF E000h to FFFF FFFFh, 0=FFFF C000h to FFFF DFFFh)
* FSPR - FAW.FSPR Access Window Protection (0=protections enabled) Once changed to 0 cannot be reset.
3) ROM Code Protection Register `ROMCODE.CODE[31:0]`
* 0000 0000h: ROM code protection enabled (ROM code protection 1)
* 0000 0001h: ROM code protection enabled (ROM code protection 2)
* Other than above: ROM code protection disabled
4) Options Trusted Memory (TM) Enable `TMEF.TMEF[2:0] = b000` - prevents reading of blocks 8 and 9 (see 59.17 Trusted Memory) - Location for keys or code that should not be read

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

@ -10,6 +10,8 @@ This README describes configuration of supported targets.
* [Infineon AURIX TC3xx](#infineon-aurix-tc3xx)
* [Intel x86-64 Intel FSP](#intel-x86_64-with-intel-fsp-support)
* [Kontron VX3060-S2](#kontron-vx3060-s2)
* [Microchip PIC32CK](#microchip-pic32ck)
* [Microchip PIC32CZ](#microchip-pic32cz)
* [Microchip SAMA5D3](#microchip-sama5d3)
* [Microchip SAME51](#microchip-same51)
* [Nordic nRF52840](#nordic-nrf52840)
@ -45,7 +47,6 @@ This README describes configuration of supported targets.
* [TI Hercules TMS570LC435](#ti-hercules-tms570lc435)
* [Xilinx Zynq UltraScale](#xilinx-zynq-ultrascale)
## STM32F4
Example 512KB partitioning on STM32-F407
@ -1555,6 +1556,153 @@ at addresses 0x400000 and 0x800000, authenticate, load to DRAM and stage from
Ensure that the application is compiled to run from `LOAD_ADDRESS`.
Check `test-app/ARM-sama5d3.ld` for details.
## Microchip PIC32CK
The PIC32CK is a high-performance 32-bit microcontroller family from Microchip featuring an ARM Cortex-M33 core. wolfBoot has been tested on the PIC32CKSG Curiosity board, which has GPIO pins PD20 and PB25 connected to LED0 and LED1, respectively, for status indication.
### Configuration
The PIC32CK SG family models support TrustZone. The flash and memory areas marked as secure or non secure depend on configuration settings. If setting `TZEN=0`, wolfBoot ignores TrustZone configuration, with the net effect to stage the application in the secure domain. In this case the flash area used to store BOOT and UPDATE partition should be marked as secure. The config file provided in `config/examples/pic32ck.config` sets `TZEN=0` and uses flash partition addresses that are marked as secure under default settings.
The PIC32CK supports a dual-bank update mechanism but, based on configuration settings, the swap may cause an area marked as secure to be mapped in non-secure flash space. For this reason `DUALBANK_SWAP` feature should be only used after precise configuration.
### Building
To build wolfBoot for the PIC32CK:
1. Configure the build using the example configuration file:
```sh
cp config/examples/pic32ck.config .config
make clean
make
```
2. Sign the application:
```sh
./tools/keytools/sign --ed25519 --sha256 ./test-app/image.bin wolfboot_signing_private_key.der 1
./tools/keytools/sign --ed25519 --sha256 ./test-app/image.bin wolfboot_signing_private_key.der 2
```
### Programming and Testing
To program the flash chip using the JLink tool:
Identify the correct JLink device for your PIC32CK. In the examples the model is PIC32CK2051SG.
1. Run the following command:
```sh
JLinkExe -device PIC32CK2051SG -if SWD -speed 4000 -autoconnect 1
```
2. At the JLink prompt, use the following commands:
```
halt
reset
erase
loadfile wolfboot.bin 0x08000000
loadfile test-app/image_v1_signed.bin 0x0c000000
loadfile test-app/image_v2_signed.bin 0x0c07f000
reset
q
```
3. Disconnect USB debugger and power cycle board. LED0 will turn on indicating version 1. Then press the reset button and LED1 will turn on indicating version 2.
### Programming with MPlab IPE
In order to program using the MPlab IPE, you need to create the hex files for wolfBoot, and the signed application images:
```bash
arm-none-eabi-objcopy -O ihex wolfboot.elf wolfboot.hex
arm-none-eabi-objcopy -I binary -O ihex --change-addresses=0x0C000000 test-app/image_v1_signed.bin image_v1_signed.hex
arm-none-eabi-objcopy -I binary -O ihex --change-addresses=0x0C07F000 test-app/image_v2_signed.bin image_v2_signed.hex
```
then enable advanced setting in the MPLAB IPE GUI, and enable the "Allow Import Multiple Hex file" option in the Production view.
Once the option is enabled, load the hex files into the MPLAB IPE GUI (File -> Import -> Multiple hex) and program the device.
### Behavior During Testing
- The application version 1 will boot first. The application will trigger the update and light LED0. On the next reset, wolfBoot will update the application, boot application version 2, and turn on LED1.
## Microchip PIC32CZ
The PIC32CZ is a high-performance 32-bit microcontroller family from Microchip featuring an ARM Cortex-M7 core. wolfBoot has been tested on the PIC32CZCA91 Curiosity board, which has GPIO pins PB21 and PB22 connected to LED0 and LED1, respectively, for status indication.
### Configuration
The PIC32CZ supports a dual-bank update mechanism that can be activated using the `DUALBANK_SWAP=1` option in the configuration file. When activated, the boot partition must be configured to reside in the lower Program Flash Memory (PFM) area, while the update partition should be in the upper PFM area. An example configuration for the PIC32CZ with 4MB RAM is provided in `config/examples/pic32cz.config`.
### Building
To build wolfBoot for the PIC32CZ:
1. Configure the build using the example configuration file:
```sh
cp config/examples/pic32cz.config .config
make clean
make
```
2. Sign the application:
```sh
./tools/keytools/sign --ed25519 --sha256 ./test-app/image.bin wolfboot_signing_private_key.der 1
./tools/keytools/sign --ed25519 --sha256 ./test-app/image.bin wolfboot_signing_private_key.der 2
```
### Programming and Testing
To program the flash chip using the JLink tool:
Identify the correct JLink device for your PIC32CZ board. In the examples the model is PIC32CZ4010CA90.
1. Run the following command:
```sh
JLinkExe -device PIC32CZ4010CA90 -if SWD -speed 4000 -autoconnect 1
```
2. At the JLink prompt, use the following commands:
```
halt
reset
erase
loadfile wolfboot.bin 0x08000000
loadfile test-app/image_v1_signed.bin 0x0c000000
loadfile test-app/image_v2_signed.bin 0x0c200000
reset
q
```
3. Disconnect USB debugger and power cycle board. LED0 will turn on indicating version 1. Then press the reset button and LED1 will turn on indicating version 2.
### Programming with MPLAB IPE
In order to program using the MPLAB IPE, you need to create the hex files for wolfBoot, and the signed application images:
```bash
arm-none-eabi-objcopy -O ihex wolfboot.elf wolfboot.hex
arm-none-eabi-objcopy -I binary -O ihex --change-addresses=0x0C000000 test-app/image_v1_signed.bin image_v1_signed.hex
arm-none-eabi-objcopy -I binary -O ihex --change-addresses=0x0C200000 test-app/image_v2_signed.bin image_v2_signed.hex
```
then enable advanced setting in the MPLAB IPE GUI, and enable the "Allow Import Multiple Hex file" option in the Production view.
Once the option is enabled, load the hex files into the MPLAB IPE GUI (File -> Import -> Multiple hex) and program the device.
### Behavior During Testing
The test behavior depends on whether the `DUALBANK_SWAP` feature is enabled:
- **If `DUALBANK_SWAP=1`:** The higher version of the application will be automatically selected, and LED1 will turn on.
- **If `DUALBANK_SWAP=0`:** The application version 1 will boot first. The application will trigger the update and light LED0. On the next reset, wolfBoot will update the application, boot application version 2, and turn on LED1.
## Microchip SAME51
@ -2453,7 +2601,7 @@ Boot header magic 0x00000000 invalid at 0x20000128
Copy sector 1 (part 1->2)
Copy sector 1 (part 0->1)
Copy sector 1 (part 2->0)
Erasing remainder of partitions (235 sectors)...
Erasing remainder of partition (235 sectors)...
Boot partition: 0xC000 (sz 4832, ver 0x2, type 0x201)
Boot header magic 0x00000000 invalid at 0x20000128
Copy sector 236 (part 0->2)
@ -2495,7 +2643,7 @@ Copy sector 1 (part 2->0)
Copy sector 2 (part 1->2)
Copy sector 2 (part 0->1)
Copy sector 2 (part 2->0)
Erasing remainder of partitions (88 sectors)...
Erasing remainder of partition (88 sectors)...
Boot partition: 0x100C000 (sz 4120, ver 0x2, type 0x202)
Update partition: 0x100000 (sz 4120, ver 0x1, type 0x201)
Copy sector 90 (part 0->2)

View File

@ -171,4 +171,76 @@ of the patch. If all checks succeed, the new version is installed by applying th
If the update is not confirmed, at the next reboot wolfBoot will restore the original base `image_v1_signed.bin`, using
the reverse patch contained in the delta update bundle.
## ELF loading
wolfBoot supports loading ELF (Executable and Linkable Format) images via both the RAM [update_ram.c](../src/update_ram.c) and [flash update](../src/update_flash.c) mechanisms.
### RAM ELF loading
The wolfBoot RAM loader supports loading ELF images from flash into RAM before booting. When using the RAM loader [update_ram.c](../src/update_ram.c) with `WOLFBOOT_ELF` defined, wolfBoot will verify the ELF file signature and hash as stored in the boot or update partition, and then load the ELF file into RAM based on the LMA (Load Memory Address) of each section before jumping to the entry point.
### Flash ELF loading
The wolfBoot flash loader also supports loading ELF files containing scattered LMA (Load Memory Address) segments to their respective locations in flash. This feature allows firmware images to be distributed as ELF files with sections only containing loadable regions, rather than requiring a contiguous/flat binary image for the entire memory space or image size. The flash elf loading procedure only supports loading ELF file program segments into flash memory with the same access restrictions as the BOOT partition (e.g. will use the same hal_flash/ext_flash functions) and does not support loading sections into RAM.
#### How it works
When wolfBoot is compiled with `WOLFBOOT_ELF_FLASH_SCATTER` defined, it includes support for:
1. **Section Loading**: During boot or update, wolfBoot:
- Parses the ELF headers to identify section locations
- Loads each section into its designated memory address
- Sets up the proper entry point for execution
- Verifies the scattered hash after loading
2. **Dual-Layer Verification**: wolfBoot performs two distinct integrity checks:
- Initial verification of the ELF file signature and integrity check (hash) as stored in the boot or update partition
- A second "scattered hash" verification that re-computes the same image hash as in the manifest header, but computed over the actual loaded segments in their final memory locations (Load Memory Address - LMA) rather than their data in the ELF file
3. **Update Support**: The scattered ELF support is transparently integrated with wolfBoot's flash update mechanism:
- ELF images can be used as update packages
- The bootloader automatically handles loading and verifying scattered sections during the update process
- If the scattered hash verification fails after an update, the system can fall back to the previous version
#### Using Scattered ELF Images
To use scattered ELF images:
1. Ensure wolfBoot is compiled with `WOLFBOOT_ELF` and `WOLFBOOT_ELF_FLASH_SCATTER`
2. Build your firmware as an ELF file with the desired memory layout
3. Preprocess the ELF file using [squashelf](../tools/squashelf/README.md) to strip the ELF file to just loadable sections and ensure it is compatible with wolfBoot (if building using wolfBoot's Makefile build system, this step is performed automatically)
4. Sign the ELF image using the wolfBoot signing tools
5. Transfer the signed ELF image to the target like any other update
The on boot or during update, the bootloader will automatically perform both layers of verification:
1. Verifying the signature and hash over the image in the BOOT or UPDATE partition
2. Computing and verifying the scattered hash after loading sections to their final locations
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

@ -33,6 +33,11 @@
#include "IfxPort.h" /* for IfxPort_* */
#include "IfxScuRcu.h" /* for IfxScuRcu_performReset */
#include "Ifx_Ssw_Infra.h" /* for Ifx_Ssw_jumpToFunction */
#if defined(DEBUG_UART)
#include "IfxAsclin_Asc.h"
#include "Cpu/Irq/IfxCpu_Irq.h"
#include "IfxCpu.h"
#endif /* DEBUG_UART */
#ifdef WOLFBOOT_ENABLE_WOLFHSM_CLIENT
/* wolfHSM headers */
@ -81,6 +86,15 @@ static uint32_t sectorBuffer[WOLFBOOT_SECTOR_SIZE / sizeof(uint32_t)];
#define LED_OFF(led)
#endif /* WOLFBOOT_AURIX_GPIO_TIMING */
#if defined(DEBUG_UART)
#define UART_PIN_RX IfxAsclin0_RXA_P14_1_IN /* RX pin of the board */
#define UART_PIN_TX IfxAsclin0_TX_P14_0_OUT /* TX pin of the board */
#define UART_BAUDRATE 115200
static Ifx_ASCLIN* g_asclinRegs = &MODULE_ASCLIN0;
static int uartInit(void);
static int uartTx(const uint8_t c);
#endif /* DEBUG_UART */
#ifdef WOLFBOOT_ENABLE_WOLFHSM_CLIENT
int hal_hsm_init_connect(void);
int hal_hsm_disconnect(void);
@ -353,6 +367,10 @@ void hal_init(void)
LED_OFF(LED_PROG);
LED_OFF(LED_ERASE);
LED_OFF(LED_READ);
#if defined(DEBUG_UART)
uartInit();
#endif
}
/*
@ -441,25 +459,91 @@ int RAMFUNCTION hal_flash_erase(uint32_t address, int len)
{
LED_ON(LED_ERASE);
const uint32_t sectorAddr = GET_SECTOR_ADDR(address);
const size_t numSectors =
(len == 0) ? 0 : ((len - 1) / WOLFBOOT_SECTOR_SIZE) + 1;
const IfxFlash_FlashType type = getFlashTypeFromAddr(address);
/* Handle zero length case */
if (len <= 0) {
LED_OFF(LED_ERASE);
return 0;
}
/* Disable ENDINIT protection */
const uint16 endInitSafetyPassword =
const uint32_t startSectorAddr = GET_SECTOR_ADDR(address);
const uint32_t endAddress = address + len - 1;
const uint32_t endSectorAddr = GET_SECTOR_ADDR(endAddress);
const IfxFlash_FlashType type = getFlashTypeFromAddr(address);
const uint16 endInitSafetyPassword =
IfxScuWdt_getSafetyWatchdogPasswordInline();
IfxScuWdt_clearSafetyEndinitInline(endInitSafetyPassword);
uint32_t currentSectorAddr;
IfxFlash_eraseMultipleSectors(sectorAddr, numSectors);
/* If address and len are both sector-aligned, perform simple bulk erase */
if ((address == startSectorAddr) &&
(endAddress == endSectorAddr + WOLFBOOT_SECTOR_SIZE - 1)) {
const size_t numSectors =
(endSectorAddr - startSectorAddr) / WOLFBOOT_SECTOR_SIZE + 1;
/* Reenable ENDINIT protection */
IfxScuWdt_setSafetyEndinitInline(endInitSafetyPassword);
/* Disable ENDINIT protection */
IfxScuWdt_clearSafetyEndinitInline(endInitSafetyPassword);
IfxFlash_waitUnbusy(FLASH_MODULE, type);
IfxFlash_eraseMultipleSectors(startSectorAddr, numSectors);
/* Reenable ENDINIT protection */
IfxScuWdt_setSafetyEndinitInline(endInitSafetyPassword);
IfxFlash_waitUnbusy(FLASH_MODULE, type);
}
/* For non-sector aligned erases, handle each sector carefully */
else {
/* Process each affected sector */
for (currentSectorAddr = startSectorAddr;
currentSectorAddr <= endSectorAddr;
currentSectorAddr += WOLFBOOT_SECTOR_SIZE) {
/* Check if this is a partial sector erase */
const int isFirstSector = (currentSectorAddr == startSectorAddr);
const int isLastSector = (currentSectorAddr == endSectorAddr);
const int isPartialStart =
isFirstSector && (address > startSectorAddr);
const int isPartialEnd =
isLastSector &&
(endAddress < (endSectorAddr + WOLFBOOT_SECTOR_SIZE - 1));
/* For partial sectors, need to read-modify-write */
if (isPartialStart || isPartialEnd) {
/* Read the sector into the sector buffer */
cacheSector(currentSectorAddr, type);
/* Calculate which bytes within the sector to erase */
uint32_t eraseStartOffset =
isPartialStart ? (address - currentSectorAddr) : 0;
uint32_t eraseEndOffset = isPartialEnd
? (endAddress - currentSectorAddr)
: (WOLFBOOT_SECTOR_SIZE - 1);
uint32_t eraseLen = eraseEndOffset - eraseStartOffset + 1;
/* Fill the section to be erased with the erased byte value */
memset((uint8_t*)sectorBuffer + eraseStartOffset,
FLASH_BYTE_ERASED, eraseLen);
/* Erase the sector */
IfxScuWdt_clearSafetyEndinitInline(endInitSafetyPassword);
IfxFlash_eraseSector(currentSectorAddr);
IfxScuWdt_setSafetyEndinitInline(endInitSafetyPassword);
IfxFlash_waitUnbusy(FLASH_MODULE, type);
/* Program the modified buffer back */
programCachedSector(currentSectorAddr, type);
}
/* For full sector erase, just erase directly */
else {
IfxScuWdt_clearSafetyEndinitInline(endInitSafetyPassword);
IfxFlash_eraseSector(currentSectorAddr);
IfxScuWdt_setSafetyEndinitInline(endInitSafetyPassword);
IfxFlash_waitUnbusy(FLASH_MODULE, type);
}
}
}
LED_OFF(LED_ERASE);
return 0;
}
@ -581,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)
@ -681,3 +768,118 @@ int hal_hsm_disconnect(void)
#endif /* WOLFBOOT_ENABLE_WOLFHSM_CLIENT */
#if defined(DEBUG_UART)
static int uartInit(void)
{
/* Define local pin structures for init function */
IfxAsclin_Rx_In uartRxPin = UART_PIN_RX;
IfxAsclin_Tx_Out uartTxPin = UART_PIN_TX;
IfxAsclin_enableModule(g_asclinRegs);
IfxAsclin_setClockSource(g_asclinRegs, IfxAsclin_ClockSource_noClock);
IfxAsclin_initRxPin(&uartRxPin, IfxPort_InputMode_pullUp,
IfxPort_PadDriver_cmosAutomotiveSpeed1);
IfxAsclin_initTxPin(&uartTxPin, IfxPort_OutputMode_pushPull,
IfxPort_PadDriver_cmosAutomotiveSpeed1);
IfxAsclin_setFrameMode(g_asclinRegs, IfxAsclin_FrameMode_initialise);
/* Configure baudrate - must temporarily enable clocks */
IfxAsclin_setClockSource(g_asclinRegs, IfxAsclin_ClockSource_ascFastClock);
IfxAsclin_setPrescaler(g_asclinRegs, 1);
if (IfxAsclin_setBitTiming(
g_asclinRegs, (float32)UART_BAUDRATE,
IfxAsclin_OversamplingFactor_16,
IfxAsclin_SamplePointPosition_9, /* Sample point 9 */
IfxAsclin_SamplesPerBit_three) == FALSE) { /* Three samples */
IfxAsclin_disableModule(g_asclinRegs);
return -1;
}
IfxAsclin_setClockSource(g_asclinRegs, IfxAsclin_ClockSource_noClock);
IfxAsclin_setDataLength(g_asclinRegs, IfxAsclin_DataLength_8);
IfxAsclin_enableParity(g_asclinRegs, FALSE);
IfxAsclin_setStopBit(g_asclinRegs, IfxAsclin_StopBit_1);
IfxAsclin_setIdleDelay(g_asclinRegs, IfxAsclin_IdleDelay_0);
IfxAsclin_enableLoopBackMode(g_asclinRegs, FALSE);
IfxAsclin_setShiftDirection(g_asclinRegs,
IfxAsclin_ShiftDirection_lsbFirst);
IfxAsclin_setRxFifoOutletWidth(g_asclinRegs, IfxAsclin_RxFifoOutletWidth_1);
IfxAsclin_setRxFifoInterruptLevel(g_asclinRegs,
IfxAsclin_RxFifoInterruptLevel_1);
IfxAsclin_setRxFifoInterruptMode(g_asclinRegs,
IfxAsclin_FifoInterruptMode_combined);
IfxAsclin_setTxFifoInletWidth(g_asclinRegs, IfxAsclin_TxFifoInletWidth_1);
IfxAsclin_setTxFifoInterruptLevel(g_asclinRegs,
IfxAsclin_TxFifoInterruptLevel_15);
IfxAsclin_setTxFifoInterruptMode(g_asclinRegs,
IfxAsclin_FifoInterruptMode_combined);
IfxAsclin_setFrameMode(g_asclinRegs, IfxAsclin_FrameMode_asc);
IfxAsclin_setClockSource(g_asclinRegs, IfxAsclin_ClockSource_ascFastClock);
IfxAsclin_disableAllFlags(g_asclinRegs);
IfxAsclin_clearAllFlags(g_asclinRegs);
IfxAsclin_enableRxFifoInlet(g_asclinRegs, TRUE);
IfxAsclin_enableTxFifoOutlet(g_asclinRegs, TRUE);
IfxAsclin_flushRxFifo(g_asclinRegs);
IfxAsclin_flushTxFifo(g_asclinRegs);
return 0; /* Success */
}
static int uartTx(const uint8_t c)
{
/* Write the data value to the ASCLIN peripheral's transmit FIFO. */
/* Note: IfxAsclin_write8 takes a pointer */
uint8 data_to_send = c;
IfxAsclin_write8(g_asclinRegs, &data_to_send, 1);
/* Wait (poll) for the transmit FIFO to be empty */
/* TODO: Consider adding a timeout mechanism here if necessary */
while (IfxAsclin_getTxFifoFillLevel(g_asclinRegs) != 0) {
/* Busy wait */
}
return 0; /* Success */
}
void uart_write(const char* buf, unsigned int sz)
{
static char rambuf[512];
const static char lf = '\r';
unsigned int i;
if (sz > sizeof(rambuf)) {
sz = sizeof(rambuf);
}
memcpy(rambuf, buf, sz);
buf = rambuf;
for (i = 0; i < sz; i++) {
/* If newline character is detected, send carriage return first */
if (buf[i] == '\n') {
/* Send carriage return before newline */
if (uartTx(lf) != 0) {
/* Handle error if needed */
break;
}
}
/* Call uart_tx for each byte, which now polls until TX FIFO is empty */
if (uartTx((uint8_t)buf[i]) != 0) {
/* Handle error if needed */
break;
}
}
/* No final wait needed here, as uart_tx waits after each byte */
}
#endif /* DEBUG_UART */

168
hal/hal.c
View File

@ -33,12 +33,14 @@
#ifndef TEST_ADDRESS
#define TEST_SZ WOLFBOOT_SECTOR_SIZE
#define TEST_ADDRESS WOLFBOOT_PARTITION_UPDATE_ADDRESS
#define TEST_ADDRESS_BANKA WOLFBOOT_PARTITION_BOOT_ADDRESS
#define TEST_ADDRESS_BANKB WOLFBOOT_PARTITION_UPDATE_ADDRESS
#endif
int hal_flash_test(void)
{
int ret = 0;
uint32_t i, len;
uint32_t i;
uint8_t* pagePtr = (uint8_t*)TEST_ADDRESS;
static uint8_t pageData[TEST_SZ];
@ -60,8 +62,12 @@ int hal_flash_test(void)
}
/* Write Page */
hal_flash_unlock();
ret = hal_flash_write(TEST_ADDRESS, (uint8_t*)pageData, sizeof(pageData));
hal_flash_lock();
wolfBoot_printf("Write Page: Ret %d\n", ret);
if (ret != 0)
return ret;
#endif /* !TEST_FLASH_READONLY */
/* Compare Page */
@ -74,4 +80,164 @@ int hal_flash_test(void)
wolfBoot_printf("Internal Flash Test Passed\n");
return ret;
}
#ifndef TEST_FLASH_READONLY
int hal_flash_test_write_once(void)
{
uint8_t test_byte, expected_byte;
unsigned int i, b;
int ret = 0;
/* Erase the test sector */
hal_flash_unlock();
ret = hal_flash_erase(TEST_ADDRESS, TEST_SZ);
hal_flash_lock();
if (ret != 0) {
wolfBoot_printf("Erase Sector failed: Ret %d\n", ret);
return -1;
}
expected_byte = 0xFF;
/* For each bit in the byte (from LSB to MSB) */
for (b = 0; b < 8; b++) {
/* Toggle bit from 1 to 0 */
test_byte = 0xFF & ~(1 << b);
expected_byte &= ~(1 << b);
/* Write the data */
hal_flash_unlock();
ret = hal_flash_write(TEST_ADDRESS, &test_byte, sizeof(test_byte));
hal_flash_lock();
if (ret != 0) {
wolfBoot_printf("Write failed at bit %d: Ret %d\n", b, ret);
return -1;
}
/* Verify the write by direct comparison */
if (memcmp((void*)TEST_ADDRESS, &expected_byte, sizeof(expected_byte)) != 0) {
wolfBoot_printf("Verification failed at byte %d, bit %d\n", i, b);
return -1;
}
}
wolfBoot_printf("Write-once test passed\n");
return 0;
}
/* Test if unaligned write works. First test writing 1 bytes at SECTOR + 1.
* Then test writing 2 bytes that span the sector boundary.
*/
int hal_flash_test_align(void)
{
int ret = 0;
uint8_t test_data_1 = 0xAA;
uint8_t test_data_2[2] = {0xBB, 0xCC};
uint8_t read_back[2];
/* erase both sectors */
hal_flash_unlock();
ret = hal_flash_erase(TEST_ADDRESS, TEST_SZ * 2);
hal_flash_lock();
if (ret != 0) {
wolfBoot_printf("Erase Sector failed: Ret %d\n", ret);
return -1;
}
/* Write 1 byte at SECTOR + 1 */
hal_flash_unlock();
ret = hal_flash_write(TEST_ADDRESS + 1, &test_data_1, 1);
hal_flash_lock();
if (ret != 0) {
wolfBoot_printf("Unaligned write (1 byte) failed: Ret %d\n", ret);
return -1;
}
/* Verify 1 byte write */
if (*(uint8_t*)(TEST_ADDRESS + 1) != test_data_1) {
wolfBoot_printf("Unaligned write verification (1 byte) failed\n");
return -1;
}
/* Write 2 bytes spanning sector boundary */
hal_flash_unlock();
ret = hal_flash_write(TEST_ADDRESS + TEST_SZ - 1, test_data_2, 2);
hal_flash_lock();
if (ret != 0) {
wolfBoot_printf("Unaligned write (2 bytes) failed: Ret %d\n", ret);
return -1;
}
/* Verify 2 bytes write */
memcpy(read_back, (void*)(TEST_ADDRESS + TEST_SZ - 1), 2);
if (read_back[0] != test_data_2[0] || read_back[1] != test_data_2[1]) {
wolfBoot_printf("Unaligned write verification (2 bytes) failed\n");
return -1;
}
wolfBoot_printf("Unaligned write test passed\n");
return 0;
}
#endif
/* This test can be run only if swapping the flash do not reboot the board */
#if defined(DUALBANK_SWAP) && !defined(TEST_FLASH_READONLY)
int hal_flash_test_dualbank(void)
{
int ret = 0;
uint32_t i;
uint8_t cur_fill = 0xb0;
uint8_t new_fill = 0xf0;
uint8_t fill;
uint32_t pagePtr;
wolfBoot_printf("swap flash test at 0x%x\n", TEST_ADDRESS);
for (i = 0; i < 2; i++) {
fill = (i == 0) ? cur_fill : new_fill;
pagePtr = (i == 0) ? TEST_ADDRESS_BANKA : TEST_ADDRESS_BANKB;
/* Erase sector */
hal_flash_unlock();
ret = hal_flash_erase(pagePtr, WOLFBOOT_SECTOR_SIZE);
hal_flash_lock();
if (ret != 0) {
wolfBoot_printf("Erase Sector failed: Ret %d\n", ret);
return -1;
}
/* Write Page */
hal_flash_unlock();
ret = hal_flash_write(pagePtr, (uint8_t*)&fill, sizeof(fill));
hal_flash_lock();
if (ret != 0) {
wolfBoot_printf("Write Page failed: Ret %d\n", ret);
return -1;
}
}
if (*((uint8_t*)(TEST_ADDRESS_BANKA)) != cur_fill) {
wolfBoot_printf("Bank A data mismatch: %x != %x\n", *((uint8_t*)TEST_ADDRESS_BANKA), cur_fill);
return -1;
}
if (*((uint8_t*)(TEST_ADDRESS_BANKB)) != new_fill) {
wolfBoot_printf("Bank B data mismatch: %x != %x\n", *((uint8_t*)TEST_ADDRESS_BANKB), new_fill);
return -1;
}
hal_flash_dualbank_swap();
if (*((uint8_t*)(TEST_ADDRESS_BANKA)) != new_fill) {
wolfBoot_printf("Bank A data mismatch after swap: %x != %x\n", *((uint8_t*)TEST_ADDRESS_BANKA), new_fill);
return -1;
}
if (*((uint8_t*)(TEST_ADDRESS_BANKB)) != cur_fill) {
wolfBoot_printf("Bank B data mismatch after swap: %x != %x\n", *((uint8_t*)TEST_ADDRESS_BANKB), cur_fill);
return -1;
}
wolfBoot_printf("DUALBANK_SWAP test passed");
return 0;
}
#endif /* DUALBANK_SWAP */
#endif /* TEST_FLASH */

513
hal/pic32c.c 100644
View File

@ -0,0 +1,513 @@
/* pic32c.c
*
* Copyright (C) 2025 wolfSSL Inc.
*
* This file is part of wolfBoot.
*
* wolfBoot is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfBoot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
#include "image.h"
#include "loader.h"
#include <stdint.h>
#include <string.h>
#if defined(TARGET_pic32cz)
#include "pic32cz_registers.h"
#elif defined(TARGET_pic32ck)
#include "pic32ck_registers.h"
#endif
#define FCW_CTRLA (*(volatile uint32_t *)(FCW_BASE + 0x00U))
#define FCW_CTRLB (*(volatile uint32_t *)(FCW_BASE + 0x04U))
#define FCW_MUTEX (*(volatile uint32_t *)(FCW_BASE + 0x08U))
#define FCW_INTENCLR (*(volatile uint32_t *)(FCW_BASE + 0x0CU))
#define FCW_INTENSET (*(volatile uint32_t *)(FCW_BASE + 0x10U))
#define FCW_INTFLAG (*(volatile uint32_t *)(FCW_BASE + 0x14U))
#define FCW_STATUS (*(volatile uint32_t *)(FCW_BASE + 0x18U))
#define FCW_KEY (*(volatile uint32_t *)(FCW_BASE + 0x1CU))
#define FCW_ADDR (*(volatile uint32_t *)(FCW_BASE + 0x20U))
#define FCW_SRCADDR (*(volatile uint32_t *)(FCW_BASE + 0x24U))
#define FCW_DATA ((volatile uint32_t *)(FCW_BASE + 0x28U))
#define FCW_SWAP (*(volatile uint32_t *)(FCW_BASE + 0x48U))
#define FCW_PWP ((volatile uint32_t *)(FCW_BASE + 0x4CU))
#define FCW_LBWP (*(volatile uint32_t *)(FCW_BASE + 0x6CU))
#define FCW_UBWP (*(volatile uint32_t *)(FCW_BASE + 0x70U))
#define FCW_UOWP (*(volatile uint32_t *)(FCW_BASE + 0x74U))
#define FCW_CWP (*(volatile uint32_t *)(FCW_BASE + 0x78U))
#define FCW_HSMINTENCLR (*(volatile uint32_t *)(FCW_BASE + 0x80U))
#define FCW_HSMINTENSET (*(volatile uint32_t *)(FCW_BASE + 0x84U))
#define FCW_HSMINTFLAG (*(volatile uint32_t *)(FCW_BASE + 0x88U))
#define FCW_HSMCWP (*(volatile uint32_t *)(FCW_BASE + 0x8CU))
#define FCW_HSMLDAT ((volatile uint32_t *)(FCW_BASE + 0x90U))
#define FCW_HSMUDAT ((volatile uint32_t *)(FCW_BASE + 0xB0U))
#define FCW_UNLOCK_WRKEY 0x91C32C01
#define FCW_UNLOCK_SWAPKEY 0x91C32C02
#define FCW_UNLOCK_CFGKEY 0x91C32C04
#define FCW_OP_ERASE_SECTOR 0x4
#define FCW_OP_QUAD_DOUBLE_WORD_WRITE 0x2
#define FCW_OP_NOOP 0x0
#define FCW_BUSY_MASK (1 << 0)
#define FCW_CTRLA_PREPG_BIT (1 << 7)
#define FCW_CTRLA_NVMOP_MASK ((1 << 4) - 1)
#define FCW_INTFLAG_DONE_BIT (1 << 0)
#define FCW_SWAP_PFSWAP (1 << 8)
/*
* bit 0 lock
* bit [1:2] owner (01 = mcu)
*/
#define FCW_OWN_MCU (0x1 << 1)
#define FCW_OWN_AND_LOCK 0x3
#define FCW_MUTEX_LOCK_MASK 0x1
#define FCW_WRITE_SIZE (4 * 8)
static uint32_t pic32_last_err = 0;
#define OSCCTRL_STATUS (*(volatile uint32_t *)(OSCCTRL_BASE + 0x10U))
#define OSCCTRL_INTFLAG (*(volatile uint32_t *)(OSCCTRL_BASE + 0x0CU))
#define OSCCTRL_PLL0CTRL (*(volatile uint32_t *)(OSCCTRL_BASE + 0x40U))
#define OSCCTRL_PLL0FBDIV (*(volatile uint32_t *)(OSCCTRL_BASE + 0x44U))
#define OSCCTRL_PLL0REFDIV (*(volatile uint32_t *)(OSCCTRL_BASE + 0x48U))
#define OSCCTRL_PLL0POSTDIV0 (*(volatile uint32_t *)(OSCCTRL_BASE + 0x4CU))
#define OSCCTRL_FRACDIV0 (*(volatile uint32_t *)(OSCCTRL_BASE + 0x6CU))
#define OSCCTRL_SYNCBUSY (*(volatile uint32_t *)(OSCCTRL_BASE + 0x78U))
#define OSCCTRL_SYNCBUSY_FRACDIV0_MASK (1 << 6)
#define OSCCTRL_FRACDIV0_REMDIV_SHIFT (7)
#define OSCCTRL_FRACDIV0_REMDIV(X) ((X) << OSCCTRL_FRACDIV0_REMDIV_SHIFT)
#define OSCCTRL_FRACDIV0_INTDIV_SHIFT (16)
#define OSCCTRL_FRACDIV0_INTDIV(X) ((X) << OSCCTRL_FRACDIV0_INTDIV_SHIFT)
#define OSCCTRL_PLL0POSTDIV0_EN (1 << 0x7)
#define OSCCTRL_PLL0CTRL_BWSEL_SHIFT (11)
#define OSCCTRL_PLL0CTRL_BWSEL(X) ((X) << OSCCTRL_PLL0CTRL_BWSEL_SHIFT)
#define OSCCTRL_PLL0CTRL_REFSEL_SHIFT (8)
#define OSCCTRL_PLL0CTRL_REFSEL(X) ((X) << OSCCTRL_PLL0CTRL_REFSEL_SHIFT)
#define OSCCTRL_PLL0CTRL_EN (1 << 1)
#define OSCCTRL_STATUS_PLL0LOCK (1 << 24)
#define OSCCTRL_INTFLAG_PLL0LOCKR (1 << 24)
#define OSCCTRL_FRACDIV0 (*(volatile uint32_t *)(OSCCTRL_BASE + 0x6CU))
#define OSCCTRL_FRACDIV0_REMDIV_SHIFT (7)
#define OSCCTRL_FRACDIV0_REMDIV(X) ((X) << OSCCTRL_FRACDIV0_REMDIV_SHIFT)
#define OSCCTRL_FRACDIV0_INTDIV_SHIFT (16)
#define OSCCTRL_FRACDIV0_INTDIV(X) ((X) << OSCCTRL_FRACDIV0_INTDIV_SHIFT)
#define OSCCTRL_SYNCBUSY (*(volatile uint32_t *)(OSCCTRL_BASE + 0x78U))
#define OSCCTRL_SYNCBUSY_FRACDIV0_MASK (1 << 6)
#define GCLK_CTRLA (*(volatile uint32_t *)(GCLK_BASE + 0x00U))
#define GCLK_SYNCBUSY (*(volatile uint32_t *)(GCLK_BASE + 0x4U))
#define GCLK_GENCTRL ((volatile uint32_t *)(GCLK_BASE + 0x20U))
#define GCLK_GENCTRL_SRC_PLL0 (6)
#define GCLK_GENCTRL_GENEN (1 << 8)
#define GCLK_GENCTRL_DIV_SHIFT (16)
#define GCLK_GENCTRL_DIV(X) ((X) << GCLK_GENCTRL_DIV_SHIFT)
#define GCLK_SYNCBUSY_GENCTRL0 (1 << 2)
#define GCLK_CTRLA_SWRST (1)
#define MCLK_INTFLAG (*(volatile uint32_t *)(MCLK_BASE + 0x08U))
#define MCLK_DIV0 (*(volatile uint32_t *)(MCLK_BASE + 0x0CU))
#define MCLK_DIV1 (*(volatile uint32_t *)(MCLK_BASE + 0x10U))
#define MCLK_INTFLAG_CKRDY (1)
void pic32_fcw_grab(void)
{
do {
while (FCW_MUTEX & FCW_MUTEX_LOCK_MASK) {
/* wait for ownership */
/* if mutex is locked by us, we can unlock it */
if ((FCW_MUTEX & FCW_OWN_MCU) == FCW_OWN_MCU) {
FCW_MUTEX = FCW_OWN_MCU;
}
}
FCW_MUTEX = FCW_OWN_AND_LOCK;
} while (FCW_MUTEX != FCW_OWN_AND_LOCK);
}
void pic32_fcw_release(void)
{
FCW_MUTEX = FCW_OWN_MCU;
}
static void pic32_fcw_start_op(uint32_t op)
{
FCW_CTRLA = FCW_CTRLA_PREPG_BIT | (op & FCW_CTRLA_NVMOP_MASK);
}
static uint32_t pic32_get_errs(void)
{
return FCW_INTFLAG;
}
static void pic32_clear_errs(void)
{
FCW_INTFLAG = 0xffffffff;
}
static void pic32_fcw_wait_complete(void)
{
while (FCW_STATUS & FCW_BUSY_MASK) {}
}
static int pic32_write_dqword_aligned(uint32_t addr, const uint8_t *data)
{
uint32_t i;
uint32_t *_data = (uint32_t *)data;
uint32_t err;
pic32_fcw_wait_complete();
FCW_ADDR = addr;
for (i = 0; i < 8; i++) {
FCW_DATA[i] = _data[i];
}
FCW_KEY = FCW_UNLOCK_WRKEY;
pic32_fcw_start_op(FCW_OP_QUAD_DOUBLE_WORD_WRITE);
pic32_fcw_wait_complete();
err = pic32_get_errs();
pic32_last_err = err;
err &= ~FCW_INTFLAG_DONE_BIT;
pic32_clear_errs();
return err;
}
static int pic32_addr_is_dqword_aligned(uint32_t addr)
{
return ((addr & 0x1F) == 0);
}
static uint32_t pic32_addr_dqword_align(uint32_t addr)
{
return (addr & ~0x1F);
}
static void pic32_copy_dqword(uint8_t *dst, uint32_t addr)
{
int i;
for (i = 0; i < FCW_WRITE_SIZE; i++) {
dst[i] = *(volatile uint8_t *)(addr + i);
}
}
static int pic32_fcw_erase_sector(uint32_t addr)
{
uint32_t err;
pic32_fcw_wait_complete();
FCW_ADDR = addr;
FCW_KEY = FCW_UNLOCK_WRKEY;
pic32_fcw_start_op(FCW_OP_ERASE_SECTOR);
pic32_fcw_wait_complete();
err = pic32_get_errs();
pic32_clear_errs();
pic32_last_err = err;
err &= ~FCW_INTFLAG_DONE_BIT;
return err;
}
static void pic32_delay_cnt(uint32_t ticks)
{
uint32_t i = 0;
for (i = 0; i < ticks; i++) {
__asm__("nop");
}
}
static uint8_t pic32_mask_zeros(uint8_t programmed, uint8_t to_program)
{
return to_program | (~programmed);
}
int pic32_flash_write(uint32_t address, const uint8_t *data, int len)
{
uint8_t buff[FCW_WRITE_SIZE], curr[FCW_WRITE_SIZE];
uint32_t _addr;
uint8_t i;
int ret;
while (len > 0) {
if (!pic32_addr_is_dqword_aligned(address) || len < FCW_WRITE_SIZE) {
_addr = pic32_addr_dqword_align(address);
/* Setup an aligned buffer with the following rules:
* - For addresses outside the writing range: 0xFF (no change)
* - For addresses inside the writing range: data | !current_data
*
* This approach ensures we only flip bits from 1 to 0 when writing
* without an erase operation. When the address is aligned and length
* is at least WOLFBOOT_SECTOR_SIZE, an erase was already performed,
* so we can write data directly.
*/
pic32_copy_dqword(curr, _addr);
memset(buff, 0xff, FCW_WRITE_SIZE);
i = address - _addr;
for (; i < FCW_WRITE_SIZE && len > 0; i++, len--) {
buff[i] = pic32_mask_zeros(curr[i], *data);
data++;
address++;
}
ret = pic32_write_dqword_aligned(_addr, buff);
if (ret != 0)
return ret;
continue;
}
ret = pic32_write_dqword_aligned(address, data);
if (ret != 0)
return ret;
address += FCW_WRITE_SIZE;
data += FCW_WRITE_SIZE;
len -= FCW_WRITE_SIZE;
}
return 0;
}
int pic32_flash_erase(uint32_t addr, int len)
{
int err;
while (len > 0) {
if (len < WOLFBOOT_SECTOR_SIZE) {
return -1;
}
err = pic32_fcw_erase_sector(addr);
if (err != 0)
return err;
addr += WOLFBOOT_SECTOR_SIZE;
len -= WOLFBOOT_SECTOR_SIZE;
}
return 0;
}
#ifdef DUALBANK_SWAP
static int pic32_fcw_pfswap_get()
{
return !!(FCW_SWAP & FCW_SWAP_PFSWAP);
}
static void pic32_fcw_pfswap_set(int sw)
{
uint32_t reg;
reg = FCW_SWAP;
reg &= FCW_SWAP_PFSWAP;
if (sw)
reg |= FCW_SWAP_PFSWAP;
FCW_KEY = FCW_UNLOCK_SWAPKEY;
FCW_SWAP = reg;
}
void pic32_flash_dualbank_swap(void)
{
uint32_t sw;
uint32_t reg;
pic32_fcw_wait_complete();
sw = pic32_fcw_pfswap_get();
pic32_fcw_pfswap_set(!sw);
}
#endif /* DUALBANK_SWAP */
void pic32_clock_fracdiv0_set(int intdiv, int remdiv)
{
OSCCTRL_FRACDIV0 =
OSCCTRL_FRACDIV0_INTDIV(intdiv) | OSCCTRL_FRACDIV0_REMDIV(remdiv);
while (OSCCTRL_SYNCBUSY & OSCCTRL_SYNCBUSY_FRACDIV0_MASK) {}
}
void pic32_clock_pll0_init(int refdiv, int fbdiv, int bw, int postdiv)
{
uint32_t reg;
/* configure pll0 */
OSCCTRL_PLL0CTRL = 0;
OSCCTRL_PLL0REFDIV = refdiv;
OSCCTRL_PLL0FBDIV = fbdiv;
/* enable PLL0 output 0 divied by 3 (300Mhz) */
OSCCTRL_PLL0POSTDIV0 = OSCCTRL_PLL0POSTDIV0_EN | postdiv;
reg = OSCCTRL_PLL0CTRL;
/* set the bandwith value */
reg |= OSCCTRL_PLL0CTRL_BWSEL(bw);
reg |= OSCCTRL_PLL0CTRL_REFSEL(0x2);
reg |= OSCCTRL_PLL0CTRL_EN;
OSCCTRL_PLL0CTRL |= reg;
/* wait to the PLL to lock */
#if defined(TARGET_pic32cz)
while (!(OSCCTRL_STATUS & OSCCTRL_STATUS_PLL0LOCK)) {}
#endif
#if defined(TARGET_pic32ck)
while (!(OSCCTRL_INTFLAG & OSCCTRL_INTFLAG_PLL0LOCKR)) {}
#endif
}
void pic32_clock_gclk_gen0(int mclk_div1, int cpudiv)
{
uint32_t reg;
/* setup clock division before changing the generator */
if (mclk_div1 != 1)
MCLK_DIV1 = mclk_div1;
while (!(MCLK_INTFLAG & MCLK_INTFLAG_CKRDY)) {}
GCLK_GENCTRL[0] =
GCLK_GENCTRL_SRC_PLL0 | GCLK_GENCTRL_GENEN | GCLK_GENCTRL_DIV(cpudiv);
while (GCLK_SYNCBUSY & GCLK_SYNCBUSY_GENCTRL0) {}
}
void pic32_clock_reset(void)
{
/* reset GCLK module */
GCLK_CTRLA = GCLK_CTRLA_SWRST;
/* wait for the reset to complete */
while (GCLK_CTRLA & GCLK_CTRLA_SWRST) {}
/* reset MCLK_DIV1 to reset value */
MCLK_DIV1 = 1;
while (!(MCLK_INTFLAG & MCLK_INTFLAG_CKRDY)) {}
/* turn off PLL0 */
OSCCTRL_PLL0CTRL = 0;
OSCCTRL_PLL0POSTDIV0 = 0x20202020;
/* reset PLL0 values */
OSCCTRL_PLL0REFDIV = 0;
OSCCTRL_PLL0FBDIV = 0;
/* reset the fracdiv0 to reset value */
pic32_clock_fracdiv0_set(32, 0);
}
#if defined(TEST_FLASH)
int hal_flash_test_align(void);
int hal_flash_test_write_once(void);
int hal_flash_test(void);
int hal_flash_test_dualbank(void);
void pic32_flash_test(void)
{
int ret;
ret = hal_flash_test();
if (ret != 0)
wolfBoot_panic();
ret = hal_flash_test_align();
if (ret != 0)
wolfBoot_panic();
ret = hal_flash_test_write_once();
if (ret != 0)
wolfBoot_panic();
#ifdef DUALBANK_SWAP
ret = hal_flash_test_dualbank();
if (ret != 0)
wolfBoot_panic();
#endif
}
#endif /* TEST_FLASH */
#ifdef TEST_CLOCK
/* SysTick registers and constants */
#define SYSTICK_BASE (0xE000E010U)
#define SYSTICK_RVR_MASK (0x00FFFFFF)
#define SYSTICK_CSR (*(volatile uint32_t *)(SYSTICK_BASE + 0x00U))
#define SYSTICK_RVR (*(volatile uint32_t *)(SYSTICK_BASE + 0x04U))
#define SYSTICK_CVR (*(volatile uint32_t *)(SYSTICK_BASE + 0x08U))
#define SYSTICK_CSR_ENABLE (1 << 0)
#define SYSTICK_CSR_CLKSOURCE (1 << 2)
#define SYSTICK_CSR_COUNTFLAG (1 << 16)
#if defined(TARGET_pic32ck)
#define PORT_BASE (0x44801000U)
#define LED_NO 25
#elif defined(TARGET_pic32cz)
#define PORT_BASE (0x44840000U)
#define LED_NO 21
#endif
#define PORTB_BASE (PORT_BASE + 0x80 * 1)
#define PORTB_DIRSET (*(volatile uint32_t *)(PORTB_BASE + 0x08))
#define PORTB_DIRSET_OUT(X) (1 << (X))
#define PORTB_OUTTGL (*(volatile uint32_t *)(PORTB_BASE + 0x1C))
#define PORTB_OUTTGL_PIN(X) (1 << (X))
static int systick_init_1ms(uint32_t cpu_freq)
{
/* Calculate the reload value for 1ms period */
uint32_t reload_value = (cpu_freq / 1000) - 1;
/* Check if reload value fits in 24 bits */
if (reload_value > SYSTICK_RVR_MASK) {
return -1;
}
/* Set reload value */
SYSTICK_RVR = reload_value;
/* Clear current value */
SYSTICK_CVR = 0;
/* Configure SysTick: enable counter, no interrupt, use CPU clock */
SYSTICK_CSR = SYSTICK_CSR_ENABLE | SYSTICK_CSR_CLKSOURCE;
return 0;
}
static void systick_delay_ms(uint32_t ms)
{
uint32_t i;
for (i = 0; i < ms; i++) {
/* Wait until COUNTFLAG is set */
while (!(SYSTICK_CSR & SYSTICK_CSR_COUNTFLAG)) {
/* Wait */
}
}
}
/**
* Tests clock configuration using SysTick. Initializes 1ms ticks,
* toggles LED every 1s for 10s, then every 100ms for 2s.
*/
void pic32_clock_test(unsigned long cpu_freq)
{
int i;
PORTB_DIRSET = PORTB_DIRSET_OUT(LED_NO);
/* Initialize SysTick with 1ms period based on target frequency */
if (systick_init_1ms(cpu_freq) != 0) {
wolfBoot_panic();
}
for (i = 0; i < 10; i++) {
systick_delay_ms(1000);
/* Wait for 1 second */
PORTB_OUTTGL = PORTB_OUTTGL_PIN(LED_NO);
}
/* end test by fast toggling */
for (i = 0; i < 20; i++) {
systick_delay_ms(100);
/* Wait for 1 second */
PORTB_OUTTGL = PORTB_OUTTGL_PIN(LED_NO);
}
}
#endif /* TEST_CLOCK */

46
hal/pic32c.h 100644
View File

@ -0,0 +1,46 @@
/* pic32c.h
*
* Copyright (C) 2025 wolfSSL Inc.
*
* This file is part of wolfBoot.
*
* wolfBoot is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfBoot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
#ifndef PIC32C_H
#define PIC32C_H
#include <stdint.h>
#if defined(TEST_FLASH)
void pic32_flash_test(void);
#endif
#if defined(DUALBANK_SWAP)
void pic32_flash_dualbank_swap(void);
#endif
void pic32_fcw_grab(void);
void pic32_fcw_release(void);
int pic32_flash_erase(uint32_t addr, int len);
int pic32_flash_write(uint32_t address, const uint8_t *data, int len);
void pic32_clock_pll0_init(int refdiv, int fbdiv, int bw, int postdiv);
void pic32_clock_fracdiv0_set(int intdiv, int remdiv);
void pic32_clock_gclk_gen0(int mclk_div1, int cpudiv);
void pic32_clock_reset(void);
#if defined(TEST_CLOCK)
void pic32_clock_test(unsigned long cpu_freq);
#endif /* TEST_CLOCK */
#endif /* PIC32C_H */

79
hal/pic32ck.c 100644
View File

@ -0,0 +1,79 @@
/* pic32ck.c
*
* Copyright (C) 2025 wolfSSL Inc.
*
* This file is part of wolfBoot.
*
* wolfBoot is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfBoot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
#include <hal/pic32c.h>
#include <stdint.h>
int hal_flash_write(uint32_t address, const uint8_t *data, int len)
{
return pic32_flash_write(address, data, len);
}
void hal_flash_lock(void)
{
pic32_fcw_grab();
}
void hal_flash_unlock(void)
{
pic32_fcw_release();
}
int hal_flash_erase(uint32_t addr, int len)
{
return pic32_flash_erase(addr, len);
}
#ifdef DUALBANK_SWAP
void hal_flash_dualbank_swap(void)
{
pic32_flash_dualbank_swap();
}
#endif /* DUALBANK_SWAP */
void hal_init(void)
{
#if defined(TEST_CLOCK)
/* toggle led at 1hz for 10sec */
pic32_clock_test(48000000U);
#endif
pic32_clock_fracdiv0_set(0, 0);
pic32_clock_pll0_init(12, 240, 1, 8);
pic32_clock_gclk_gen0(1, 1);
#if defined(TEST_FLASH)
pic32_flash_test();
while (1) {}
#endif
#if defined(TEST_CLOCK)
/* toggle led at 1hz */
pic32_clock_test(120000000U);
pic32_clock_reset();
pic32_clock_test(48000000U);
while (1) {};
#endif
}
void hal_prepare_boot(void)
{
#ifndef TZEN
pic32_clock_reset();
#endif
}

55
hal/pic32ck.ld 100644
View File

@ -0,0 +1,55 @@
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = @BOOTLOADER_PARTITION_SIZE@
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00040000
}
SECTIONS
{
.text :
{
_start_text = .;
KEEP(*(.isr_vector))
*(.text*)
*(.rodata*)
. = ALIGN(4);
_end_text = .;
} > FLASH
.edidx :
{
. = ALIGN(4);
*(.ARM.exidx*)
} > FLASH
_stored_data = .;
.data : AT (_stored_data)
{
_start_data = .;
KEEP(*(.data*))
. = ALIGN(4);
KEEP(*(.ramcode))
. = ALIGN(4);
_end_data = .;
} > RAM
.bss (NOLOAD) :
{
_start_bss = .;
__bss_start__ = .;
*(.bss*)
*(COMMON)
. = ALIGN(4);
_end_bss = .;
__bss_end__ = .;
_end = .;
} > RAM
. = ALIGN(4);
}
_wolfboot_partition_boot_address = @WOLFBOOT_PARTITION_BOOT_ADDRESS@;
_wolfboot_partition_size = @WOLFBOOT_PARTITION_SIZE@;
_wolfboot_partition_update_address = @WOLFBOOT_PARTITION_UPDATE_ADDRESS@;
_wolfboot_partition_swap_address = @WOLFBOOT_PARTITION_SWAP_ADDRESS@;
END_STACK = ORIGIN(RAM) + LENGTH(RAM);

View File

@ -0,0 +1,30 @@
/* pic32ck_registers.h
*
* Copyright (C) 2025 wolfSSL Inc.
*
* This file is part of wolfBoot.
*
* wolfBoot is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfBoot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
#ifndef PIC32CK_REGISTERS_H
#define PIC32CK_REGISTERS_H
#define FCW_BASE (0x44004000U)
#define OSCCTRL_BASE (0x4400C000U)
#define GCLK_BASE (0x44010000U)
#define MCLK_BASE (0x44012000U)
#endif /* PIC32CK_REGISTERS_H */

108
hal/pic32cz.c 100644
View File

@ -0,0 +1,108 @@
/* pic32cz.c
*
* Copyright (C) 2021 wolfSSL Inc.
*
* This file is part of wolfBoot.
*
* wolfBoot is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfBoot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
#include "image.h"
#include <stdint.h>
#include <string.h>
#include <hal/pic32c.h>
#define SUPC_BASE (0x44020000U)
#define SUPC_VREGCTRL (*(volatile uint32_t *)(SUPC_BASE + 0x1CU))
#define SUPC_STATUS (*(volatile uint32_t *)(SUPC_BASE + 0x0CU))
#define SUPC_VREGCTRL_AVREGEN_PLLREG_EN (4)
#define SUPC_VREGCTRL_AVREGEN_SHIFT (16)
#define SUPC_STATUS_ADDVREGRDY_PLL (4)
#define SUPC_STATUS_ADDVREGRDY_SHIFT (8)
static void pic32_supc_vreg_pll_enable(void)
{
uint32_t reg;
SUPC_VREGCTRL |= SUPC_VREGCTRL_AVREGEN_PLLREG_EN
<< SUPC_VREGCTRL_AVREGEN_SHIFT;
/* wait for the vreg to be ready */
while (!(SUPC_STATUS &
(SUPC_STATUS_ADDVREGRDY_PLL << SUPC_STATUS_ADDVREGRDY_SHIFT))) {}
}
#ifdef DUALBANK_SWAP
void hal_flash_dualbank_swap(void)
{
pic32_flash_dualbank_swap();
}
#endif /* DUALBANK_SWAP */
int hal_flash_write(uint32_t address, const uint8_t *data, int len)
{
return pic32_flash_write(address, data, len);
}
void hal_flash_unlock(void)
{
pic32_fcw_grab();
}
void hal_flash_lock(void)
{
pic32_fcw_release();
}
int hal_flash_erase(uint32_t addr, int len)
{
return pic32_flash_erase(addr, len);
}
static void pic32_delay_cnt(uint32_t ticks)
{
uint32_t i = 0;
for (i = 0; i < ticks; i++) {
__asm__("nop");
}
}
void hal_init(void)
{
#if defined(TEST_CLOCK)
pic32_clock_test(48000000);
#endif
pic32_supc_vreg_pll_enable();
pic32_clock_pll0_init(12, 225, 1, 3);
pic32_clock_gclk_gen0(2, 1);
pic32_delay_cnt(700);
#if defined(TEST_FLASH)
pic32_flash_test();
while (1) {}
#endif
#if defined(TEST_CLOCK)
pic32_clock_test(300000000);
pic32_clock_reset();
pic32_clock_test(48000000);
while (1) {};
#endif
}
void hal_prepare_boot(void)
{
pic32_clock_reset();
}

55
hal/pic32cz.ld 100644
View File

@ -0,0 +1,55 @@
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = @BOOTLOADER_PARTITION_SIZE@
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00040000
}
SECTIONS
{
.text :
{
_start_text = .;
KEEP(*(.isr_vector))
*(.text*)
*(.rodata*)
. = ALIGN(4);
_end_text = .;
} > FLASH
.edidx :
{
. = ALIGN(4);
*(.ARM.exidx*)
} > FLASH
_stored_data = .;
.data : AT (_stored_data)
{
_start_data = .;
KEEP(*(.data*))
. = ALIGN(4);
KEEP(*(.ramcode))
. = ALIGN(4);
_end_data = .;
} > RAM
.bss (NOLOAD) :
{
_start_bss = .;
__bss_start__ = .;
*(.bss*)
*(COMMON)
. = ALIGN(4);
_end_bss = .;
__bss_end__ = .;
_end = .;
} > RAM
. = ALIGN(4);
}
_wolfboot_partition_boot_address = @WOLFBOOT_PARTITION_BOOT_ADDRESS@;
_wolfboot_partition_size = @WOLFBOOT_PARTITION_SIZE@;
_wolfboot_partition_update_address = @WOLFBOOT_PARTITION_UPDATE_ADDRESS@;
_wolfboot_partition_swap_address = @WOLFBOOT_PARTITION_SWAP_ADDRESS@;
END_STACK = ORIGIN(RAM) + LENGTH(RAM);

View File

@ -0,0 +1,30 @@
/* pic32cz_registers.h
*
* Copyright (C) 2025 wolfSSL Inc.
*
* This file is part of wolfBoot.
*
* wolfBoot is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfBoot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
#ifndef PIC32CZ_REGISTERS_H
#define PIC32CZ_REGISTERS_H
#define FCW_BASE (0x44002000U)
#define OSCCTRL_BASE (0x44040000U)
#define GCLK_BASE (0x44050000U)
#define MCLK_BASE (0x44052000U)
#endif /* PIC32CZ_REGISTERS_H */

View File

@ -54,29 +54,19 @@ static inline void hal_panic(void)
extern flash_ctrl_t g_flash0_ctrl;
extern flash_cfg_t g_flash0_cfg;
void hal_init(void)
#if defined(WOLFBOOT_RENESAS_SCEPROTECT) && !defined(WOLFBOOT_RENESAS_APP)
static int sipInitDone = 0;
int hal_renesas_init(void)
{
fsp_err_t err;
uint32_t *pubkey;
if (sipInitDone)
return 0;
#if defined(WOLFBOOT_RENESAS_SCEPROTECT) && !defined(WOLFBOOT_RENESAS_APP)
/* retrieve installed pubkey from flash */
uint32_t *pubkey = keystore_get_buffer(0);
#endif
err = R_FLASH_HP_Close(&g_flash0_ctrl);
err = R_FLASH_HP_Open(&g_flash0_ctrl, &g_flash0_cfg);
pubkey = keystore_get_buffer(0);
if(err != FSP_ERR_ALREADY_OPEN && err != FSP_SUCCESS){
printf("ERROR: %d\n", err);
hal_panic();
}
/* Setup Default Block 0 as Startup Setup Block */
err = R_FLASH_HP_StartUpAreaSelect(&g_flash0_ctrl, FLASH_STARTUP_AREA_BLOCK0, true);
if(err != FSP_SUCCESS){
printf("ERROR: %d\n", err);
hal_panic();
}
#if defined(WOLFBOOT_RENESAS_SCEPROTECT) && !defined(WOLFBOOT_RENESAS_APP)
err = wolfCrypt_Init();
if (err != 0) {
printf("ERROR: wolfCrypt_Init %d\n", err);
@ -93,12 +83,39 @@ void hal_init(void)
pkInfo.keyflgs_crypt.bits.rsapub2048_installedkey_set = 1;
pkInfo.keyflgs_crypt.bits.message_type = 1;
err = wc_CryptoCb_CryptInitRenesasCmn(NULL, &pkInfo);
if (err < 0) {
printf("ERROR: wc_CryptoCb_CryptInitRenesasCmn %d\n", err);
hal_panic();
return err;
}
sipInitDone = 1;
return 0;
}
#endif
void hal_init(void)
{
fsp_err_t err;
err = R_FLASH_HP_Close(&g_flash0_ctrl);
err = R_FLASH_HP_Open(&g_flash0_ctrl, &g_flash0_cfg);
if (err != FSP_ERR_ALREADY_OPEN && err != FSP_SUCCESS){
wolfBoot_printf("ERROR: %d\n", err);
hal_panic();
}
/* Setup Default Block 0 as Startup Setup Block */
err = R_FLASH_HP_StartUpAreaSelect(&g_flash0_ctrl, FLASH_STARTUP_AREA_BLOCK0, true);
if (err != FSP_SUCCESS){
wolfBoot_printf("ERROR: %d\n", err);
hal_panic();
}
#if defined(WOLFBOOT_RENESAS_SCEPROTECT) && !defined(WOLFBOOT_RENESAS_APP)
err = hal_renesas_init();
if (err != 0) {
wolfBoot_printf("ERROR: hal_renesas_init %d\n", err);
hal_panic();
}
#endif
}

View File

@ -367,14 +367,92 @@ void hal_clk_init(void)
PROTECT_ON(); /* write protect on */
}
void hal_init(void)
#if defined(WOLFBOOT_RENESAS_TSIP) && !defined(WOLFBOOT_RENESAS_APP)
static int sipInitDone = 0;
int hal_renesas_init(void)
{
#if defined(WOLFBOOT_RENESAS_TSIP) && \
!defined(WOLFBOOT_RENESAS_APP)
int err;
uint32_t key_type = 0;
int tsip_key_type = -1;
/* This structure is generated using Renesas Security Key Management Tool
* See docs/Renesas.md */
struct enc_pub_key *encrypted_user_key_data;
if (sipInitDone)
return 0;
err = wolfCrypt_Init();
if (err != 0) {
return err;
}
/* retrive installed pubkey data from flash */
encrypted_user_key_data = (struct enc_pub_key*)keystore_get_buffer(0);
key_type = keystore_get_key_type(0);
switch (key_type) {
case AUTH_KEY_RSA2048:
tsip_key_type = TSIP_KEY_TYPE_RSA2048;
break;
case AUTH_KEY_RSA3072:
tsip_key_type = TSIP_KEY_TYPE_RSA3072;
break;
case AUTH_KEY_RSA4096:
tsip_key_type = TSIP_KEY_TYPE_RSA4096;
break;
case AUTH_KEY_ECC256:
tsip_key_type = TSIP_KEY_TYPE_ECDSAP256;
break;
case AUTH_KEY_ECC384:
tsip_key_type = TSIP_KEY_TYPE_ECDSAP384;
break;
case AUTH_KEY_ECC521:
case AUTH_KEY_ED25519:
case AUTH_KEY_ED448:
default:
tsip_key_type = -1;
break;
}
if (tsip_key_type == -1) {
wolfBoot_printf("key type (%d) not supported\n", key_type);
return -1;
}
/* Load encrypted UFPK (User Factory Programming Key) */
tsip_inform_user_keys_ex(
(byte*)&encrypted_user_key_data->wufpk,
(byte*)&encrypted_user_key_data->initial_vector,
(byte*)&encrypted_user_key_data->encrypted_user_key,
0/* dummy */
);
/* Load a wrapped public key into TSIP */
if (tsip_use_PublicKey_buffer_crypt(&pkInfo,
(const char*)&encrypted_user_key_data->encrypted_user_key,
sizeof(encrypted_user_key_data->encrypted_user_key),
tsip_key_type) != 0) {
wolfBoot_printf("ERROR tsip_use_PublicKey_buffer\n");
return -1;
}
/* Init Crypt Callback */
pkInfo.sign_hash_type = sha256_mac; /* TSIP does not support SHA2-384/512 */
pkInfo.keyflgs_crypt.bits.message_type = 1;
err = wc_CryptoCb_CryptInitRenesasCmn(NULL, &pkInfo);
if (err < 0) {
wolfBoot_printf("ERROR: wc_CryptoCb_CryptInitRenesasCmn %d\n", err);
return -1;
}
sipInitDone = 1;
return 0;
}
#endif /* TSIP */
void hal_init(void)
{
#if defined(WOLFBOOT_RENESAS_TSIP) && !defined(WOLFBOOT_RENESAS_APP)
int err;
#endif
/* For CCRX, mcu_clock_setup() in resetprg.c will set up clocks. */
@ -393,72 +471,13 @@ void hal_init(void)
hal_flash_init();
#if defined(WOLFBOOT_RENESAS_TSIP) && \
!defined(WOLFBOOT_RENESAS_APP)
err = wolfCrypt_Init();
#if defined(WOLFBOOT_RENESAS_TSIP) && !defined(WOLFBOOT_RENESAS_APP)
err = hal_renesas_init();
if (err != 0) {
wolfBoot_printf("ERROR: wolfCrypt_Init %d\n", err);
hal_panic();
}
/* retrive installed pubkey data from flash */
encrypted_user_key_data = (struct enc_pub_key*)keystore_get_buffer(0);
key_type = keystore_get_key_type(0);
switch (key_type) {
case AUTH_KEY_RSA2048:
tsip_key_type = TSIP_RSA2048;
break;
case AUTH_KEY_RSA3072:
tsip_key_type = TSIP_RSA3072;
break;
case AUTH_KEY_RSA4096:
tsip_key_type = TSIP_RSA4096;
break;
case AUTH_KEY_ECC256:
tsip_key_type = TSIP_ECCP256;
break;
case AUTH_KEY_ECC384:
tsip_key_type = TSIP_ECCP384;
break;
case AUTH_KEY_ECC521:
case AUTH_KEY_ED25519:
case AUTH_KEY_ED448:
default:
tsip_key_type = -1;
break;
}
if (tsip_key_type == -1) {
wolfBoot_printf("key type (%d) not supported\n", key_type);
wolfBoot_printf("ERROR: hal_renesas_init %d\n", err);
hal_panic();
}
/* Load encrypted UFPK (User Factory Programming Key) */
tsip_inform_user_keys_ex(
(byte*)&encrypted_user_key_data->wufpk,
(byte*)&encrypted_user_key_data->initial_vector,
(byte*)&encrypted_user_key_data->encrypted_user_key,
0/* dummy */
);
/* Load a wrapped public key into TSIP */
if (tsip_use_PublicKey_buffer_crypt(&pkInfo,
(const char*)&encrypted_user_key_data->encrypted_user_key,
sizeof(encrypted_user_key_data->encrypted_user_key),
tsip_key_type) != 0) {
wolfBoot_printf("ERROR tsip_use_PublicKey_buffer\n");
hal_panic();
}
/* Init Crypt Callback */
pkInfo.sign_hash_type = sha256_mac; /* TSIP does not support SHA2-384/512 */
pkInfo.keyflgs_crypt.bits.message_type = 1;
err = wc_CryptoCb_CryptInitRenesasCmn(NULL, &pkInfo);
if (err < 0) {
wolfBoot_printf("ERROR: wc_CryptoCb_CryptInitRenesasCmn %d\n", err);
hal_panic();
}
#endif /* TSIP */
#endif
}
void hal_prepare_boot(void)

View File

@ -34,7 +34,7 @@
#include "wolfssl/wolfcrypt/wc_port.h"
#include "wolfssl/wolfcrypt/port/Renesas/renesas-fspsm-crypt.h"
#include "wolfssl/wolfcrypt/port/Renesas/renesas-fspsm-types.h"
FSPSM_ST pkInfo;
uint8_t wrapped_public_key[RSIP_BYTE_SIZE_WRAPPED_KEY_VALUE_RSA_2048_PUBLIC];
rsip_wrapped_key_t *p_wrapped_public_key = (rsip_wrapped_key_t *) wrapped_public_key;
@ -125,20 +125,22 @@ void ext_flash_unlock(void)
#endif
void hal_init(void)
{
#if defined(WOLFBOOT_RENESAS_RSIP) && !defined(WOLFBOOT_RENESAS_APP)
static int sipInitDone = 0;
int hal_renesas_init(void)
{
fsp_err_t err;
int ret;
rsa_public_t rsip_pub_key;
const size_t key_size = sizeof(rsip_pub_key);
err = wolfCrypt_Init();
if (err != 0) {
printf("ERROR: wolfCrypt_Init %d\n", err);
hal_panic();
if (sipInitDone)
return 0;
ret = wolfCrypt_Init();
if (ret != 0) {
wolfBoot_printf("ERROR: wolfCrypt_Init %d\n", ret);
return ret;
}
/* copy the key from ext flash to RAM */
@ -146,8 +148,8 @@ void hal_init(void)
(uint8_t*)RENESAS_RSIP_INSTALLEDKEY_RAM_ADDR, key_size);
if (ret != key_size){
wolfBoot_printf("Error reading public key at %lx\n",
RENESAS_RSIP_INSTALLEDKEY_FLASH_ADDR);
hal_panic();
RENESAS_RSIP_INSTALLEDKEY_FLASH_ADDR);
return -1;
}
/* import enrypted key */
XMEMCPY(&rsip_pub_key, (const void*)RENESAS_RSIP_INSTALLEDKEY_RAM_ADDR, key_size);
@ -166,12 +168,23 @@ void hal_init(void)
pkInfo.keyflgs_crypt.bits.message_type = 1;
pkInfo.hash_type = RSIP_HASH_TYPE_SHA256;
err = wc_CryptoCb_CryptInitRenesasCmn(NULL, &pkInfo);
if (err < 0) {
wolfBoot_printf("ERROR: wc_CryptoCb_CryptInitRenesasCmn %d\n", err);
return err;
}
sipInitDone = 1;
return 0;
}
#endif
void hal_init(void)
{
#if defined(WOLFBOOT_RENESAS_RSIP) && !defined(WOLFBOOT_RENESAS_APP)
int err = hal_renesas_init();
if (err != 0) {
printf("ERROR: hal_renesas_init %d\n", err);
hal_panic();
}
#endif
}

View File

@ -43,7 +43,7 @@
#include "target.h"
#include "printf.h"
#ifdef WOLFBOOT_ELF_SCATTERED
#ifdef WOLFBOOT_ELF_FLASH_SCATTER
#include "elf.h"
#endif
@ -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);
@ -355,7 +358,7 @@ void do_boot(const uint32_t *app_offset)
main = (main_entry)((uint8_t*)pSymbolAddress + epc->entryoff);
main(main_argc, main_argv, NULL, NULL);
#elif defined (WOLFBOOT_ELF_SCATTERED)
#elif defined (WOLFBOOT_ELF_FLASH_SCATTER)
uint8_t *entry_point = (sim_ram_base + (unsigned long)app_offset);
printf("entry point: %p\n", entry_point);
printf("app offset: %p\n", app_offset);
@ -365,7 +368,7 @@ void do_boot(const uint32_t *app_offset)
/* TODO: call main ! */
/* main(main_argc, main_argv); */
wolfBoot_printf("Simulator for ELF_SCATTERED image not implemented yet. Exiting...\n");
wolfBoot_printf("Simulator for ELF_FLASH_SCATTER image not implemented yet. Exiting...\n");
exit(0);
#else
char *envp[1] = {NULL};

View File

@ -168,9 +168,7 @@ typedef struct elf64_program_header {
typedef int (*elf_mmu_map_cb)(uint64_t, uint64_t, uint32_t);
int elf_load_image_mmu(uint8_t *image, uintptr_t *entry, elf_mmu_map_cb mmu_cb);
int elf_load_image(uint8_t *image, uintptr_t *entry, int is_ext);
int elf_store_image_scattered(const unsigned char *image, unsigned long *entry_out, int ext_flash);
int elf_check_image_scattered(uint8_t part, unsigned long *entry_out);
int elf_hdr_size(const unsigned char *ehdr);
int64_t elf_hdr_pht_combined_size(const unsigned char* ehdr);
int elf_open(const unsigned char *ehdr, int *is_elf32);

View File

@ -27,19 +27,19 @@
#if defined(__WOLFBOOT) || defined(UNIT_TEST)
#include <stdint.h>
#include <wolfssl/wolfcrypt/settings.h>
#include <wolfssl/wolfcrypt/sha256.h>
#include "wolfssl/wolfcrypt/settings.h"
#include "wolfssl/wolfcrypt/sha256.h"
#include "target.h"
#include "wolfboot/wolfboot.h"
#ifdef ENCRYPT_WITH_CHACHA
#include <wolfssl/wolfcrypt/chacha.h>
#include "wolfssl/wolfcrypt/chacha.h"
#else
#include <wolfssl/wolfcrypt/aes.h>
#include "wolfssl/wolfcrypt/aes.h"
#endif
#include <wolfssl/wolfcrypt/pwdbased.h>
#include "wolfssl/wolfcrypt/pwdbased.h"
#ifdef ENCRYPT_WITH_CHACHA
@ -65,7 +65,7 @@ int aes_init(void);
void aes_set_iv(uint8_t *nonce, uint32_t address);
#endif /* ENCRYPT_WITH_CHACHA */
/* Internal read/write functions (not exported in the libwolfboot API) */
/* external flash encryption read/write functions */
int ext_flash_encrypt_write(uintptr_t address, const uint8_t *data, int len);
int ext_flash_decrypt_read(uintptr_t address, uint8_t *data, int len);

View File

@ -29,6 +29,7 @@ extern "C" {
#endif
#include "target.h"
#include <stdint.h>
/* Architecture specific calls */
#ifdef MMU
@ -161,6 +162,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

@ -833,11 +833,17 @@ int wolfBoot_open_image_external(struct wolfBoot_image* img, uint8_t part, uint8
int wolfBoot_open_image_address(struct wolfBoot_image* img, uint8_t* image);
int wolfBoot_verify_integrity(struct wolfBoot_image *img);
int wolfBoot_verify_authenticity(struct wolfBoot_image *img);
int wolfBoot_get_partition_state(uint8_t part, uint8_t *st);
int wolfBoot_set_partition_state(uint8_t part, uint8_t newst);
int wolfBoot_get_update_sector_flag(uint16_t sector, uint8_t *flag);
int wolfBoot_set_update_sector_flag(uint16_t sector, uint8_t newflag);
#ifdef WOLFBOOT_ELF_FLASH_SCATTER
/* Support for ELF scatter/gather format */
int wolfBoot_load_flash_image_elf(int part, unsigned long* entry_out,
int ext_flash);
int wolfBoot_check_flash_image_elf(uint8_t part, unsigned long* entry_out);
#endif
uint8_t* wolfBoot_peek_image(struct wolfBoot_image *img, uint32_t offset,
uint32_t* sz);
@ -925,6 +931,9 @@ static inline int wb_flash_write_verify_word(struct wolfBoot_image *img,
#else
# define SWAP_EXT (0)
# define BOOT_EXT (0)
# define UPDATE_EXT (0)
# define PART_IS_EXT(x) (0)
# define PARTN_IS_EXT(x) (0)
# define wb_flash_erase(im, of, siz) \

View File

@ -37,6 +37,8 @@
#define WOLFSSL_USER_MUTEX /* avoid wc_port.c wc_InitAndAllocMutex */
#define WOLFCRYPT_ONLY
#define SIZEOF_LONG_LONG 8
#define HAVE_EMPTY_AGGREGATES 0
#define HAVE_ANONYMOUS_INLINE_AGGREGATES 0
/* Stdlib Types */
#define CTYPE_USER /* don't let wolfCrypt types.h include ctype.h */
@ -111,12 +113,16 @@ extern int tolower(int c);
# define FREESCALE_LTC_TFM
# endif
/* Some ECC options are disabled to reduce size */
# if !defined(WOLFCRYPT_SECURE_MODE)
# if !defined(WOLFBOOT_TPM)
# define NO_ECC_SIGN
# define NO_ECC_DHE
/* For Renesas RX do not enable the misc.c constant time code
* due to issue with 64-bit types */
# if defined(__RX__)
# define WOLFSSL_NO_CT_OPS /* don't use constant time ops in misc.c */
# endif
# if !defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT)
# define NO_ECC_EXPORT
# define NO_ECC_KEY_EXPORT
@ -499,6 +505,9 @@ extern int tolower(int c);
#define WOLF_CRYPTO_CB_ONLY_ECC
#define WOLF_CRYPTO_CB_ONLY_RSA
#define WOLFSSL_NO_SW_MATH
#define MAX_CRYPTO_DEVID_CALLBACKS 2
#define WC_NO_DEFAULT_DEVID
#define WOLFSSL_AES_SMALL_TABLES
#ifdef WOLFBOOT_RENESAS_TSIP
#define WOLFSSL_RENESAS_TSIP
@ -507,6 +516,10 @@ extern int tolower(int c);
#define WOLFSSL_RENESAS_TSIP_CRYPTONLY
#define NO_WOLFSSL_RENESAS_TSIP_CRYPT_HASH
#define RENESAS_TSIP_INSTALLEDKEY_ADDR 0xFFFF0000
#ifndef RENESAS_TSIP_INSTALLEDENCKEY_ADDR
#define RENESAS_TSIP_INSTALLEDENCKEY_ADDR \
(RENESAS_TSIP_INSTALLEDKEY_ADDR + 0x100)
#endif
#define ENCRYPTED_KEY_BYTE_SIZE ENC_PUB_KEY_SIZE
#define RENESAS_DEVID 7890
#endif
@ -533,6 +546,7 @@ extern int tolower(int c);
#ifdef WOLFBOOT_ENABLE_WOLFHSM_CLIENT
# define WOLF_CRYPTO_CB
# undef HAVE_ANONYMOUS_INLINE_AGGREGATES
# define HAVE_ANONYMOUS_INLINE_AGGREGATES 1
# define WOLFSSL_KEY_GEN
#endif /* WOLFBOOT_ENABLE_WOLFHSM_CLIENT */

View File

@ -79,7 +79,7 @@ extern "C" {
#define HDR_SIGNATURE 0x20
#define HDR_POLICY_SIGNATURE 0x21
#define HDR_SECONDARY_SIGNATURE 0x22
#define HDR_ELF_SCATTERED_HASH 0x23
#define HDR_CERT_CHAIN 0x23
#define HDR_PADDING 0xFF
/* Auth Key types */
@ -162,9 +162,23 @@ extern "C" {
#if defined(__WOLFBOOT) || defined(UNIT_TEST_AUTH)
#include "wolfssl/wolfcrypt/settings.h"
#include "wolfssl/wolfcrypt/visibility.h"
#include "wolfssl/wolfcrypt/wc_port.h"
#include "wolfssl/wolfcrypt/types.h"
#ifdef WOLFBOOT_RENESAS_TSIP
/* Include these before any algorithm headers */
#include "mcu/all/r_bsp_common.h"
#include "r_bsp_config.h"
#include "r_tsip_rx_if.h"
#include "wolfssl/wolfcrypt/port/Renesas/renesas_tsip_types.h"
#endif
/* Hashing configuration */
#if defined(WOLFBOOT_HASH_SHA256)
# include "wolfssl/wolfcrypt/sha256.h"
#include "wolfssl/wolfcrypt/sha256.h"
# ifndef WOLFBOOT_SHA_BLOCK_SIZE
# define WOLFBOOT_SHA_BLOCK_SIZE (256)
# endif
@ -179,7 +193,7 @@ extern "C" {
typedef wc_Sha256 wolfBoot_hash_t;
# define HDR_HASH HDR_SHA256
#elif defined(WOLFBOOT_HASH_SHA384)
# include "wolfssl/wolfcrypt/sha512.h"
#include "wolfssl/wolfcrypt/sha512.h"
# ifndef WOLFBOOT_SHA_BLOCK_SIZE
# define WOLFBOOT_SHA_BLOCK_SIZE (256)
# endif
@ -194,9 +208,9 @@ extern "C" {
typedef wc_Sha384 wolfBoot_hash_t;
# define HDR_HASH HDR_SHA384
#elif defined(WOLFBOOT_HASH_SHA3_384)
# include "wolfssl/wolfcrypt/sha3.h"
#include "wolfssl/wolfcrypt/sha3.h"
# ifndef WOLFBOOT_SHA_BLOCK_SIZE
# define WOLFBOOT_SHA_BLOCK_SIZE (128)
# define WOLFBOOT_SHA_BLOCK_SIZE (256)
# endif
# define WOLFBOOT_SHA_HDR HDR_SHA3_384
# define WOLFBOOT_SHA_DIGEST_SIZE (48)
@ -223,7 +237,8 @@ extern "C" {
#endif
#if defined(__WOLFBOOT) || defined (__FLASH_OTP_PRIMER) || defined (UNIT_TEST_AUTH) || defined(WOLFBOOT_TPM)
#if defined(__WOLFBOOT) || defined (__FLASH_OTP_PRIMER) || \
defined (UNIT_TEST_AUTH) || defined(WOLFBOOT_TPM)
/* Authentication configuration */
#if defined(WOLFBOOT_NO_SIGN)
@ -326,6 +341,12 @@ extern "C" {
#define FLASH_WORD_ERASED 0x00000000UL
#endif
#ifdef __WOLFBOOT
/* include after PART_* are defined */
/* for wolfBoot_verify_integrity and wolfBoot_verify_authenticity */
#include "image.h"
#endif
void wolfBoot_update_trigger(void);
void wolfBoot_success(void);
uint32_t wolfBoot_image_size(uint8_t *image);

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

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

View File

@ -549,7 +549,10 @@ ifeq ($(ENCRYPT),1)
endif
ifeq ($(EXT_FLASH),1)
CFLAGS+= -D"EXT_FLASH=1" -D"PART_UPDATE_EXT=1" -D"PART_SWAP_EXT=1"
CFLAGS+= -D"EXT_FLASH=1" -D"PART_UPDATE_EXT=1"
ifeq ($(NO_SWAP_EXT),)
CFLAGS+= -D"PART_SWAP_EXT=1"
endif
ifeq ($(NO_XIP),1)
CFLAGS+=-D"PART_BOOT_EXT=1"
endif
@ -762,9 +765,7 @@ OBJS+=$(SECURE_OBJS)
#
ifeq ($(RAM_CODE),1)
ifeq ($(ENCRYPT),1)
ifneq ($(ENCRYPT_WITH_CHACHA),1)
LSCRIPT_IN=NONE
else
ifeq ($(ENCRYPT_WITH_CHACHA),1)
LSCRIPT_IN=hal/$(TARGET)_chacha_ram.ld
endif
endif
@ -786,8 +787,8 @@ ifeq ($(ELF),1)
ifneq ($(DEBUG_ELF),)
CFLAGS+=-DDEBUG_ELF=$(DEBUG_ELF)
endif
ifeq ($(ELF_SCATTERED),1)
CFLAGS+=-D"WOLFBOOT_ELF_SCATTERED=1"
ifeq ($(ELF_FLASH_SCATTER),1)
CFLAGS+=-D"WOLFBOOT_ELF_FLASH_SCATTER=1"
endif
endif
@ -907,5 +908,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

@ -43,7 +43,7 @@ struct BLOCK_HDR_PACKED block_hdr {
#define BLOCK_HDR_SIZE (sizeof (struct block_hdr))
#if defined(EXT_ENCRYPTED) && defined(__WOLFBOOT)
#include "encrypt.h"
#include "image.h"
#define ext_flash_check_write ext_flash_encrypt_write
#define ext_flash_check_read ext_flash_decrypt_read
#elif defined(__WOLFBOOT)

170
src/elf.c
View File

@ -33,10 +33,6 @@
#include "hal/nxp_ppc.h"
#endif
#ifdef WOLFBOOT_ELF_SCATTERED
#include "image.h"
#endif
/* support for elf parsing debug printf */
#if defined(DEBUG) || defined(ELF_PARSER)
#if defined(DEBUG_ELF) && DEBUG_ELF == 0
@ -48,6 +44,12 @@
#endif
#ifdef WOLFBOOT_ELF_FLASH_SCATTER
static int check_scatter_format(const unsigned char* ehdr, int is_elf32);
#endif
#if defined(MMU) || defined (WOLFBOOT_FSP) || defined (ARCH_PPC)
/* Loader for elf32 or elf64 format program headers
* Returns the entry point function
@ -160,134 +162,66 @@ int elf_open(const unsigned char *ehdr, int *is_elf32)
}
wolfBoot_printf("ELF image found\n");
*is_elf32 = !!(ident[ELF_CLASS_OFF] == ELF_CLASS_32);
return 0;
#ifdef WOLFBOOT_ELF_FLASH_SCATTER
return check_scatter_format(ehdr, *is_elf32);
#else
return 0;
#endif
}
int elf_hdr_size(const unsigned char *ehdr)
#ifdef WOLFBOOT_ELF_FLASH_SCATTER
/* Opens an elf file, also checking that the file is formatted correctly for
* scattered loading. Returns 0 if the elf file is formatted correctly, -1
* otherwise. */
static int check_scatter_format(const unsigned char* ehdr, int is_elf32)
{
/* Check that the program header table immediately follows the elf header */
if (is_elf32) {
const elf32_header* elf32_hdr = (const elf32_header*)ehdr;
/* For 32-bit ELF, program header table should start at offset equal to
* sizeof(elf32_header) */
if (elf32_hdr->ph_offset != sizeof(elf32_header)) {
wolfBoot_printf("ELF32: Program header table not immediately after "
"ELF header\n");
return -1;
}
}
else {
const elf64_header* elf64_hdr = (const elf64_header*)ehdr;
/* For 64-bit ELF, program header table should start at offset equal to
* sizeof(elf64_header) */
if (elf64_hdr->ph_offset != sizeof(elf64_header)) {
wolfBoot_printf("ELF64: Program header table not immediately after "
"ELF header\n");
return -1;
}
}
return 0;
}
/* Returns the combined size of the elf header and program header table. This
* assumes the program header table immediately follows the elf header. */
int64_t elf_hdr_pht_combined_size(const unsigned char* ehdr)
{
int sz = 0;
int is_elf32;
if (elf_open(ehdr, &is_elf32) != 0)
return -1;
if (is_elf32) {
const elf32_header *elf32_hdr = (const elf32_header *)ehdr;
sz = sizeof(elf32_header);
const elf32_header* elf32_hdr = (const elf32_header*)ehdr;
sz = sizeof(elf32_header);
sz += elf32_hdr->ph_entry_count * sizeof(elf32_program_header);
} else {
const elf64_header *elf64_hdr = (const elf64_header *)ehdr;
sz = sizeof(elf64_header);
}
else {
const elf64_header* elf64_hdr = (const elf64_header*)ehdr;
sz = sizeof(elf64_header);
sz += elf64_hdr->ph_entry_count * sizeof(elf64_program_header);
}
return sz;
}
#if !defined(MMU) && !defined(WOLFBOOT_FSP) && !defined(ARCH_PPC) && defined (WOLFBOOT_ELF_SCATTERED)
#endif /* WOLFBOOT_ELF_FLASH_SCATTER */
#ifdef ARCH_SIM
# define BASE_OFF ARCH_FLASH_OFFSET
#else
# define BASE_OFF 0
#endif
int elf_store_image_scattered(const unsigned char *hdr, unsigned long *entry_out, int ext_flash) {
const unsigned char *image;
int is_elf32;
unsigned short entry_count;
unsigned long entry_off;
int i;
image = hdr + IMAGE_HEADER_SIZE;
if (elf_open(image, &is_elf32) != 0) {
return -1;
}
if (is_elf32) {
const elf32_header *eh;
const elf32_program_header *ph;
wolfBoot_printf("ELF image is 32 bit\n");
eh = (const elf32_header *)image;
entry_count = eh->ph_entry_count;
entry_off = eh->ph_offset;
*entry_out = (unsigned long)eh->entry;
ph = (const elf32_program_header *)(image + entry_off);
for (i = 0; i < entry_count; ++i) {
unsigned long paddr;
unsigned long filesz;
unsigned long offset;
if (ph[i].type != ELF_PT_LOAD)
continue;
paddr = (unsigned long)ph[i].paddr;
offset = (unsigned long)ph[i].offset;
filesz = (unsigned long)ph[i].file_size;
wolfBoot_printf("Writing section at address %lx offset %lx\n", paddr, offset);
#ifdef EXT_FLASH
if (ext_flash) {
ext_flash_unlock();
ext_flash_erase(paddr + BASE_OFF, filesz);
ext_flash_write(paddr + BASE_OFF, image + offset, filesz);
ext_flash_lock();
}
else
#endif
{
hal_flash_unlock();
hal_flash_erase(paddr + BASE_OFF, filesz);
hal_flash_write(paddr + BASE_OFF, image + offset, filesz);
hal_flash_lock();
}
}
} else { /* 64 bit ELF */
const elf64_header *eh;
const elf64_program_header *ph;
wolfBoot_printf("ELF image is 64 bit\n");
eh = (const elf64_header *)image;
entry_count = eh->ph_entry_count;
entry_off = eh->ph_offset;
*entry_out = (unsigned long)eh->entry;
ph = (const elf64_program_header *)(image + entry_off);
for (i = 0; i < entry_count; ++i) {
unsigned long paddr;
unsigned long filesz;
unsigned long offset;
if (ph[i].type != ELF_PT_LOAD)
continue;
paddr = (unsigned long)ph[i].paddr;
offset = (unsigned long)ph[i].offset;
filesz = (unsigned long)ph[i].file_size;
wolfBoot_printf("Writing section at address %lx offset %lx\n", paddr, offset);
#ifdef EXT_FLASH
if (ext_flash) {
ext_flash_unlock();
ext_flash_erase(paddr + BASE_OFF, filesz);
ext_flash_write(paddr + BASE_OFF, image + offset, filesz);
ext_flash_lock();
}
else
#endif
{
hal_flash_unlock();
hal_flash_erase(paddr + BASE_OFF, filesz);
hal_flash_write(paddr + BASE_OFF, image + offset, filesz);
hal_flash_lock();
}
}
}
return 0;
}
#endif /* !defined(MMU) && !defined(WOLFBOOT_FSP) && !defined(ARCH_PPC) && defined (WOLFBOOT_ELF_SCATTERED) */
int elf_load_image(uint8_t *image, uintptr_t *entry, int ext_flash)
{
#ifdef MMU
return elf_load_image_mmu(image, entry, NULL);
#else
return elf_store_image_scattered(image, (unsigned long *)entry, ext_flash);
#endif
}
#endif /* WOLFBOOT_ELF */

View File

@ -53,7 +53,13 @@
#endif
/* Globals */
static uint8_t digest[WOLFBOOT_SHA_DIGEST_SIZE];
static uint8_t digest[WOLFBOOT_SHA_DIGEST_SIZE] XALIGNED(4);
#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)
@ -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);
}
@ -748,7 +813,7 @@ uint16_t wolfBoot_get_header(struct wolfBoot_image *img, uint16_t type,
}
#ifdef EXT_FLASH
static uint8_t ext_hash_block[WOLFBOOT_SHA_BLOCK_SIZE];
static uint8_t ext_hash_block[WOLFBOOT_SHA_BLOCK_SIZE] XALIGNED(4);
#endif
/**
* @brief Get a block of data to be hashed.
@ -772,7 +837,7 @@ static uint8_t *get_sha_block(struct wolfBoot_image *img, uint32_t offset)
}
#ifdef EXT_FLASH
static uint8_t hdr_cpy[IMAGE_HEADER_SIZE];
static uint8_t hdr_cpy[IMAGE_HEADER_SIZE] XALIGNED(4);
static int hdr_cpy_done = 0;
/**
@ -1327,283 +1392,548 @@ int wolfBoot_verify_integrity(struct wolfBoot_image *img)
return 0;
}
#ifdef WOLFBOOT_ELF_SCATTERED
#ifdef WOLFBOOT_ELF_FLASH_SCATTER
#include "elf.h"
#define PADDING_BLOCK_SIZE 64
#ifdef ARCH_SIM
#define BASE_OFF ARCH_FLASH_OFFSET
#else
#define BASE_OFF 0
#endif
int elf_check_image_scattered(uint8_t part, unsigned long *entry_out)
/* Maximum size of ELF header for any architecture */
typedef union {
elf32_header elf32;
elf64_header elf64;
} elfHeaderMaxBuf;
/*
* Copies an arbitrary amount of data between two flash memory locations
* (internal or external) using an intermediate RAM buffer.
*/
static int copy_flash_buffered(uintptr_t src_addr, uintptr_t dst_addr,
size_t total_size, int is_src_ext,
int is_dst_ext)
{
size_t bytes_copied = 0;
#ifndef BUFFER_DECLARED
#define BUFFER_DECLARED
static uint8_t buffer[FLASHBUFFER_SIZE] XALIGNED(4);
#endif
#ifdef WOLFBOOT_FLASH_MULTI_SECTOR_ERASE
/* Mass erase destination flash in one go before writing */
#ifdef EXT_FLASH
if (is_dst_ext) {
ext_flash_unlock();
ext_flash_erase(dst_addr, total_size);
ext_flash_lock();
}
else
#endif
{
hal_flash_unlock();
hal_flash_erase(dst_addr, total_size);
hal_flash_lock();
}
#endif /* WOLFBOOT_FLASH_MULTI_SECTOR_ERASE */
/* Loop until all requested bytes are copied */
while (bytes_copied < total_size) {
/* Determine the size of the next chunk to copy */
size_t remaining_bytes = total_size - bytes_copied;
size_t chunk_size = (remaining_bytes > FLASHBUFFER_SIZE)
? FLASHBUFFER_SIZE
: remaining_bytes;
/* Read a chunk from the source flash into the RAM buffer */
#ifdef EXT_FLASH
if (is_src_ext) {
ext_flash_unlock();
ext_flash_read(src_addr + bytes_copied, buffer, chunk_size);
ext_flash_lock();
}
else
#endif
{
memcpy(buffer, (const void*)(src_addr + bytes_copied), chunk_size);
}
/* Write the chunk from the RAM buffer to the destination flash */
#ifdef EXT_FLASH
if (is_dst_ext) {
ext_flash_unlock();
#ifndef WOLFBOOT_FLASH_MULTI_SECTOR_ERASE
ext_flash_erase(dst_addr + bytes_copied, chunk_size);
#endif
ext_flash_write(dst_addr + bytes_copied, buffer, chunk_size);
ext_flash_lock();
}
else
#endif
{
hal_flash_unlock();
#ifndef WOLFBOOT_FLASH_MULTI_SECTOR_ERASE
hal_flash_erase(dst_addr + bytes_copied, chunk_size);
#endif
hal_flash_write(dst_addr + bytes_copied, buffer, chunk_size);
hal_flash_lock();
}
/* Update the count of bytes successfully copied */
bytes_copied += chunk_size;
}
/* All bytes copied successfully */
return 0;
}
/*
* Reads data from a given wolfBoot partition's firmware image, properly
* handling internal/external flash.
*/
static int read_flash_fwimage(struct wolfBoot_image* img, uint32_t offset,
void* buffer, uint32_t size)
{
if (img == NULL || buffer == NULL) {
return -1;
}
/* Prevent reading past the end of the image */
if ((uint64_t)offset + size > img->fw_size) {
wolfBoot_printf(
"ERROR: read_flash_fwimage attempt to read past fw_size! "
"Offset %lu, Size %u, TotalSize %lu\n",
(unsigned long)offset, size, (unsigned long)img->fw_size);
return -1;
}
#ifdef EXT_FLASH
if (PART_IS_EXT(img)) {
if (ext_flash_check_read((uintptr_t)img->fw_base + offset, buffer,
size) < 0) {
wolfBoot_printf(
"ERROR: ext_flash_check_read failed at offset %lu, size %u\n",
(unsigned long)offset, size);
return -1;
}
}
else
#endif
{
/* Internal flash: Direct memory access */
memcpy(buffer, (uint8_t*)img->fw_base + offset, size);
}
return 0;
}
/*
* Reads data from a raw flash address (no offset) into a RAM buffer,
* properly handling internal/external flash.
*/
static int read_flash_addr(void* src, void* buffer, uint32_t size, int src_ext)
{
if (src == NULL || buffer == NULL) {
return -1;
}
#ifdef EXT_FLASH
if (src_ext) {
if (ext_flash_check_read((uintptr_t)src, buffer, size) < 0) {
wolfBoot_printf(
"ERROR: ext_flash_check_read failed at address %p, size %u\n",
src, size);
return -1;
}
}
else
#endif
{
/* Internal flash: Direct memory access */
memcpy(buffer, src, size);
}
return 0;
}
/*
* Hashes a chunk of the firmware image one SHA block at a time, properly
* handling internal/external flash
*/
static int update_hash_flash_fwimg(wolfBoot_hash_t* ctx,
struct wolfBoot_image* img, uint32_t offset,
uint32_t size)
{
uint32_t current_offset = offset;
uint32_t remaining_size = size;
uint8_t read_buf[WOLFBOOT_SHA_BLOCK_SIZE] XALIGNED_STACK(4); /* Use local buffer */
while (remaining_size > 0) {
uint32_t read_size = (remaining_size > WOLFBOOT_SHA_BLOCK_SIZE)
? WOLFBOOT_SHA_BLOCK_SIZE
: remaining_size;
if (read_flash_fwimage(img, current_offset, read_buf, read_size) != 0) {
wolfBoot_printf("ERROR: Failed to read image data for hashing. "
"Offset: %lu, Size: %u\n",
(unsigned long)current_offset, read_size);
return -1;
}
update_hash(ctx, read_buf, read_size);
remaining_size -= read_size;
current_offset += read_size;
}
return 0;
}
/*
* Hashes a chunk of flash memory at a given absolute address, reading one
* SHA block at a time, properly handling internal/external flash
*/
static int update_hash_flash_addr(wolfBoot_hash_t* ctx, uintptr_t addr,
uint32_t size, int src_ext)
{
uint8_t buffer[WOLFBOOT_SHA_BLOCK_SIZE] XALIGNED_STACK(4);
uint32_t remaining_size = size;
uintptr_t current_addr = addr;
while (remaining_size > 0) {
uint32_t read_size = (remaining_size > WOLFBOOT_SHA_BLOCK_SIZE)
? WOLFBOOT_SHA_BLOCK_SIZE
: remaining_size;
if (read_flash_addr((void*)current_addr, buffer, read_size, src_ext) !=
0) {
wolfBoot_printf(
"ERROR: Failed to read data from address %p, size %u\n",
(void*)current_addr, read_size);
return -1;
}
update_hash(ctx, buffer, read_size);
remaining_size -= read_size;
current_addr += read_size;
}
return 0;
}
int wolfBoot_check_flash_image_elf(uint8_t part, unsigned long* entry_out)
{
/* Open the partition containing the image */
int is_elf32;
struct wolfBoot_image boot;
uint8_t *elf_h, *p;
int elf_hdr_sz = 0;
int len;
int is_elf32;
unsigned short entry_count;
unsigned short entry_size;
unsigned long entry_off;
long final_offset = -1;
uint8_t calc_digest[WOLFBOOT_SHA_DIGEST_SIZE];
uint8_t *exp_digest;
int stored_sha_len;
int i;
uint8_t padding_block[PADDING_BLOCK_SIZE];
int entry_out_set = 0;
uint8_t * elf_h;
size_t elf_hdr_sz = 0;
uint32_t len;
uint16_t entry_count = 0;
size_t entry_off = 0;
size_t ph_size = 0;
size_t current_ph_offset = 0;
int64_t final_offset = -1;
uint8_t calc_digest[WOLFBOOT_SHA_DIGEST_SIZE] XALIGNED_STACK(4);
uint8_t* exp_digest;
int32_t stored_sha_len;
int i;
int32_t entry_out_set = 0;
uint8_t elfHdrBuf[sizeof(elfHeaderMaxBuf)];
uint8_t ph_buf[sizeof(elf64_program_header)]; /* Buffer for current PH */
uint8_t ph_next_buf[sizeof(elf64_program_header)]; /* Buffer for next PH */
wolfBoot_hash_t ctx;
if (wolfBoot_open_image(&boot, part) < 0)
if (wolfBoot_open_image(&boot, part) < 0) {
return -1;
p = get_img_hdr(&boot);
}
/* Initialize hash, feed the manifest header to it */
if (header_hash(&ctx, &boot) < 0)
if (header_hash(&ctx, &boot) < 0) {
return -1;
}
stored_sha_len = get_header(&boot, HDR_HASH, &exp_digest);
if (stored_sha_len != WOLFBOOT_SHA_DIGEST_SIZE)
if (stored_sha_len != WOLFBOOT_SHA_DIGEST_SIZE) {
return -1;
/* Get the elf header size */
elf_h = p + IMAGE_HEADER_SIZE;
if (elf_open(elf_h, &is_elf32) < 0)
return -1;
elf_hdr_sz = elf_hdr_size(elf_h);
wolfBoot_printf("Elf header size: %d\n", elf_hdr_sz);
memset(padding_block, 0, PADDING_BLOCK_SIZE);
/* Feed the elf header to the hash function */
len = elf_hdr_sz;
p = elf_h;
while (len > 0) {
if (len > WOLFBOOT_SHA_BLOCK_SIZE) {
update_hash(&ctx, p, WOLFBOOT_SHA_BLOCK_SIZE);
len -= WOLFBOOT_SHA_BLOCK_SIZE;
} else {
update_hash(&ctx, p, len);
break;
}
p += WOLFBOOT_SHA_BLOCK_SIZE;
}
wolfBoot_printf("Hashed ELF header.\n");
/* Feed the program headers to the hash function */
/* Get the elf header from the image into a local buffer. We may overread
* the buffer depending on architecture */
memset(elfHdrBuf, 0, sizeof(elfHdrBuf));
read_flash_fwimage(&boot, 0, elfHdrBuf, sizeof(elfHeaderMaxBuf));
elf_h = elfHdrBuf;
if (elf_open(elf_h, &is_elf32) < 0) {
return -1;
}
/* Set up common variables based on ELF type */
if (is_elf32) {
elf32_header *eh = (elf32_header *)elf_h;
elf32_program_header *ph;
entry_count = eh->ph_entry_count;
entry_size = eh->ph_entry_size;
entry_off = eh->ph_offset;
elf32_header* eh = (elf32_header*)elf_h;
entry_count = eh->ph_entry_count;
entry_off = eh->ph_offset;
ph_size = sizeof(elf32_program_header);
if (!entry_out_set) {
*entry_out = eh->entry;
*entry_out = eh->entry;
entry_out_set = 1;
}
wolfBoot_printf("EH entry offset: %d\n", (int)entry_off);
ph = (elf32_program_header *)(elf_h + entry_off);
/* Add padding until the first program header into hash function */
len = ph[0].offset - elf_hdr_sz;
wolfBoot_printf("Adding %d bytes padding\n", (int)len);
while (len > 0) {
if (len > PADDING_BLOCK_SIZE) {
update_hash(&ctx, padding_block, PADDING_BLOCK_SIZE);
len -= PADDING_BLOCK_SIZE;
} else {
update_hash(&ctx, padding_block, len);
break;
}
}
for (i = 0; i < entry_count; i++) {
unsigned long paddr;
unsigned long filesz;
unsigned long offset;
paddr = (unsigned long)ph[i].paddr;
offset = (unsigned long)ph[i].offset;
filesz = (unsigned long)ph[i].file_size;
wolfBoot_printf("Paddr: 0x%lx offset: %lu, size: %lu\n", paddr,
offset, filesz);
/* Feed any non-loaded parts to the hash function */
if (ph[i].type != ELF_PT_LOAD) {
len = filesz;
//wolfBoot_printf("Feeding ghost segment, len %d\n", len);
continue;
while (len > 0) {
if (len > WOLFBOOT_SHA_BLOCK_SIZE) {
update_hash(&ctx, elf_h + offset, WOLFBOOT_SHA_BLOCK_SIZE);
len -= WOLFBOOT_SHA_BLOCK_SIZE;
paddr += WOLFBOOT_SHA_BLOCK_SIZE;
} else {
update_hash(&ctx, elf_h + offset, len);
break;
}
}
} else {
/* Feed the loaded parts to the hash function */
len = filesz;
wolfBoot_printf("Feeding stored segment, len %d\n", len);
while (len > 0) {
if (len > WOLFBOOT_SHA_BLOCK_SIZE) {
update_hash(&ctx, (void *)(paddr + BASE_OFF),
WOLFBOOT_SHA_BLOCK_SIZE);
len -= WOLFBOOT_SHA_BLOCK_SIZE;
paddr += WOLFBOOT_SHA_BLOCK_SIZE;
} else {
update_hash(&ctx, (void *)(paddr + BASE_OFF),
len);
break;
}
}
}
/* Add padding until next program header, if any. */
if ((i < entry_count - 1) && (ph[i+1].offset > (offset + filesz))) {
unsigned long padding = ph[i+1].offset - (offset + filesz);
wolfBoot_printf("Adding padding: %lu (from %lx to %lx)\n", padding, (unsigned long)offset + filesz, (unsigned long)ph[i+1].offset);
while (padding > 0) {
if (padding > PADDING_BLOCK_SIZE) {
update_hash(&ctx, padding_block, PADDING_BLOCK_SIZE);
padding -= PADDING_BLOCK_SIZE;
} else {
update_hash(&ctx, padding_block, padding);
break;
}
}
} else {
final_offset = offset + filesz;
}
}
} else { /* 64-bit ELF */
elf64_header *eh = (elf64_header *)elf_h;
elf64_program_header *ph;
entry_count = eh->ph_entry_count;
entry_size = eh->ph_entry_size;
entry_off = eh->ph_offset;
wolfBoot_printf("ELF: [CHECK] 32-bit, entry=0x%08X, "
"ph_offset=0x%lX, ph_count=%u\n",
eh->entry, (unsigned long)entry_off, entry_count);
}
else { /* 64-bit ELF */
elf64_header* eh = (elf64_header*)elf_h;
entry_count = eh->ph_entry_count;
entry_off = eh->ph_offset;
ph_size = sizeof(elf64_program_header);
if (!entry_out_set) {
*entry_out = eh->entry;
*entry_out = eh->entry;
entry_out_set = 1;
}
wolfBoot_printf("ELF: [CHECK] 64-bit, entry=0x%08lx, "
"ph_offset=0x%08lx, ph_count=%d\n",
eh->entry, (unsigned long)entry_off, entry_count);
}
wolfBoot_printf("EH entry offset: %d\n", (int)entry_off);
ph = (elf64_program_header *)(elf_h + entry_off);
/* Add padding until the first program header into hash function */
len = ph[0].offset - elf_hdr_sz;
wolfBoot_printf("Adding %d bytes padding\n", len);
while (len > 0) {
if (len > PADDING_BLOCK_SIZE) {
update_hash(&ctx, padding_block, PADDING_BLOCK_SIZE);
len -= PADDING_BLOCK_SIZE;
} else {
update_hash(&ctx, padding_block, len);
break;
}
elf_hdr_sz = (size_t)elf_hdr_pht_combined_size(elf_h);
wolfBoot_printf("ELF: [CHECK] Header size: %zu bytes\n", elf_hdr_sz);
/* Hash the elf header and program header in the image, assuming the PHT
* immediately follows the ELF header */
update_hash_flash_fwimg(&ctx, &boot, 0, elf_hdr_sz);
current_ph_offset = entry_off;
/* Calculate padding between ELF+PHT header and first segment */
if (entry_count > 0) {
uint64_t first_offset;
read_flash_fwimage(&boot, current_ph_offset, ph_buf, ph_size);
if (is_elf32) {
first_offset = ((elf32_program_header*)ph_buf)->offset;
}
else {
first_offset = ((elf64_program_header*)ph_buf)->offset;
}
for (i = 0; i < entry_count; i++) {
unsigned long paddr;
unsigned long filesz;
unsigned long offset;
paddr = (unsigned long)ph[i].paddr;
offset = (unsigned long)ph[i].offset;
filesz = (unsigned long)ph[i].file_size;
wolfBoot_printf("Paddr: 0x%lx offset: %lu, size: %lu\n", paddr,
offset, filesz);
/* Feed any non-loaded parts to the hash function */
if (ph[i].type != ELF_PT_LOAD) {
len = filesz;
//wolfBoot_printf("Feeding ghost segment, len %d\n", len);
continue;
while (len > 0) {
if (len > WOLFBOOT_SHA_BLOCK_SIZE) {
update_hash(&ctx, elf_h + offset, WOLFBOOT_SHA_BLOCK_SIZE);
len -= WOLFBOOT_SHA_BLOCK_SIZE;
paddr += WOLFBOOT_SHA_BLOCK_SIZE;
} else {
update_hash(&ctx, elf_h + offset, len);
break;
}
}
} else {
/* Feed the loaded parts to the hash function */
len = filesz;
wolfBoot_printf("Feeding stored segment, len %d\n", len);
while (len > 0) {
if (len > WOLFBOOT_SHA_BLOCK_SIZE) {
update_hash(&ctx, (void *)(paddr + BASE_OFF),
WOLFBOOT_SHA_BLOCK_SIZE);
len -= WOLFBOOT_SHA_BLOCK_SIZE;
paddr += WOLFBOOT_SHA_BLOCK_SIZE;
} else {
update_hash(&ctx, (void *)(paddr + BASE_OFF),
len);
break;
}
}
}
/* Add padding until next program header, if any. */
if ((i < entry_count - 1) && (ph[i+1].offset > (offset + filesz))) {
unsigned long padding = ph[i+1].offset - (offset + filesz);
wolfBoot_printf("Adding padding: %lu\n", padding);
while (padding > 0) {
if (padding > PADDING_BLOCK_SIZE) {
update_hash(&ctx, padding_block, PADDING_BLOCK_SIZE);
padding -= PADDING_BLOCK_SIZE;
} else {
update_hash(&ctx, padding_block, padding);
break;
}
}
} else {
final_offset = offset + filesz;
}
if (first_offset > elf_hdr_sz) {
len = first_offset - elf_hdr_sz;
wolfBoot_printf(
"ELF: [CHECK] Adding %d bytes padding before first segment\n",
(int32_t)len);
update_hash_flash_fwimg(&ctx, &boot, elf_hdr_sz, len); /* Hash actual file content */
}
}
if (final_offset < 0)
return -1;
if (final_offset + IMAGE_HEADER_SIZE > (long)boot.fw_size)
return -1;
/* Walk the program header table and hash each loadable segment. */
for (i = 0; i < entry_count; i++) {
uint64_t paddr;
uint64_t filesz;
uint64_t offset;
uint32_t type;
uint64_t next_offset = 0; /* Initialize */
/* read the current program header into a local buffer */
read_flash_fwimage(&boot, current_ph_offset, ph_buf, ph_size);
/* Extract common fields based on ELF type */
if (is_elf32) {
elf32_program_header* ph = (elf32_program_header*)ph_buf;
paddr = ph->paddr;
offset = ph->offset;
filesz = ph->file_size;
type = ph->type;
}
else { /* 64-bit */
elf64_program_header* ph = (elf64_program_header*)ph_buf;
paddr = ph->paddr;
offset = ph->offset;
filesz = ph->file_size;
type = ph->type;
}
/* Handle loadable segments */
if (type == ELF_PT_LOAD) {
uintptr_t load_addr = (uintptr_t)(paddr + BASE_OFF);
/* Feed the loadable parts to the hash function */
wolfBoot_printf("ELF: [CHECK] Hashing loadable segment: "
"paddr = 0x%08lx, loadaddr = 0x%08lx, "
"offset = 0x%08lx, size = %lu\n",
(unsigned long)paddr, (unsigned long)load_addr,
(unsigned long)offset, (unsigned long)filesz);
update_hash_flash_addr(&ctx, load_addr, (uint32_t)filesz,
PART_IS_EXT(&boot));
}
else {
wolfBoot_printf("ELF: [CHECK] ERROR: non-loadable segment\n");
return -1;
}
/* Add padding until next program header, if any. */
if (i < entry_count - 1) {
read_flash_fwimage(&boot, current_ph_offset + ph_size, ph_next_buf,
ph_size);
if (is_elf32) {
next_offset = ((elf32_program_header*)ph_next_buf)->offset;
}
else {
next_offset = ((elf64_program_header*)ph_next_buf)->offset;
}
if (next_offset > (offset + filesz)) {
uint32_t padding = next_offset - (offset + filesz);
wolfBoot_printf("ELF: [CHECK] Adding padding: %u bytes (from "
"0x%08lx to 0x%08lx)\n",
padding, (unsigned long)(offset + filesz),
(unsigned long)next_offset);
update_hash_flash_fwimg(&ctx, &boot, offset + filesz, padding); /* Hash actual file content */
}
}
final_offset =
offset + filesz; /* Track end offset of last processed segment */
current_ph_offset += ph_size;
} /* End of program header loop */
if (final_offset < 0 && entry_count > 0) {
/* Should have processed at least one segment if entry_count > 0 */
wolfBoot_printf("ELF: [CHECK] Error determining final offset\n");
return -1;
}
else if (final_offset < 0 && entry_count == 0) {
/* No program headers, hash only ELF header + PHT */
final_offset = elf_hdr_sz;
}
/* Check if final offset is valid */
if (final_offset > (int64_t)boot.fw_size) {
wolfBoot_printf("ELF: [CHECK] Final offset (%d) exceeds image size (%d)\n",
(int32_t)final_offset, (int32_t)boot.fw_size);
return -1;
}
/* Hash any trailing data after the last segment/header */
len = boot.fw_size - final_offset;
p = boot.hdr + IMAGE_HEADER_SIZE + final_offset;
p = get_img_hdr(&boot) + IMAGE_HEADER_SIZE + final_offset;
wolfBoot_printf("Appending %d bytes of data from image, from position %lu...(0x%p)\n", len, IMAGE_HEADER_SIZE + final_offset, p);
while (len > 0) {
if (len > WOLFBOOT_SHA_BLOCK_SIZE) {
update_hash(&ctx, p, WOLFBOOT_SHA_BLOCK_SIZE);
len -= WOLFBOOT_SHA_BLOCK_SIZE;
p += WOLFBOOT_SHA_BLOCK_SIZE;
} else {
update_hash(&ctx, p, len);
break;
}
if (len > 0) {
wolfBoot_printf("ELF: [CHECK] Hashing %u bytes of trailing data from "
"offset 0x%llX\n",
len, (unsigned long long)final_offset);
update_hash_flash_fwimg(&ctx, &boot, final_offset, len);
}
/* Finalize SHA calculation */
final_hash(&ctx, calc_digest);
if (memcmp(calc_digest, exp_digest, WOLFBOOT_SHA_DIGEST_SIZE) != 0) {
wolfBoot_printf("SHA failed for scattered ELF!\n");
wolfBoot_printf("Expected %02x%02x%02x%02x%02x%02x%02x%02x\n",
exp_digest[0], exp_digest[1], exp_digest[2], exp_digest[3],
exp_digest[4], exp_digest[5], exp_digest[6], exp_digest[7]);
wolfBoot_printf("Calculated %02x%02x%02x%02x%02x%02x%02x%02x\n",
calc_digest[0], calc_digest[1], calc_digest[2], calc_digest[3],
calc_digest[4], calc_digest[5], calc_digest[6], calc_digest[7]);
wolfBoot_printf("ELF: [CHECK] SHA verification FAILED\n");
wolfBoot_printf(
"ELF: [CHECK] Expected %02x%02x%02x%02x%02x%02x%02x%02x\n",
exp_digest[0], exp_digest[1], exp_digest[2], exp_digest[3],
exp_digest[4], exp_digest[5], exp_digest[6], exp_digest[7]);
wolfBoot_printf(
"ELF: [CHECK] Calculated %02x%02x%02x%02x%02x%02x%02x%02x\n",
calc_digest[0], calc_digest[1], calc_digest[2], calc_digest[3],
calc_digest[4], calc_digest[5], calc_digest[6], calc_digest[7]);
return -2;
}
wolfBoot_printf("Scattered ELF verified.\n");
wolfBoot_printf("ELF: [CHECK] Verification successful\n");
return 0;
}
int wolfBoot_load_flash_image_elf(int part, unsigned long* entry_out, int ext_flash)
{
const unsigned char* image;
int is_elf32;
uint16_t entry_count;
size_t entry_off;
size_t ph_size;
int i;
const void* eh;
struct wolfBoot_image boot;
uint8_t elfHdrBuf[sizeof(elfHeaderMaxBuf)];
if (wolfBoot_open_image(&boot, part) < 0) {
return -1;
}
image = boot.fw_base;
/* Get the elf header from the image into a local buffer. We may overread
* the buffer depending on architecture */
memset(elfHdrBuf, 0, sizeof(elfHdrBuf));
read_flash_fwimage(&boot, 0, elfHdrBuf, sizeof(elfHeaderMaxBuf));
if (elf_open(elfHdrBuf, &is_elf32) != 0) {
return -1;
}
/* Set up header pointers based on ELF type */
if (is_elf32) {
eh = (const elf32_header*)elfHdrBuf;
entry_count = ((const elf32_header*)eh)->ph_entry_count;
entry_off = ((const elf32_header*)eh)->ph_offset;
*entry_out = (unsigned long)((const elf32_header*)eh)->entry;
wolfBoot_printf("ELF: [STORE] 32-bit, entry=0x%08lx, "
"ph_offset=0x%08lx, ph_count=%d\n",
(unsigned long)((const elf32_header*)eh)->entry,
(unsigned long)entry_off, entry_count);
}
else {
eh = (const elf64_header*)elfHdrBuf;
entry_count = ((const elf64_header*)eh)->ph_entry_count;
entry_off = ((const elf64_header*)eh)->ph_offset;
*entry_out = (unsigned long)((const elf64_header*)eh)->entry;
wolfBoot_printf("ELF: [STORE] 64-bit, entry=0x%08lx, "
"ph_offset=0x%08lx, ph_count=%d\n",
(unsigned long)((const elf64_header*)eh)->entry,
(unsigned long)entry_off, entry_count);
}
/* Walk the program header table and store each loadable segment */
for (i = 0; i < entry_count; ++i) {
unsigned long paddr, filesz, offset;
int is_loadable;
uintptr_t load_addr;
/* Read the current program header into a local buffer */
if (is_elf32) {
elf32_program_header p32;
read_flash_fwimage(&boot, entry_off, &p32, sizeof(p32));
is_loadable = (p32.type == ELF_PT_LOAD);
paddr = (unsigned long)p32.paddr;
offset = (unsigned long)p32.offset;
filesz = (unsigned long)p32.file_size;
ph_size = sizeof(p32);
}
else {
elf64_program_header p64;
read_flash_fwimage(&boot, entry_off, &p64, sizeof(p64));
is_loadable = (p64.type == ELF_PT_LOAD);
paddr = (unsigned long)p64.paddr;
offset = (unsigned long)p64.offset;
filesz = (unsigned long)p64.file_size;
ph_size = sizeof(p64);
}
/* Skip non-loadable segments */
if (!is_loadable) {
wolfBoot_printf("ELF: [STORE] ERROR: non-loadable segment\n");
return -1;
}
load_addr = (uintptr_t)(paddr + BASE_OFF);
wolfBoot_printf("ELF: [STORE] Writing loadable segment: "
"loadaddr=0x%08lx, offset=0x%08lx, size=%lu\n",
load_addr, offset, filesz);
copy_flash_buffered((uintptr_t)(image + offset), load_addr, filesz,
ext_flash, ext_flash);
entry_off += ph_size;
}
wolfBoot_printf("ELF: [STORE] Image loading complete\n");
return 0;
}
#undef BASE_OFF
#endif
@ -1636,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);
@ -1648,7 +1988,14 @@ int wolfBoot_verify_authenticity(struct wolfBoot_image *img)
* TSIP encrypted key is installed at
* RENESAS_TSIP_INSTALLEDKEY_ADDR
*/
extern int hal_renesas_init(void);
int rc = hal_renesas_init();
if (rc != 0) {
wolfBoot_printf("hal_renesas_init failed! %d\n", rc);
return rc;
}
key_slot = 0;
#elif defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT) && \
defined(WOLFBOOT_USE_WOLFHSM_PUBKEY_ID)
/* Don't care about the key slot, we are using a fixed wolfHSM keyId */
@ -1690,6 +2037,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

@ -64,7 +64,7 @@
#if defined(EXT_ENCRYPTED)
static int encrypt_initialized = 0;
static uint8_t encrypt_iv_nonce[ENCRYPT_NONCE_SIZE];
static uint8_t encrypt_iv_nonce[ENCRYPT_NONCE_SIZE] XALIGNED(4);
#if defined(__WOLFBOOT)
#include "encrypt.h"
#elif !defined(XMEMSET)
@ -1335,6 +1335,14 @@ int wolfBoot_fallback_is_possible(void)
#ifdef EXT_ENCRYPTED
#include "encrypt.h"
#if defined(WOLFBOOT_RENESAS_TSIP)
#include "wolfssl/wolfcrypt/port/Renesas/renesas-tsip-crypt.h"
/* Provides wrap_enc_key_t structure generated using
* Renesas Security Key Management Tool. See docs/Renesas.md */
#include "enckey_data.h"
#endif
#if !defined(EXT_FLASH) && !defined(MMU)
#error option EXT_ENCRYPTED requires EXT_FLASH or MMU mode
#endif
@ -1357,20 +1365,25 @@ static uint8_t ENCRYPT_KEY[ENCRYPT_KEY_SIZE + ENCRYPT_NONCE_SIZE];
static int RAMFUNCTION hal_set_key(const uint8_t *k, const uint8_t *nonce)
{
uintptr_t addr, addr_align, addr_off;
int ret = 0;
int sel_sec = 0;
uint32_t trailer_relative_off = 4;
#if !defined(WOLFBOOT_SMALL_STACK) && !defined(NVM_FLASH_WRITEONCE) && !defined(WOLFBOOT_ENCRYPT_CACHE)
uint8_t ENCRYPT_CACHE[NVM_CACHE_SIZE] XALIGNED_STACK(32);
#endif
#ifdef MMU
#ifdef WOLFBOOT_RENESAS_TSIP
/* must be flashed to RENESAS_TSIP_INSTALLEDENCKEY_ADDR */
(void)k;
(void)nonce;
return 0;
#elif defined(MMU)
XMEMCPY(ENCRYPT_KEY, k, ENCRYPT_KEY_SIZE);
XMEMCPY(ENCRYPT_KEY + ENCRYPT_KEY_SIZE, nonce, ENCRYPT_NONCE_SIZE);
return 0;
#else
uintptr_t addr, addr_align, addr_off;
int ret = 0;
int sel_sec = 0;
uint32_t trailer_relative_off = 4;
#if !defined(WOLFBOOT_SMALL_STACK) && !defined(NVM_FLASH_WRITEONCE) && \
!defined(WOLFBOOT_ENCRYPT_CACHE)
uint8_t ENCRYPT_CACHE[NVM_CACHE_SIZE] XALIGNED_STACK(32);
#endif
addr = ENCRYPT_TMP_SECRET_OFFSET + WOLFBOOT_PARTITION_BOOT_ADDRESS;
addr_align = addr & (~(WOLFBOOT_SECTOR_SIZE - 1));
addr_off = addr & (WOLFBOOT_SECTOR_SIZE - 1);
@ -1463,7 +1476,11 @@ int RAMFUNCTION wolfBoot_set_encrypt_key(const uint8_t *key,
*/
int RAMFUNCTION wolfBoot_get_encrypt_key(uint8_t *k, uint8_t *nonce)
{
#if defined(MMU)
#ifdef WOLFBOOT_RENESAS_TSIP
wrap_enc_key_t* enc_key =(wrap_enc_key_t*)RENESAS_TSIP_INSTALLEDENCKEY_ADDR;
XMEMCPY(k, enc_key->encrypted_user_key, ENCRYPT_KEY_SIZE);
XMEMCPY(nonce, enc_key->initial_vector, ENCRYPT_NONCE_SIZE);
#elif defined(MMU)
XMEMCPY(k, ENCRYPT_KEY, ENCRYPT_KEY_SIZE);
XMEMCPY(nonce, ENCRYPT_KEY + ENCRYPT_KEY_SIZE, ENCRYPT_NONCE_SIZE);
#else
@ -1491,7 +1508,9 @@ int RAMFUNCTION wolfBoot_get_encrypt_key(uint8_t *k, uint8_t *nonce)
*/
int RAMFUNCTION wolfBoot_erase_encrypt_key(void)
{
#if defined(MMU)
#ifdef WOLFBOOT_RENESAS_TSIP
/* nothing to erase */
#elif defined(MMU)
ForceZero(ENCRYPT_KEY, ENCRYPT_KEY_SIZE + ENCRYPT_NONCE_SIZE);
#else
uint8_t ff[ENCRYPT_KEY_SIZE + ENCRYPT_NONCE_SIZE];
@ -1553,6 +1572,7 @@ int RAMFUNCTION chacha_init(void)
#elif defined(ENCRYPT_WITH_AES128) || defined(ENCRYPT_WITH_AES256)
Aes aes_dec, aes_enc;
/**
* @brief Initialize AES encryption.
*
@ -1564,26 +1584,37 @@ Aes aes_dec, aes_enc;
*/
int aes_init(void)
{
int devId;
uint8_t *stored_nonce;
uint8_t *key;
uint8_t ff[ENCRYPT_KEY_SIZE];
#ifdef WOLFBOOT_RENESAS_TSIP
int ret;
wrap_enc_key_t* enc_key;
devId = RENESAS_DEVID + 1;
enc_key =(wrap_enc_key_t*)RENESAS_TSIP_INSTALLEDENCKEY_ADDR;
key = enc_key->encrypted_user_key;
stored_nonce = enc_key->initial_vector;
wolfCrypt_Init(); /* required to setup the crypto callback defaults */
#else /* non TSIP */
devId = INVALID_DEVID;
#if defined(MMU) || defined(UNIT_TEST)
uint8_t *key = ENCRYPT_KEY;
key = ENCRYPT_KEY;
#else
uint8_t *key = (uint8_t *)(WOLFBOOT_PARTITION_BOOT_ADDRESS +
key = (uint8_t*)(WOLFBOOT_PARTITION_BOOT_ADDRESS +
ENCRYPT_TMP_SECRET_OFFSET);
#endif
uint8_t ff[ENCRYPT_KEY_SIZE];
uint8_t iv_buf[ENCRYPT_NONCE_SIZE];
uint8_t* stored_nonce;
#ifdef NVM_FLASH_WRITEONCE
key -= WOLFBOOT_SECTOR_SIZE * nvm_select_fresh_sector(PART_BOOT);
#endif
stored_nonce = key + ENCRYPT_KEY_SIZE;
#endif /* WOLFBOOT_RENESAS_TSIP */
XMEMSET(&aes_enc, 0, sizeof(aes_enc));
XMEMSET(&aes_dec, 0, sizeof(aes_dec));
wc_AesInit(&aes_enc, NULL, 0);
wc_AesInit(&aes_dec, NULL, 0);
wc_AesInit(&aes_enc, NULL, devId);
wc_AesInit(&aes_dec, NULL, devId);
/* Check against 'all 0xff' or 'all zero' cases */
XMEMSET(ff, 0xFF, ENCRYPT_KEY_SIZE);
@ -1593,12 +1624,37 @@ int aes_init(void)
if (XMEMCMP(key, ff, ENCRYPT_KEY_SIZE) == 0)
return -1;
#ifdef WOLFBOOT_RENESAS_TSIP
/* Unwrap key and get key index */
#if ENCRYPT_KEY_SIZE == 32
ret = R_TSIP_GenerateAes256KeyIndex(enc_key->wufpk, enc_key->initial_vector,
enc_key->encrypted_user_key, &aes_enc.ctx.tsip_keyIdx);
#else
ret = R_TSIP_GenerateAes128KeyIndex(enc_key->wufpk, enc_key->initial_vector,
enc_key->encrypted_user_key, &aes_enc.ctx.tsip_keyIdx);
#endif
if (ret != TSIP_SUCCESS) {
return -1;
}
/* set encryption key size */
aes_enc.ctx.keySize = ENCRYPT_KEY_SIZE;
/* copy TSIP ctx to decryption key */
XMEMCPY(&aes_dec.ctx, &aes_enc.ctx, sizeof(aes_enc.ctx));
/* register AES crypto callback */
wc_CryptoCb_RegisterDevice(devId, wc_tsip_AesCipher, NULL);
#endif /* WOLFBOOT_RENESAS_TSIP */
/* AES_ENCRYPTION is used for both directions in CTR
* IV is set later with "wc_AesSetIV" */
wc_AesSetKeyDirect(&aes_enc, key, ENCRYPT_KEY_SIZE, NULL, AES_ENCRYPTION);
wc_AesSetKeyDirect(&aes_dec, key, ENCRYPT_KEY_SIZE, NULL, AES_ENCRYPTION);
/* Set global IV nonce used in aes_set_iv */
XMEMCPY(encrypt_iv_nonce, stored_nonce, ENCRYPT_NONCE_SIZE);
XMEMCPY(iv_buf, stored_nonce, ENCRYPT_NONCE_SIZE);
/* AES_ENCRYPTION is used for both directions in CTR */
wc_AesSetKeyDirect(&aes_enc, key, ENCRYPT_KEY_SIZE, iv_buf, AES_ENCRYPTION);
wc_AesSetKeyDirect(&aes_dec, key, ENCRYPT_KEY_SIZE, iv_buf, AES_ENCRYPTION);
encrypt_initialized = 1;
return 0;
}
@ -1606,10 +1662,10 @@ int aes_init(void)
* @brief Set the AES initialization vector (IV) for CTR mode.
*
* This function sets the AES initialization vector (IV) for the Counter (CTR)
* mode encryption. It takes a 12-byte nonce and a 32-bit IV counter value to
* mode encryption. It takes a 16-byte nonce and a 32-bit IV counter value to
* construct the 16-byte IV used for encryption.
*
* @param nonce Pointer to the 12-byte nonce (IV) buffer.
* @param nonce Pointer to the 16-byte nonce (IV) buffer.
* @param iv_ctr The IV counter value.
*
*/
@ -1702,7 +1758,8 @@ int RAMFUNCTION ext_flash_encrypt_write(uintptr_t address, const uint8_t *data,
int sz = len, i, step;
uint8_t part;
uint32_t iv_counter = 0;
#if defined(EXT_ENCRYPTED) && !defined(WOLFBOOT_SMALL_STACK) && !defined(NVM_FLASH_WRITEONCE)
#if defined(EXT_ENCRYPTED) && !defined(WOLFBOOT_SMALL_STACK) && \
!defined(NVM_FLASH_WRITEONCE)
uint8_t ENCRYPT_CACHE[NVM_CACHE_SIZE] XALIGNED_STACK(32);
#endif
@ -1779,8 +1836,8 @@ int RAMFUNCTION ext_flash_encrypt_write(uintptr_t address, const uint8_t *data,
*/
int RAMFUNCTION ext_flash_decrypt_read(uintptr_t address, uint8_t *data, int len)
{
uint8_t block[ENCRYPT_BLOCK_SIZE];
uint8_t dec_block[ENCRYPT_BLOCK_SIZE];
uint8_t block[ENCRYPT_BLOCK_SIZE] XALIGNED_STACK(4);
uint8_t dec_block[ENCRYPT_BLOCK_SIZE] XALIGNED_STACK(4);
uint32_t row_address = address, row_offset, iv_counter = 0;
int i;
int flash_read_size;
@ -1814,9 +1871,8 @@ int RAMFUNCTION ext_flash_decrypt_read(uintptr_t address, uint8_t *data, int len
crypto_set_iv(encrypt_iv_nonce, iv_counter);
break;
case PART_SWAP:
{
break;
}
break;
default:
return -1;
}
@ -1859,7 +1915,7 @@ int RAMFUNCTION ext_flash_decrypt_read(uintptr_t address, uint8_t *data, int len
unaligned_trailer_size = read_remaining;
if (unaligned_trailer_size > 0)
{
uint8_t dec_block[ENCRYPT_BLOCK_SIZE];
uint8_t dec_block[ENCRYPT_BLOCK_SIZE] XALIGNED_STACK(4);
if (ext_flash_read(address, block, ENCRYPT_BLOCK_SIZE)
!= ENCRYPT_BLOCK_SIZE)
return -1;

View File

@ -392,6 +392,7 @@ void uart_vprintf(const char* fmt, va_list argp)
uart_write("0x", 2);
/* fall through */
case 'x':
case 'X':
{
int n = (int)va_arg(argp, int);
uart_writenum(n, 16, zeropad, maxdigits);

View File

@ -38,11 +38,6 @@
int WP11_Library_Init(void);
#endif
/* Support for ELF scatter/gather format */
#ifdef WOLFBOOT_ELF_SCATTERED
#include "elf.h"
#endif
#ifdef RAM_CODE
#ifndef TARGET_rp2350
extern unsigned int _start_text;
@ -55,7 +50,7 @@ static volatile const uint32_t __attribute__((used)) wolfboot_version = WOLFBOOT
#ifdef EXT_FLASH
# ifndef BUFFER_DECLARED
# define BUFFER_DECLARED
static uint8_t buffer[FLASHBUFFER_SIZE];
static uint8_t buffer[FLASHBUFFER_SIZE] XALIGNED(4);
# endif
#endif
@ -158,7 +153,7 @@ static int RAMFUNCTION wolfBoot_copy_sector(struct wolfBoot_image *src,
#ifdef EXT_ENCRYPTED
wolfBoot_get_encrypt_key(key, nonce);
if(src->part == PART_SWAP)
if (src->part == PART_SWAP)
iv_counter = dst_sector_offset;
else
iv_counter = src_sector_offset;
@ -171,14 +166,14 @@ static int RAMFUNCTION wolfBoot_copy_sector(struct wolfBoot_image *src,
if (PART_IS_EXT(src)) {
#ifndef BUFFER_DECLARED
#define BUFFER_DECLARED
static uint8_t buffer[FLASHBUFFER_SIZE];
static uint8_t buffer[FLASHBUFFER_SIZE] XALIGNED(4);
#endif
wb_flash_erase(dst, dst_sector_offset, WOLFBOOT_SECTOR_SIZE);
while (pos < WOLFBOOT_SECTOR_SIZE) {
if (src_sector_offset + pos <
(src->fw_size + IMAGE_HEADER_SIZE + FLASHBUFFER_SIZE)) {
/* bypass decryption, copy encrypted data into swap */
if (dst->part == PART_SWAP) {
/* bypass decryption, copy encrypted data into swap if its external */
if (dst->part == PART_SWAP && SWAP_EXT) {
ext_flash_read((uintptr_t)(src->hdr) + src_sector_offset + pos,
(void *)buffer, FLASHBUFFER_SIZE);
} else {
@ -207,7 +202,7 @@ static int RAMFUNCTION wolfBoot_copy_sector(struct wolfBoot_image *src,
return pos;
}
#ifndef DISABLE_BACKUP
#if !defined(DISABLE_BACKUP) && !defined(CUSTOM_PARTITION_TRAILER)
#ifdef EXT_ENCRYPTED
# define TRAILER_OFFSET_WORDS \
@ -219,17 +214,21 @@ static int RAMFUNCTION wolfBoot_copy_sector(struct wolfBoot_image *src,
/**
* @brief Performs the final swap and erase operations during a secure update,
* ensuring that if power is lost during the update, the process can be resumed
* on next boot.
* on next boot. Not supported with CUSTOM_PARTITION_TRAILER
*
* This function handles the final phase of the three-way swap update process.
* It ensures that the update is atomic and power-fail safe by:
* 1. Saving the last sector of the boot partition to the swap area
* 1. Saving the sector at tmpBootPos (staging sector) to the swap area
* 2. Setting a magic trailer value to mark the swap as in progress
* 3. Erasing the last sector(s) of the boot partition
* 4. Restoring the saved sector from swap back to boot
* 3. Erasing the last sector(s) of the boot partition (where partition state is stored)
* 4. Restoring the saved staging sector from swap back to boot
* 5. Setting the boot partition state to TESTING
* 6. Erasing the last sector(s) of the update partition
*
* The staging sector (tmpBootPos) is positioned right before the final sectors
* that will be erased. This sector is preserved and used to store a magic trailer
* that indicates a swap operation is in progress.
*
* The function can be called in two modes:
* - Normal mode (resume=0): Initiates the swap and erase process
* - Resume mode (resume=1): Checks if a swap was interrupted and completes it
@ -250,6 +249,8 @@ static int wolfBoot_swap_and_final_erase(int resume)
#endif
);
int swapDone = 0;
/* Calculate position of staging sector - just before the final sectors
* that store partition state */
uintptr_t tmpBootPos = WOLFBOOT_PARTITION_SIZE - eraseLen -
WOLFBOOT_SECTOR_SIZE;
uint32_t tmpBuffer[TRAILER_OFFSET_WORDS + 1];
@ -260,7 +261,8 @@ static int wolfBoot_swap_and_final_erase(int resume)
wolfBoot_open_image(swap, PART_SWAP);
wolfBoot_get_partition_state(PART_UPDATE, &updateState);
/* read trailer */
/* Read the trailer from the staging sector to check if we're resuming an
* interrupted operation */
#if defined(EXT_FLASH) && PARTN_IS_EXT(PART_BOOT)
ext_flash_read((uintptr_t)(boot->hdr + tmpBootPos), (void*)tmpBuffer,
sizeof(tmpBuffer));
@ -268,12 +270,13 @@ static int wolfBoot_swap_and_final_erase(int resume)
memcpy(tmpBuffer, boot->hdr + tmpBootPos, sizeof(tmpBuffer));
#endif
/* check for trailing magic (BOOT) */
/* Check if the magic trailer exists - indicates an interrupted swap
* operation */
/* final swap and erase flag is WOLFBOOT_MAGIC_TRAIL */
if (tmpBuffer[TRAILER_OFFSET_WORDS] == WOLFBOOT_MAGIC_TRAIL) {
swapDone = 1;
}
/* if resuming, quit if swap isn't done */
/* If we're in resume mode but no swap was in progress, return */
if ((resume == 1) && (swapDone == 0) &&
(updateState != IMG_STATE_FINAL_FLAGS)
) {
@ -285,35 +288,38 @@ static int wolfBoot_swap_and_final_erase(int resume)
ext_flash_unlock();
#endif
/* If update state isn't set to FINAL_FLAGS, this is the first run of the function */
/* IMG_STATE_FINAL_FLAGS allows re-entry without blowing away swap */
if (updateState != IMG_STATE_FINAL_FLAGS) {
/* store the sector at tmpBootPos into swap */
/* First, backup the staging sector (sector at tmpBootPos) into swap partition */
/* This sector will be modified with the magic trailer, so we need to preserve it */
wolfBoot_copy_sector(boot, swap, tmpBootPos / WOLFBOOT_SECTOR_SIZE);
/* set FINAL_SWAP for re-entry */
/* Mark update as being in final swap phase to allow resumption if power fails */
wolfBoot_set_partition_state(PART_UPDATE, IMG_STATE_FINAL_FLAGS);
}
#ifdef EXT_ENCRYPTED
if (swapDone == 0) {
/* get encryption key and iv if encryption is enabled */
/* For encrypted images: Get the encryption key and IV */
wolfBoot_get_encrypt_key((uint8_t*)tmpBuffer,
(uint8_t*)&tmpBuffer[ENCRYPT_KEY_SIZE/sizeof(uint32_t)]);
/* write TRAIL, encryption key and iv if enabled to tmpBootPos*/
/* Set the magic trailer in the buffer and write it to the staging sector */
tmpBuffer[TRAILER_OFFSET_WORDS] = WOLFBOOT_MAGIC_TRAIL;
wb_flash_erase(boot, tmpBootPos, WOLFBOOT_SECTOR_SIZE);
wb_flash_write(boot, tmpBootPos, (void*)tmpBuffer, sizeof(tmpBuffer));
}
#endif
/* erase the last boot sector(s) */
/* Erase the last sector(s) of boot partition (where partition state is stored) */
wb_flash_erase(boot, WOLFBOOT_PARTITION_SIZE - eraseLen, eraseLen);
/* set the encryption key */
#ifdef EXT_ENCRYPTED
/* Initialize encryption with the saved key */
wolfBoot_set_encrypt_key((uint8_t*)tmpBuffer,
(uint8_t*)&tmpBuffer[ENCRYPT_KEY_SIZE/sizeof(uint32_t)]);
/* wolfBoot_set_encrypt_key calls hal_flash_unlock, need to unlock again */
hal_flash_unlock();
#endif
/* write the original contents of tmpBootPos back */
/* Restore the original contents of the staging sector (with the magic trailer if encrypted) */
if (tmpBootPos < boot->fw_size + IMAGE_HEADER_SIZE) {
wolfBoot_copy_sector(swap, boot, tmpBootPos / WOLFBOOT_SECTOR_SIZE);
}
@ -321,10 +327,11 @@ static int wolfBoot_swap_and_final_erase(int resume)
wb_flash_erase(boot, tmpBootPos, WOLFBOOT_SECTOR_SIZE);
}
/* mark boot as TESTING */
/* Mark boot partition as TESTING - this tells bootloader to fallback if update fails */
wolfBoot_set_partition_state(PART_BOOT, IMG_STATE_TESTING);
/* erase the last sector(s) of update. This resets the update partition state
* to IMG_STATE_NEW */
/* Erase the last sector(s) of update partition */
/* This resets the update partition state to IMG_STATE_NEW */
wb_flash_erase(update, WOLFBOOT_PARTITION_SIZE - eraseLen, eraseLen);
#ifdef EXT_FLASH
@ -334,7 +341,7 @@ static int wolfBoot_swap_and_final_erase(int resume)
return 0;
}
#endif
#endif /* !DISABLE_BACKUP && !CUSTOM_PARTITION_TRAILER */
#ifdef DELTA_UPDATES
@ -534,9 +541,10 @@ out:
ext_flash_lock();
#endif
hal_flash_lock();
#if !defined(DISABLE_BACKUP) && !defined(CUSTOM_PARTITION_TRAILER)
/* start re-entrant final erase, return code is only for resumption in
* wolfBoot_start */
#ifndef DISABLE_BACKUP
if (ret == 0) {
wolfBoot_swap_and_final_erase(0);
}
@ -770,7 +778,7 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed)
}
}
/* Erase remainder of partitions */
/* Erase remainder of partition */
#if defined(WOLFBOOT_FLASH_MULTI_SECTOR_ERASE) || defined(PRINTF_ENABLED)
/* calculate number of remaining bytes */
/* reserve 1 sector for status (2 sectors for NV write once) */
@ -780,7 +788,7 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed)
size = WOLFBOOT_PARTITION_SIZE - (sector * sector_size) - sector_size;
#endif
wolfBoot_printf("Erasing remainder of partitions (%d sectors)...\n",
wolfBoot_printf("Erasing remainder of partition (%d sectors)...\n",
size/sector_size);
#endif
@ -810,36 +818,46 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed)
ext_flash_lock();
#endif
hal_flash_lock();
#if !defined(CUSTOM_PARTITION_TRAILER)
/* start re-entrant final erase, return code is only for resumption in
* wolfBoot_start*/
* wolfBoot_start */
wolfBoot_swap_and_final_erase(0);
#else
/* Mark boot partition as TESTING - this tells bootloader to fallback if update fails */
wolfBoot_set_partition_state(PART_BOOT, IMG_STATE_TESTING);
#endif
#else /* DISABLE_BACKUP */
#ifdef WOLFBOOT_ELF_SCATTERED
#ifdef WOLFBOOT_ELF_FLASH_SCATTER
unsigned long entry;
void *base = (void *)WOLFBOOT_PARTITION_BOOT_ADDRESS;
void* base = (void*)WOLFBOOT_PARTITION_BOOT_ADDRESS;
wolfBoot_printf("ELF Scattered image digest check\n");
if (elf_check_image_scattered(PART_BOOT, &entry) < 0) {
wolfBoot_printf("ELF Scattered image digest check: failed. Restoring scattered image...\n");
elf_store_image_scattered(base, &entry, PART_IS_EXT(boot));
if (elf_check_image_scattered(PART_BOOT, &entry) < 0) {
wolfBoot_printf("Fatal: Could not verify digest after scattering. Panic().\n");
if (wolfBoot_check_flash_image_elf(PART_BOOT, &entry) < 0) {
wolfBoot_printf("ELF Scattered image digest check: failed. Restoring "
"scattered image...\n");
wolfBoot_load_flash_image_elf(PART_BOOT, &entry, PART_IS_EXT(boot));
if (wolfBoot_check_flash_image_elf(PART_BOOT, &entry) < 0) {
wolfBoot_printf(
"Fatal: Could not verify digest after scattering. Panic().\n");
wolfBoot_panic();
}
}
wolfBoot_printf("Scattered image correctly verified. Setting entry point to %lx\n", entry);
boot.fw_base = (void *)entry;
wolfBoot_printf(
"Scattered image correctly verified. Setting entry point to %lx\n",
entry);
boot.fw_base = (void*)entry;
#endif
/* Direct Swap without power fail safety */
hal_flash_unlock();
#ifdef EXT_FLASH
#ifdef EXT_FLASH
ext_flash_unlock();
#endif
#endif
#ifdef EXT_ENCRYPTED
#ifdef EXT_ENCRYPTED
wolfBoot_get_encrypt_key(key, nonce);
#endif
#endif
/* Directly copy the content of the UPDATE partition into the BOOT
* partition. */
@ -1022,7 +1040,7 @@ void RAMFUNCTION wolfBoot_start(void)
#endif
#endif
#if !defined(DISABLE_BACKUP)
#if !defined(DISABLE_BACKUP) && !defined(CUSTOM_PARTITION_TRAILER)
/* resume the final erase in case the power failed before it finished */
resumedFinalErase = wolfBoot_swap_and_final_erase(1);
if (resumedFinalErase != 0)
@ -1079,20 +1097,28 @@ void RAMFUNCTION wolfBoot_start(void)
}
PART_SANITY_CHECK(&boot);
#ifdef WOLFBOOT_ELF_SCATTERED
#ifdef WOLFBOOT_ELF_FLASH_SCATTER
unsigned long entry;
void *base = (void *)WOLFBOOT_PARTITION_BOOT_ADDRESS;
wolfBoot_printf("ELF Scattered image digest check\n");
if (elf_check_image_scattered(PART_BOOT, &entry) < 0) {
wolfBoot_printf("ELF Scattered image digest check: failed. Restoring scattered image...\n");
elf_store_image_scattered(base, &entry, PART_IS_EXT(boot));
if (elf_check_image_scattered(PART_BOOT, &entry) < 0) {
wolfBoot_printf("Fatal: Could not verify digest after scattering. Panic().\n");
if (wolfBoot_check_flash_image_elf(PART_BOOT, &entry) < 0) {
wolfBoot_printf("ELF Scattered image digest check: failed. Restoring "
"scattered image...\n");
if (wolfBoot_load_flash_image_elf(PART_BOOT, &entry,
PART_IS_EXT(&boot)) < 0) {
wolfBoot_printf(
"ELF: [BOOT] ERROR: could not store scattered image\n");
wolfBoot_panic();
}
if (wolfBoot_check_flash_image_elf(PART_BOOT, &entry) < 0) {
wolfBoot_printf(
"Fatal: Could not verify digest after scattering. Panic().\n");
wolfBoot_panic();
}
}
wolfBoot_printf("Scattered image correctly verified. Setting entry point to %lx\n", entry);
boot.fw_base = (void *)entry;
wolfBoot_printf(
"Scattered image correctly verified. Setting entry point to %lx\n",
entry);
boot.fw_base = (void*)entry;
#endif

View File

@ -238,10 +238,10 @@ ifeq ($(TARGET),sim)
# Override linker flags
LDFLAGS+=-Wl,-Map=image.map
endif
ifeq ($(ELF_SCATTERED),1)
ifeq ($(ELF_FLASH_SCATTER),1)
LSCRIPT_TEMPLATE=sim_scattered.ld
APP_OBJS=app_sim_scattered.o ../src/string.o
CFLAGS+=-D"WOLFBOOT_ELF_SCATTERED=1" -nostartfiles -ffreestanding -static -nostdlib
CFLAGS+=-D"WOLFBOOT_ELF_FLASH_SCATTER=1" -nostartfiles -ffreestanding -static -nostdlib
LDFLAGS+=-ffreestanding -nostartfiles -static -T$(LSCRIPT) -nostdlib
else
APP_OBJS=app_sim.o
@ -451,6 +451,14 @@ ifeq ($(TARGET),nxp_ls1028a)
LSCRIPT_TEMPLATE:=AARCH64-ls1028a.ld
endif
ifeq ($(TARGET),pic32ck)
APP_OBJS+=../hal/pic32c.o
endif
ifeq ($(TARGET), pic32cz)
APP_OBJS+=../hal/pic32c.o
endif
CFLAGS+=-I../lib/wolfssl
ifeq ($(WOLFHSM_CLIENT),1)
@ -478,10 +486,22 @@ image.srec: image.elf
@echo "\t[SREC] $@"
$(Q)$(OBJCOPY) $(OBJCOPY_FLAGS) -O srec $^ $@
image.elf: $(APP_OBJS) $(LSCRIPT)
ifeq ($(ELF_FLASH_SCATTER),1)
# When ELF_FLASH_SCATTER=1, preprocess the ELF file with the squashelf tool
SQUASHELF_TOOL = ../tools/squashelf/squashelf
image-orig.elf: $(APP_OBJS) $(LSCRIPT)
@echo "\t[LD] $@"
$(Q)$(LD) $(LDFLAGS) $(APP_OBJS) $(OUTPUT_FLAG) $@
image.elf: image-orig.elf
@echo "\t[SQUASHELF] $@"
$(Q)$(SQUASHELF_TOOL) -v --nosht $< $@
else
# Default behavior when ELF_FLASH_SCATTER is not set
image.elf: $(APP_OBJS) $(LSCRIPT)
@echo "\t[LD] $@"
$(Q)$(LD) $(LDFLAGS) $(APP_OBJS) $(OUTPUT_FLAG) $@
endif
standalone: image.bin

View File

@ -0,0 +1,80 @@
/* app_pic32ck.c
*
* Test bare-metal boot-led-on application
*
* Copyright (C) 2025 wolfSSL Inc.
*
* This file is part of wolfBoot.
*
* wolfBoot is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfBoot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
#include "hal.h"
#include "target.h"
#include "wolfboot/wolfboot.h"
#include <stdint.h>
#if defined(TARGET_pic32ck)
#define PORT_BASE (0x44801000U)
#define PORTB_BASE (PORT_BASE + 0x80 * 1)
#define PORTB_DIRSET (*(volatile uint32_t *)(PORTB_BASE + 0x08))
#define PORTB_DIRSET_OUT(X) (1 << (X))
#define PORTB_OUTSET (*(volatile uint32_t *)(PORTB_BASE + 0x18))
#define PORTB_OUTSET_OUT(X) (1 << (X))
#define PORTB_OUTCLR (*(volatile uint32_t *)(PORTB_BASE + 0x14))
#define PORTB_OUTCLR_OUT(X) (1 << (X))
#define PORTD_BASE (PORT_BASE + 0x80 * 3)
#define PORTD_DIRSET (*(volatile uint32_t *)(PORTD_BASE + 0x08))
#define PORTD_DIRSET_OUT(X) (1 << (X))
#define PORTD_OUTSET (*(volatile uint32_t *)(PORTD_BASE + 0x18))
#define PORTD_OUTSET_OUT(X) (1 << (X))
#define PORTD_OUTCLR (*(volatile uint32_t *)(PORTD_BASE + 0x14))
#define PORTD_OUTCLR_OUT(X) (1 << (X))
#define LED0_PIN (20)
#define LED1_PIN (25)
static void led0_on(void)
{
PORTD_DIRSET = PORTD_DIRSET_OUT(LED0_PIN);
PORTD_OUTCLR = PORTD_OUTCLR_OUT(LED0_PIN);
}
static void led1_on(void)
{
PORTB_DIRSET = PORTB_DIRSET_OUT(LED1_PIN);
PORTB_OUTCLR = PORTB_OUTCLR_OUT(LED1_PIN);
}
void main(void)
{
uint32_t boot_version;
hal_init();
boot_version = wolfBoot_current_firmware_version();
if (boot_version == 1) {
wolfBoot_update_trigger();
led0_on();
} else if (boot_version >= 2) {
wolfBoot_success();
led1_on();
}
/* Wait for reboot */
while (1) {}
}
#endif /* TARGET_pic32ck */

View File

@ -0,0 +1,72 @@
/* app_pic32cz.c
*
* Test bare-metal boot-led-on application
*
* Copyright (C) 2025 wolfSSL Inc.
*
* This file is part of wolfBoot.
*
* wolfBoot is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfBoot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
#include "hal.h"
#include "target.h"
#include "wolfboot/wolfboot.h"
#include <stdint.h>
#if defined(TARGET_pic32cz)
#define PORT_BASE (0x44840000U)
#define PORTB_BASE (PORT_BASE + 0x80 * 1)
#define PORTB_DIRSET (*(volatile uint32_t *)(PORTB_BASE + 0x08))
#define PORTB_DIRSET_OUT(X) (1 << (X))
#define PORTB_OUTSET (*(volatile uint32_t *)(PORTB_BASE + 0x18))
#define PORTB_OUTSET_OUT(X) (1 << (X))
#define PORTB_OUTCLR (*(volatile uint32_t *)(PORTB_BASE + 0x14))
#define PORTB_OUTCLR_OUT(X) (1 << (X))
#define LED0_PIN 21
#define LED1_PIN 22
static void led0_on(void)
{
PORTB_DIRSET = PORTB_DIRSET_OUT(LED0_PIN);
PORTB_OUTCLR = PORTB_OUTCLR_OUT(LED0_PIN);
}
static void led1_on(void)
{
PORTB_DIRSET = PORTB_DIRSET_OUT(LED1_PIN);
PORTB_OUTCLR = PORTB_OUTCLR_OUT(LED1_PIN);
}
void main(void)
{
uint32_t boot_version;
hal_init();
boot_version = wolfBoot_current_firmware_version();
if (boot_version == 1) {
wolfBoot_update_trigger();
led0_on();
} else if (boot_version >= 2) {
wolfBoot_success();
led1_on();
}
/* Wait for reboot */
while (1) {}
}
#endif /* TARGET_pic32cz */

View File

@ -168,8 +168,8 @@ const char Cfile_Banner[]=
const char Store_hdr[] = "\n"
"#if defined(__APPLE__) && defined(__MACH__)\n"
"#define KEYSTORE_SECTION __attribute__((section (\"__KEYSTORE,__keystore\")))\n"
"#elif defined(__CCRX__) /* Renesas RX */\n"
"#define KEYSTORE_SECTION\n"
"#elif defined(__CCRX__) || defined(WOLFBOOT_RENESAS_RSIP) || defined(WOLFBOOT_RENESAS_TSIP) || defined(WOLFBOOT_RENESAS_SCEPROTECT)\n"
"#define KEYSTORE_SECTION /* Renesas RX */\n"
"#elif defined(TARGET_x86_64_efi)\n"
"#define KEYSTORE_SECTION\n"
"#else\n"
@ -258,7 +258,7 @@ const char Keystore_API[] =
"{\n"
" if (id >= keystore_num_pubkeys())\n"
" return 0;\n"
" return (int)PubKeys[id].part_id_mask;\n"
" return PubKeys[id].part_id_mask;\n"
"}\n"
"\n"
"uint32_t keystore_get_key_type(int id)\n"

2
tools/keytools/keygen.py 100755 → 100644
View File

@ -172,7 +172,7 @@ Keystore_API += "uint32_t keystore_get_mask(int id)\n"
Keystore_API += "{\n"
Keystore_API += " if (id >= keystore_num_pubkeys())\n"
Keystore_API += " return -1;\n"
Keystore_API += " return (int)PubKeys[id].part_id_mask;\n"
Keystore_API += " return PubKeys[id].part_id_mask;\n"
Keystore_API += "}\n\n"
Keystore_API += "#endif /* Keystore public key size check */\n"
Keystore_API += "#endif /* WOLFBOOT_NO_SIGN */\n"

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,14 @@ 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"
WHNVMTOOL="$WOLFBOOT_DIR/lib/wolfHSM/tools/whnvmtool/whnvmtool"
# Default algorithm configuration
DEFAULT_SIGN_ALGO="ecc256"
@ -36,8 +42,12 @@ DEFAULT_HASH_ALGO="sha256"
# Default values
HSM=""
ELF=""
OPERATIONS=()
# Important Constants
PFLASH1_RANGE="0xA0300000-0xA0500000"
# Structure to hold command options
declare -A KEYGEN_OPTS=(
[sign_algo]="$DEFAULT_SIGN_ALGO"
@ -47,10 +57,12 @@ declare -A SIGN_OPTS=(
[sign_algo]="$DEFAULT_SIGN_ALGO"
[hash_algo]="$DEFAULT_HASH_ALGO"
[build_type]="Release"
[file_ext]=".bin"
)
declare -A MACROS_OPTS=(
[sign_algo]=""
[hash_algo]=""
[use_elf_format]=""
)
CURRENT_OPTS=""
@ -83,53 +95,118 @@ 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
declare -A LCF_OPTS=(
[sign_algo]="$DEFAULT_SIGN_ALGO"
[use_elf_format]=""
)
# Add TARGET_OPTS to command options structure
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]]"
echo ""
echo "Global Options:"
echo " --hsm Use wolfHSM version"
echo " --elf Use ELF format for firmware images (affects target, macros, lcf, and sign commands)"
echo ""
echo "Commands and their options:"
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"
@ -140,45 +217,50 @@ 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"
echo " $0 sign --hash-algo sha256 --debug"
echo " $0 keygen --sign-algo ecc256 sign --hash-algo sha256"
echo " $0 --hsm keygen --sign-algo ecc256 --localkeys sign --debug"
echo " $0 target"
echo " $0 --elf target macros lcf sign"
echo " $0 clean"
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
@ -188,10 +270,20 @@ do_sign() {
local sign_algo="${SIGN_OPTS[sign_algo]:-${KEYGEN_OPTS[sign_algo]}}"
local pq_params="${COMMON_OPTS[sign_pq_params]}"
local header_size
local bin_path="$base_path/$app_name/TriCore ${SIGN_OPTS[build_type]} (GCC)/$app_name.bin"
local bin_path="$base_path/$app_name/TriCore ${SIGN_OPTS[build_type]} (GCC)/$app_name${SIGN_OPTS[file_ext]}"
# If signing an elf file, first preprocess it with squashelf
if [[ "${SIGN_OPTS[file_ext]}" == ".elf" ]]; then
local temp_file="${bin_path}.squashed"
echo "Preprocessing ELF file with $SQUASHELF"
"$SQUASHELF" -v --nosht -r "$PFLASH1_RANGE" "$bin_path" "$temp_file"
echo "Replacing original ELF with squashed version"
cp "$temp_file" "$bin_path"
rm "$temp_file"
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"
@ -199,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
@ -209,10 +309,28 @@ do_gen_target() {
local target_h_template="${TARGET_H}.in"
local wolfboot_sector_size=0x4000
local wolfboot_partition_size=0x17E000
local wolfboot_partition_boot_address=0xA0300000
local wolfboot_partition_update_address=0xA047E000
local wolfboot_partition_swap_address=0xA05FC000
# Select partition values based on whether --elf option was specified
local wolfboot_partition_size
local wolfboot_partition_boot_address
local wolfboot_partition_update_address
local wolfboot_partition_swap_address
if [[ -n "${TARGET_OPTS[use_elf_format]}" ]]; then
# These addresses and values must match those defined in the test-app
# linker file
wolfboot_partition_size=0xC0000
wolfboot_partition_boot_address=0xA047C000
wolfboot_partition_update_address=0xA053C000
wolfboot_partition_swap_address=0xA05FC000
else
# These addresses and values must match those defined in the test-app
# linker file
wolfboot_partition_size=0x17C000
wolfboot_partition_boot_address=0xA0300000
wolfboot_partition_update_address=0xA047C000
wolfboot_partition_swap_address=0xA05FC000
fi
local wolfboot_dts_boot_address=""
local wolfboot_dts_update_address=""
@ -237,7 +355,7 @@ do_gen_target() {
do_clean() {
echo "Cleaning generated files"
local macros_out="$WOLFBOOT_DIR/IDE/AURIX/wolfBoot-tc3xx${HSM:+-wolfHSM}/wolfBoot_macros.txt"
rm -f "$PRVKEY_DER"
rm -f "$PUBKEY_DER"
rm -f "$TARGET_H"
@ -258,13 +376,17 @@ 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=""
local image_signature_size=""
local ml_dsa_image_signature_size=""
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,,}]:-}"
@ -279,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"
@ -289,6 +418,12 @@ do_gen_macros() {
use_wolfhsm_pubkey_id="-DWOLFBOOT_USE_WOLFHSM_PUBKEY_ID"
fi
# Set ELF format macros if --elf option was specified
if [[ -n "${MACROS_OPTS[use_elf_format]}" ]]; then
use_wolfboot_elf="-DWOLFBOOT_ELF"
use_wolfboot_elf_flash_scattered="-DWOLFBOOT_ELF_FLASH_SCATTER"
fi
# Quirk: set additional (redundant) macros for ML DSA based on pq_params
if [[ "${sign_algo,,}" == ml_dsa* ]]; then
local level="${pq_params:-2}" # Default to level 2 if not specified
@ -318,6 +453,9 @@ do_gen_macros() {
-e "s/@ML_DSA_IMAGE_SIGNATURE_SIZE@/$ml_dsa_image_signature_size/g" \
-e "s/@WOLFBOOT_HUGE_STACK@/$use_huge_stack/g" \
-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
@ -326,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"
@ -345,10 +490,19 @@ do_gen_lcf() {
# Determine target directory based on HSM flag
local base_dir="$WOLFBOOT_DIR/IDE/AURIX"
local app_dir="test-app${HSM:+-wolfHSM}"
local lcf_template="$base_dir/$app_dir/Lcf_Gnuc_Tricore_Tc.lsl.in"
# Select template file based on whether --elf option was specified
local template_name
if [[ -n "${LCF_OPTS[use_elf_format]}" ]]; then
template_name="Lcf_Gnuc_Tricore_elf.lsl.in"
else
template_name="Lcf_Gnuc_Tricore_Tc.lsl.in"
fi
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" \
@ -363,6 +517,15 @@ while [[ $# -gt 0 ]]; do
KEYGEN_OPTS[nolocalkeys]="1"
shift
;;
--elf)
ELF="1"
# Set ELF mode for relevant command options
SIGN_OPTS[file_ext]=".elf"
LCF_OPTS[use_elf_format]="1"
TARGET_OPTS[use_elf_format]="1"
MACROS_OPTS[use_elf_format]="1"
shift
;;
keygen|sign|target|clean|macros|nvm|lcf)
break
;;
@ -391,7 +554,7 @@ while [[ $# -gt 0 ]]; do
;;
target)
OPERATIONS+=("target")
CURRENT_OPTS=""
CURRENT_OPTS="TARGET_OPTS"
shift
;;
clean)
@ -406,7 +569,7 @@ while [[ $# -gt 0 ]]; do
;;
nvm)
OPERATIONS+=("nvm")
CURRENT_OPTS=""
CURRENT_OPTS="NVM_OPTS"
shift
;;
lcf)
@ -473,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
@ -486,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

@ -13,7 +13,7 @@ flash.erase all
flash.reprogram all /erase
data.load.binary "&testApp" 0xA0300000
data.load.binary "&updateApp" 0xA047E000
data.load.binary "&updateApp" 0xA047C000
data.load.elf "&wolfBoot"
flash.reprogram off

View File

@ -0,0 +1,31 @@
system.down
system.up
LOCAL &wolfBoot &testApp &updateApp
&wolfBoot="..\..\..\IDE\AURIX\wolfBoot-tc3xx\TriCore Debug (GCC)\wolfBoot-tc3xx.elf"
&testApp="..\..\..\IDE\AURIX\test-app\TriCore Debug (GCC)\test-app_v1_signed.bin"
&updateApp="..\..\..\IDE\AURIX\test-app\TriCore Debug (GCC)\test-app_v2_signed.bin"
DO ~~/demo/tricore/flash/tc37x.cmm PREPAREONLY
flash.erase all
flash.reprogram all /erase
data.load.binary "&testApp" 0xA047C000
data.load.binary "&updateApp" 0xA053C000
data.load.elf "&wolfBoot"
flash.reprogram off
break.set core0_main
system.down
system.up
go
; Bring up the important views
SYStem
Frame /Locals /Caller
List

View File

@ -0,0 +1,31 @@
system.down
system.up
LOCAL &wolfBoot &testApp &updateApp
&wolfBoot="..\..\..\IDE\AURIX\wolfBoot-tc3xx\TriCore Release (GCC)\wolfBoot-tc3xx.elf"
&testApp="..\..\..\IDE\AURIX\test-app\TriCore Release (GCC)\test-app_v1_signed.bin"
&updateApp="..\..\..\IDE\AURIX\test-app\TriCore Release (GCC)\test-app_v2_signed.bin"
DO ~~/demo/tricore/flash/tc37x.cmm PREPAREONLY
flash.erase all
flash.reprogram all /erase
data.load.binary "&testApp" 0xA047C000
data.load.binary "&updateApp" 0xA053C000
data.load.elf "&wolfBoot"
flash.reprogram off
break.set core0_main
system.down
system.up
go
; Bring up the important views
SYStem
Frame /Locals /Caller
List

View File

@ -13,7 +13,7 @@ flash.erase all
flash.reprogram all /erase
data.load.binary "&testApp" 0xA0300000
data.load.binary "&updateApp" 0xA047E000
data.load.binary "&updateApp" 0xA047C000
data.load.elf "&wolfBoot"
flash.reprogram off

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

@ -16,7 +16,7 @@ FLASH.Erase 0xA00A0000--0xA05FFFFF
flash.reprogram all /erase
data.load.binary "&testApp" 0xA0300000
data.load.binary "&updateApp" 0xA047E000
data.load.binary "&updateApp" 0xA047C000
data.load.elf "&wolfBoot"
flash.reprogram off

View File

@ -16,7 +16,7 @@ FLASH.Erase 0xA00A0000--0xA05FFFFF
flash.reprogram all /erase
data.load.binary "&testApp" 0xA0300000
data.load.binary "&updateApp" 0xA047E000
data.load.binary "&updateApp" 0xA047C000
data.load.elf "&wolfBoot"
flash.reprogram off

View File

@ -0,0 +1,36 @@
# wolfBoot Squash ELF Tool
CC = gcc
LD = gcc
CFLAGS = -Wall -Wextra -Werror
LDFLAGS =
TARGET = squashelf
CFLAGS_EXTRA = # Allow additional flags to be passed via command line
# option variables
DEBUG_FLAGS = -g -DDEBUG
OPTIMIZE = -O2
# Options
ifeq ($(DEBUG),1)
CFLAGS+=$(DEBUG_FLAGS)
else
CFLAGS+=$(OPTIMIZE)
endif
.PHONY: clean all debug
all: $(TARGET)
debug: CFLAGS+=$(DEBUG_FLAGS)
debug: all
$(TARGET): $(TARGET).o
@echo "Building squashelf tool"
$(CC) -o $@ $< $(LDFLAGS) $(CFLAGS_EXTRA)
%.o: %.c
$(CC) $(CFLAGS) $(CFLAGS_EXTRA) -c -o $@ $<
clean:
rm -f $(TARGET) *.o

View File

@ -0,0 +1,62 @@
# squashelf
`squashelf` is a command-line utility that processes ELF (Executable and Linkable Format) files. It extracts `PT_LOAD` segments, optionally filters them based on specified Load Memory Address (LMA) ranges, sorts them by LMA, and writes them to a new, reorganized ELF file. The output ELF file contains only the selected `PT_LOAD` segments and their corresponding data, potentially omitting the Section Header Table (SHT).
## Purpose
This tool can be useful for:
* Creating stripped-down ELF files containing only loadable code and data segments.
* Preparing ELF files for specific bootloaders or embedded systems environments that primarily work with `PT_LOAD` segments.
## Usage
```bash
squashelf [options] <input.elf> <output.elf>
```
## Options
* `-n`, `--nosht`:
Omit the Section Header Table (SHT) from the output ELF. By default, a minimal SHT with a single NULL section is created. Omitting the SHT shouldn't have any effect on loaders that only use PT_LOAD segments, but may cause tools like readelf to complain. Leave it in for max compatibility, or remove it for the smallest possible elf file.
* `-r <min>-<max>[,<min>-<max>...]`, `--range <min>-<max>[,<min>-<max>...]`:
Specify one or more LMA ranges. Only `PT_LOAD` segments fully contained within any of these ranges (inclusive of `min`, exclusive of `max`) will be included in the output. Addresses can be provided in decimal or hexadecimal (using `0x` prefix).
Multiple ranges can be specified by separating them with commas.
Example: `-r 0x10000-0x20000,0x30000-0x40000` or `-r 65536-131072,196608-262144`.
* `-v`, `--verbose`:
Enable verbose output, providing detailed information about the processing steps, segment selection, and file operations.
* `-z`, `--zero-size-segments`:
Include segments with zero file size in the output. By default, these segments are excluded.
* `-h`, `--help`:
Display a help message with detailed information about all available options and examples.
## Examples
* Extract all `PT_LOAD` segments from `input.elf`, sort them by LMA, and write them to `output_all.elf` with a minimal SHT:
```bash
squashelf input.elf output_all.elf
```
* Extract `PT_LOAD` segments from `input.elf` that fall within the LMA range `0x80000000` to `0x8FFFFFFF`, omit the SHT, and write the result to `output_filtered.elf`:
```bash
squashelf --nosht --range 0x80000000-0x8FFFFFFF input.elf output_filtered.elf
```
* Extract segments from multiple memory regions with verbose output:
```bash
squashelf -v --range 0x10000000-0x20000000,0x30000000-0x40000000 input.elf output_multi.elf
```
* Include zero-size segments and show detailed processing information:
```bash
squashelf -v -z --range 0x10000000-0x20000000 input.elf output_with_zeros.elf
```
* Display help message with all options and examples:
```bash
squashelf --help
```

View File

@ -0,0 +1,953 @@
/* squashelf.c
*
* ELF file squasher
*
* Run on HOST machine to preprocess (squash) ELF files for the wolfBoot flash
* updater by extracting PT_LOAD segments, optionally filtering them based on
* specified LMA ranges, sorting them by LMA, and writing them to a new,
* reorganized ELF file. See README.md for more information.
*
* Copyright (C) 2025 wolfSSL Inc.
*
* This file is part of wolfBoot.
*
* wolfBoot is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfBoot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <getopt.h>
#include <stdint.h>
#include <stdarg.h> /* Needed for variadic macros */
#include <stdbool.h> /* Needed for bool type */
#include "../../include/elf.h"
/* Macro for verbose printing */
#define DEBUG_PRINT(fmt, ...) \
do { \
if (verbose) \
fprintf(stderr, fmt, ##__VA_ARGS__); \
} while (0)
/* Constants needed from libelf/gelf.h but not in wolfBoot's elf.h */
#define EI_NIDENT 16
#define SHN_UNDEF 0
/* Structure to store an address range */
typedef struct {
uint64_t min;
uint64_t max;
} AddressRange;
/*
* comparePhdr:
* qsort comparator ordering program headers by load address (p_paddr).
* Sorts ascending so segments land in increasing memory order.
*/
static int comparePhdr32(const void* a, const void* b)
{
const elf32_program_header* pa = a;
const elf32_program_header* pb = b;
if (pa->paddr < pb->paddr) {
return -1;
}
if (pa->paddr > pb->paddr) {
return 1;
}
return 0;
}
static int comparePhdr64(const void* a, const void* b)
{
const elf64_program_header* pa = a;
const elf64_program_header* pb = b;
if (pa->paddr < pb->paddr) {
return -1;
}
if (pa->paddr > pb->paddr) {
return 1;
}
return 0;
}
/* Function to parse a single range string */
static int parseRange(const char* rangeStr, AddressRange* range)
{
char* copyStr = strdup(rangeStr);
if (!copyStr) {
return 0;
}
/* Parse the range string (e.g., "0xA00000000-0xB0000000") */
char* dashPos = strchr(copyStr, '-');
if (!dashPos) {
free(copyStr);
return 0;
}
/* Split the string */
*dashPos = '\0';
char* minStr = copyStr;
char* maxStr = dashPos + 1;
range->min = strtoull(minStr, NULL, 0);
range->max = strtoull(maxStr, NULL, 0);
free(copyStr);
if (range->min >= range->max) {
return 0;
}
return 1;
}
/* Function to check if an address is within any of the specified ranges */
static bool isInRanges(uint64_t addr, AddressRange* ranges, int rangeCount)
{
for (int i = 0; i < rangeCount; i++) {
if (addr >= ranges[i].min && addr <= ranges[i].max) {
return true;
}
}
return false;
}
/* Function to check if two ranges overlap */
static bool rangesOverlap(const AddressRange* a, const AddressRange* b)
{
return (a->min <= b->max && b->min <= a->max);
}
/* Function to check if any ranges in the array overlap */
static bool hasOverlappingRanges(AddressRange* ranges, int rangeCount)
{
for (int i = 0; i < rangeCount; i++) {
for (int j = i + 1; j < rangeCount; j++) {
if (rangesOverlap(&ranges[i], &ranges[j])) {
return true;
}
}
}
return false;
}
/* Function to parse range argument and populate ranges array */
static bool parseRangeArgument(const char* optarg, AddressRange** ranges,
int* rangeCount, int verbose)
{
/* First, count the number of ranges (commas + 1) */
const char* ptr = optarg;
*rangeCount = 1;
while ((ptr = strchr(ptr, ',')) != NULL) {
(*rangeCount)++;
ptr++;
}
/* Allocate memory for ranges */
*ranges = malloc(*rangeCount * sizeof(AddressRange));
if (!*ranges) {
fprintf(stderr, "Memory allocation failed\n");
return false;
}
/* Parse each range */
char* rangeStr = strdup(optarg);
if (!rangeStr) {
fprintf(stderr, "Memory allocation failed\n");
free(*ranges);
*ranges = NULL;
return false;
}
char* token;
char* saveptr;
int currRange = 0;
token = strtok_r(rangeStr, ",", &saveptr);
while (token != NULL && currRange < *rangeCount) {
if (!parseRange(token, &(*ranges)[currRange])) {
fprintf(stderr, "Invalid range format in '%s'. Expected: min-max\n",
token);
free(rangeStr);
free(*ranges);
*ranges = NULL;
return false;
}
DEBUG_PRINT("Range %d: 0x%lx - 0x%lx\n", currRange + 1,
(*ranges)[currRange].min, (*ranges)[currRange].max);
currRange++;
token = strtok_r(NULL, ",", &saveptr);
}
free(rangeStr);
if (currRange != *rangeCount) {
fprintf(stderr, "Error parsing ranges\n");
free(*ranges);
*ranges = NULL;
return false;
}
/* Check for overlapping ranges */
if (hasOverlappingRanges(*ranges, *rangeCount)) {
fprintf(stderr,
"Warning: Address ranges contain overlapping regions.\n");
}
return true;
}
/* Function to print detailed help message */
static void printHelp(const char* programName)
{
printf("Usage: %s [options] <input.elf> <output.elf>\n\n", programName);
printf("Process ELF files by extracting PT_LOAD segments, optionally "
"filtering them based on\n");
printf("specified Load Memory Address (LMA) ranges, sorting them by LMA, "
"and writing them to\n");
printf("a new, reorganized ELF file.\n\n");
printf("Options:\n");
printf(" -n, --nosht Omit the Section Header Table (SHT) "
"from the output ELF.\n");
printf(" By default, a minimal SHT with a "
"single NULL section is created.\n");
printf(" Omitting the SHT shouldn't have any "
"effect on loaders that only\n");
printf(" use PT_LOAD segments, but may cause "
"tools like readelf to complain.\n");
printf(" Leave it in for max compatibility, "
"or remove it for the smallest\n");
printf(" possible elf file.\n\n");
printf(" -r, --range <min>-<max>[,<min>-<max>...]\n");
printf(" Specify one or more LMA ranges. Only "
"PT_LOAD segments fully\n");
printf(" contained within any of these ranges "
"(inclusive of min, exclusive\n");
printf(" of max) will be included in the "
"output. Addresses can be provided\n");
printf(" in decimal or hexadecimal (using 0x "
"prefix).\n");
printf(" Multiple ranges can be specified by "
"separating them with commas.\n");
printf(" Example: -r "
"0x10000-0x20000,0x30000-0x40000\n\n");
printf(" -v, --verbose Enable verbose output, providing "
"detailed information about\n");
printf(" the processing steps, segment "
"selection, and file operations.\n\n");
printf(" -z, --zero-size-segments Include segments with zero file size "
"in the output.\n");
printf(" By default, these segments are "
"excluded.\n\n");
printf(
" -h, --help Display this help message and exit.\n\n");
printf("Examples:\n");
printf(" %s input.elf output.elf\n", programName);
printf(" Extract all PT_LOAD segments, sort them by LMA, and write to "
"output.elf\n\n");
printf(" %s --nosht --range 0x80000000-0x8FFFFFFF input.elf "
"output_filtered.elf\n",
programName);
printf(" Extract segments within the specified range and omit the "
"SHT\n\n");
printf(" %s -v --range 0x10000000-0x20000000,0x30000000-0x40000000 "
"input.elf output_multi.elf\n",
programName);
printf(" Extract segments from multiple memory regions with verbose "
"output\n\n");
printf(" %s -v -z --range 0x10000000-0x20000000 input.elf "
"output_with_zeros.elf\n",
programName);
printf(" Include zero-size segments and show detailed processing "
"information\n\n");
}
/* Function to print usage message */
static void printUsage(const char* programName)
{
fprintf(stderr,
"Usage: %s [-n | --nosht] [-r | --range "
"min-max[,min-max,...]] "
"[-v | --verbose] [-z | --zero-size-segments] "
"[-h | --help] "
"<input.elf> <output.elf>\n",
programName);
}
/* Read ELF header from file */
static bool read_elf_header(int fd, void* ehdr, int* elfClass, bool* is_elf32)
{
uint8_t ident[EI_NIDENT];
/* Read ELF identification bytes */
if (pread(fd, ident, EI_NIDENT, 0) != EI_NIDENT) {
perror("read ELF identification");
return false;
}
/* Check if this is a valid ELF file */
if (memcmp(ident, ELF_IDENT_STR, 4) != 0) {
fprintf(stderr, "Not a valid ELF file\n");
return false;
}
/* Determine ELF class (32 or 64 bit) */
*elfClass = ident[ELF_CLASS_OFF];
if (*elfClass != ELF_CLASS_32 && *elfClass != ELF_CLASS_64) {
fprintf(stderr, "Unsupported ELF class: %d\n", *elfClass);
return false;
}
*is_elf32 = (*elfClass == ELF_CLASS_32);
/* Read the appropriate header based on class */
if (*is_elf32) {
elf32_header* hdr32 = (elf32_header*)ehdr;
if (pread(fd, hdr32, sizeof(*hdr32), 0) != sizeof(*hdr32)) {
perror("read ELF header");
return false;
}
}
else {
elf64_header* hdr64 = (elf64_header*)ehdr;
if (pread(fd, hdr64, sizeof(*hdr64), 0) != sizeof(*hdr64)) {
perror("read ELF header");
return false;
}
}
return true;
}
/* Read a program header from file */
static bool read_program_header(int fd, void* phdr, bool is_elf32, size_t index,
off_t ph_offset, size_t ph_entsize)
{
off_t offset = ph_offset + (index * ph_entsize);
if (is_elf32) {
elf32_program_header* ph32 = (elf32_program_header*)phdr;
if (pread(fd, ph32, sizeof(*ph32), offset) != sizeof(*ph32)) {
perror("read program header");
return false;
}
}
else {
elf64_program_header* ph64 = (elf64_program_header*)phdr;
if (pread(fd, ph64, sizeof(*ph64), offset) != sizeof(*ph64)) {
perror("read program header");
return false;
}
}
return true;
}
int main(int argCount, char** argValues)
{
bool success = false;
int noSht = 0;
int hasRange = 0;
int allowZeroSizeSeg = 0; /* New flag for zero-size segments */
AddressRange* ranges = NULL;
int rangeCount = 0;
const char* inputFile = NULL;
const char* outputFile = NULL;
int verbose = 0;
int opt;
int option_index = 0; /* For getopt_long */
int inputFd = -1;
int outputFd = -1;
void** data_buffers = NULL;
size_t loadCount = 0;
size_t phdrCount = 0;
int elfClass = 0;
bool is_elf32 = false;
void* phdrs = NULL;
void* outPhdrs = NULL;
/* Squash maybe uninitialized warnings introduced by -Wextra */
phdrs = NULL;
outPhdrs = NULL;
/* Allocate memory for headers */
union {
elf32_header h32;
elf64_header h64;
} elfHeader;
/* Define long options */
static struct option long_options[] = {
{"nosht", no_argument, 0, 'n'}, /* --nosht is equivalent to -n */
{"range", required_argument, 0, 'r'}, /* --range is equivalent to -r */
{"verbose", no_argument, 0, 'v'}, /* --verbose is equivalent to -v */
{"zero-size-segments", no_argument, 0, 'z'}, /* --zero-size-segments */
{"help", no_argument, 0, 'h'}, /* --help is equivalent to -h */
{0, 0, 0, 0}};
/* Use getopt_long to parse command-line options */
optind = 1; /* Reset optind */
while ((opt = getopt_long(argCount, argValues, "nr:vzh", long_options,
&option_index)) != -1) {
switch (opt) {
case 'n':
noSht = 1;
break;
case 'r': {
hasRange = 1;
if (!parseRangeArgument(optarg, &ranges, &rangeCount,
verbose)) {
return EXIT_FAILURE;
}
} break;
case 'v':
verbose = 1;
break;
case 'z':
allowZeroSizeSeg = 1;
break;
case 'h':
printHelp(argValues[0]);
return EXIT_SUCCESS;
case '?': /* getopt_long prints an error message */
printUsage(argValues[0]);
if (ranges) {
free(ranges);
}
return EXIT_FAILURE;
default:
/* Should not happen */
if (ranges) {
free(ranges);
}
abort();
}
}
/* Check for the correct number of positional arguments */
if (optind + 2 != argCount) {
printUsage(argValues[0]);
if (ranges) {
free(ranges);
}
return EXIT_FAILURE;
}
inputFile = argValues[optind];
outputFile = argValues[optind + 1];
/* Print initial configuration if verbose */
DEBUG_PRINT("Verbose mode enabled.\n");
DEBUG_PRINT("Input file: %s\n", inputFile);
DEBUG_PRINT("Output file: %s\n", outputFile);
DEBUG_PRINT("No SHT: %s\n", noSht ? "yes" : "no");
DEBUG_PRINT("Allow zero-size segments: %s\n",
allowZeroSizeSeg ? "yes" : "no");
if (hasRange) {
DEBUG_PRINT("Range filter: %d ranges specified\n", rangeCount);
for (int i = 0; i < rangeCount; i++) {
DEBUG_PRINT(" Range %d: 0x%lx - 0x%lx\n", i + 1, ranges[i].min,
ranges[i].max);
}
}
/* Open input ELF file for reading */
inputFd = open(inputFile, O_RDONLY);
if (inputFd < 0) {
perror("open inputFile");
goto cleanup;
}
DEBUG_PRINT("Opened input file: %s (fd: %d)\n", inputFile, inputFd);
/* Read ELF header */
if (!read_elf_header(inputFd, &elfHeader, &elfClass, &is_elf32)) {
fprintf(stderr, "Failed to read ELF header\n");
goto cleanup;
}
DEBUG_PRINT("Detected ELF class: %s\n", is_elf32 ? "ELF32" : "ELF64");
/* Get program header count */
if (is_elf32) {
phdrCount = elfHeader.h32.ph_entry_count;
DEBUG_PRINT("Read input ELF header. Program header count: %u\n",
elfHeader.h32.ph_entry_count);
}
else {
phdrCount = elfHeader.h64.ph_entry_count;
DEBUG_PRINT("Read input ELF header. Program header count: %u\n",
elfHeader.h64.ph_entry_count);
}
DEBUG_PRINT("Confirmed program header count: %zu\n", phdrCount);
if (is_elf32) {
phdrs = malloc(phdrCount * sizeof(elf32_program_header));
if (!phdrs) {
perror("malloc phdrs");
goto cleanup;
}
}
else {
phdrs = malloc(phdrCount * sizeof(elf64_program_header));
if (!phdrs) {
perror("malloc phdrs");
goto cleanup;
}
}
/* Extract only PT_LOAD segments from the input PHT */
for (size_t i = 0; i < phdrCount; i++) {
union {
elf32_program_header h32;
elf64_program_header h64;
} ph;
if (!read_program_header(inputFd, &ph, is_elf32, i,
is_elf32 ? elfHeader.h32.ph_offset
: elfHeader.h64.ph_offset,
is_elf32 ? elfHeader.h32.ph_entry_size
: elfHeader.h64.ph_entry_size)) {
continue;
}
uint32_t p_type = is_elf32 ? ph.h32.type : ph.h64.type;
if (p_type == ELF_PT_LOAD) {
uint64_t p_filesz = is_elf32 ? ph.h32.file_size : ph.h64.file_size;
uint64_t p_paddr = is_elf32 ? ph.h32.paddr : ph.h64.paddr;
uint64_t p_memsz = is_elf32 ? ph.h32.mem_size : ph.h64.mem_size;
/* Skip segments with zero filesz unless explicitly allowed */
if (p_filesz == 0 && !allowZeroSizeSeg) {
DEBUG_PRINT(" Skipping segment %zu (LMA 0x%lx) - "
"zero filesz\n",
i, (unsigned long)p_paddr);
continue;
}
/* Apply range filter if specified */
if (hasRange) {
uint64_t segmentStart = p_paddr;
uint64_t segmentEnd = p_paddr + p_memsz - 1;
/* Check if segment start and end are both within any range */
bool startInRange =
isInRanges(segmentStart, ranges, rangeCount);
bool endInRange = isInRanges(segmentEnd, ranges, rangeCount);
if (!startInRange || !endInRange) {
DEBUG_PRINT(" Skipping segment %zu (LMA 0x%lx - 0x%lx) - "
"outside specified ranges\n",
i, (unsigned long)segmentStart,
(unsigned long)segmentEnd);
continue;
}
}
/* Add the segment to the loadable segments array */
if (is_elf32) {
elf32_program_header* ph32_array = (elf32_program_header*)phdrs;
memcpy(&ph32_array[loadCount], &ph.h32,
sizeof(elf32_program_header));
DEBUG_PRINT(
" Keeping segment %zu (LMA 0x%lx, size 0x%lx/0x%lx, "
"offset 0x%lx, align %lu)\n",
i, (unsigned long)ph.h32.paddr,
(unsigned long)ph.h32.file_size,
(unsigned long)ph.h32.mem_size,
(unsigned long)ph.h32.offset, (unsigned long)ph.h32.align);
}
else {
elf64_program_header* ph64_array = (elf64_program_header*)phdrs;
memcpy(&ph64_array[loadCount], &ph.h64,
sizeof(elf64_program_header));
DEBUG_PRINT(
" Keeping segment %zu (LMA 0x%lx, size 0x%lx/0x%lx, "
"offset 0x%lx, align %lu)\n",
i, (unsigned long)ph.h64.paddr,
(unsigned long)ph.h64.file_size,
(unsigned long)ph.h64.mem_size,
(unsigned long)ph.h64.offset, (unsigned long)ph.h64.align);
}
loadCount++;
}
else {
DEBUG_PRINT(" Skipping segment %zu (type %u)\n", i, p_type);
}
}
DEBUG_PRINT("Found %zu PT_LOAD segments matching criteria.\n", loadCount);
if (loadCount == 0) {
fprintf(stderr, "No PT_LOAD segments found\n");
goto cleanup;
}
/* Allocate memory for the output program headers */
if (is_elf32) {
/* Sort the loadable segments by their LMA (paddr) */
qsort(phdrs, loadCount, sizeof(elf32_program_header), comparePhdr32);
outPhdrs = malloc(loadCount * sizeof(elf32_program_header));
if (!outPhdrs) {
perror("malloc outPhdrs");
goto cleanup;
}
memcpy(outPhdrs, phdrs, loadCount * sizeof(elf32_program_header));
}
else {
/* Sort the loadable segments by their LMA (paddr) */
qsort(phdrs, loadCount, sizeof(elf64_program_header), comparePhdr64);
outPhdrs = malloc(loadCount * sizeof(elf64_program_header));
if (!outPhdrs) {
perror("malloc outPhdrs");
goto cleanup;
}
memcpy(outPhdrs, phdrs, loadCount * sizeof(elf64_program_header));
}
DEBUG_PRINT("Sorted PT_LOAD segments by LMA.\n");
/* Allocate storage for segment data */
data_buffers = calloc(loadCount, sizeof(void*));
if (!data_buffers) {
perror("calloc data_buffers");
goto cleanup;
}
/* Read segment data from input file */
for (size_t i = 0; i < loadCount; i++) {
uint64_t p_offset, p_filesz;
if (is_elf32) {
elf32_program_header* ph32_array = (elf32_program_header*)outPhdrs;
p_offset = ph32_array[i].offset;
p_filesz = ph32_array[i].file_size;
}
else {
elf64_program_header* ph64_array = (elf64_program_header*)outPhdrs;
p_offset = ph64_array[i].offset;
p_filesz = ph64_array[i].file_size;
}
if (p_filesz > 0) {
data_buffers[i] = malloc(p_filesz);
if (!data_buffers[i]) {
perror("malloc segment buffer");
goto cleanup;
}
/* Read the data */
ssize_t bytes_read =
pread(inputFd, data_buffers[i], p_filesz, p_offset);
if (bytes_read < 0) {
perror("pread segment data");
goto cleanup;
}
else if ((size_t)bytes_read != p_filesz) {
fprintf(stderr,
"Short read for segment %zu (expected %lu, got %zd)\n",
i, (unsigned long)p_filesz, bytes_read);
goto cleanup;
}
DEBUG_PRINT("Read %zu bytes for segment %zu\n", (size_t)p_filesz,
i);
}
}
/* Open output file for writing */
outputFd = open(outputFile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (outputFd < 0) {
perror("open outputFile");
goto cleanup;
}
DEBUG_PRINT("Opened output file: %s (fd: %d)\n", outputFile, outputFd);
/*
* Now we manually construct the output ELF file in this format:
* [ELF header][Program Header Table][Loadable Segments][Optional Section
* Header Table]
*/
/* Step 1: Calculate file layout */
size_t ehdr_size = (is_elf32) ? sizeof(elf32_header) : sizeof(elf64_header);
size_t phdr_size = (is_elf32) ? sizeof(elf32_program_header)
: sizeof(elf64_program_header);
/* Calculate PHT offset and size */
size_t pht_offset = ehdr_size;
size_t pht_size = loadCount * phdr_size;
/* Calculate segment offsets and update PHDRs */
size_t current_offset = pht_offset + pht_size;
/* No forced global alignment - we'll respect each segment's individual
* alignment */
DEBUG_PRINT("Starting segment layout at offset: 0x%lx\n", current_offset);
/* Update segment offsets */
for (size_t i = 0; i < loadCount; i++) {
uint64_t p_align, p_filesz;
if (is_elf32) {
elf32_program_header* ph32_array = (elf32_program_header*)outPhdrs;
p_align = ph32_array[i].align;
/* Align the segment according to its alignment requirement if
* needed */
if (p_align > 1) {
current_offset =
(current_offset + p_align - 1) & ~(p_align - 1);
}
/* Update the segment's offset */
ph32_array[i].offset = current_offset;
p_filesz = ph32_array[i].file_size;
DEBUG_PRINT(" Segment %zu offset: 0x%lx\n", i,
(unsigned long)current_offset);
}
else {
elf64_program_header* ph64_array = (elf64_program_header*)outPhdrs;
p_align = ph64_array[i].align;
/* Align the segment according to its alignment requirement if
* needed */
if (p_align > 1) {
current_offset =
(current_offset + p_align - 1) & ~(p_align - 1);
}
/* Update the segment's offset */
ph64_array[i].offset = current_offset;
p_filesz = ph64_array[i].file_size;
DEBUG_PRINT(" Segment %zu offset: 0x%lx\n", i,
(unsigned long)current_offset);
}
/* Move to next position */
current_offset += p_filesz;
}
/* Calculate SHT offset if needed */
size_t sht_offset = 0;
if (!noSht) {
/* Align SHT to 8-byte boundary */
current_offset = (current_offset + 7) & ~7;
sht_offset = current_offset;
}
/* Step 2: Prepare and write ELF header */
/* Update header fields */
if (is_elf32) {
elfHeader.h32.ph_offset = pht_offset;
elfHeader.h32.ph_entry_count = loadCount;
if (noSht) {
elfHeader.h32.sh_offset = 0;
elfHeader.h32.sh_entry_count = 0;
elfHeader.h32.sh_str_index = SHN_UNDEF;
}
else {
elfHeader.h32.sh_offset = sht_offset;
elfHeader.h32.sh_entry_count = 1; /* Just the NULL section */
elfHeader.h32.sh_str_index = SHN_UNDEF;
}
/* Write ELF header to output file */
if (write(outputFd, &elfHeader.h32, sizeof(elfHeader.h32)) !=
sizeof(elfHeader.h32)) {
perror("write ELF header");
goto cleanup;
}
}
else {
elfHeader.h64.ph_offset = pht_offset;
elfHeader.h64.ph_entry_count = loadCount;
if (noSht) {
elfHeader.h64.sh_offset = 0;
elfHeader.h64.sh_entry_count = 0;
elfHeader.h64.sh_str_index = SHN_UNDEF;
}
else {
elfHeader.h64.sh_offset = sht_offset;
elfHeader.h64.sh_entry_count = 1; /* Just the NULL section */
elfHeader.h64.sh_str_index = SHN_UNDEF;
}
/* Write ELF header to output file */
if (write(outputFd, &elfHeader.h64, sizeof(elfHeader.h64)) !=
sizeof(elfHeader.h64)) {
perror("write ELF header");
goto cleanup;
}
}
DEBUG_PRINT("Wrote ELF header to output file.\n");
/* Step 3: Write Program Header Table */
if (is_elf32) {
elf32_program_header* ph32_array = (elf32_program_header*)outPhdrs;
for (size_t i = 0; i < loadCount; i++) {
if (write(outputFd, &ph32_array[i], sizeof(ph32_array[i])) !=
sizeof(ph32_array[i])) {
perror("write program header");
goto cleanup;
}
}
}
else {
elf64_program_header* ph64_array = (elf64_program_header*)outPhdrs;
for (size_t i = 0; i < loadCount; i++) {
if (write(outputFd, &ph64_array[i], sizeof(ph64_array[i])) !=
sizeof(ph64_array[i])) {
perror("write program header");
goto cleanup;
}
}
}
DEBUG_PRINT("Wrote Program Header Table (%zu entries).\n", loadCount);
/* Step 4: Write segment data */
for (size_t i = 0; i < loadCount; i++) {
uint64_t p_offset, p_filesz;
if (is_elf32) {
elf32_program_header* ph32_array = (elf32_program_header*)outPhdrs;
p_offset = ph32_array[i].offset;
p_filesz = ph32_array[i].file_size;
}
else {
elf64_program_header* ph64_array = (elf64_program_header*)outPhdrs;
p_offset = ph64_array[i].offset;
p_filesz = ph64_array[i].file_size;
}
/* Seek to the offset where this segment should be written */
if (lseek(outputFd, p_offset, SEEK_SET) != (off_t)p_offset) {
perror("lseek to segment offset");
goto cleanup;
}
/* Skip segments with zero file size */
if (p_filesz == 0) {
DEBUG_PRINT(" Segment %zu has zero filesz, skipping data write\n",
i);
continue;
}
/* Write the segment data */
ssize_t bytes_written = write(outputFd, data_buffers[i], p_filesz);
if (bytes_written < 0) {
perror("write segment data");
goto cleanup;
}
else if ((size_t)bytes_written != p_filesz) {
fprintf(stderr,
"Short write for segment %zu (expected %lu, wrote %zd)\n",
i, (unsigned long)p_filesz, bytes_written);
goto cleanup;
}
DEBUG_PRINT(" Wrote segment %zu data (0x%lx bytes at offset 0x%lx)\n",
i, (unsigned long)p_filesz, (unsigned long)p_offset);
}
/* Step 5: Write Section Header Table if not using --nosht */
if (!noSht) {
/* Seek to the Section Header Table offset */
if (lseek(outputFd, sht_offset, SEEK_SET) != (off_t)sht_offset) {
perror("lseek to SHT offset");
goto cleanup;
}
/* Write a NULL section header (all zeros) */
if (is_elf32) {
elf32_section_header shdr32 = {0}; /* All zeros for NULL section */
if (write(outputFd, &shdr32, sizeof(shdr32)) != sizeof(shdr32)) {
perror("write NULL section header");
goto cleanup;
}
}
else {
elf64_section_header shdr64 = {0}; /* All zeros for NULL section */
if (write(outputFd, &shdr64, sizeof(shdr64)) != sizeof(shdr64)) {
perror("write NULL section header");
goto cleanup;
}
}
DEBUG_PRINT("Wrote NULL section header at offset 0x%lx\n", sht_offset);
}
DEBUG_PRINT("Successfully wrote output ELF file.\n");
success = true;
cleanup:
/* Clean up resources */
if (data_buffers) {
for (size_t i = 0; i < loadCount; i++) {
free(data_buffers[i]);
}
free(data_buffers);
}
if (phdrs) {
free(phdrs);
}
if (outPhdrs) {
free(outPhdrs);
}
if (inputFd >= 0) {
close(inputFd);
}
if (outputFd >= 0) {
close(outputFd);
}
if (ranges) {
free(ranges);
}
return (success ? EXIT_SUCCESS : EXIT_FAILURE);
}

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 \