انتقل إلى المحتوى الرئيسي

STM32F429 — Ada/SPARK Physical Access Controller

The STM32F429 serves as a physical access controller with formal verification guarantees. Written in Ada with SPARK annotations, it provides mathematical proof that security-critical operations (relay control) are correct.

Role in the Architecture

Technical Specifications

FeatureDetail
MCUSTM32F429 (ARM Cortex-M4F, 180MHz)
RAM256KB SRAM + 64KB CCM
Flash2MB
LanguageAda 2022 with SPARK formal verification
CryptoHardware PKA (ECDSA P-256), HASH accelerator (SHA-256)
ConnectivityEthernet (LWIP + MQTT)
Key StorageOTP (One-Time Programmable) area
Loop interval5 seconds

Why Ada/SPARK?

FeatureAda/SPARKCRust
Formal verificationGNATprove (mathematical proof)Frama-C (complex)Not available
Runtime safetyNo buffer overflows (proven)ManualCompile-time (Rust)
Design by ContractNative (Pre/Post/Type_Invariant)Library-basedNot native
Real-timeRavenscar profile (deterministic)No guaranteesNo guarantees
DO-178C / IEC 61508CertifiedCertifiableNot yet
Industry adoptionRail, aerospace, defenseUniversalGrowing

For a controller that opens/closes physical doors, mathematical proof of correctness isn't a luxury — it's a safety requirement. A bug in relay control could lock people inside a burning building.

SPARK-Proven Relay Control

The relay control module has formal contracts that GNATprove mathematically verifies:

-- relay_control.ads (specification)
package Relay_Control with SPARK_Mode is

type Relay_State is (Open, Closed);
type Attestation_Status is (Not_Verified, Verified, Revoked);

-- PRECONDITION: Relay can only be Closed if attestation is Verified
procedure Set_Relay (
State : in Relay_State;
Attest : in Attestation_Status
) with
Pre => (if State = Closed then Attest = Verified),
Post => (if Attest = Revoked then Current_State = Open);

function Current_State return Relay_State;

end Relay_Control;
-- relay_control.adb (implementation)
package body Relay_Control with SPARK_Mode is

Internal_State : Relay_State := Open;

procedure Set_Relay (
State : in Relay_State;
Attest : in Attestation_Status
) is
begin
-- Safety override: ALWAYS open relay if attestation revoked
if Attest = Revoked then
Internal_State := Open;
GPIO.Write_Pin (RELAY_PIN, GPIO.Low);
return;
end if;

-- Normal operation
Internal_State := State;
case State is
when Open => GPIO.Write_Pin (RELAY_PIN, GPIO.Low);
when Closed => GPIO.Write_Pin (RELAY_PIN, GPIO.High);
end case;
end Set_Relay;

function Current_State return Relay_State is (Internal_State);

end Relay_Control;

What GNATprove Guarantees

Running gnatprove on this code mathematically proves:

  1. It's impossible to close the relay without valid attestation — the precondition prevents it at compile time
  2. Revoked attestation always opens the relay — the postcondition guarantees it
  3. No runtime exceptions — no division by zero, no overflow, no array out-of-bounds
  4. No uninitialized variables — all paths initialize before use

Main Loop

-- main.adb
procedure Main is
Attest_Status : Relay_Control.Attestation_Status;
Tamper : Tamper_Detection.Tamper_Event;
begin
-- Initialize hardware
MQTT_Publisher.Connect (Host => "mosquitto", Port => 8883);

loop
-- 1. Verify firmware attestation (every loop = every 5s)
Attest_Status := Vault_Attestation.Verify_Device_Certificate;

-- 2. Control relay based on attestation
if Attest_Status = Relay_Control.Verified then
Relay_Control.Set_Relay (Closed, Attest_Status);
else
Relay_Control.Set_Relay (Open, Attest_Status);
end if;

-- 3. Publish status
MQTT_Publisher.Publish (
Topic => "aurora/sensors/stm32_pac_01/status",
Payload => Build_Status_Payload (Attest_Status),
QoS => At_Least_Once
);

-- 4. Check tamper sensors
Tamper := Tamper_Detection.Check_Tamper;
if Tamper /= Tamper_Detection.None then
MQTT_Publisher.Publish (
Topic => "aurora/sensors/stm32_pac_01/alerts",
Payload => Build_Tamper_Alert (Tamper),
QoS => Exactly_Once
);
end if;

-- 5. Publish telemetry
MQTT_Publisher.Publish (
Topic => "aurora/sensors/stm32_pac_01/telemetry",
Payload => Build_Telemetry_Payload,
QoS => At_Least_Once
);

-- Wait 5 seconds
delay 5.0;
end loop;
end Main;

Vault Attestation

package Vault_Attestation with SPARK_Mode is

type Attestation_Result is (Success, Failure, Timeout);
subtype Hash_String is String (1 .. 64); -- SHA-256 hex
subtype Device_ID is String (1 .. 20);

function Verify_Device_Certificate return Relay_Control.Attestation_Status;
function Get_Firmware_Hash return Hash_String;
function Get_Boot_Count return Natural;

end Vault_Attestation;

The STM32 uses hardware peripherals for cryptographic operations:

OperationHardwarePurpose
ECDSA P-256 Sign/VerifyPKA (Public Key Accelerator)Attestation signatures
SHA-256HASH acceleratorFirmware hashing
Key StorageOTP areaPrivate key (write-once)
CertificateFlash partitionVault-issued x.509

Tamper Detection

package Tamper_Detection is

type Tamper_Event is (None, Case_Open, Wire_Cut, Voltage_Anomaly, Temperature_Anomaly);
type Tamper_Severity is (Normal, Warning, Critical);

function Check_Tamper return Tamper_Event;
function Get_Severity (Event : Tamper_Event) return Tamper_Severity;

end Tamper_Detection;
EventDetection MethodSeverity
Case_OpenMechanical switch (GPIO interrupt)Critical
Wire_CutContinuity loop (GPIO pull-up)Critical
Voltage_AnomalyADC monitoring (±10% threshold)Warning
Temperature_AnomalyInternal temp sensor (>85°C)Warning

MQTT Publisher

package MQTT_Publisher is

type QoS_Level is (At_Most_Once, At_Least_Once, Exactly_Once);

procedure Connect (Host : String; Port : Positive);
procedure Disconnect;
procedure Publish (Topic : String; Payload : String; QoS : QoS_Level);

end MQTT_Publisher;

The STM32 connects with mTLS using Vault-issued x.509 certificates:

  • Client certificate: ECDSA P-256, issued by Vault PKI
  • CA certificate: Vault root CA
  • TLS version: 1.3

Build System

# Build with GNAT (Ada compiler)
gprbuild -P firmware/stm32/stm32_aurora.gpr -XBUILD=release

# Run SPARK formal verification
gnatprove -P firmware/stm32/stm32_aurora.gpr --level=2 --prover=z3

# Flash via ST-Link
st-flash write firmware/stm32/bin/main.bin 0x08000000

SPARK Proof Levels

LevelWhat it provesTime
0Flow analysis (no uninitialized reads)Seconds
1+ No runtime exceptions (overflow, range)Minutes
2+ Contracts satisfied (pre/post conditions)Minutes
3+ All assertions provenHours
4+ Full functional correctnessHours+

AuroraSOC uses level 2 — sufficient to prove the relay safety invariant.