From bea64f3d069dc5eccae7e79cc5393e7a1571eb76 Mon Sep 17 00:00:00 2001 From: JP Hutchins Date: Mon, 22 Jun 2026 13:00:33 -0700 Subject: [PATCH] apps: demonstrate MCUboot sysbuild signing-key custody problem A single SB_CONFIG_BOOT_SIGNATURE_KEY_FILE feeds two images that resolve relative key paths against different bases: the application image against APPLICATION_CONFIG_DIR/WEST_TOPDIR, and the MCUboot image against APPLICATION_CONFIG_DIR/MCUBOOT_DIR. A key referenced relative to the application is therefore not resolvable by the bootloader image, which pushes signing keys into unowned module trees (the custody problem). Add apps/ with runnable sysbuild sub-apps (shared common/src/main.c), README.md (empirically verified resolution matrix) and FINDINGS.md (upstream position + citations). Verified on nrf52840dk/nrf52840, ECDSA-P256: - app_relative_key bare relative -> FAILS, resolves into mcuboot - key_via_cmake_list_dir ${CMAKE_CURRENT_LIST_DIR} -> FAILS, resolves into zephyr - key_in_mcuboot_module key copied into mcuboot -> builds, dirties the module - key_in_app_dir ${APP_DIR} -> builds, key stays in the app (+ twister test) ${APP_DIR} is the only relative form that keeps the key in the user-owned repo and resolves for both images (expanded once in sysbuild scope at share/sysbuild/cmake/modules/sysbuild_extensions.cmake), and it is undocumented -- likely a docs PR upstream. Add mcuboot to the west.yml import allowlist (needed for these examples). Co-Authored-By: Claude Opus 4.8 (1M context) --- apps/FINDINGS.md | 220 ++++++++++++++++++ apps/README.md | 136 +++++++++++ apps/app_relative_key/CMakeLists.txt | 13 ++ apps/app_relative_key/README.md | 32 +++ .../keys/insecure-ec-p256.pem | 5 + apps/app_relative_key/prj.conf | 5 + apps/app_relative_key/sysbuild.conf | 19 ++ apps/common/src/main.c | 15 ++ apps/key_in_app_dir/CMakeLists.txt | 13 ++ apps/key_in_app_dir/README.md | 57 +++++ apps/key_in_app_dir/keys/insecure-ec-p256.pem | 5 + apps/key_in_app_dir/prj.conf | 5 + apps/key_in_app_dir/sample.yaml | 22 ++ apps/key_in_app_dir/sysbuild.conf | 19 ++ apps/key_in_mcuboot_module/CMakeLists.txt | 13 ++ apps/key_in_mcuboot_module/README.md | 32 +++ .../keys/insecure-ec-p256.pem | 5 + apps/key_in_mcuboot_module/prj.conf | 5 + apps/key_in_mcuboot_module/sysbuild.conf | 21 ++ apps/key_via_cmake_list_dir/CMakeLists.txt | 13 ++ apps/key_via_cmake_list_dir/README.md | 31 +++ .../keys/insecure-ec-p256.pem | 5 + apps/key_via_cmake_list_dir/prj.conf | 5 + apps/key_via_cmake_list_dir/sysbuild.conf | 22 ++ west.yml | 1 + 25 files changed, 719 insertions(+) create mode 100644 apps/FINDINGS.md create mode 100644 apps/README.md create mode 100644 apps/app_relative_key/CMakeLists.txt create mode 100644 apps/app_relative_key/README.md create mode 100644 apps/app_relative_key/keys/insecure-ec-p256.pem create mode 100644 apps/app_relative_key/prj.conf create mode 100644 apps/app_relative_key/sysbuild.conf create mode 100644 apps/common/src/main.c create mode 100644 apps/key_in_app_dir/CMakeLists.txt create mode 100644 apps/key_in_app_dir/README.md create mode 100644 apps/key_in_app_dir/keys/insecure-ec-p256.pem create mode 100644 apps/key_in_app_dir/prj.conf create mode 100644 apps/key_in_app_dir/sample.yaml create mode 100644 apps/key_in_app_dir/sysbuild.conf create mode 100644 apps/key_in_mcuboot_module/CMakeLists.txt create mode 100644 apps/key_in_mcuboot_module/README.md create mode 100644 apps/key_in_mcuboot_module/keys/insecure-ec-p256.pem create mode 100644 apps/key_in_mcuboot_module/prj.conf create mode 100644 apps/key_in_mcuboot_module/sysbuild.conf create mode 100644 apps/key_via_cmake_list_dir/CMakeLists.txt create mode 100644 apps/key_via_cmake_list_dir/README.md create mode 100644 apps/key_via_cmake_list_dir/keys/insecure-ec-p256.pem create mode 100644 apps/key_via_cmake_list_dir/prj.conf create mode 100644 apps/key_via_cmake_list_dir/sysbuild.conf diff --git a/apps/FINDINGS.md b/apps/FINDINGS.md new file mode 100644 index 000000000..f5fb44d6a --- /dev/null +++ b/apps/FINDINGS.md @@ -0,0 +1,220 @@ +# Official position & community findings + +Companion to [`README.md`](README.md). The README documents what these apps +*empirically* do on current `main`; this file documents the *upstream position* +— issues, PRs, docs, and vendor guidance — gathered from a web sweep of the +Zephyr/MCUboot trackers, Nordic DevZone, and official documentation. + +> Provenance & confidence: collected by an automated multi-source search. GitHub +> PR/issue numbers and the official-doc statements are high-confidence. DevZone +> dates are inferred from the referenced NCS versions (paraphrase-grade). Two +> items flagged inline are unreconciled and worth a direct check before you cite +> them in anger. + +## 0. Bottom line (empirically verified on this workspace) + +- A signing key referenced by a **bare relative path** is **not resolvable by + the MCUboot image** — it anchors to `MCUBOOT_DIR`, so the key has to sit inside + the mcuboot module. Putting it at the workspace/repo root does not help (the + bootloader never falls back to `WEST_TOPDIR`). +- The **only relative form that keeps the key in the user-owned app and resolves + for both images is `${APP_DIR}/…`** — and it is **undocumented**. The value is + expanded once by `string(CONFIGURE)` in *sysbuild* scope + (`sysbuild_extensions.cmake:709`), so `${APP_DIR}` is the main app for every + image (identical key for both, no mismatch), while `${CMAKE_CURRENT_LIST_DIR}` + — the form a reader of #59063 would try — expands into the **zephyr** module + and fails. +- The cross-image asymmetry itself is **intentional** (maintainers declined a + `WEST_TOPDIR` fallback for the bootloader, mcuboot PR #2591). So the smallest + correct upstream action is likely a **documentation PR**: state that + `SB_CONFIG_BOOT_SIGNATURE_KEY_FILE` is `string(CONFIGURE)`-expanded in sysbuild + scope and that `${APP_DIR}` is the supported anchor for an in-repo key. A code + fix giving the bootloader an application-relative fallback would also close the + trap, but is currently out of scope by maintainer decision. + +See [`README.md`](README.md) for the full verified resolution matrix and the +runnable apps. + +## 1. Is this a known problem? + +**Yes — and the cross-image asymmetry is intentional, not a bug.** + +The "one relative key path cannot satisfy two images" problem is documented in +**zephyr discussion [#59063](https://github.com/zephyrproject-rtos/zephyr/discussions/59063)** +("Configuring custom bootloader signature key with sysbuild"), stated in exactly +these terms: `CONFIG_BOOT_SIGNATURE_KEY_FILE` (bootloader) anchors to the MCUboot +module dir while `CONFIG_MCUBOOT_SIGNATURE_KEY_FILE` (app) anchors to the +workspace topdir. Corroborating reports of relative paths being concatenated onto +the wrong base: discussion +[#56453](https://github.com/zephyrproject-rtos/zephyr/discussions/56453), DevZone +[#118977](https://devzone.nordicsemi.com/f/nordic-q-a/118977), +[#113221](https://devzone.nordicsemi.com/f/nordic-q-a/113221), +[#65235](https://devzone.nordicsemi.com/f/nordic-q-a/65235). Maintainer awareness +of the broader sysbuild-vs-image relative-anchoring class of bug: +[#73066](https://github.com/zephyrproject-rtos/zephyr/issues/73066) (closed via +PR #73390). + +What is **not** resolved upstream: no doc or merged PR prescribes a single +canonical key location resolvable by both images via one *relative* path. The +encryption-key analog ([#97181](https://github.com/zephyrproject-rtos/zephyr/discussions/97181)) +is still unanswered. + +## 2. Official Zephyr position + +Best articulated by maintainer **nordicjm (Jamie McCrae, Nordic)**: do not rely +on bare relative paths — resolve to absolute at configure time. + +- **[#59063](https://github.com/zephyrproject-rtos/zephyr/discussions/59063)** + (answered): recommended fix is + `SB_CONFIG_BOOT_SIGNATURE_KEY_FILE="${APP_DIR}/my_key.pem"` in `sysbuild.conf`, + or an absolute path. Keys *may* live in the application directory; the path + just has to resolve to absolute. + + **Verified on this workspace (current main):** `${APP_DIR}` works — it is + expanded once by `string(CONFIGURE)` in *sysbuild* scope + (`zephyr/share/sysbuild/cmake/modules/sysbuild_extensions.cmake:709`, where + `APP_DIR` is the main app), so both images get the identical absolute path + (see [`key_in_app_dir`](key_in_app_dir/)). **But `${APP_DIR}` is undocumented** + for this purpose, and the sibling form `${CMAKE_CURRENT_LIST_DIR}` — also + floated in #59063 — *fails* in `sysbuild.conf`, because in that same scope it + expands to the sysbuild module dir inside zephyr (see + [`key_via_cmake_list_dir`](key_via_cmake_list_dir/)). The discussion's other + suggestion, `set(SB_CONFIG_BOOT_SIGNATURE_KEY_FILE ...)` in `sysbuild.cmake`, + was also observed to be overridden by Kconfig and silently fall back to the + insecure default key. Net: the one reliable in-repo form is undocumented and + the documented-looking alternatives are wrong — **this points at a docs PR** + (state that the value is `string(CONFIGURE)`-expanded in sysbuild scope and + that `${APP_DIR}` is the supported anchor), independent of any code fix to the + bare-relative asymmetry. +- **[#85270](https://github.com/zephyrproject-rtos/zephyr/issues/85270)** (closed + "not planned", 2025-02-06): the supported knob is the sysbuild-scope + `SB_CONFIG_BOOT_SIGNATURE_KEY_FILE` + a `SB_CONFIG_BOOT_SIGNATURE_TYPE_*` + selection, not the per-image `CONFIG_MCUBOOT_SIGNATURE_KEY_FILE`. + +**Source (zephyr main):** `cmake/mcuboot.cmake` resolves, in order, absolute → +`${APPLICATION_CONFIG_DIR}/` (if it exists) → `${WEST_TOPDIR}/`; an +in-code comment marks the `WEST_TOPDIR` fallback as backward-compat from the old +`west sign` callouts. The same loop covers both signing and encryption keys. + +**Docs gap:** the [Signing Binaries](https://docs.zephyrproject.org/latest/build/signing/index.html) +page only says the bundled key is "the insecure default … for development and +testing" and "if you have your own key, change this appropriately" — it is silent +on `APPLICATION_CONFIG_DIR` / `WEST_TOPDIR` / `MCUBOOT_DIR` and the dual-image +propagation. [west sign](https://docs.zephyrproject.org/latest/develop/west/sign.html) +documents only the app-side ("relative to the west workspace topdir") half of the +asymmetry. [sysbuild/images](https://docs.zephyrproject.org/latest/build/sysbuild/images.html) +confirms the *intended design* that one `SB_CONFIG_BOOT_SIGNATURE_KEY_FILE` drives +both images and that `FILE_SUFFIX` can give them different keys — but still says +nothing about where the key should live. + +## 3. Official Nordic / NCS position + +- **NCS docs** (`bootloader_signature_keys`): the clearest official statement + anywhere — **"Use only absolute paths"** for `SB_CONFIG_BOOT_SIGNATURE_KEY_FILE`, + the key must be set in `sysbuild.conf` to be honored, defaults/auto-generated + keys are dev-only, and store your private key **outside the build directory**. +- **Relative base is broken on NCS:** DevZone + [#113899](https://devzone.nordicsemi.com/f/nordic-q-a/113899) (NCS 2.7.0, + `${APPLICATION_CONFIG_DIR}` expanded to empty → `FileNotFoundError: /keys/...`; + Nordic confirmed an SDK bug, sdk-nrf PR #16894) and + [#115651](https://devzone.nordicsemi.com/f/nordic-q-a/115651) (`${APP_DIR}` + unexpanded → collapses to `/keys/...`; manual + `get_filename_component(... ABSOLUTE ...)` workaround). +- **Wrong-consumer / two-image custody:** + [#118977](https://devzone.nordicsemi.com/f/nordic-q-a/118977) — do **not** set + the symbol on the parent/app image (causes the malformed + `.../bootloader/mcuboot/` concatenation); target the child image, + e.g. `set(mcuboot_CONFIG_BOOT_SIGNATURE_KEY_FILE …)`. + [#80629](https://devzone.nordicsemi.com/f/nordic-q-a/80629) — decoupling + public-key storage from signing is "not supported … modify NCS"; documents the + two-CMakeLists coupling. +- **Nordic concedes it's a build-system coupling, not an MCUboot flaw:** + [#123846](https://devzone.nordicsemi.com/f/nordic-q-a/123846) — escape paths are + nRF54L15 KMU (public key in hardware) or upstream MCUboot HSM work + ([mcuboot #599](https://github.com/mcu-tools/mcuboot/issues/599)). +- **Keys-in-app-repo pattern:** + [#124004](https://devzone.nordicsemi.com/f/nordic-q-a/124004) — the nRF Desktop + pattern steers `APPLICATION_CONFIG_DIR` / `SB_APPLICATION_CONFIG_DIR` to + per-board `configuration//` folders, each with its own `sysbuild.conf`. + +## 4. The recent `APPLICATION_CONFIG_DIR` change + +- The `APPLICATION_CONFIG_DIR`-relative resolution shipped in **MCUboot v2.3.0** + (TrustedFirmware announcement dated 2025-12-19): "key files … will instead be + found relative to the build system `APPLICATION_CONFIG_DIR` variable," with + `string(CONFIGURE)` allowing escaped CMake vars like `${CMAKE_CURRENT_LIST_DIR}`. +- **mcuboot PR [#2701](https://github.com/mcu-tools/mcuboot/pull/2701)** (JPHutchins, + merged 2026-06-09) — adds **multi-key** support (comma-separated key list). Its + diff *re-adds the existing* `IS_ABSOLUTE → APPLICATION_CONFIG_DIR → MCUBOOT_DIR` + block verbatim inside a new `foreach`, so it **carries forward** rather than + introduces the resolution logic. This is the change visible at mcuboot `main` + HEAD. (The "Chaitanya Tata" author on the pinned `zephyrproject-rtos/mcuboot` + squash commit `0fae8920` is a downstream-packaging artifact, not the author of + the resolution block.) +- **mcuboot PR [#2591](https://github.com/mcu-tools/mcuboot/pull/2591)** + ("Support resolving relative key file path via WEST_TOPDIR", closed **unmerged** + 2026-01-12) — nordicjm rejected adding a `WEST_TOPDIR` fallback to the bootloader: + *"Not acceptable … use sysbuild and use an escaped path e.g. `${APP_DIR}/my.pem`."* + This is the clearest statement that the asymmetry (app image has `WEST_TOPDIR`, + bootloader deliberately does not) is **by design**. +- **mcuboot PR [#2702](https://github.com/mcu-tools/mcuboot/pull/2702)** (merged + 2026-05-11) — formalizes public-only PEM support for `getpub`/`verify`, + underpinning the custody model (the bootloader only needs public bytes) without + changing path resolution. + +**Verdict:** the `APPLICATION_CONFIG_DIR` work makes each image's own relative +resolution well-defined but does **not** fix the dual-image custody mismatch — the +two images still have different `APPLICATION_CONFIG_DIR` values and different +fallbacks. The reliable answer is an absolute path or `${APP_DIR}` (verified +here). Note that `${CMAKE_CURRENT_LIST_DIR}`, although mentioned in #59063 for the +`sysbuild.cmake` context, does **not** work in `sysbuild.conf` — the value is +expanded in sysbuild scope, where it points into the zephyr module (see §0 and +[`key_via_cmake_list_dir`](key_via_cmake_list_dir/)). + +> Unreconciled: the v2.3.0 release date (2025-12-19) predates PR #2701's merge +> (2026-06-09); the `APPLICATION_CONFIG_DIR` alignment and the multi-key feature +> are most likely distinct commits. The specific upstream commit/PR that first +> introduced the `APPLICATION_CONFIG_DIR` branch (on both the mcuboot and zephyr +> sides) was not pinned down and is worth confirming via `git log -p` / +> `git blame` on a non-shallow clone. + +## 5. Community workarounds (roughly by endorsement) + +1. **`${APP_DIR}` in `sysbuild.conf` (the one that works — but undocumented):** + `SB_CONFIG_BOOT_SIGNATURE_KEY_FILE="${APP_DIR}/my_key.pem"` — resolves to + absolute at configure time, satisfies both images, keys stay in the app repo. + (This is the [`key_in_app_dir`](key_in_app_dir/) app. Verified working on + current `main`.) Do **not** substitute `${CMAKE_CURRENT_LIST_DIR}` here — it + expands into the zephyr module and fails ([`key_via_cmake_list_dir`](key_via_cmake_list_dir/)). +2. **Absolute path:** Nordic's official "use only absolute paths." Reliable, but + not portable across machines/CI. +3. **Per-board `APPLICATION_CONFIG_DIR` steering** (nRF Desktop pattern): keys + under `sysbuild/<…>/` in the app repo, selected per board. +4. **Child-image-only `set()`:** `set(mcuboot_CONFIG_BOOT_SIGNATURE_KEY_FILE …)` + to avoid the wrong-consumer double-prefix bug. +5. **External / HSM signing:** override `SIGNING_SCRIPT` + ([#64532](https://github.com/zephyrproject-rtos/zephyr/discussions/64532)), or + sign externally and hand the bootloader a public-only PEM — for shops that + cannot place any PEM in the tree. +6. **In-module key (the anti-pattern):** the failure mode these apps exist to + discourage. The bundled dev keys are public ("every developer in the world has + it") — never ship them. + +## 6. Key citations + +- zephyr discussion #59063 — canonical dual-image custody Q&A; `${APP_DIR}` fix. +- zephyr issue #85270 — use sysbuild-scope symbol; closed "not planned". +- zephyr `cmake/mcuboot.cmake`, `boot/zephyr/Kconfig`, + `share/sysbuild/images/bootloader/{Kconfig,CMakeLists.txt}` — the resolvers and + propagation. +- docs.zephyrproject.org `build/signing`, `develop/west/sign`, + `build/sysbuild/images` — official (incomplete) signing docs. +- mcuboot PR #2701 (multi-key, carries forward resolution), #2591 (rejected + WEST_TOPDIR fallback — asymmetry by design), #2702 (public-only PEM), + issue #599 (HSM), `docs/release-notes.md` / trustedfirmware.org v2.3.0. +- NCS `bootloader_signature_keys` — "use only absolute paths". +- DevZone #113899, #115651, #118977, #80629, #123846, #124004, #113221, #65235, + #116215, #92584 — years of relative-path / wrong-consumer friction and the + vendor's absolute-path guidance. +- Foundries.io `zephyr-mcuboot-keys`, Hubble Zephyr DFU guide — third-party + consensus: generate your own key, never ship in-tree example keys. diff --git a/apps/README.md b/apps/README.md new file mode 100644 index 000000000..405fe612d --- /dev/null +++ b/apps/README.md @@ -0,0 +1,136 @@ +# MCUboot signing-key custody under sysbuild + +These apps demonstrate, and work around, a concrete custody problem in the +Zephyr + MCUboot **sysbuild** signing flow: + +> A signing key referenced by a path **relative to the application** is **not +> resolvable by the MCUboot image**. The bootloader resolves relative key paths +> against the **mcuboot module** (`MCUBOOT_DIR`), never against your +> application or the workspace. The naive consequence is that the key has to be +> dropped into a repository you do not own. + +All behaviour below was verified on **zephyr `main`** (`1cfe02062`) and +**mcuboot `main`** (`0fae892`), board `nrf52840dk/nrf52840`, signature type +ECDSA-P256. See [`FINDINGS.md`](FINDINGS.md) for the upstream issues, the +maintainers' position, and the relevant recent PRs. + +## Why it happens + +One sysbuild Kconfig string is propagated to **two** images that resolve +relative paths against **different** bases: + +| | symbol the image sees | what the image does with the key | relative-path resolution order | +|---|---|---|---| +| application image | `CONFIG_MCUBOOT_SIGNATURE_KEY_FILE` | `imgtool sign` the app | absolute → `APPLICATION_CONFIG_DIR` (the app) → **`WEST_TOPDIR`** | +| MCUboot image | `CONFIG_BOOT_SIGNATURE_KEY_FILE` | `imgtool getpub` → embed the public key | absolute → `APPLICATION_CONFIG_DIR` (mcuboot's own source dir) → **`MCUBOOT_DIR`** | + +Both come from a single `SB_CONFIG_BOOT_SIGNATURE_KEY_FILE`: + +- `zephyr/share/sysbuild/images/bootloader/Kconfig:205` — defines + `SB_CONFIG_BOOT_SIGNATURE_KEY_FILE` (default is *inside* the mcuboot module: + `$(ZEPHYR_MCUBOOT_MODULE_DIR)/root-*.pem`). +- `zephyr/share/sysbuild/image_configurations/MAIN_image_default.cmake:9` — + copies it to the app image as `CONFIG_MCUBOOT_SIGNATURE_KEY_FILE`. +- `zephyr/share/sysbuild/images/bootloader/CMakeLists.txt:20` — copies it to + the MCUboot image as `CONFIG_BOOT_SIGNATURE_KEY_FILE`. + +The two resolvers: + +- app image — `zephyr/cmake/mcuboot.cmake:53` (`APPLICATION_CONFIG_DIR` then + `WEST_TOPDIR`). +- MCUboot image — `bootloader/mcuboot/boot/zephyr/CMakeLists.txt:355` + (`APPLICATION_CONFIG_DIR` then `MCUBOOT_DIR`). + +Because the two `APPLICATION_CONFIG_DIR` values differ, and the two fallbacks +differ (`WEST_TOPDIR` vs `MCUBOOT_DIR`), **no single bare relative path can +satisfy both images**. The `APPLICATION_CONFIG_DIR` branches are recent +(MCUboot v2.3.0, carried forward by mcuboot PR #2701) and make each image's own +resolution well-defined, but they do **not** close this gap. Resolving to an +absolute path before propagation does — which is why the maintainer-sanctioned +fix is an absolute path or an escaped CMake variable, not a bare relative path. + +## Verified resolution matrix + +| `SB_CONFIG_BOOT_SIGNATURE_KEY_FILE` | key physically at | app image signs from | MCUboot image getpub from | build | +|---|---|---|---|---| +| `keys/insecure-ec-p256.pem` (bare relative) | app `keys/` | `/keys/` ✓ | `/keys/` ✗ | **FAIL** | +| `repo-root-key.pem` (bare relative) | `WEST_TOPDIR` root | `/` ✓ | `/` ✗ | **FAIL** | +| `${CMAKE_CURRENT_LIST_DIR}/keys/...` | app `keys/` | ✗ resolves into zephyr | ✗ resolves into zephyr | **FAIL** | +| `keys/insecure-ec-p256.pem` | app `keys/` **and** `/keys/` | ✓ | ✓ | PASS — dirties mcuboot | +| `/abs/path/.../key.pem` | anywhere (app `keys/`) | ✓ | ✓ | PASS — not portable | +| `${APP_DIR}/keys/insecure-ec-p256.pem` | app `keys/` | ✓ | ✓ | **PASS — custody kept** | + +Two things worth pulling out: + +- **Repo/workspace root doesn't help** (row 2): it fixes the *application* image + (its `WEST_TOPDIR` fallback) but **never** the bootloader, which only ever + falls back to `MCUBOOT_DIR`. +- **The escaped variable matters** (rows 3 and 6). `${...}` in the key value is + expanded by one `string(CONFIGURE)` in *sysbuild* scope + (`zephyr/share/sysbuild/cmake/modules/sysbuild_extensions.cmake:709`), where + `APP_DIR` is the main app and `CMAKE_CURRENT_LIST_DIR` is the sysbuild module + dir. So `${APP_DIR}` resolves to your app (and identically for both images, so + they can never disagree on the key), while `${CMAKE_CURRENT_LIST_DIR}` — + natural to try, and suggested for the `sysbuild.cmake` context in zephyr + discussion #59063 — resolves *into the zephyr module* and fails. + +The two failing forms implicate the two unowned modules from the task framing: a +**bare relative** path resolves into **mcuboot**, and `${CMAKE_CURRENT_LIST_DIR}` +resolves into **zephyr**. + +## The apps + +| app | `SB_CONFIG_BOOT_SIGNATURE_KEY_FILE` | result | +|---|---|---| +| [`app_relative_key`](app_relative_key/) | `keys/insecure-ec-p256.pem` | **fails** — the PoC (resolves into mcuboot) | +| [`key_via_cmake_list_dir`](key_via_cmake_list_dir/) | `${CMAKE_CURRENT_LIST_DIR}/keys/...` | **fails** — the "obvious" variable (resolves into zephyr) | +| [`key_in_mcuboot_module`](key_in_mcuboot_module/) | `keys/insecure-ec-p256.pem` + key copied into mcuboot | builds **by dirtying mcuboot** | +| [`key_in_app_dir`](key_in_app_dir/) | `${APP_DIR}/keys/insecure-ec-p256.pem` | builds, **key stays in this repo** — has a [positive Twister test](key_in_app_dir/sample.yaml) | + +The takeaway, and the likely upstream action: **`${APP_DIR}` is the only form +that keeps the key in the user-owned repo and resolves for both images — and it +is undocumented.** The official signing docs never mention `${APP_DIR}`, +`${CMAKE_CURRENT_LIST_DIR}`, or that the value is `string(CONFIGURE)`-expanded in +sysbuild scope. So the smallest correct fix may be a **documentation PR** (state +that `SB_CONFIG_BOOT_SIGNATURE_KEY_FILE` is expanded in sysbuild scope and that +`${APP_DIR}` is the supported anchor for an in-repo key). A code fix giving the +bootloader image an application-relative fallback would also close the +bare-relative trap — but the maintainers declined that for `WEST_TOPDIR` in +mcuboot PR #2591, so the asymmetry is currently intentional. + +Each app commits its own `keys/insecure-ec-p256.pem` — an **insecure, +demo-only** ECDSA P-256 key (never reuse it). Regenerate with: + +```sh +python "$(west topdir)/bootloader/mcuboot/scripts/imgtool.py" \ + keygen -k apps//keys/insecure-ec-p256.pem -t ecdsa-p256 +``` + +## Building + +From the workspace, with the venv active (so CMake finds `imgtool`'s deps): + +```sh +source .venv/bin/activate + +# 1. PoC — expected to FAIL at the MCUboot image's autogen-pubkey.c step: +west build -b nrf52840dk/nrf52840 --sysbuild -d build/poc \ + example-application/apps/app_relative_key +# ninja: error: '.../bootloader/mcuboot/keys/insecure-ec-p256.pem', +# needed by 'zephyr/autogen-pubkey.c', missing ... + +# 2. Sanctioned fix — builds, key never leaves this repo: +west build -b nrf52840dk/nrf52840 --sysbuild -d build/appdir \ + example-application/apps/key_in_app_dir + +# 3. Custody-violating workaround — copy the key into the mcuboot module first: +cp example-application/apps/key_in_mcuboot_module/keys/insecure-ec-p256.pem \ + "$(west topdir)/bootloader/mcuboot/keys/" +west build -b nrf52840dk/nrf52840 --sysbuild -d build/inmod \ + example-application/apps/key_in_mcuboot_module +# ... then `rm -r "$(west topdir)/bootloader/mcuboot/keys"` to un-dirty mcuboot. +``` + +RSA (the sysbuild default signature type) currently breaks on these `main` +revisions for an unrelated reason — `BOOT_RSA_MBEDTLS_LEGACY` collides with the +mbedtls → TF-PSA-Crypto transition — so these apps select ECDSA-P256. diff --git a/apps/app_relative_key/CMakeLists.txt b/apps/app_relative_key/CMakeLists.txt new file mode 100644 index 000000000..6aa3e907b --- /dev/null +++ b/apps/app_relative_key/CMakeLists.txt @@ -0,0 +1,13 @@ +# Copyright (c) 2026 Intercreate, Inc. +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(app_relative_key) + +# Built only through sysbuild so MCUboot is included and the signing-key +# resolution path under test is actually exercised. +test_sysbuild() + +target_sources(app PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../common/src/main.c) diff --git a/apps/app_relative_key/README.md b/apps/app_relative_key/README.md new file mode 100644 index 000000000..24c417447 --- /dev/null +++ b/apps/app_relative_key/README.md @@ -0,0 +1,32 @@ +# app_relative_key — PoC: app-relative key is not resolvable by MCUboot + +The key lives in this application at +[`keys/insecure-ec-p256.pem`](keys/), referenced by a **bare relative path** — +the natural thing to write: + +```ini +SB_CONFIG_BOOT_SIGNATURE_KEY_FILE="keys/insecure-ec-p256.pem" +``` + +**This build fails.** The application image finds the key (it resolves relative +paths against this app, then `WEST_TOPDIR`), but the MCUboot image resolves the +same string against the **mcuboot module** and aborts: + +```console +$ west build -b nrf52840dk/nrf52840 --sysbuild -d build/poc \ + example-application/apps/app_relative_key +... +MCUBoot bootloader key file: .../bootloader/mcuboot/keys/insecure-ec-p256.pem +ninja: error: '.../bootloader/mcuboot/keys/insecure-ec-p256.pem', + needed by 'zephyr/autogen-pubkey.c', missing and no known rule to make it +``` + +Note the key it *wants* is under `bootloader/mcuboot/` — a repo you do not own. +Putting the key at the workspace/repo root does not help either: that only +satisfies the application image's `WEST_TOPDIR` fallback, never the bootloader +(which only falls back to `MCUBOOT_DIR`). + +See [`../key_in_app_dir`](../key_in_app_dir/) for the sanctioned fix and +[`../key_in_mcuboot_module`](../key_in_mcuboot_module/) for the custody-violating +workaround. Mechanism and citations: [`../README.md`](../README.md), +[`../FINDINGS.md`](../FINDINGS.md). diff --git a/apps/app_relative_key/keys/insecure-ec-p256.pem b/apps/app_relative_key/keys/insecure-ec-p256.pem new file mode 100644 index 000000000..5f98de04f --- /dev/null +++ b/apps/app_relative_key/keys/insecure-ec-p256.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgHmiw+kPx/xiFddAJ +aGPvbrSGR7H94CP92llQ4DSz6J+hRANCAAQKOPfEdjHoOfPIXnU1ZVVYDbUSo0A0 +w8qorHpERfHbq34qa3ogoFJCHQIK2N4xEYlylQu0VRgklQXBDP/45blD +-----END PRIVATE KEY----- diff --git a/apps/app_relative_key/prj.conf b/apps/app_relative_key/prj.conf new file mode 100644 index 000000000..722eba792 --- /dev/null +++ b/apps/app_relative_key/prj.conf @@ -0,0 +1,5 @@ +# Copyright (c) 2026 Intercreate, Inc. +# SPDX-License-Identifier: Apache-2.0 +# +# Application image configuration. MCUboot mode and signing are driven from +# sysbuild.conf so a single source of truth configures both images. diff --git a/apps/app_relative_key/sysbuild.conf b/apps/app_relative_key/sysbuild.conf new file mode 100644 index 000000000..105322d58 --- /dev/null +++ b/apps/app_relative_key/sysbuild.conf @@ -0,0 +1,19 @@ +# Copyright (c) 2026 Intercreate, Inc. +# SPDX-License-Identifier: Apache-2.0 +# +# PoC: a signing key that lives in THIS (user-owned) application, referenced by +# a bare relative path -- the path a developer naturally writes. +# +# This build FAILS. The single SB_CONFIG_BOOT_SIGNATURE_KEY_FILE string is +# propagated to two images that resolve relative paths against DIFFERENT bases: +# * application image -> APPLICATION_CONFIG_DIR (this app) else WEST_TOPDIR +# * MCUboot image -> APPLICATION_CONFIG_DIR (mcuboot) else MCUBOOT_DIR +# The key exists at apps/app_relative_key/keys/, so the application image signs +# fine, but the MCUboot image looks for it under bootloader/mcuboot/keys/ and +# the build aborts generating zephyr/autogen-pubkey.c. + +SB_CONFIG_BOOTLOADER_MCUBOOT=y +SB_CONFIG_MCUBOOT_MODE_OVERWRITE_ONLY=y +SB_CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256=y + +SB_CONFIG_BOOT_SIGNATURE_KEY_FILE="keys/insecure-ec-p256.pem" diff --git a/apps/common/src/main.c b/apps/common/src/main.c new file mode 100644 index 000000000..44d968f77 --- /dev/null +++ b/apps/common/src/main.c @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2026 Intercreate, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +int main(void) +{ + printk("Address of sample %p\n", (void *)__rom_region_start); + printk("MCUboot signing-key custody demo: %s\n", CONFIG_BOARD); + return 0; +} diff --git a/apps/key_in_app_dir/CMakeLists.txt b/apps/key_in_app_dir/CMakeLists.txt new file mode 100644 index 000000000..2071f3c81 --- /dev/null +++ b/apps/key_in_app_dir/CMakeLists.txt @@ -0,0 +1,13 @@ +# Copyright (c) 2026 Intercreate, Inc. +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(key_in_app_dir) + +# Built only through sysbuild so MCUboot is included and the signing-key +# resolution path under test is actually exercised. +test_sysbuild() + +target_sources(app PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../common/src/main.c) diff --git a/apps/key_in_app_dir/README.md b/apps/key_in_app_dir/README.md new file mode 100644 index 000000000..b3f54b171 --- /dev/null +++ b/apps/key_in_app_dir/README.md @@ -0,0 +1,57 @@ +# key_in_app_dir — sanctioned fix: key stays in this repo via `${APP_DIR}` + +Same key, same app-owned location as the PoC — but anchored with the +`${APP_DIR}` escaped CMake variable instead of a bare relative path: + +```ini +SB_CONFIG_BOOT_SIGNATURE_KEY_FILE="${APP_DIR}/keys/insecure-ec-p256.pem" +``` + +sysbuild expands `${APP_DIR}` (via `string(CONFIGURE ...)`) to **this +application's directory for both images**, so the application image and the +MCUboot image both resolve to `apps/key_in_app_dir/keys/insecure-ec-p256.pem`. +**The build succeeds and the mcuboot module stays clean:** + +```console +$ west build -b nrf52840dk/nrf52840 --sysbuild -d build/appdir \ + example-application/apps/key_in_app_dir +... +MCUBoot bootloader key file: .../apps/key_in_app_dir/keys/insecure-ec-p256.pem +[16/16] Completed 'mcuboot' +``` + +This is the maintainer-recommended approach (zephyr discussion #59063, mcuboot +PR #2591). An absolute path works too but is not portable across machines/CI. +See [`../FINDINGS.md`](../FINDINGS.md). + +## Positive test — and why it proves *both* images agree + +`${APP_DIR}` is expanded by a single `string(CONFIGURE)` in sysbuild scope +(`zephyr/share/sysbuild/cmake/modules/sysbuild_extensions.cmake:709`), where +`APP_DIR` is the main application. The same expansion runs for every image, so +both the application image and the MCUboot image get the **identical** absolute +path — there is no chance of the app being signed with one key while the +bootloader embeds another. Verified on the build above: + +```console +# application image (signs the app): +$ grep MCUBOOT_SIGNATURE_KEY_FILE build/appdir/key_in_app_dir/zephyr/.config +CONFIG_MCUBOOT_SIGNATURE_KEY_FILE=".../apps/key_in_app_dir/keys/insecure-ec-p256.pem" + +# MCUboot image (embeds the public key): +$ grep '^CONFIG_BOOT_SIGNATURE_KEY_FILE' build/appdir/mcuboot/zephyr/.config +CONFIG_BOOT_SIGNATURE_KEY_FILE=".../apps/key_in_app_dir/keys/insecure-ec-p256.pem" + +# and the bootloader did NOT fall back to the insecure default key: +$ grep -c "Using default MCUboot signing key" build/appdir/build.log +0 +``` + +[`sample.yaml`](sample.yaml) runs this under Twister (`build_only`): a clean +sysbuild build is only possible if `${APP_DIR}` resolved for **both** images +(the MCUboot image fails to generate `autogen-pubkey.c` otherwise, and the +application image FATALs at the `imgtool sign` configure step otherwise): + +```sh +west twister -i -T example-application/apps/key_in_app_dir -p nrf52840dk/nrf52840 +``` diff --git a/apps/key_in_app_dir/keys/insecure-ec-p256.pem b/apps/key_in_app_dir/keys/insecure-ec-p256.pem new file mode 100644 index 000000000..5f98de04f --- /dev/null +++ b/apps/key_in_app_dir/keys/insecure-ec-p256.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgHmiw+kPx/xiFddAJ +aGPvbrSGR7H94CP92llQ4DSz6J+hRANCAAQKOPfEdjHoOfPIXnU1ZVVYDbUSo0A0 +w8qorHpERfHbq34qa3ogoFJCHQIK2N4xEYlylQu0VRgklQXBDP/45blD +-----END PRIVATE KEY----- diff --git a/apps/key_in_app_dir/prj.conf b/apps/key_in_app_dir/prj.conf new file mode 100644 index 000000000..722eba792 --- /dev/null +++ b/apps/key_in_app_dir/prj.conf @@ -0,0 +1,5 @@ +# Copyright (c) 2026 Intercreate, Inc. +# SPDX-License-Identifier: Apache-2.0 +# +# Application image configuration. MCUboot mode and signing are driven from +# sysbuild.conf so a single source of truth configures both images. diff --git a/apps/key_in_app_dir/sample.yaml b/apps/key_in_app_dir/sample.yaml new file mode 100644 index 000000000..6863b193d --- /dev/null +++ b/apps/key_in_app_dir/sample.yaml @@ -0,0 +1,22 @@ +# Copyright (c) 2026 Intercreate, Inc. +# SPDX-License-Identifier: Apache-2.0 +# +# Positive test: ${APP_DIR} keeps the MCUboot signing key in the application +# repo and is resolved for BOTH the application image and the MCUboot image. +# +# This is build_only on purpose: if ${APP_DIR} failed to expand for the MCUboot +# image, generating zephyr/autogen-pubkey.c would fail; if it failed for the +# application image, the imgtool sign step would FATAL at configure. A clean +# sysbuild build therefore proves both resolutions succeeded. +sample: + description: ${APP_DIR} resolves the signing key in the app for both images + name: key_in_app_dir +tests: + apps.key_in_app_dir.app_dir_key_resolves: + sysbuild: true + build_only: true + tags: mcuboot sysbuild signing + platform_allow: + - nrf52840dk/nrf52840 + integration_platforms: + - nrf52840dk/nrf52840 diff --git a/apps/key_in_app_dir/sysbuild.conf b/apps/key_in_app_dir/sysbuild.conf new file mode 100644 index 000000000..9f3246fdd --- /dev/null +++ b/apps/key_in_app_dir/sysbuild.conf @@ -0,0 +1,19 @@ +# Copyright (c) 2026 Intercreate, Inc. +# SPDX-License-Identifier: Apache-2.0 +# +# Sanctioned fix: keep the signing key in THIS (user-owned) application, but +# anchor it with the ${APP_DIR} escaped CMake variable instead of a bare +# relative path. +# +# sysbuild expands ${APP_DIR} (via string(CONFIGURE ...)) to this application's +# directory for BOTH the application image and the MCUboot image, so both +# resolve to apps/key_in_app_dir/keys/insecure-ec-p256.pem. The build SUCCEEDS +# and the mcuboot module stays clean. This is the maintainer-recommended +# approach (zephyr discussions #59063, mcuboot PR #2591); an absolute path +# works too but is not portable across machines. + +SB_CONFIG_BOOTLOADER_MCUBOOT=y +SB_CONFIG_MCUBOOT_MODE_OVERWRITE_ONLY=y +SB_CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256=y + +SB_CONFIG_BOOT_SIGNATURE_KEY_FILE="${APP_DIR}/keys/insecure-ec-p256.pem" diff --git a/apps/key_in_mcuboot_module/CMakeLists.txt b/apps/key_in_mcuboot_module/CMakeLists.txt new file mode 100644 index 000000000..472ea3a75 --- /dev/null +++ b/apps/key_in_mcuboot_module/CMakeLists.txt @@ -0,0 +1,13 @@ +# Copyright (c) 2026 Intercreate, Inc. +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(key_in_mcuboot_module) + +# Built only through sysbuild so MCUboot is included and the signing-key +# resolution path under test is actually exercised. +test_sysbuild() + +target_sources(app PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../common/src/main.c) diff --git a/apps/key_in_mcuboot_module/README.md b/apps/key_in_mcuboot_module/README.md new file mode 100644 index 000000000..df4a47528 --- /dev/null +++ b/apps/key_in_mcuboot_module/README.md @@ -0,0 +1,32 @@ +# key_in_mcuboot_module — workaround by dirtying an unowned repo + +This app keeps the **bare relative path** (identical to the +[`app_relative_key`](../app_relative_key/) PoC): + +```ini +SB_CONFIG_BOOT_SIGNATURE_KEY_FILE="keys/insecure-ec-p256.pem" +``` + +The key is committed here at [`keys/insecure-ec-p256.pem`](keys/) for the +application image. To make the **MCUboot** image resolve the same relative path, +you must place the key where its only relative fallback looks — inside the +mcuboot module (`MCUBOOT_DIR`): + +```console +$ cp example-application/apps/key_in_mcuboot_module/keys/insecure-ec-p256.pem \ + "$(west topdir)/bootloader/mcuboot/keys/" + +$ west build -b nrf52840dk/nrf52840 --sysbuild -d build/inmod \ + example-application/apps/key_in_mcuboot_module +... +MCUBoot bootloader key file: .../bootloader/mcuboot/keys/insecure-ec-p256.pem +[16/16] Completed 'key_in_mcuboot_module' + +# undo the dirtying: +$ rm -r "$(west topdir)/bootloader/mcuboot/keys" +``` + +`bootloader/mcuboot` is a repo you do not own. The build only succeeds because +the key now lives in **two** places — your app *and* the mcuboot module — which +is exactly the custody problem. Prefer [`../key_in_app_dir`](../key_in_app_dir/), +which keeps the key in this repo only. See [`../FINDINGS.md`](../FINDINGS.md). diff --git a/apps/key_in_mcuboot_module/keys/insecure-ec-p256.pem b/apps/key_in_mcuboot_module/keys/insecure-ec-p256.pem new file mode 100644 index 000000000..5f98de04f --- /dev/null +++ b/apps/key_in_mcuboot_module/keys/insecure-ec-p256.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgHmiw+kPx/xiFddAJ +aGPvbrSGR7H94CP92llQ4DSz6J+hRANCAAQKOPfEdjHoOfPIXnU1ZVVYDbUSo0A0 +w8qorHpERfHbq34qa3ogoFJCHQIK2N4xEYlylQu0VRgklQXBDP/45blD +-----END PRIVATE KEY----- diff --git a/apps/key_in_mcuboot_module/prj.conf b/apps/key_in_mcuboot_module/prj.conf new file mode 100644 index 000000000..722eba792 --- /dev/null +++ b/apps/key_in_mcuboot_module/prj.conf @@ -0,0 +1,5 @@ +# Copyright (c) 2026 Intercreate, Inc. +# SPDX-License-Identifier: Apache-2.0 +# +# Application image configuration. MCUboot mode and signing are driven from +# sysbuild.conf so a single source of truth configures both images. diff --git a/apps/key_in_mcuboot_module/sysbuild.conf b/apps/key_in_mcuboot_module/sysbuild.conf new file mode 100644 index 000000000..e26df9186 --- /dev/null +++ b/apps/key_in_mcuboot_module/sysbuild.conf @@ -0,0 +1,21 @@ +# Copyright (c) 2026 Intercreate, Inc. +# SPDX-License-Identifier: Apache-2.0 +# +# Custody-violating workaround: keep the bare relative path (identical to the +# app_relative_key PoC) and instead satisfy the MCUboot image by placing the +# key INSIDE the mcuboot module tree. +# +# The key is committed here at keys/insecure-ec-p256.pem (used by the +# application image). To make the MCUboot image resolve the same relative path, +# copy that key into the mcuboot module, which it resolves against MCUBOOT_DIR: +# +# cp keys/insecure-ec-p256.pem "$(west topdir)/bootloader/mcuboot/keys/" +# +# bootloader/mcuboot is a repo you do not own -- that copy is the custody +# problem. The build SUCCEEDS, but only because an unowned module was dirtied. + +SB_CONFIG_BOOTLOADER_MCUBOOT=y +SB_CONFIG_MCUBOOT_MODE_OVERWRITE_ONLY=y +SB_CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256=y + +SB_CONFIG_BOOT_SIGNATURE_KEY_FILE="keys/insecure-ec-p256.pem" diff --git a/apps/key_via_cmake_list_dir/CMakeLists.txt b/apps/key_via_cmake_list_dir/CMakeLists.txt new file mode 100644 index 000000000..1106f0ab2 --- /dev/null +++ b/apps/key_via_cmake_list_dir/CMakeLists.txt @@ -0,0 +1,13 @@ +# Copyright (c) 2026 Intercreate, Inc. +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(key_via_cmake_list_dir) + +# Built only through sysbuild so MCUboot is included and the signing-key +# resolution path under test is actually exercised. +test_sysbuild() + +target_sources(app PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../common/src/main.c) diff --git a/apps/key_via_cmake_list_dir/README.md b/apps/key_via_cmake_list_dir/README.md new file mode 100644 index 000000000..45793e181 --- /dev/null +++ b/apps/key_via_cmake_list_dir/README.md @@ -0,0 +1,31 @@ +# key_via_cmake_list_dir — the "obvious" variable that fails (into zephyr) + +A developer who knows the key path is `string(CONFIGURE)`-expanded will reach +for `${CMAKE_CURRENT_LIST_DIR}` — it is even suggested in zephyr discussion +#59063 (for the `sysbuild.cmake` context, where it equals the app dir): + +```ini +SB_CONFIG_BOOT_SIGNATURE_KEY_FILE="${CMAKE_CURRENT_LIST_DIR}/keys/insecure-ec-p256.pem" +``` + +**This build fails.** The value is expanded by a single `string(CONFIGURE)` in +**sysbuild scope** +(`zephyr/share/sysbuild/cmake/modules/sysbuild_extensions.cmake:709`), not in +your app, so `${CMAKE_CURRENT_LIST_DIR}` becomes the sysbuild module directory: + +```console +$ west build -b nrf52840dk/nrf52840 --sysbuild -d build/cld \ + example-application/apps/key_via_cmake_list_dir +... +MCUBoot bootloader key file: + .../zephyr/share/sysbuild/cmake/modules/keys/insecure-ec-p256.pem +... Configuring/builds fail: that path does not exist. +``` + +Note the path lands **inside the zephyr module** — the mirror image of the bare +relative path, which lands inside the mcuboot module. Both unowned modules get +implicated depending on which non-working form you pick. + +Only [`../key_in_app_dir`](../key_in_app_dir/) (using `${APP_DIR}`) actually +resolves to this application — and that variable is undocumented. See +[`../FINDINGS.md`](../FINDINGS.md). diff --git a/apps/key_via_cmake_list_dir/keys/insecure-ec-p256.pem b/apps/key_via_cmake_list_dir/keys/insecure-ec-p256.pem new file mode 100644 index 000000000..5f98de04f --- /dev/null +++ b/apps/key_via_cmake_list_dir/keys/insecure-ec-p256.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgHmiw+kPx/xiFddAJ +aGPvbrSGR7H94CP92llQ4DSz6J+hRANCAAQKOPfEdjHoOfPIXnU1ZVVYDbUSo0A0 +w8qorHpERfHbq34qa3ogoFJCHQIK2N4xEYlylQu0VRgklQXBDP/45blD +-----END PRIVATE KEY----- diff --git a/apps/key_via_cmake_list_dir/prj.conf b/apps/key_via_cmake_list_dir/prj.conf new file mode 100644 index 000000000..722eba792 --- /dev/null +++ b/apps/key_via_cmake_list_dir/prj.conf @@ -0,0 +1,5 @@ +# Copyright (c) 2026 Intercreate, Inc. +# SPDX-License-Identifier: Apache-2.0 +# +# Application image configuration. MCUboot mode and signing are driven from +# sysbuild.conf so a single source of truth configures both images. diff --git a/apps/key_via_cmake_list_dir/sysbuild.conf b/apps/key_via_cmake_list_dir/sysbuild.conf new file mode 100644 index 000000000..29fb8f37b --- /dev/null +++ b/apps/key_via_cmake_list_dir/sysbuild.conf @@ -0,0 +1,22 @@ +# Copyright (c) 2026 Intercreate, Inc. +# SPDX-License-Identifier: Apache-2.0 +# +# The "obvious" variable that does NOT work. ${CMAKE_CURRENT_LIST_DIR} is the +# variable zephyr discussion #59063 suggests for the sysbuild.cmake context, +# where it equals the application directory. But the key value is expanded by a +# single string(CONFIGURE) in SYSBUILD scope +# (zephyr/share/sysbuild/cmake/modules/sysbuild_extensions.cmake:709), so here +# ${CMAKE_CURRENT_LIST_DIR} expands to +# /zephyr/share/sysbuild/cmake/modules +# i.e. a path INSIDE the zephyr module. The key is not there, so the build +# FAILS -- and the path it wants is in a repo you do not own. +# +# Contrast with key_in_app_dir, which uses ${APP_DIR} (expands to this +# application) and works. ${APP_DIR} is the only relative form that works, and +# it is undocumented -- see ../FINDINGS.md. + +SB_CONFIG_BOOTLOADER_MCUBOOT=y +SB_CONFIG_MCUBOOT_MODE_OVERWRITE_ONLY=y +SB_CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256=y + +SB_CONFIG_BOOT_SIGNATURE_KEY_FILE="${CMAKE_CURRENT_LIST_DIR}/keys/insecure-ec-p256.pem" diff --git a/west.yml b/west.yml index c3e406760..c48affe0b 100644 --- a/west.yml +++ b/west.yml @@ -20,3 +20,4 @@ manifest: - cmsis_6 # required by the ARM port for Cortex-M - hal_nordic # required by the custom_plank board (Nordic based) - hal_stm32 # required by the nucleo_f302r8 board (STM32 based) + - mcuboot # required by the sysbuild MCUboot signing-key examples