3.3 KiB
0x0026a Custom AES-128-CMAC Secure Bootloader
This project builds a custom secure-boot chain for the Pico 2 (RP2350) that sits entirely above the ROM bootloader. A first-stage bootloader in XIP flash authenticates a signed application with AES-128-CMAC (RFC 4493) before jumping to it, and the signed application decrypts and prints a secret banner using the SDK Mbed TLS AES API.
What You'll Learn
- Why a bootloader-only CRC/manifest is insufficient and what a MAC adds
- How AES-128-CMAC works (K1/K2 subkeys) and the RFC 4493 test vectors
- Custom linker-script editing (
pico_flash_region.ld,memmap_app.ld) to relocate the app - Flashing three images (bootloader, manifest, app) with OpenOCD
- Reverse engineering both images with GDB and Ghidra (find the key, IV, ciphertext, banner)
See the full tutorial: WEEK11a.md / WEEK11a.pdf
Flash Map
| Region | Address | Content |
|---|---|---|
| Bootloader | 0x10000000 |
0x0026a bootloader (27772 B) |
| Manifest | 0x1001F000 |
header + AES-128-CMAC tag |
| App | 0x10020000 |
0x0026a signed app (21656 B) |
The CMSIS-RM9 flash-safe range starts at 0x10000000 on the RP2350, so adding 2 MB relocates the app to 0x10020000.
Artifacts
| File | Description |
|---|---|
0x0026a_bootloader.bin |
Bootloader image (flash at 0x10000000) |
0x0026a_bootloader.uf2 |
Bootloader-only UF2 for the BOOTSEL drag-and-drop |
0x0026a_app.bin |
Signed app image (flash at 0x10020000) |
manifest.bin |
Manifest (flash at 0x1001F000) |
0x0026a_full.uf2 |
All three images in one BOOTSEL drag |
WEEK11a.md / WEEK11a.pdf |
Full lab notebook (in WEEK11/) |
Build
cmake -B build -DPICO_SDK_PATH=~/.pico-sdk/sdk/2.2.0
cmake --build build
python3 tools/sign_firmware.py build/0x0026a_app.bin --out manifest.bin --key "IronNetBootKey!!" --app-base 0x10020000
PICOTOOL=~/.pico-sdk/picotool/2.3.1/picotool/picotool python3 tools/make_full_uf2.py
This produces 0x0026a_bootloader.bin/.uf2 and 0x0026a_app.bin; the signer
writes manifest.bin; make_full_uf2.py packs all three into
0x0026a_full.uf2.
Flash
Easiest - copy 0x0026a_full.uf2 to the RP2350 BOOTSEL drive; one drag writes
the whole chain (bootloader + manifest + app).
Or with OpenOCD:
openocd -f interface/picoprobe.cfg -f target/rp2350.cfg -c \
"program 0x0026a_bootloader.bin 0x10000000 verify; \
program manifest.bin 0x1001F000 verify; \
program 0x0026a_app.bin 0x10020000 verify; reset run; shutdown"
Verify at Runtime
Halt the core after boot and read the program counter. A valid signed image leaves PC inside the app (0x100201xx); a tampered or unsigned image leaves PC in the bootloader boot_halt idle loop near 0x10000eb2.
Disclaimers
This is an educational project for the Embedded-Hacking course. The key IronNetBootKey!! ships in the repo and is intentionally embedded in both images so it can be recovered with GDB/Ghidra. Do not use this scheme to protect real products.