Hardware Abstraction Layer (HAL) Internals
PQC-IIoT uses a trait-based Abstraction Layer to decouple cryptographic logic from the underlying execution environment. This allows the same high-level application code to run on a standard Linux gateway (using software crypto) and an embedded device with a secure element (TPM/HSM).
The SecurityProvider Trait
The core of the HAL is the SecurityProvider trait, defined in src/security/provider.rs. It serves as the single point of truth for all sensitive operations.
#![allow(unused)] fn main() { pub trait SecurityProvider: Send + Sync { // Identity Management fn public_keys(&self) -> &PeerKeys; // Cryptographic Primitives fn sign(&self, message: &[u8]) -> Result<Vec<u8>>; fn decrypt(&self, capsule: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>>; // Lifecycle fn zeroize(&mut self); } }
Design Principles
- Secret Key Isolation: The
SecurityProviderimplementation is the only component that holds private keys. High-level clients (SecureMqttClient) only hold a reference to the provider. - Thread Safety: The
Send + Syncbounds ensure the provider can be shared safely across Tokio tasks or threads. - Error Propagation: All fallible operations return a
Resulttype that maps hardware-specific errors (e.g.,TPM_E_AUTH_FAIL) to the library'sErrorenum.
Reference Implementation: SoftwareSecurityProvider
The default implementation uses pure Rust crates (pqcrypto, aes-gcm) and stores keys in protected memory regions.
Memory Layout
- Key Storage:
Box<dyn SecretKey>allows polymorphic storage of different algorithm keys (though currently concrete types are used for FFI reasons). - Protection: We use the
zeroizecrate. When the provider is dropped, the memory containing private keys is overwritten with zeros using volatile writes, preventing sensitive data from remaining in the heap.
#![allow(unused)] fn main() { // Internals of SoftwareSecurityProvider struct SoftwareSecurityProvider { kyber_sk: zeroize::Zeroizing<Box<[u8]>>, // Auto-zeroize on drop falcon_sk: zeroize::Zeroizing<Box<[u8]>>, // ... } }
Hardware Provider Architecture (TPM 2.0)
When using a Hardware Security Module (HSM) or TPM:
- Handles vs Keys: The provider does not store the raw private key. Instead, it stores a handle (e.g.,
0x81000001for a persistent TPM key). - Off-loading:
sign(msg)sends the hash of the message to the TPM via TCG Software Stack (TSS).- The TPM performs the signing internally using the restricted key.
- The signature is returned.
- No Extraction: The private key never leaves the hardware boundary.
Integration Flow
sequenceDiagram
participant App
participant Provider
participant TPM
App->>Provider: sign(data)
Provider->>Provider: hash(data)
Provider->>TPM: TPM2_Sign(handle, digest)
TPM->>TPM: Verify Authorization
TPM->>TPM: Compute Falcon Sig
TPM-->>Provider: Signature
Provider-->>App: Signature