This document describes the chain of trust from immutable ROM through to the application on E1M-AEN-family SoMs, plus the signing key lifecycle that makes it work.
Status: boot + signature verification bench-proven on E1M-AEN801 (single-slot); swap-using-scratch's swap/rollback path is untested. The chain of trust below -- SES -> MCUboot (ITCM) -> slot0 (MRAM XIP) -> application -- is measured working on real silicon (
AE822FA0E5597LS0Rev A0, alp-sdk0da1f1b4), withCONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256=yandCONFIG_BOOT_VALIDATE_SLOT0=yread back from the builtmcuboot/zephyr/.configrather than assumed:PC=80012FBC,VTOR=80010800,CFSR=00000000,IPSR=000. Verification was proven live, not inferred from a clean boot: flipping one byte of the TLV0x22ECDSA signature (file offset0x4a30,0xda->0xdb, with TLV0x10/SHA-256 and TLV0x01/key left intact) producesD: bootutil_verify_sig: ECDSA builtin key 0thenE: Unable to find bootable image,VTOR = 0x00000000-- a halt, not a swap-back to a previous slot (this build had none:CONFIG_SINGLE_APPLICATION_SLOT=y).SIGNATURE_TYPE_NONE+VALIDATE_SLOT0=yboots in twelve seconds (watched ten minutes, CycleCnt advancing).A separate build on the reference
swap-using-scratchprofile boots and logsI: Bootloader chainload address offset: 0x10000-- boot only. The swap/rollback path itself (failed-image swap-back, mid-swap power loss recovery, revert-on-unconfirmed) was NOT exercised -- see "Failure modes + rollback" below.The verified backend is TinyCrypt (
CONFIG_BOOT_ECDSA_TINYCRYPT=y), not yet MbedTLS PSA. The built.configalso confirmsCONFIG_SINGLE_APPLICATION_SLOT=yandCONFIG_FLASH_BASE_ADDRESS=0x0(MCUboot itself is ITCM-linked). Still required:CONFIG_DCACHE=n(a separate, established hang inSCB_EnableDCache), the board'sROM_START_OFFSET=0x800, and thezephyr/patches/mcubootdo_bootflash-base patch (a candidate for upstreaming rather than carrying indefinitely). Compile-verification also gates on the real in-tree board file (alp_e1m_aen801_m55_he/alp_e1m_aen801_m55_hp, underzephyr/boards/alp/).The customer path is now proven too. A plain J-Link
loadbinof animgtool-signed image straight to slot0 (0x80010000) -- no SETOOLS, no ATOC, no SE-UART -- is verified by MCUboot and chainloaded, and survived three cold power-cycles. Proven at0x80010000only -- writing the ATOC region or erasing MCUboot itself was not tested. Both refusal shapes (tampered signature, or a non-MCUboot image) leave the debug port alive (Secure debug: enabled, core halts and single-steps normally) -- a bad slot0 write does not brick J-Link access. This is a single-slot result (CONFIG_SINGLE_APPLICATION_SLOT=y); A/B swap and OTA are untested, so don't read it as an upgrade-path guarantee. Full recipe:docs/aen-provisioning.md§0.5.
┌─────────────────────────────────────────────────────────────┐
│ Alif Ensemble Secure Enclave ROM │
│ - Immutable. Verifies the first-stage bootloader. │
│ - Roots its trust in an Alif-fab-time-burned public key. │
├─────────────────────────────────────────────────────────────┤
│ First-stage bootloader (Alif-provided) │
│ - Hands off to MCUboot. │
├─────────────────────────────────────────────────────────────┤
│ MCUboot (alp-sdk-built via sysbuild) │
│ - Verifies the application image's ECDSA-P256 signature │
│ against the public key compiled into the bootloader. │
│ - Failed verification triggers swap-back to the previous │
│ slot (swap-using-scratch mode). │
│ - Public key verification path routes through MbedTLS PSA │
│ once the v0.3.x OPTIGA Trust M PSA driver lands, so HW │
│ acceleration is transparent. │
├─────────────────────────────────────────────────────────────┤
│ Application image │
│ - Signed by the production private key held on the │
│ air-gapped signing workstation. (In-chip custody in │
│ OPTIGA Trust M is the intended end state -- the driver │
│ has no signing call yet; see "Signing key lifecycle".) │
└─────────────────────────────────────────────────────────────┘
[UNTESTED]-- the "Failed verification triggers swap-back to the previous slot" bullet above. Neither bench session ran swap-using-scratch's two-slot swap-back; the single-slot build that was tested has no previous slot to fall back to, and measuredE: Unable to find bootable image/VTOR = 0x00000000-- a halt, not a swap. See the status block above and "Failure modes + rollback" below.
The SDK touches the MCUboot layer. The Alif Secure Enclave ROM + first-stage are out of scope -- they ship with the SoM and Alif provides their signing keys.
The recommended path is the top-level boot: block in your
project's board.yaml -- the loader (scripts/alp_orchestrate/)
emits the matching SB_CONFIG_* overlay (sysbuild Kconfig) into
build/alp_sysbuild.conf and passes it via
-DSB_CONF_FILE. No hand-edited sysbuild.conf.
# board.yaml
boot:
method: mcuboot
signing:
algorithm: ecdsa_p256
key_file: keys/prod_ecdsa_p256.pem
swap_algorithm: scratch # valid on a two-slot target; omit (or drop
# this line) on a single-slot target such
# as E1M-AEN801 -- see below, it errors.key_file: is relative to the directory holding board.yaml, must
exist, and names a PRIVATE PEM (not a .pub.pem, which cannot sign). A
missing file fails the plan with OrchestratorError naming the resolved
path. On AEN (curated sysbuild base) with ECDSA-P256, omitting signing:
uses the SDK-anchored default dev key keys/mcuboot_shared_dev_ecdsa_p256.pem;
on other families or algorithms no key is passed unless key_file: is set,
and sysbuild falls back to MCUboot's built-in key.
See docs/board-config-features.md §Bootloader
for the full field reference (including why there is no
slots: / scratch_size_kib: / anti_rollback: field).
Omit the boot: block entirely to inherit the SDK's stock
per-family defaults unchanged: on AEN-Zephyr that is the curated
zephyr/sysbuild/aen/sysbuild.conf
alone (MCUboot + ECDSA-P256 +
SB_CONFIG_MCUBOOT_MODE_SINGLE_APP=y) -- this base applies to every
AEN SKU, not just a single-slot one, since no boot: block means the
loader emits no overlay at all for it to layer against.
Keep the boot: block but omit swap_algorithm: and the default
instead follows the target's own DT, not one value for every SKU: a
target whose memory_map: declares a disjoint per-core <role>_slot0
region (#1069: both M55 cores share the same physical App MRAM, so
slot0 was split into disjoint per-core windows and the
secondary/scratch slot dropped rather than forced to fit -- since #1445
that is every AEN SoM, not just E1M-AEN801) has no slot1/scratch
partition, so the generated overlay resolves to single-app boot
(SB_CONFIG_MCUBOOT_MODE_SINGLE_APP=y, the same symbol the curated
base above already ships for it); a target with no such region keeps
the historical swap-using-scratch default
(SB_CONFIG_MCUBOOT_MODE_SWAP_SCRATCH=y) -- and since the generated
overlay is layered AFTER the curated base (#807: same ;-joined
SB_CONF_FILE list, later file wins on a repeated symbol), presence
of a boot: block is what makes that scratch default apply even
though the curated base's own default is single-app. Setting
swap_algorithm: scratch (or move/overwrite) explicitly on a
single-slot target such as E1M-AEN801 is a build-time error -- there
is no slot1/scratch partition for it to use (#1413).
Keeping the block but omitting method: inherits the family's
bootloader strategy the same way -- mcuboot on AEN, none on
V2N/V2N-M1, where U-Boot owns boot. This overlay is a Zephyr
artefact: a project with no os: zephyr slice never runs sysbuild and
gets none. Slot/scratch partition sizes come from the board DT
partitions {} node, not from boot: -- declare the actual layout
via storage: if you want it explicit.
-
Clone the repo.
-
Nothing to generate: the build signs with the committed shared development key
keys/mcuboot_shared_dev_ecdsa_p256.pem, which the factory MCUboot on pre-provisioned modules trusts (#2421). It is public and gives no security. To use your own key instead, runbash keys/generate_dev_key.shand re-provision MCUboot built with it (seekeys/README.md). -
Build with sysbuild:
# A board.yaml-driven app needs its alp.conf first: prefer `tan build`, or pass # -DEXTRA_CONF_FILE=<alp.conf> (docs/board-config-emit.md; #866). west build -b alp_e1m_aen801_m55_he/ae822fa0e5597ls0/rtss_he \ path/to/app \ --sysbuild \ -- -DSB_CONF_FILE=<abs-alp-sdk>/zephyr/sysbuild/aen/sysbuild.conf \ -DSB_CONFIG_BOOT_SIGNATURE_KEY_FILE="<abs-alp-sdk>/keys/mcuboot_shared_dev_ecdsa_p256.pem"
(Or, if your
board.yamlcarries aboot:block, the loader's emitted overlay atbuild/alp_sysbuild.confis the canonical-DSB_CONF_FILEpath.) -
build/<app>/zephyr/zephyr.signed.binis your signed image (build/mcuboot/zephyr/zephyr.binis the MCUboot bootloader). -
Flash both the MCUboot bootloader and the signed app:
west flash --bin-file build/mcuboot/zephyr/zephyr.bin --domain mcuboot west flash --bin-file build/<app>/zephyr/zephyr.signed.bin
On the E1M-AEN boards,
west flashon a sysbuild (MCUboot) build refuses (alp-sdk#2274): thealif_flashrunner cannot stage both domains' ATOC entries in one burn. Usedocs/aen-provisioning.md§0.5 (Option B for a module whose MCUboot is already provisioned; the SETOOLS MCUboot provisioning inzephyr/sysbuild/aen/README.mdotherwise). Option B writes the app only; without a resident MCUboot the module will not boot (recoverable via SETOOLS re-provisioning).
The dev key has signing power equivalent to "every developer who's ever cloned the repo". Never use it in a fielded device.
The OPTIGA Trust M signing path described below is NOT implemented. The SDK's OPTIGA Trust M driver probes the part, reads its Coprocessor UID and runs raw APDU sessions through Infineon's host library (
chips/optiga_trust_m/,vendors/optiga-trust-m/). There is no typed key generation, key export or signing call, no PSA driver, and the Shielded Connection is off (the contract is pinned bytests/scripts/test_optiga_trust_m_contract.py). Do not architect a product's key management around it until typed calls and binding-secret provisioning land (#1164).Use the air-gapped workstation flow below. The OPTIGA design is retained here as the intended end state, clearly marked.
Provisioning happens at SoM manufacturing time -- before the device ships.
- Generate the production key on an air-gapped workstation. A machine that never touches the network, holding the ECDSA-P256 private key on encrypted storage (ideally a smartcard or HSM you already trust). This machine is the key store.
- Publish the public half. Commit it as
keys/mcuboot_prod_ecdsa_p256.pub.pem; the manufacturing CA signs it so downstream tooling can verify provenance. Only the public half ever enters git. - Compile the bootloader against the production public key.
Override
SB_CONFIG_BOOT_SIGNATURE_KEY_FILEto point atkeys/mcuboot_prod_ecdsa_p256.pub.pemwhen building MCUboot for production firmware. - Sign release images across the air gap. Carry the unsigned
image to the workstation, sign it there with stock
imgtool, and carryzephyr.signed.binback. The release pipeline never holds signing power.
The security property this gives you: the private key never touches a networked machine. Compromise of the dev key, the build host, or CI does not yield production signing power -- physical access to the air-gapped workstation does. That is a weaker guarantee than in-chip key custody (an operator with access can copy the key), so treat workstation access control as the control that matters.
Once the OPTIGA Trust M host-library transport lands, the private
key is generated inside the chip (slot 0xE0F0 is the convention
for the MCUboot signing key) and never leaves its secure NVM; the
public half is read out over I²C, and the release pipeline signs
through a dedicated host with physical access to a provisioned part
(imgtool supports external ECDSA signers via
--public-key-format hash). That removes the copy-the-key risk
above. None of it works yet.
MCUboot supports compiling against multiple public keys, so rotation does not depend on the secure element:
- Generate the next-generation key on the air-gapped workstation. (End state, pending #481: provision a new OPTIGA Trust M slot, e.g. 0xE0F1.)
- Compile the bootloader with both
*_pub.pemfiles committed underkeys/. Signed images from either key are accepted. - Roll the new key out to fielded devices via OTA (signed by the current key).
- Wait one full update window (typically 90 days).
- Compile the bootloader with only the new key. Roll out the new bootloader. Old key is now untrusted.
The rotation cadence is policy, not a hard constraint -- the chain stays intact regardless of cadence as long as no signed image escapes a slot whose key is still trusted.
[UNTESTED] -- both bench sessions ran CONFIG_SINGLE_APPLICATION_SLOT=y
(no secondary slot); the table below is swap-using-scratch's documented
two-slot design and has not itself been exercised. What is measured:
a single-slot build with a bad signature halts (E: Unable to find bootable image, VTOR = 0x00000000) rather than falling back to a
previous slot -- there is no previous slot in that configuration, which
also means row 1 below does not describe the single-slot case.
MCUboot in swap-using-scratch mode is designed to handle three
pathological cases:
| Failure | What happens |
|---|---|
| Signed image fails verification | Boot the previous slot. No state change. |
| Mid-swap power loss | Scratch slot holds the in-flight bytes; next |
| boot recovers either to the old or the new | |
| image atomically. | |
| Swapped-in image crashes before | MCUboot's "test" mark stays set; next boot |
boot_set_confirmed() lands |
swaps back to the previous slot automatically. |
boot_set_confirmed() is the application's signal that the
new image is healthy. Apps that don't call it within a
documented time window get rolled back -- a watchdog-friendly
safety net for OTA.
zephyr/sysbuild/aen/README.md-- how to invoke the sysbuild config.keys/README.md-- key files + dev key generation.docs/cc3501e-bridge.md-- the CC3501E Wi-Fi bridge's role in OTA delivery on AEN.VERSIONS.md-- versioned roadmap; the secure-boot / OTA scaffolding landed in the v0.4 cycle.