Skip to main content

Fortis โ€” Bare-Metal RISC-V Chain of Trust

Bare-metal RISC-V measured boot โ€” SHA-256 + ML-KEM-768 chain of trust. Real crypto on real hardware (QEMU). Proof of concept for Caliptra-style root of trust. 51 KiB binary, zero heap, no_std.

What It Doesโ€‹

Fortis is a bare-metal RISC-V firmware that implements a measured boot chain โ€” the same technology used by cloud providers to verify that server firmware hasn't been tampered with. Each boot stage is hashed (SHA-256) and stored in PCR-like measurement registers. A post-quantum key exchange (ML-KEM-768) verifies the firmware's authenticity. The entire chain runs without an OS, without heap allocation, and without standard library.

Key Featuresโ€‹

๐Ÿ” SHA-256 Measurementโ€‹

Real FIPS 180-4 SHA-256 via the sha2 crate โ€” not a placeholder. Each boot stage is hashed and chained: PCR[0] โ†’ PCR[1] โ†’ PCR[2]. Changing any stage cascades through all measurements.

๐Ÿ›ก๏ธ ML-KEM-768โ€‹

Post-quantum key encapsulation (FIPS 203) running on bare metal. The firmware decapsulates a ciphertext to derive a shared secret โ€” proving identity without quantum-vulnerable cryptography.

๐Ÿ“Š PCR Registersโ€‹

3 Platform Configuration Register-like measurement registers. Chained via SHA-256(old_pcr || new_measurement). Exactly how TPM/Caliptra works in production hardware.

๐Ÿ–ฅ๏ธ MMIO UARTโ€‹

Direct hardware register access to ns16550a UART at 0x10000000. No OS, no drivers, no abstraction โ€” raw read_volatile/write_volatile to QEMU's virt machine.

โšก 51 KiB Binaryโ€‹

Entire firmware fits in 51 KiB of loadable content. No heap, no alloc, no standard library. Every byte is accounted for. Runs on QEMU virt with OpenSBI.

๐Ÿงช CI-Tested on QEMUโ€‹

GitHub Actions builds the firmware and runs it in qemu-system-riscv64. Automatically verifies "CHAIN OF TRUST: PASSED" in the output. No mocking โ€” real RISC-V execution.

Architectureโ€‹

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ OpenSBI (M-mode) โ”‚
โ”‚ Firmware base: 0x80200000 โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
โ”‚ jump to S-mode
โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Fortis Entry Point โ”‚
โ”‚ 1. Mask interrupts โ”‚
โ”‚ 2. Set up 64 KiB stack โ”‚
โ”‚ 3. Clear BSS section โ”‚
โ”‚ 4. Jump to rust_main โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
โ”‚
โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Stage 1: Boot Stub โ”‚
โ”‚ SHA-256(stub_data) โ†’ PCR[0] โ”‚
โ”‚ Verify: PCR[0] matches expected โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
โ”‚
โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Stage 2: Firmware โ”‚
โ”‚ SHA-256(PCR[0] || fw_data) โ†’ PCR[1] โ”‚
โ”‚ Verify: PCR[1] matches expected โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
โ”‚
โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Stage 3: Post-Quantum Verify โ”‚
โ”‚ ML-KEM-768 decapsulate(ct) โ†’ ss โ”‚
โ”‚ SHA-256(PCR[1] || ss) โ†’ PCR[2] โ”‚
โ”‚ Verify: shared secret matches โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
โ”‚
โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Stage 4: Attestation Report โ”‚
โ”‚ Print all PCR values + verification โ”‚
โ”‚ CHAIN OF TRUST: PASSED (3/3) โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Tech Stackโ€‹

ComponentTechnology
LanguageRust (#![no_std], no heap)
Targetriscv64gc-unknown-none-elf
SHA-256sha2 0.11 (FIPS 180-4)
ML-KEM-768ml-kem 0.3 (FIPS 203)
UART driverns16550a MMIO (custom)
PlatformQEMU virt (RISC-V 64)
FirmwareOpenSBI (M-mode)
Binary size~51 KiB (.text + .rodata)
CIGitHub Actions + QEMU

Links: GitHub