Summary
The release path can reach the development trust anchor, and after #116 that anchor works — its private half is printed in RFC 8032. Nothing structural stops it being compiled into the firmware release.yml publishes, and today it is.
Verified
EBLDR_PRODUCTION_KEY appears nowhere except inside core/keystore.c: there is no CMake option for it and no build sets it. Every firmware job in .github/workflows/release.yml (stm32f4, stm32h7, nrf52, rpi4, riscv64_virt, esp32, esp32c3, x86_64_efi) configures with only -DEBLDR_BOARD=… -DCMAKE_BUILD_TYPE=Release, so every published artifact compiles the #ifndef EBLDR_PRODUCTION_KEY branch.
- No board under
boards/ implements otp_read or otp_write (grep -rl otp_read boards/ is empty), so eos_keystore_init() finds no OTP on every one of them, fills slot 0 from default_dev_key, and sets ks->source = EOS_KEY_SOURCE_COMPILED. Blast radius: all eight release boards.
- The sole guard is a compile-time
#warning, which fails nothing and is invisible in a release log.
- The
#ifdef EBLDR_PRODUCTION_KEY branch does not build: it declares extern const uint8_t ebldr_production_key[] and nothing in the tree defines, generates or documents that symbol. The comment at core/keystore.c:25 describes this as settled ("Production builds set EBLDR_PRODUCTION_KEY, which replaces it").
.ai/security.md: "Test keys and development keys must be structurally incapable of signing a release artifact. Check that the release path cannot reach them." It reaches them.
Required
- A real
EBLDR_PRODUCTION_KEY CMake option that generates ebldr_production_key[] from a supplied public key, so the production branch compiles.
- A configure-time refusal when a Release build for a real board carries no production anchor (with an explicit, named opt-out for bring-up).
release.yml passes the anchor to every firmware job, and refuses an artifact that embeds the development anchor bytes.
- The keystore comment and
docs/key_lifecycle.md describe what actually exists.
Found during review of #116 (P0 finding). Pre-existing; #116 did not create it but makes it live.
Summary
The release path can reach the development trust anchor, and after #116 that anchor works — its private half is printed in RFC 8032. Nothing structural stops it being compiled into the firmware
release.ymlpublishes, and today it is.Verified
EBLDR_PRODUCTION_KEYappears nowhere except insidecore/keystore.c: there is no CMake option for it and no build sets it. Every firmware job in.github/workflows/release.yml(stm32f4, stm32h7, nrf52, rpi4, riscv64_virt, esp32, esp32c3, x86_64_efi) configures with only-DEBLDR_BOARD=… -DCMAKE_BUILD_TYPE=Release, so every published artifact compiles the#ifndef EBLDR_PRODUCTION_KEYbranch.boards/implementsotp_readorotp_write(grep -rl otp_read boards/is empty), soeos_keystore_init()finds no OTP on every one of them, fills slot 0 fromdefault_dev_key, and setsks->source = EOS_KEY_SOURCE_COMPILED. Blast radius: all eight release boards.#warning, which fails nothing and is invisible in a release log.#ifdef EBLDR_PRODUCTION_KEYbranch does not build: it declaresextern const uint8_t ebldr_production_key[]and nothing in the tree defines, generates or documents that symbol. The comment atcore/keystore.c:25describes this as settled ("Production builds set EBLDR_PRODUCTION_KEY, which replaces it")..ai/security.md: "Test keys and development keys must be structurally incapable of signing a release artifact. Check that the release path cannot reach them." It reaches them.Required
EBLDR_PRODUCTION_KEYCMake option that generatesebldr_production_key[]from a supplied public key, so the production branch compiles.release.ymlpasses the anchor to every firmware job, and refuses an artifact that embeds the development anchor bytes.docs/key_lifecycle.mddescribe what actually exists.Found during review of #116 (P0 finding). Pre-existing; #116 did not create it but makes it live.