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
220 changes: 220 additions & 0 deletions apps/FINDINGS.md
Original file line number Diff line number Diff line change
@@ -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}/<file>` (if it exists) → `${WEST_TOPDIR}/<file>`; 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/<abs path>` 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/<board>/` 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.
Loading