Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 162 additions & 0 deletions .github/workflows/wolfboot-tz-integration.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
# Nightly integration: build wolfBoot's STM32H5 TrustZone demo against
# the latest wolfHSM main and run it under the m33mu emulator. This
# catches drift between wolfHSM and wolfBoot main (in either direction)
# that the pinned-submodule per-PR builds do not. The full client suite
# runs software RSA and ECC, which is slow under the emulator, so this
# is a nightly job rather than a per-PR gate.
#
# On failure it opens (or updates) a tracking issue assigned to
# wolfSSL-Bot.

name: wolfBoot TrustZone integration nightly (m33mu)

on:
schedule:
# 07:00 UTC daily
- cron: '0 7 * * *'
workflow_dispatch:

permissions:
contents: read
issues: write

jobs:
wolfboot-stm32h5-tz-wolfhsm:
runs-on: ubuntu-latest
timeout-minutes: 60
container:
image: ghcr.io/wolfssl/wolfboot-ci-m33mu:latest

steps:
- name: Checkout wolfHSM (this repo, main)
uses: actions/checkout@v4
with:
path: wolfHSM

- name: Checkout wolfBoot master
uses: actions/checkout@v4
with:
repository: wolfSSL/wolfBoot
path: wolfBoot
# Skip lib/wolfHSM submodule init - we replace it with this
# checkout. Other submodules still need to come down.
submodules: false

- name: Initialise wolfBoot submodules (except lib/wolfHSM)
working-directory: wolfBoot
run: |
git config -f .gitmodules submodule.lib/wolfHSM.update none
git submodule update --init --recursive

- name: Point lib/wolfHSM at this checkout
working-directory: wolfBoot
run: |
rm -rf lib/wolfHSM
ln -s "$GITHUB_WORKSPACE/wolfHSM" lib/wolfHSM
ls -l lib/wolfHSM

- name: Build wolfBoot STM32H5 TZ demo
working-directory: wolfBoot/port/stmicro/stm32h5-tz-wolfhsm
run: make

- name: Run wolfHSM whTest_ClientConfig under m33mu
working-directory: wolfBoot/port/stmicro/stm32h5-tz-wolfhsm
run: |
set -o pipefail
mkdir -p /tmp/m33mu-wolfhsm-persist
cp out/wolfboot.bin out/image_v1_signed.bin /tmp/m33mu-wolfhsm-persist/
cd /tmp/m33mu-wolfhsm-persist
# m33mu timeout below the 60 min job timeout so a hung run
# still produces a log. Software RSA keygen dominates.
m33mu wolfboot.bin image_v1_signed.bin:0x60000 \
--persist --uart-stdout --timeout 3000 \
--expect-bkpt 0x7d --quit-on-faults \
2>&1 | tee /tmp/m33mu-wolfhsm.log

- name: Verify wolfHSM whTest_ClientConfig
run: |
grep -q "wolfHSM whTest_ClientConfig PASSED" /tmp/m33mu-wolfhsm.log
grep -q "RNG DEVID=0x5748534D SUCCESS" /tmp/m33mu-wolfhsm.log
grep -q "AES GCM DEVID=0x5748534D SUCCESS" /tmp/m33mu-wolfhsm.log
grep -q "RSA SUCCESS" /tmp/m33mu-wolfhsm.log
grep -q "ECC ephemeral ECDH SUCCESS" /tmp/m33mu-wolfhsm.log
grep -q "SHA256 DEVID=0x5748534D SUCCESS" /tmp/m33mu-wolfhsm.log
grep -q "HKDF SUCCESS" /tmp/m33mu-wolfhsm.log
grep -q "\\[BKPT\\] imm=0x7d" /tmp/m33mu-wolfhsm.log
grep -q "\\[EXPECT BKPT\\] Success" /tmp/m33mu-wolfhsm.log

- name: Archive emulator log on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: m33mu-wolfhsm.log
path: /tmp/m33mu-wolfhsm.log

- name: Open or update tracking issue on failure
if: failure()
uses: actions/github-script@v7
with:
script: |
const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
const marker = 'nightly-tz-integration';
const title = 'Nightly wolfBoot TrustZone (m33mu) integration failed';
const body = [
'The nightly wolfBoot STM32H5 TrustZone integration run failed.',
'',
`Run: ${runUrl}`,
'',
'This builds wolfBoot main against wolfHSM main and runs the',
'STM32H5 demo under m33mu. A failure usually means a regression',
'in the NSC transport, the wolfBoot port, or a wolfHSM/wolfBoot',
'API drift. The emulator log is attached to the run as an artifact.',
].join('\n');

// Reuse one open tracking issue instead of opening a new one
// every night.
const existing = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
labels: marker,
});

if (existing.data.length > 0) {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: existing.data[0].number,
body: `Still failing as of ${runUrl}`,
});
return;
}

// Best-effort label create (ignore if it already exists).
try {
await github.rest.issues.createLabel({
owner: context.repo.owner,
repo: context.repo.repo,
name: marker,
color: 'b60205',
});
} catch (e) { /* label exists */ }

try {
await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title,
body,
labels: [marker],
assignees: ['wolfSSL-Bot'],
});
} catch (e) {
// Assignee may not be assignable; retry without it so the
// issue still gets filed.
await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title,
body,
labels: [marker],
});
}
78 changes: 78 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,81 @@ clean:
make -C benchmark clean
make -C tools clean
make -C examples clean

# ---- SBOM generation ----
CC ?= cc
WOLFSSL_DIR ?= ../wolfssl
WOLFHSM_CFG_DIR ?= test/config
VERSION = $(shell sed -n 's/^. wolfHSM Release v//p' ChangeLog.md | head -1 | cut -d' ' -f1)
SRCS := $(sort $(wildcard src/*.c))
SBOM_CDX = wolfhsm-$(VERSION).cdx.json
SBOM_SPDX = wolfhsm-$(VERSION).spdx.json

.PHONY: sbom

sbom:
@if [ -z "$(VERSION)" ]; then \
echo "ERROR: could not parse version from ChangeLog.md." >&2; \
exit 1; \
fi
@if [ -z "$(WOLFSSL_DIR)" ] || [ ! -d "$(WOLFSSL_DIR)" ]; then \
echo "ERROR: WOLFSSL_DIR=$(WOLFSSL_DIR) is not a directory." >&2; \
echo " Set WOLFSSL_DIR to your wolfssl source tree." >&2; \
exit 1; \
fi
@if [ ! -f "$(WOLFSSL_DIR)/scripts/gen-sbom" ]; then \
echo "ERROR: $(WOLFSSL_DIR)/scripts/gen-sbom not found." >&2; \
echo " The sbom target needs a wolfSSL source tree that includes" >&2; \
echo " scripts/gen-sbom (wolfSSL PR #10343, pending a future release)." >&2; \
echo " Set WOLFSSL_DIR to such a tree." >&2; \
exit 1; \
fi
@if [ ! -f "$(WOLFHSM_CFG_DIR)/wolfhsm_cfg.h" ]; then \
echo "ERROR: $(WOLFHSM_CFG_DIR)/wolfhsm_cfg.h not found." >&2; \
echo " Set WOLFHSM_CFG_DIR to the directory holding the" >&2; \
echo " wolfhsm_cfg.h (and user_settings.h) your build uses." >&2; \
exit 1; \
fi
@echo "wolfHSM version: $(VERSION)"
@echo "Sources: $(words $(SRCS)) .c files in src/"
@echo "Config: $(WOLFHSM_CFG_DIR)/wolfhsm_cfg.h"
# Effective build config for the SBOM: preprocess wolfhsm/wh_settings.h with
# the same defines and include path the test build compiles under, so the -dM
# dump holds every WOLFHSM_CFG_* option (explicit and defaulted) plus the
# wolfSSL options from user_settings.h — the configuration the library is
# actually built with. Point WOLFHSM_CFG_DIR at the directory holding your
# build's wolfhsm_cfg.h/user_settings.h for an integrator-accurate SBOM.
#
# ponytail: wh_settings.h pulls libc headers (stdint/stdio/strings/stdatomic),
# so ~330 toolchain constants (INT16_MAX, ACCESSPERMS, ...) ride along into
# the SBOM next to the ~175 real config macros. The dump is deliberately NOT
# filtered here: a prefix allowlist would silently drop real options that
# carry no standard prefix (GCM_TABLE_4BIT, FP_MAX_BITS, SINGLE_THREADED).
# The durable fix belongs in gen-sbom's noise filter (wolfSSL PR #10343,
# scripts/gen-sbom _NOISE_MACRO_RE), either of:
# a) provenance filtering: accept a -dD dump and use its #line markers to
# drop macros defined in system headers, or
# b) an --options-baseline flag: subtract a second -dM dump made with the
# same flags minus the -include, plus the libc headers it pulls.
# Once gen-sbom grows that, this recipe needs no change — it already hands
# over the full dump.
@_defines=$$(mktemp "$${TMPDIR:-/tmp}/wolfhsm-defines.XXXXXX") && \
trap 'rm -f "$$_defines"' 0 && \
if ! $(CC) -dM -E -DWOLFHSM_CFG -DWOLFSSL_USER_SETTINGS \
-I. -I$(WOLFHSM_CFG_DIR) -I$(WOLFSSL_DIR) \
-include wolfhsm/wh_settings.h -x c /dev/null >"$$_defines"; then \
echo "ERROR: $(CC) -dM -E on wolfhsm/wh_settings.h failed." >&2; exit 1; \
fi && \
if ! command -v python3 >/dev/null 2>&1; then \
echo "ERROR: python3 not found." >&2; exit 1; \
fi && \
python3 $(WOLFSSL_DIR)/scripts/gen-sbom \
--name wolfhsm \
--version $(VERSION) \
--supplier "wolfSSL Inc." \
--license-file LICENSING \
--options-h "$$_defines" \
--srcs $(SRCS) \

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — SRCS := $(sort $(wildcard src/*.c)) for reproducible ordering.

--cdx-out $(SBOM_CDX) \
--spdx-out $(SBOM_SPDX)
@echo "Done: $(SBOM_CDX) $(SBOM_SPDX)"
55 changes: 55 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,58 @@ please refer to the following resources.
- [wolfHSM Manual](https://www.wolfssl.com/documentation/manuals/wolfhsm/index.html)
- [wolfHSM API Reference](https://www.wolfssl.com/documentation/manuals/wolfhsm/appendix01.html)
- [wolfHSM Examples](https://github.com/wolfSSL/wolfHSM/tree/main/examples)

## SBOM / EU CRA Compliance

wolfHSM generates a Software Bill of Materials (SBOM) in CycloneDX 1.6 and
SPDX 2.3 formats to support compliance with the EU Cyber Resilience Act (CRA).

Generate both SBOMs with the `sbom` Makefile target:

```sh

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 [Low] README manual invocation diverges from the Makefile sbom target · Logic

The README documents a manual gen-sbom invocation that differs from what make sbom actually runs: the README passes --options-h $WOLFSSL_DIR/include/wolfssl/options.h (a checked-in wolfSSL header) whereas the Makefile generates a defines file via $(CC) -dM -E; the README omits --license-file LICENSING, --cdx-out, and --spdx-out that the Makefile passes; and the README derives the version with head -1 ... | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' while the Makefile uses a sed pattern. Two documented-but-divergent invocation paths risk drifting out of sync and produce SBOMs with different content depending on which route a user follows.

Fix: Lead the README section with make sbom WOLFSSL_DIR=... (matching the new target, including --license-file) and keep the raw gen-sbom command as an explicit 'manual invocation' fallback, so both routes produce equivalent SBOMs and the documented flags stay consistent with the Makefile.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — README manual invocation now matches the Makefile target (same flags and version derivation), with the raw command kept as a documented fallback.

make sbom WOLFSSL_DIR=../wolfssl
```

This parses the version from `ChangeLog.md`, collects `src/*.c`, and writes
`wolfhsm-<version>.cdx.json` and `wolfhsm-<version>.spdx.json`.

The SBOM records the build configuration by preprocessing
`wolfhsm/wh_settings.h` against a config directory. `WOLFHSM_CFG_DIR`
(default: `test/config`) selects the directory holding the `wolfhsm_cfg.h`
and `user_settings.h` your build uses — point it at your port's config so the
recorded `WOLFHSM_CFG_*` and wolfSSL options match the library you ship:

```sh
make sbom WOLFSSL_DIR=../wolfssl WOLFHSM_CFG_DIR=path/to/your/config
```

Note: alongside the real config macros, the dump currently includes libc
constants pulled in by `wh_settings.h`'s system includes; see the comment on
the `sbom` target in `Makefile` for why they are not filtered here and what
the planned gen-sbom fix is.

`WOLFSSL_DIR` must point to a wolfssl source tree containing `scripts/gen-sbom`,
which ships in wolfSSL PR #10343 (pending a future wolfSSL release). If the
script is absent the target fails with a message telling you what is missing.

Requires `python3` and `pyspdxtools` (`pip install spdx-tools`).

To invoke `gen-sbom` directly instead of through the target, run the same
command it runs:

```sh
cc -dM -E -DWOLFHSM_CFG -DWOLFSSL_USER_SETTINGS \
-I. -Itest/config -I$WOLFSSL_DIR \
-include wolfhsm/wh_settings.h -x c /dev/null > wolfhsm-defines.h
python3 $WOLFSSL_DIR/scripts/gen-sbom \
--name wolfhsm \
--version $(sed -n 's/^# wolfHSM Release v\([0-9][0-9.]*\).*/\1/p' ChangeLog.md | head -1) \
--supplier "wolfSSL Inc." \
--license-file LICENSING \
--options-h wolfhsm-defines.h \
--srcs src/*.c \
--cdx-out wolfhsm.cdx.json \
--spdx-out wolfhsm.spdx.json
```

For further CRA guidance see [wolfssl/doc/CRA.md](https://github.com/wolfSSL/wolfssl/blob/master/doc/CRA.md).
44 changes: 44 additions & 0 deletions port/armv8m-tz/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# ARMv8-M TrustZone NSC bridge transport

A synchronous wolfHSM transport for ARMv8-M TrustZone targets that
bridges a non-secure client to a secure-world server through a single
Non-Secure Callable (NSC) veneer. See `wh_transport_nsc.h` for the C
API.

## How it works

Client and server share a single secure-callable function. The
non-secure side packs a wolfHSM request into a buffer, calls the
veneer, and waits for the veneer to return with the response written
into a second buffer. The secure side runs the wolfHSM server in the
same process; there is no separate task, no IRQ, and no shared-memory
ring — just a function call across the security boundary.

## Host-side veneer contract

The integrator provides one function with this exact shape:

```c
int wcs_wolfhsm_transmit(const uint8_t *cmd, uint32_t cmdSz,
uint8_t *rsp, uint32_t *rspSz);
```

declared `cmse_nonsecure_entry` from the secure side. On entry,
`cmd[0..cmdSz)` holds the request and `*rspSz` is the maximum response
size the client can accept; on return the function writes the
response into `rsp[0..*rspSz)` and updates `*rspSz` to the actual
size. Return value follows wolfHSM's `WH_ERROR_*` convention.

The veneer must not block on anything outside the secure server; the
non-secure side treats it as a synchronous call.

## Known integrations

- **wolfBoot STM32H5 demo app** at
[`port/stmicro/stm32h5-tz-wolfhsm/`](https://github.com/wolfSSL/wolfBoot/tree/main/port/stmicro/stm32h5-tz-wolfhsm)
in wolfBoot. Reference integration on a NUCLEO-H563ZI;
verified end-to-end on real silicon and on the m33mu emulator.

To add a new integration, copy the veneer skeleton from wolfBoot's
demo, wire `wh_transport_nsc.h` into the non-secure client init, and
write a `whServerCb` table for the secure server.
Loading
Loading