feat(h563-boot): secure boot (RSA-2048 + TRNG seed + anti-rollback), vendor LL drivers, robustness#93
Merged
Merged
Conversation
Gate the OTA app image by an ECDSA-P256 signature in addition to the existing CRC-32, so only images signed with the matching private key boot. The CRC is kept as a fast integrity pre-check (defense in depth). Scheme: secp256r1, SHA-256 over the payload bytes, raw 64-byte r||s appended after the payload (offset derived from image_size, no new header field). The device verifies with mbedtls_ecdsa_verify on raw r,s MPIs against a public key baked into the bootloader -- no ASN.1/DER on target. - mkimage.py: sign the payload (Python cryptography, DER->raw r||s) and append the 64-byte signature; add --key and --tamper-signature flags. - sec_ecdsa.c/.h: ecdsa_verify_p256() helper over mbedTLS. - app_jump.c app_is_valid(): verify the signature after the CRC -- the single chokepoint gating boot, 0xFF01, 0xFF02 and 0xFF03. - 0xFF01 CheckProgramming: require header+payload+signature transferred. - 0xFF02 ActivateSoftware: re-validate the inactive image (CRC+signature) right before the bank swap, closing the activate-without-revalidation hole. - bootloader build: add mbedTLS ecp/ecp_curves/ecdsa/bignum/bignum_core/ sha256/asn1parse/asn1write and the matching config (ECP/ECDSA/SHA256/ secp256r1). Both ELFs build clean under -Werror, --specs=nano.specs. - Demo apps: re-sign A/B/Bbad; add a Bsigbad variant (CRC-good but signature-bad) plus an all-four target for negative secure-boot tests. - sim_tester: regenerate app_b_image_blob.h from the signed App-B image. - host ecdsa-test: verify the signed image and prove payload/signature tamper is rejected, independent of the simulator. DEMO keys: app/signing_key_dev.pem (private) and image_pubkey.h (public) are committed for the example only; a real product keeps the private key in an HSM/offline and bakes only the public half.
Replace the ECDSA-P256 secure-boot scheme with RSA-2048 PKCS#1 v1.5 over SHA-256 of the app payload. RSA verify (m^65537 mod n) is ~100x cheaper than an ECDSA-P256 verify and completes well under the simulators 50M-instruction cap, which the ECDSA path exceeded. - sec_rsa.c/.h: rsa_verify_sha256() via mbedtls_rsa_pkcs1_verify against the baked public key (modulus n + e=65537); replaces sec_ecdsa.c/.h. - Signature is 256 raw big-endian bytes appended after the payload; CRC-32 is kept as a fast integrity pre-check (defense in depth). OTA_IMAGE_SIG_SIZE=256. - Enforcement unchanged: app_is_valid() gates boot, 0xFF01 CheckProgramming, 0xFF02 ActivateSoftware re-validation, and 0xFF03 PerformRollback. - mbedTLS module set switched from ECP/ECDSA to RSA: rsa, rsa_alt_helpers, bignum, bignum_core, sha256, md, oid (+ asn1parse/asn1write pulled in transitively by rsa.c key-parse/DigestInfo helpers); dropped ecp, ecp_curves, ecdsa. Config enables RSA_C, PKCS1_V15, OID_C, MD_C, SHA256_C. - mkimage.py signs with the RSA-2048 dev key (PKCS#1 v1.5 + SHA-256). - image_pubkey.h baked public key + signing_key_dev.pem regenerated as RSA-2048 (DEMO keys; a real product keeps the private key offline/HSM). - sim_tester/app_b_image_blob.h regenerated as the RSA-signed App-B image. - Host rsa-test replaces ecdsa-test (verifies signed image; rejects payload tamper and a CRC-good bad-signature image). Both firmware ELFs build clean under -Werror --specs=nano.specs; all host suites (rsa, cmac, image, bootstate) pass.
…ounter Sim tester no longer bakes the SecurityAccess key. It reads the seed from the server's 0x67 01 response and computes AES-128-CMAC(secret, seed) live via the same aes_cmac one-shot the bootloader verifies with. sim_tester_init now stores the shared secret instead of discarding it. bl_transfer_data enforces the ISO-14229 14.3 block-sequence-counter: expected starts at 0x01 after RequestDownload, increments per accepted block and wraps 0xFF -> 0x00. Matching counter is programmed, the previous counter is ACKed without re-writing (retransmission), any other value returns NRC 0x73 wrongBlockSequenceCounter. flash_tool host BSC wrap corrected to 0xFF -> 0x00 (was 0x01). sec_cmac_test gains a demo-credential vector asserting the live key equals the known-good AES-CMAC(secret, seed).
Draw the 0x27 SecurityAccess seed from the H563 hardware TRNG (RNG block @0x40C80800: enable RCC AHB2 RNGEN + RNG_CR.RNGEN, then poll RNG_SR.DRDY and read RNG_DR for each 32-bit word) instead of a fixed DEMO_SEED, making the AES-CMAC challenge a fresh per-attempt nonce that cannot be replayed. The DRDY poll is bounded so a stuck/unclocked RNG returns conditionsNotCorrect rather than hanging the bootloader. DEMO_SEED removed; the sim tester already reads the seed live from the 0x67 01 response. Add configurable monotonic anti-rollback: OTA_ANTIROLLBACK_ENFORCE (default 1, override -DOTA_ANTIROLLBACK_ENFORCE=0). The floor is the currently-active app's header version; activating an image with a lower version is rejected (NRC 0x22, milestone "BL: rollback blocked") at 0xFF01 CheckProgramming and re-checked at 0xFF02 ActivateSoftware. Recovery (no valid active image) skips enforcement so it is never bricked. Comparison is a pure host-testable function ota_version_allows(); adds ota_version_test.c (upgrade/equal allowed, downgrade rejected when enforced, downgrade allowed when not) wired as the otaversion-test make target.
Extract the pure OTA image validation core into image_validate.c/.h (magic, size range, region-fit, CRC-32, RSA-2048 signature, initial-SP-in-RAM), free of any MCU asm or flash I/O. app_is_valid() in app_jump.c is now a thin wrapper that calls image_validate() over the bank's memory-mapped app region; app_jump.c keeps only the SCB/MSP/asm jump. app_image_test.c drops its forked image_buf_is_valid() and exercises the SAME image_validate() against real signed image buffers (app_b_image.bin, app_bsigbad_image.bin), linking the mbedTLS RSA/SHA modules so the signature path runs on the host too. Minor hardening: - host/Makefile: add -Werror; document that -lmbedcrypto comes from the libmbedtls-dev package; reconcile README wording. - bootloader/Makefile: replace the machine-specific UDSLIB_DIR scratch default with $(abspath $(CURDIR)/../../..) so a fresh checkout builds with no override; check-udslib still validates the resolved path. - main.c: document the deliberate no-security read of DID 0xF1A0 (active-bank indicator) as a diagnostic/orchestration aid that leaks no secret.
…coded MMIO Replace hand-typed REG32 register addresses across the H563 UDS OTA bootloader with ST CMSIS device-header instances (FLASH, FDCAN1, USART3, RNG, SysTick) and STM32H5 LL helpers for RNG. Hand-typed addresses had caused real bugs (wrong RNG base, wrong flash key offset); all bases and bit fields now come from stm32h563xx.h and the LL headers. - flash_h5.c/.h: FLASH_TypeDef + FLASH_CR_/SR_/CCR_/OPTCR_/OPTSR_ macros; device-header include guarded on __arm__ so host bootstate-test (stubbed flash I/O) stays register-header-free. - fdcan.c: FDCAN1/USART3 instances + bit macros; message RAM via SRAMCAN_BASE. - main.c: RNG via LL (clock enable, enable, DRDY poll, read); SysTick via CMSIS SysTick_Config. Same 1 ms tick, HSI reset clock unchanged. - boot_confirm.c (app): FLASH_TypeDef + bit macros; rebuilt images identical. - Makefiles: locate CMSIS/Cube under the zephyr workspace via CUBE_DIR/ CMSIS_DIR (same pattern as MBEDTLS_DIR); -DSTM32H563xx, -DUSE_FULL_LL_DRIVER. Behavior identical: flash sequences, UART milestones, and reset clock unchanged. Both ELFs build clean under -Werror; all host tests pass; the cross-repo sim smoke reaches APP-B v2 (14/14 assertions).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Modernizes and hardens the STM32H563 UDS OTA bootloader example (
examples/h563_uds_bootloader/), addressing the authenticity, replay, and robustness gaps from an adversarial review and moving the whole bootloader onto ST vendor drivers. Each change is verified against a faithful simulator and re-checked on a real NUCLEO-H563ZI over SWD.Secure boot (authenticity)
app_is_valid(boot),0xFF01 CheckProgramming, and re-checked in0xFF02 ActivateSoftware(closes the activate-without-revalidation hole).mkimage.pysigns; demo keys are clearly marked DEMO (private key offline/HSM in production). RSA-2048 verify (~hundreds of k instructions) was chosen over ECDSA-P256 because EC verify does not fit the simulator's instruction budget and is far heavier on the MCU.OTA_ANTIROLLBACK_ENFORCEis on (default on; overridable). The version-decision is a pure, host-tested function.Test integrity
Vendor drivers (no hand-coded MMIO)
stm32h563xx.h(FLASH->,FDCAN1->,RNG,SysTick) and the STM32H5 LL drivers — no hand-typed addresses remain. This eliminates the class of bug that hand-coded MMIO caused (a wrong RNG base, a wrong flash key-register offset). Vendor sources are referenced via Makefile variables under the same workspace already used for mbedTLS; the firmware keeps the HSI reset clock (no PLL bring-up).Robustness
-Werror; sane default paths.Verified: both firmware ELFs build clean under
-Werror; host suites (CMAC, RSA, image via the real validator, boot-state incl. torn-write, version) pass; the end-to-end simulation smoke reaches APP-B v2 (14/14) with the simulator's H5 flash program-error and read-while-write fidelity gates enabled; and on real silicon the bootloader boots, RSA-verifies a signed App-A, and jumps with no fault.Relates to #64.