A reproducible STM32F429 secure-boot and embedded-security reference laboratory with authenticated firmware, rollback-aware A/B updates, runtime diagnostics, and hardware-in-the-loop validation.
Status: v1.1.0-rc1 research release candidate; hardware validated at RDP Level 0 on an STM32F429IGT6-class board
Target platform: STM32F429 family,stm32f429_1mlayout profile
License: BSD 3-Clause
Primary focus: defensive embedded-security research and reviewable reference code
STM32 Security Lab is a controlled research repository for studying how secure boot, signed firmware, update state, rollback policy, flash behavior, runtime diagnostics, and hardware recovery interact on real STM32F429 hardware.
The repository is intended to be understandable and reproducible for a new developer. It is not a certified production boot chain, but the current EXP045/EXP066 secure-boot and secure-update path has been tested on real hardware at RDP Level 0 and is suitable as an open reference implementation for review, experimentation, and further hardening.
Only run destructive tests on hardware you own or are explicitly authorized to test.
This is an open-source research platform and release candidate, not a production-certified or formally verified security system. RDP2 and WRP are not enabled; the current RDP2 decision is No-Go because the controlled power-loss campaign and parts of the independent UART-only lifecycle remain open. Test-key firmware is not intended for provisioned devices.
flowchart TD
Reset[Reset or power-on] --> Bootloader[EXP045 Stage-0 bootloader]
Bootloader --> Entry[Bounded UART entry window]
Entry -->|valid SUPD HELLO| UpdateMode[Binary update mode]
Entry -->|text line| Console[Read-only diagnostic console]
Entry -->|timeout or noise| BootPolicy[Boot policy]
Console -->|boot command or timeout| BootPolicy
UpdateMode --> Installer[Streaming update installer]
Installer --> Metadata[Redundant boot metadata]
Metadata --> BootPolicy
BootPolicy --> Verify[Manifest, SHA-512, Ed25519, vector checks]
Verify -->|accepted| App[EXP066 research platform]
Verify -->|rejected| Fallback[Confirmed fallback or signed UART recovery]
App --> Confirm[Application confirmation]
Confirm --> Metadata
The trusted bootloader verifies a slot-linked signed image before jumping to
the application. Updates are streamed over the UART binary protocol into the
inactive slot, verified from flash, committed as CANDIDATE_READY, trial
booted, and confirmed by the application only after its health gate passes.
Detailed architecture:
- Secure Boot Architecture
- Memory Layout
- Stage-0 Slot Selection And Trial Boot
- Secure Update Streaming Design
- UART Binary Update Protocol
- Runtime Security Monitor
Current reference components and their evidence are listed in the feature matrix and security claims. The following list describes what is implemented; it is not a production approval:
- EXP045 Stage-0 secure bootloader for STM32F429.
- EXP066 slot-aware research application.
- Canonical signed-image header with Ed25519 manifest authentication.
- SHA-512 payload integrity verification.
- Redundant boot metadata with explicit states.
- Slot A/Slot B boot selection, trial boot, confirmation, and fallback.
- Persistent three-attempt trial policy, reset-cause accounting, independent watchdog and signed UART recovery bootstrap.
- Streaming secure-update installer with fixed RAM buffers and no heap.
- USART1 polling RX/TX transport and deterministic
SUPDbinary protocol. - Read-only UART diagnostic console.
stm32ctlPython host client forinfo,status,update,recovery, andreset.- Runtime Security Monitor foundation in EXP066.
- Cortex-M4 MPU policy with Stage-0/metadata read-only mapping and stack guard.
- Explicit root-of-trust, key lifecycle, rollback, WRP evaluation, and threat model documentation.
- Deterministic build checker, release artifact validation, host tests, and HIL tooling.
- Host-only security fuzzing for UART, package, metadata and boot policy, Hypothesis properties, ASan/UBSan profiles, GCC analyzer and gcov coverage reporting. Extended campaigns remain a separate, explicitly invoked step.
- Local release-candidate tooling with deterministic manifests, SPDX SBOM, provenance, artifact verification and publication scanning.
Research or experimental components:
- EXP067-EXP070 module-package, VM, native-loader, and atomic-install simulations.
- Runtime-monitor telemetry intended for evidence and diagnostics, not as an isolated security enclave.
- Physical recovery pin selection, option-byte policy, WRP/RDP provisioning, and fault-injection campaigns.
Recovery and watchdog evidence is explicitly classified in
the hardening report. The
repository distinguishes IMPLEMENTED, HOST TESTED, HARDWARE VALIDATED,
DOCUMENTED ONLY and NOT IMPLEMENTED; a host test is not hardware evidence.
| Function | Status |
|---|---|
| Secure Boot | HARDWARE VALIDATED |
| Ed25519 verification | HOST AND HARDWARE TESTED |
| SHA-512 | HOST AND HARDWARE TESTED |
| A/B update | HARDWARE VALIDATED |
| Wrong-key rejection | HARDWARE VALIDATED |
| Signature rejection | HARDWARE VALIDATED |
| Rollback rejection | HARDWARE VALIDATED |
| Trial Boot | HARDWARE VALIDATED |
| Watchdog fallback | HARDWARE VALIDATED |
| UART recovery | LIMITED HARDWARE VALIDATION |
| MPU | HARDWARE TESTED |
| Metadata corruption | PARTIAL VALIDATION |
| Power-loss resilience | NOT YET FULLY VALIDATED |
| WRP | NOT ENABLED |
| RDP2 | NOT ENABLED / NO-GO |
| Reproducible builds | LOCALLY VALIDATED |
| Remote CI | REMOTE CI PASSED |
| External audit | NOT PERFORMED |
Part-2 security references:
Part-3 security-testing references:
- Security Test Surface
- Fuzzing
- Coverage
- Security Test Profiles
- Security Testing Limitations
- Root-of-Trust Hardware Evidence
- Key Management
- MPU Policy
- Root-of-Trust Hardening Report
Open-source and release references:
- Project Scope
- Quickstart
- Documentation Index
- Release Artifacts
- Security Claims
- Release Readiness Report
| Component | Path | Purpose |
|---|---|---|
| Bootloader | firmware/exp045_bootloader_v2 |
Trusted Stage-0 verifier, boot policy, update mode, diagnostic console |
| Application | firmware/exp066_research_platform_core |
Slot-linked research platform, RSM, confirmation, LED heartbeat |
| Shared layout | config/, firmware/common/ |
Generated STM32F429 flash and linker layout |
| Update package tool | tools/update_package.py |
Build, inspect, verify, and simulate update packages |
| UART host client | tools/stm32ctl/ |
Host-side client for the bootloader binary protocol |
| Factory metadata tool | tools/boot_metadata_provision.c |
Create initial redundant CONFIRMED metadata records |
| HIL framework | tools/secure_boot_hil/ |
Secure-boot hardware-in-the-loop campaign tooling |
| Tests | tests/ |
Python tests and C host tests for verifier, update, UART, RSM, and tools |
| Documentation | docs/ |
Architecture, validation, release, hardware, and research boundaries |
.
|-- config/ Source memory-layout profile
|-- docs/ Architecture, validation, release, and research docs
|-- firmware/ Bootloader, application, and experiment firmware
|-- hardware/ Curated hardware layout and provisioning records
|-- modules/ Module-research examples and fixtures
|-- scripts/ Experiment helper scripts
|-- tests/ Python and C host tests
|-- third_party/ Vendored dependencies and license notices
|-- tools/ Signing, update, release, HIL, and host tools
|-- CHANGELOG.md
|-- CONTRIBUTING.md
|-- SECURITY.md
|-- CITATION.cff
`-- README.md
The numbered expNNN_* directories preserve the research history. Raw local
flash baselines, analyzer captures and hardware logs are intentionally excluded
from the public tree; curated evidence is maintained under docs/. The
current hardware-validated secure-update path is EXP045 plus EXP066.
The validated target is an STM32F429IGT6-class board with:
- ST-LINK-compatible SWD probe.
- USART1 on PA9 TX and PA10 RX, 115200 baud, 8N1, 3.3 V TTL.
- Active-low LEDs on LED1 PE3, LED2 PH10, LED3 PH11, LED4 PH12.
- RDP Level 0 for the published hardware-validation evidence.
Use crossed UART wiring:
STM32 PA9 / USART1_TX -> USB-UART RXD
STM32 PA10 / USART1_RX <- USB-UART TXD
GND -> USB-UART GND
Do not connect a 5 V serial adapter to the MCU pins. Do not change RDP or Option Bytes unless a separate, reviewed provisioning procedure explicitly requires it.
More detail:
Typical Linux host tools:
arm-none-eabi-gcc
arm-none-eabi-binutils
make
python3
python3-venv
openocd or stlink-tools
Python dependencies are pinned in requirements.txt and requirements-security.txt:
python3 -m venv .venv-hil
. .venv-hil/bin/activate
python -m pip install --upgrade pip
python -m pip install -r requirements.txt
python -m pip install -e tools/secure_boot_hil[dev]The repository never requires a private signing seed to run ordinary host tests. Building signed release packages requires an external 32-byte Ed25519 signing seed supplied by path; private seeds must remain outside Git.
Run the host security profiles without touching hardware:
make test-fast
make test-security PYTHON=/path/to/security-venv/bin/python
make fuzzmake fuzz is intentionally not part of a normal build. It runs bounded,
host-only parser/policy campaigns and keeps findings under the ignored
fuzz/findings-local/ directory. RDP, Option Bytes, WRP and physical flash
operations are not changed by these targets.
Build the bootloader:
make -C firmware/exp045_bootloader_v2 clean all report LAYOUT_PROFILE=stm32f429_1mBuild the EXP066 application for the default slot:
make -C firmware/exp066_research_platform_core clean all LAYOUT_PROFILE=stm32f429_1mBuild Slot A and Slot B signed update releases:
make -C firmware/exp066_research_platform_core slot-releases \
LAYOUT_PROFILE=stm32f429_1m \
SIGNING_SEED=/path/to/release_signing_seed.bin \
PUBLIC_KEY_HEADER=../exp045_bootloader_v2/src/firmware_public_key.hThe default EXP066 package image version is 2. Those packages are suitable
for factory provisioning or offline verification. A positive UART update from
an already confirmed version-2 slot must use a strictly higher package version,
otherwise rollback protection rejects it as a same-version update.
The current stm32f429_1m slot bases are:
| Region | Address |
|---|---|
| Bootloader | 0x08000000 |
| Metadata A | 0x08008000 |
| Metadata B | 0x0800c000 |
| Slot A signed image | 0x08020000 |
| Slot A payload/vector base | 0x08020200 |
| Slot B signed image | 0x08080000 |
| Slot B payload/vector base | 0x08080200 |
The generated layout files are the authority for code and linker scripts.
Flashing is intentionally manual. Review every address before writing:
st-flash write firmware/exp045_bootloader_v2/build/exp045_bootloader_v2.bin 0x08000000
st-flash write firmware/exp066_research_platform_core/build/slot_a/exp066_research_platform_core_slot_a_slot_a_update_v2.bin 0x08020000Create initial confirmed metadata:
make -C tools boot-metadata-provision
mkdir -p build/factory
tools/build/boot_metadata_provision.bin create-confirmed \
--slot a \
--image-version 2 \
--sector-image \
--copy-a-output build/factory/boot_metadata_a.bin \
--copy-b-output build/factory/boot_metadata_b.bin \
--json-output build/factory/boot_metadata_provision.json
st-flash write build/factory/boot_metadata_a.bin 0x08008000
st-flash write build/factory/boot_metadata_b.bin 0x0800c000The factory flow is documented in Factory Provisioning Workflow. It does not write Option Bytes, RDP, WRP, OTP, or any irreversible configuration.
Run all Python tests:
PYTHONDONTWRITEBYTECODE=1 python3 -m pytest -q -p no:cacheprovider testsRun C host tests:
make -C tests/host_verifier clean test
make -C tests/update_storage clean test
make -C tests/update_protocol clean test
make -C tests/uart clean test
make -C tests/diagnostic_console clean test
make -C tests/rsm_core clean test
make -C tests/reset_cause clean test
make -C tools clean testRun sanitizer variants where supported:
make -C tests/host_verifier clean test SANITIZE=1
make -C tests/update_storage clean test SANITIZE=1
make -C tests/update_protocol clean test SANITIZE=1
make -C tests/uart clean test SANITIZE=1
make -C tests/diagnostic_console clean test SANITIZE=1
make -C tools clean test SANITIZE=1Run release checks:
.venv-hil/bin/ruff check .
.venv-hil/bin/mypy tools/stm32ctl tests/test_stm32ctl.py
python3 tools/check_deterministic_build.py
python3 tools/check_no_private_keys.py
bash audit/run_repository_audit.sh
git diff --checkCreate and verify a local, test-key-only release candidate without tagging or publishing:
make release-candidate RELEASE_TEST_KEY=1
make verify-release RELEASE_DIR=dist/v1.1.0-rc1-localThis produces a local SPDX SBOM, provenance record, sorted checksums and a deterministic archive. It does not flash hardware, write Option Bytes, push or create a GitHub release.
The GitHub CI workflow runs the same host, firmware, deterministic-build, and private-key checks without flashing hardware.
The bootloader accepts an image only when all of the following checks pass:
- canonical little-endian v2 manifest;
- supported target compatibility and application image type;
- image version allowed by policy;
- payload range inside the selected slot;
- initial MSP inside supported SRAM and 8-byte aligned;
- Thumb reset vector inside the authenticated payload;
- SHA-512 payload digest match;
- Ed25519 signature over the serialized manifest;
- final jump-context revalidation before
VTOR,MSP, and branch.
Any malformed, corrupted, unsigned, downgraded, or structurally invalid image is rejected before execution.
See Secure Boot Validation and Threat Model.
The update chain keeps the full package out of RAM. The host sends the package
through the SUPD UART binary protocol in bounded frames. The target:
- validates the header and signature before erasing the inactive slot;
- checks rollback policy before writing;
- commits
WRITINGmetadata before candidate-slot erase; - writes monotonic payload blocks only at the expected offset;
- keeps a streaming SHA-512 state during transfer;
- reads the installed image back from flash;
- verifies the installed manifest, signature, payload hash, padding, and vector table;
- commits
CANDIDATE_READYonly after final verification; - resets into the trial-boot path.
Host update example:
PYTHONPATH=tools python3 -m stm32ctl \
--port /dev/ttyUSBx \
--timeout 15 \
update \
--package firmware/exp066_research_platform_core/build/slot_b/exp066_research_platform_core_slot_b_slot_b_update_v2.bin \
--public-key-header firmware/exp045_bootloader_v2/src/firmware_public_key.h \
--block-size 512If the active confirmed image is version 2, first build a higher-version
candidate package from the already slot-linked binary:
python3 tools/update_package.py build \
--application firmware/exp066_research_platform_core/build/slot_b/exp066_research_platform_core_slot_b.bin \
--seed firmware/exp065_signed_app/keys/firmware_signing_seed.bin \
--slot b \
--image-version 3 \
--output build/exp066_slot_b_v3_update_v2.bin \
--json-output build/exp066_slot_b_v3_package_build.json
python3 tools/update_package.py verify \
--package build/exp066_slot_b_v3_update_v2.bin \
--slot b \
--application firmware/exp066_research_platform_core/build/slot_b/exp066_research_platform_core_slot_b.bin \
--public-key-header firmware/exp045_bootloader_v2/src/firmware_public_key.h \
--json-output build/exp066_slot_b_v3_package_verify.jsonSee stm32ctl, UART Binary Update Protocol, and Secure Update Audit.
EXP066 includes a Runtime Security Monitor foundation. It reports public diagnostic evidence such as device family, flash size, reset cause, boot slot, health state, vector-monitor status, and event counters. Restricted diagnostics are disabled by default and must never be treated as authentication.
Normal HEALTHY state uses a non-blocking LED4 heartbeat: 100 ms on, 900 ms
off at the current health-service tick rate. Warning and fatal states use
distinct patterns.
See Runtime Security Monitor, EXP066 CLI, and EXP071 Platform Integration.
Validated on real hardware at RDP Level 0:
- secure boot from Slot A;
- SHA-512 and Ed25519 execution before application jump;
- slot selection and application handoff;
- application confirmation;
- A-to-B update to Slot B;
- B-to-A update to Slot A;
- rollback rejection for same and lower versions;
- fail-closed rejection of corrupted manifest, target, signature, and payload;
- UART CRC, sequence, partial-frame, and random-byte negative cases;
- flash readback for positive update targets;
- unchanged Option Bytes during the campaign.
Part 2 additionally recorded hardware evidence for the complete mpu_*
scenario matrix, IWDG trial reset/fallback, wrong-key/signature/manifest
rejection, and the corrected confirmed-version rollback floor. A fully
debugger-free reset campaign remains open.
Host-only validation covers parser boundaries, verifier edge cases, metadata transitions, deterministic build reproducibility, package verification, and tool behavior.
Not claimed by the current release:
- RDP2 readiness;
- production key custody or HSM-backed signing;
- hardware-backed monotonic rollback counters;
- physical recovery input;
- certified fault-injection or side-channel resistance;
- production remote-update authorization beyond signed firmware packages.
See Hardware Validation Status and Validation Summary.
Release preparation consists of:
- clean host tests and C host tests;
- deterministic build comparison;
- private-key scan;
- bootloader and Slot A/Slot B package verification;
- hardware-validation evidence for firmware or update-path changes;
- documentation and changelog review;
- annotated tag and GitHub release only after CI is green.
Do not overwrite existing tags. If the previous tag exists, use the next patch tag.
See Release Process, Release Checklist, and Release Artifacts, Changelog, and Third-Party Notices.
| Symptom | Likely cause | Check |
|---|---|---|
stm32ctl timeout during update begin |
Candidate-slot erase takes several seconds | Use --timeout 15 or higher on real hardware |
Slot decision = NONE |
Missing or invalid metadata | Recreate and flash both metadata copies |
SHA-512 or Ed25519 timing is 0 us |
Boot policy never selected an image | Inspect metadata and slot addresses |
| Signature verification fails offline | Wrong public key or stale package report | Run update_package.py verify with current package hash |
| App does not confirm trial boot | EXP066 did not reach the health gate | Use console metadata, confirmation status, and UART logs |
| UART binary mode prints text | Entry classification failed or a terminal sent text | Reset and send a valid binary HELLO first |
| ST-LINK write/read fails intermittently | Probe, USB, or target-state issue | Reconnect, reset target, and compare readback before debugging firmware |
Is this production ready? No. The secure-boot/update path is a hardware-validated research reference, not a certified product. Production use needs independent security review, key management, recovery policy, option-byte provisioning, and lifecycle design.
Does the repository contain a production signing key? No. Private signing seeds and keys must remain outside Git. The committed public key header is not secret.
Can I enable RDP2? No release document approves RDP2. RDP2 may be irreversible and is blocked until physical recovery and provisioning procedures are independently validated.
Can the host choose the update slot? No. The host sends a signed package. The bootloader determines the inactive candidate slot from confirmed metadata and rejects mismatches.
Why is the signature over the manifest instead of the full payload? The manifest contains the SHA-512 digest of the payload. Ed25519 authenticates the canonical manifest; SHA-512 binds the payload bytes to that signed manifest.
Why are some old experiment files still present?
The repository preserves numbered experiments as research history. Current
release behavior is described by the EXP045, EXP066, docs/, tools/, and
tests/ paths linked above.
- No certified fault-injection, glitch, or side-channel resistance claim.
- No production manufacturing key ceremony, rotation, revocation, or HSM flow.
- No hardware-backed monotonic anti-rollback counter.
- No physical recovery GPIO is selected in the repository.
- No automatic Option Byte, WRP, RDP, or OTP provisioning.
- No production remote-update authorization layer beyond signed packages and rollback policy.
- Physical power-removal timing should be repeated for each board and power setup before irreversible provisioning.
See Limitations.
Short-term review work:
- keep documentation, release notes, and hardware evidence current;
- repeat physical power-removal tests with controlled target power;
- add board-revision-specific LED and timing observations where available;
- harden recovery entry and provisioning procedures before any RDP planning.
Longer-term research:
- hardware-backed rollback or monotonic policy storage;
- write-protection and option-byte lifecycle studies;
- physical fault-injection and brownout campaigns on expendable boards;
- deeper Runtime Security Monitor evidence and boot mailbox design;
- reviewed production key-custody model.
See Research Roadmap.
The repository is published to support defensive research, education, reproducibility, and peer review. Users are responsible for complying with applicable law, device ownership requirements, contractual restrictions, and laboratory safety procedures.
See Contributing. Security reports should follow Security Policy.
Project-authored content is distributed under the BSD 3-Clause License. Third-party components remain under their respective licenses.
Citation metadata is provided in CITATION.cff.
STM32 Security Lab is an independent embedded-security research project created and maintained by Mathias Zimmermann. AI tools were used during development for documentation editing, refactoring suggestions, test generation, and code review; system architecture, implementation decisions, debugging, hardware validation, and final integration were performed manually on real STM32 hardware.
