UNIT 15 · LESSON 5 OF 6

Protecting Keys, Debug Ports, and Device Identity

So where do the keys actually live, who can read them, and what does an attacker gain by buying one of your devices, opening it and extracting everything inside?

INTERACTIVEOne device opened: how many are exposed?
A fleet of one hundred devices, marking those that would accept a forged image100 of 100 devices accept a forged imageeach device stores the same secret HMAC keyEvery device verifies with the same HMAC key, so the key read out of one device forgesimages for all of them.
A fleet of one hundred devices, marking those that would accept a forged image100 of 100 devices accept a forged imageeach device stores the same secret HMAC keyEvery device verifies with the same HMAC key, so thekey read out of one device forges images for all ofthem.

Try this

Devices verify with
Secret leaked
100 of 100 devices exposed.

A device that verifies a MAC must hold the MAC key, and anything stored in a device you can buy should be assumed extractable by a determined attacker. If every device shares that key, one extraction breaks the fleet. Deriving a unique key per device from a master key and the chip’s ID limits the damage to one device, at the price of a master key that the server must guard. Signatures move the secret off the device entirely; what remains to guard is the signing key.

What you will be able to do
  • For a shared MAC key, per-device MAC keys and signatures, determine how many devices are exposed when one device’s secrets or the server’s key leak.
  • Derive a per-device key from a master key and a device ID, and explain what the server must then protect.
  • Describe how debug access is restricted on an STM32F4 and an RP2350, and which locks are reversible.
  • Explain why a readable serial number is an identifier but not an identity, and what a device must hold to prove who it is.
  • Apply key-handling rules: development keys never in production, signing keys off developer machines, constant-time comparison, least access inside the device.
Before you start
  • MACs and signatures (lesson 2); root-of-trust keys (lesson 4).
  • The debug port and what it can do (unit 6, lesson 1).
Steps in this lesson
  1. Where each key lives
  2. One key per device
  3. Identifier or identity?
  4. Closing the debug port
  5. Keeping secrets away from code that does not need them
  6. Worked example: one extracted key, three designs
  7. Common misconceptions

The puzzle

Every guarantee in the last three lessons ended with “as long as the key is secret”. So where do the keys actually live, who can read them, and what does an attacker gain by buying one of your devices, opening it and extracting everything inside?

STEP 1

Where each key lives

Follow each key to the place it is stored:

  • The signing private key lives with the vendor, ideally in a hardware security module or at least a machine that does nothing else. It never goes into a device, a repository or a developer’s laptop. MCUboot ships a development key “widely distributed”, and its documentation says it “should never be used for production”; the same goes for TF-M’s default keys.
  • The verification public key, or its hash, lives in the device. It is not secret, but it must be unchangeable (lesson 4).
  • Symmetric keys (a MAC key, an image-decryption key, a key shared with a server) live in the device and must be secret. MCUboot’s encryption documentation puts the burden plainly: the manufacturer must ensure such a key is “not possible to extract”.

Design as if a determined attacker can read anything stored in a device they own. Then ask how many devices that one extraction exposes.

↑ This step uses the figure at the top of the page.

STEP 2

One key per device

If a device must hold a symmetric secret, make it unique. A common way derives each device’s key from a master key and the device’s ID:

Ki=HMAC(Kmaster, IDi)K_i = \mathrm{HMAC}(K_{\text{master}},\ \mathrm{ID}_i)

The device stores only KiK_i; the server stores only KmasterK_{\text{master}} and recomputes KiK_i when it needs it. Extracting KiK_i compromises one device. The master key is now the crown jewel, kept where the signing key is kept. Signatures are better still where they fit: the device holds nothing that signs, so opening it yields nothing to forge with.

STEP 3

Identifier or identity?

A serial number is an identifier: it says which device claims to be talking. Anyone who reads it can repeat it. The RP2040 has no on-chip ID at all; the pico-sdk’s pico_get_unique_board_id() returns the 64-bit ID of the external flash chip, useful to tell boards apart, but readable by any code and not a secret. The RP2350’s OTP separates a public 64-bit CHIPID from a private 128-bit per-device random number, RANDID.

An identity is proven, not stated: the device shows it holds a secret nobody else has, usually by signing a fresh challenge from the server with a per-device private key whose public half the server registered at manufacture. A device with a readable ID and no secret can be impersonated by anyone who has read the ID.

STEP 4

Closing the debug port

The debug port that halted your code in unit 6 can, left open, read memory, keys included, and rewrite flash. Production devices close it, and chips offer different locks:

  • The STM32F4’s readout protection (RDP) is an option-byte setting. Level 1, “read protection of the memory”, keeps flash contents from the debugger and blocks flash programming and erasing while debug is connected, and can be set back to level 0, but that regression mass-erases the flash, so it reopens the device for new code without revealing the old contents. Level 2, “full chip protection”, is permanent: the HAL warns it is “no more possible to go back to level 1 or 0”.
  • The RP2350 has OTP flags: SECURE_DEBUG_DISABLE (“Disable Secure debug access”) and DEBUG_DISABLE (“Disable all debug access”). OTP bits cannot be cleared.
  • Armv8-M cores such as the Cortex-M33 separate invasive debug (roughly, halting and changing the core) from non-invasive debug (observing it, such as tracing), each for Secure and Non-secure state; the chip decides which are allowed through the debug authentication signals CMSIS exposes as DAUTHCTRL and DAUTHSTATUS.
INTERACTIVELocking the debug port
Three levels of debug protection and what each allowsRDP level 0RDP level 1RDP level 2more protection →RDP level 1: “Read protection of the memory”debug: a debugger can connect, but flash contents are protected; flash cannot beprogrammed or erased while debug is connectedreversible: can be set back to level 0, which mass-erases the flashLevel 1 alone is not a secure-boot anchor: it protects reading, and the device can bereopened for new code (the flash is erased when it is).
Three levels of debug protection and what each allowsRDP level 0RDP level 1RDP level 2more protection →RDP level 1: “Read protection of the memory”debug: a debugger can connect, but flash contentsare protected; flash cannot be programmed or erasedwhile debug is connectedreversible: can be set back to level 0, whichmass-erases the flashLevel 1 alone is not a secure-boot anchor: itprotects reading, and the device can be reopened fornew code (the flash is erased when it is).
Chip
Protection
RDP level 1: a debugger can connect, but flash contents are protected; flash cannot be programmed or erased while debug is connected.

A debug port that can halt the core and read memory also reads keys and rewrites firmware, so production devices close it. Chips differ in how: the STM32F4’s readout protection (RDP) has two levels, only the first reversible; the RP2350 has one-time-programmable OTP flags that disable Secure debug or all debug. A permanent lock also shuts out your own failure analysis of returned units, so plan how a locked device is diagnosed.

Lock at the end of production, after programming and testing, as a scripted step. A permanent lock also locks you out: failure analysis of a returned unit must then work from logs, crash records and a recovery path designed in advance (lesson 6), or from whatever authenticated unlock the chip supports.

STEP 5

Keeping secrets away from code that does not need them

Inside the device, give each secret to as little code as possible:

  • keep keys in memory that ordinary code cannot read: the RP2350 can lock OTP pages against Non-secure access, permanently, and TrustZone keeps Secure memory out of reach of Non-secure code;
  • use keys through an API that never hands them out: a PSA Crypto key created without export permission is used by its ID (for example to verify a MAC) and its bytes never reach application code; MCUboot’s MCUBOOT_BUILTIN_KEY applies the same pattern to its (public) verification keys;
  • compare MACs, hashes and passwords in constant time (lesson 2);
  • never log, print or put keys in crash dumps.

STEP 6

Worked example: one extracted key, three designs

A fleet of 10 000 devices; an attacker buys one and extracts its secrets.

  • One shared HMAC key: the key verifies, and therefore forges, images for all 10 000.
  • Per-device keys: for chip ID e6613852d34a2b19 and the demonstration master key in the figure, KiK_i = HMAC-SHA-256(master, ID) begins b5acb064 2da62ea0. It forges images for exactly one device, the one already in the attacker’s hands.
  • Signatures: the device holds a public key; nothing extracted can sign. 0 devices exposed.

Now suppose instead the vendor’s build server is breached. The shared key, the master key and the signing key each expose all 10 000 devices. Signatures recover best, provided the second signing key was held separately (offline or in a different HSM): devices provisioned with its hash (lesson 4) can revoke the stolen key and accept releases signed with the new one, without touching the hardware.

MYTHS AND FACTS

Common misconceptions

Nobody can read our flash

Assume anyone who owns a device can, eventually; design so that what they read does not break other devices.

The serial number authenticates the device

A readable number is an identifier; identity needs a secret the device can prove it holds.

Readout protection level 1 is enough

It protects reading and can be set back to level 0 (erasing the flash), so it is not a trust anchor: new code can then be loaded; the permanent lock is level 2 (on the STM32F4), with the cost that it can never be undone, even by you.

The development key is fine until we ship

Devices provisioned with it accept anything signed by the widely distributed private key; production keys go in before the first production unit.

Check yourself

Answer in your head, then open the card.

A product verifies firmware with HMAC and one key in every unit. What is the smallest change that stops one extracted key from exposing the whole fleet, and what new asset must be protected?

Per-device keys, derived as HMAC(master, device ID) and programmed at manufacture. The server must now guard the master key, which can derive every device key.

Why is the RP2040’s pico_get_unique_board_id() not suitable as a secret for authenticating a device to a server?

It is the flash chip’s ID, readable by any code (and by anyone with the chip), so it identifies the board but proves nothing; it also changes if the flash chip is replaced.

An STM32F4 product ships with RDP level 1. What does that protect, and what does it not guarantee?

The debugger cannot read flash contents, and flash cannot be programmed or erased while debug is connected. It is reversible (it can be set back to level 0), so it is not a permanent lock.

Name two ways firmware can keep a key usable but hard to leak inside the device.

Any two of: keep it in memory locked against Non-secure or application access (OTP page locks, TrustZone Secure memory); use it through a crypto API by key ID so the key is never copied out; never log it or put it in crash dumps; compare secrets in constant time.

Sources (7)
  1. MCUboot v2.1.0, docs/imgtool.md, and MCUboot docs/signed_images.md — the key file “should be protected, and not widely distributed”; the distributed development key “should never be used for production”; signed_images.md: separate “signing custody (a production private key, held only by a release team) from verification custody”, so that “the private key is never present on developer workstations”
  2. STMicroelectronics, stm32f4xx-hal-driver, Inc/stm32f4xx_hal_flash_ex.h and Src/stm32f4xx_hal_flash_ex.c — OB_RDP_LEVEL_0 “No protection” (0xAA), OB_RDP_LEVEL_1 “Read protection of the memory” (0x55), OB_RDP_LEVEL_2 “Full chip protection” (0xCC): “WARNING: When enabling OB_RDP level 2 it’s no more possible to go back to level 1 or 0”; at level 1 “it is not possible to program or erase the flash sector i if CortexM4 debug features are connected or boot code is executed in RAM”; read-out protection can be modified “from level1 to level0”
  3. Raspberry Pi Ltd, pico-sdk 2.0.0, src/rp2350/hardware_regs/include/hardware/regs/otp_data.h — CRIT1.DEBUG_DISABLE “Disable all debug access”, CRIT1.SECURE_DEBUG_DISABLE “Disable Secure debug access”; CHIPID0–3 “public device ID” (64 bits), RANDID0–7 “private per-device random number” (128 bits); page lock states are “Thermometer-coded, so lock state can be advanced permanently from any state to any less-permissive state by programming OTP”, separately for Secure and Non-secure access
  4. Arm, CMSIS 6 v6.1.0, CMSIS/Core/Include/core_cm33.h (DCB DAUTHCTRL, DIB DAUTHSTATUS) — Armv8-M debug authentication distinguishes invasive and non-invasive debug, each for Secure and Non-secure state: DAUTHCTRL SPIDENSEL/INTSPIDEN and SPNIDENSEL/INTSPNIDEN; DAUTHSTATUS SID, SNID, NSID, NSNID fields
  5. Raspberry Pi Ltd, pico-sdk 1.5.1, pico_unique_id/include/pico/unique_id.h — the RP2040 has no on-chip unique ID; pico_get_unique_board_id() returns the external NOR flash’s “64-bit unique ID”, “suitable for use as a unique identifier for an RP2040-based board”
  6. MCUboot, docs/encrypted_images.md (“Threat model”) — “Since decrypting requires a private key (or secret if using symmetric crypto) to reside inside the device, it is the responsibility of the device manufacturer to guarantee that this key is already in the device and not possible to extract”
  7. MCUboot v2.1.0, docs/design.md (“Using hardware keys for verification”) — MCUBOOT_BUILTIN_KEY: “neither the code nor the image metadata needs to contain any public key data. During image validation only a key ID is passed to the verifier function. The key handling is entirely the responsibility of the crypto library”