Skip to content

feat(keymanager): Add Key Manager flasher-stub plugin (ESP32-C5/P4) (ESPTOOL-1324) - #77

Draft
hrushikesh430 wants to merge 6 commits into
espressif:masterfrom
hrushikesh430:feat/keymanager-approach-3
Draft

feat(keymanager): Add Key Manager flasher-stub plugin (ESP32-C5/P4) (ESPTOOL-1324)#77
hrushikesh430 wants to merge 6 commits into
espressif:masterfrom
hrushikesh430:feat/keymanager-approach-3

Conversation

@hrushikesh430

@hrushikesh430 hrushikesh430 commented May 27, 2026

Copy link
Copy Markdown

What

Adds a flasher-stub plugin that lets esptool drive the Key Manager (KM) peripheral on ESP32-C5 and ESP32-P4 to deploy and recover keys.

Why

esptool is gaining Key Manager support. The flasher stub needs a plugin that exposes KM operations over the wire so esptool can deploy/recover keys at provisioning time. The actual chip register work lives in the esp-stub-lib KM HAL (companion PR below); this plugin is the wire layer on top of it.

What's in this PR

  • src/keymanager_plugin.c — 7 wire opcodes (0xDF..0xE5), one per KM operation: key-deploy-random / -aes / -ecdh0 / -ecdh1, key-recovery, huk-deploy, huk-recovery. Each handler takes already-wrapped inputs from the host (k1_encrypted, k2_info, k1_G, k2_G) and drives the KM state machine via the esp-stub-lib HAL. No chip-side cryptoespsecure on the host does all AES/ECDH preprocessing.
  • src/commands.h — new response code RESPONSE_KM_INIT_KEY_NOT_BURNED (0xCC00). When a deploy uses the eFuse init-key path but no key block has KM_INIT_KEY burned, the plugin returns this so the host can print an actionable error instead of a silent KEY_VLD=0 failure.
  • src/CMakeLists.txt + src/ld/keymanager_plugin.ld — build and link the plugin.
  • tools/elf2json.py — export the plugin's dispatch symbol.
  • Submodule bumpesp-stub-lib → KM HAL commit.

Companion PR / dependency

Testing

Notes

  • Draft — not yet ready for review.

hrushikesh430 and others added 2 commits May 27, 2026 15:24
Add a flasher-stub plugin that lets esptool drive the Key Manager (KM)
peripheral on ESP32-C5 and ESP32-P4 to deploy and recover keys.

The plugin exposes 7 wire opcodes (0xDF..0xE5), one per KM operation:
key-deploy-random / -aes / -ecdh0 / -ecdh1, key-recovery, huk-deploy and
huk-recovery. Each handler takes already-wrapped inputs from the host
(k1_encrypted, k2_info, k1_G, k2_G) and drives the KM state machine via
the esp-stub-lib KM HAL. There is no chip-side crypto: espsecure on the
host does all AES/ECDH preprocessing.

Details:
- src/keymanager_plugin.c: the 7 opcode handlers + KM/HUK sequencing.
  If a deploy uses the eFuse init_key path but no key block has
  KM_INIT_KEY burned, it returns the new RESPONSE_KM_INIT_KEY_NOT_BURNED
  (0xCC00) so the host can print an actionable message instead of a
  silent KEY_VLD=0 failure.
- src/commands.h: add RESPONSE_KM_INIT_KEY_NOT_BURNED (0xCC00).
- src/CMakeLists.txt + src/ld/keymanager_plugin.ld: build and link the plugin.
- tools/elf2json.py: export the plugin's dispatch symbol.
- Bumps the esp-stub-lib submodule to the KM HAL commit (companion PR).

Verified end-to-end on real ESP32-C5 hardware: huk-status/deploy/recovery,
key-deploy-random/aes/ecdh0/ecdh1 (single + multi-stage) and key-recovery.
@github-actions github-actions Bot changed the title feat(keymanager): Add Key Manager flasher-stub plugin (ESP32-C5/P4) feat(keymanager): Add Key Manager flasher-stub plugin (ESP32-C5/P4) (ESPTOOL-1324) May 27, 2026
…eanly

Three issues prevented a correct ESP32-P4 Key Manager plugin from being
built from source:

1. ROM address: the plugin link globbed all ROM scripts into one sorted
   list, feeding both esp32p4.rom.ld (ECO0-4, esp_rom_km_huk_conf=0x4fc00718)
   and esp32p4.rom.eco5.ld (ECO>=5, 0x4fc0070c) and letting the ECO0-4 base
   win. esptool only drives the Key Manager on P4 >= v3.0 (ROM ECO >= 5), so
   the plugin called the wrong ROM address and crashed huk-deploy /
   huk-recovery with a Guru Meditation (Load access fault). Feed the base
   *.rom.ld first and *.rom.*.ld ECO overrides last so the ECO address wins.

2. BASE_SLIP_SEND_FRAME was never resolved: keymanager_plugin.ld forwards
   slip_send_frame to the base stub via BASE_SLIP_SEND_FRAME, but nothing
   defined it. compute_plugin_addrs.py now reads the base stub's symbol and
   emits STUB_BASE_DEFSYMS (--defsym BASE_SLIP_SEND_FRAME=<addr>), which the
   plugin link consumes. Drop the stale slip_recv_reset /
   slip_is_frame_complete / slip_get_frame_data forwards — those functions
   were removed when the transport receive path was refactored and the
   plugin does not use them (input arrives via the dispatcher's data/len).

3. Handlers were garbage-collected: keymanager_plugin.ld had no EXTERN()
   list, so --gc-sections dropped every handler except the ENTRY and
   elf2json.py could not resolve them. Add EXTERN() for all seven KM
   handlers, mirroring nand_plugin.ld.

Also bump the esp-stub-lib submodule to c8dffbd (the committed HAL cleanup).

Verified by a clean from-source build on ESP32-P4 v3.1: the plugin links,
calls esp_rom_km_huk_conf at 0x4fc0070c, and huk-deploy / huk-recovery /
key-deploy-aes / key-recovery all succeed on hardware.
The Key Manager is only supported on ESP32-P4 >= v3.0 (ROM ECO >= 5, the
revision with the 660-byte HUK_INFO). Previously the plugin link fed both the
ECO0-4 base ROM script (esp32p4.rom.ld) and the ECO>=5 one (esp32p4.rom.eco5.ld)
and relied on ordering so the ECO>=5 address won.

Make it explicit instead: when an ECO interface (*.rom.eco*.ld) exists, link
ONLY the ECO scripts (plus the shared *.rom.{api,extra,libc,libgcc}.ld) and
drop the older ECO0-4 base entirely, so the plugin is built exclusively for the
revisions esptool supports and can never pick up a stale ECO0-4 address. Chips
without an ECO variant (e.g. ESP32-C5) keep using their base interface.

Verified by clean builds: P4 links esp_rom_km_huk_conf at 0x4fc0070c with all 7
handlers; C5 still builds with its base interface.
Return RESPONSE_KM_UNSUPPORTED_CHIP (0xCC01) from the deploy, huk-deploy and
huk-recovery handlers when stub_target_km_is_supported() is false. On ESP32-P4
the HAL reports support only from v3.0 (ROM ECO >= 5, 660-byte HUK); earlier
silicon used a 384-byte HUK and is refused. Defense-in-depth alongside
esptool's revision gate (KM_MIN_CHIP_REV) and stub selection.
…rt check

The keymanager plugin calls stub_target_km_is_supported(), which lands in
esp-stub-lib PR #113; the previously committed submodule pointer predates it
and the plugin no longer compiled. Pin the submodule to the PR #113 head
(espressif/esp-stub-lib#113) — to be re-bumped to the merged commit once that
PR lands.

@AdityaHPatwardhan AdityaHPatwardhan left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

A few questions from reviewing the Key Manager plugin. One that isn't line-specific: the esp-stub-lib submodule is pinned to a fork-only commit (4ae32cb) — we should repoint it to the upstream merged SHA, so this can't merge before #113.

Comment thread src/keymanager_plugin.c
{
/* Phase 1 — IDLE: configure mode + purpose + (XTS only) key length,
* plus any mode-specific static-register tweaks via pre_start_cb. */
stub_target_km_wait_for_state(STUB_KM_STATE_IDLE);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Should wait_for_state take a timeout? It's void in the HAL, so if KM never reaches this state the single-threaded stub spins here forever and the host just sees a dead connection.

Comment thread src/keymanager_plugin.c
stub_target_km_wait_for_state(STUB_KM_STATE_LOAD);
if (load_cb != NULL) {
int err = load_cb(purpose, hdr, data, data_len);
if (err != 0) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

On a load/gain/validation error after km_start(), should we drive KM back to IDLE before returning? Otherwise the next operation hangs at the wait_for_state(IDLE) above.

Comment thread src/keymanager_plugin.c
s_km_state.pending_key_info_slots = 0U;

if (kind == KM_PENDING_HUK_INFO) {
slip_send_frame(s_km_state.huk_info, STUB_KM_HUK_INFO_WIRE_SIZE);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Should we stream through ctx->transport here instead of calling slip_send_frame directly? The NAND plugin uses the transport abstraction and we already have ctx in post_process.

Comment thread src/keymanager_plugin.c
return RESPONSE_KM_UNSUPPORTED_CHIP;
}

stub_km_key_type_t key_type = (stub_km_key_type_t)hdr->key_type;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

We cast key_type/key_len straight from the wire and use them before the GAIN-phase validity check — should we gate on the resolved purpose being valid up front?

Comment thread src/commands.h
/* Key Manager: this chip/revision does not support the 660-byte HUK
* flow. ESP32-P4 only supports it from v3.0 (ROM ECO >= 5); earlier P4
* silicon used a 384-byte HUK and is rejected. */
RESPONSE_KM_UNSUPPORTED_CHIP = 0xCC01,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Should we mention RESPONSE_KM_UNSUPPORTED_CHIP (0xCC01) in the description too, and mirror it on the esptool side? Right now only 0xCC00 is documented.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants