TPM 2.0 Implementation Plan
Archived Document Notice (2026-02-12): This file is retained for historical context and may not reflect current code, APIs, runtime defaults, CI status, or deployment posture. Use active documentation under
docs/as authoritative.
Status: Planned - Not Yet Implemented
Target: Production-ready TPM key sealing and signing
Overview
This document outlines the complete implementation plan for TPM 2.0 backend support in ICN. The goal is to enable production nodes to seal Ed25519 private keys inside TPM hardware, preventing memory extraction and providing platform binding.
Background: Why TPM for Ed25519?
TPM 2.0 does not natively support Ed25519. However, we can use TPM for:
- Sealed storage - Key sealed to TPM, bound to platform PCRs
- Software signing - After unsealing, sign in software with extra protections
- Attestation - Prove the key is TPM-sealed and platform-bound
This provides significant security benefits over pure software storage:
- Keys cannot be extracted without TPM hardware access
- Platform binding ensures keys only work on the original machine
- PCR measurement provides boot-time integrity verification
Architecture Overview
┌─────────────────────────────────────────────────────┐
│ Application Layer │
│ ┌─────────────────────────────────────────────┐ │
│ │ IdentityBundle (uses DidKey::Hardware) │ │
│ └──────────────┬──────────────────────────────┘ │
│ │ │
│ ┌──────────────▼───────────────────────────────┐ │
│ │ TpmDidSigner (implements DidSigner trait) │ │
│ │ - Unseals key from TPM on demand │ │
│ │ - Signs in software with unsealed key │ │
│ │ - Re-seals after signing (optional) │ │
│ └──────────────┬───────────────────────────────┘ │
│ │ │
├─────────────────┼───────────────────────────────── │
│ TPM Layer │ │
│ ┌──────────────▼───────────────────────────────┐ │
│ │ TpmBackend (implements KeyStoreBackend) │ │
│ │ - init(): Seal new key to TPM │ │
│ │ - unlock(): Unseal key from TPM │ │
│ │ - signing_backend(): Return TpmDidSigner │ │
│ └──────────────┬───────────────────────────────┘ │
│ │ │
│ ┌──────────────▼───────────────────────────────┐ │
│ │ tss-esapi (TPM Software Stack) │ │
│ │ - SealedData API │ │
│ │ - PCR binding │ │
│ │ - Attestation │ │
│ └──────────────┬───────────────────────────────┘ │
└─────────────────┼───────────────────────────────── ┘
│
┌────────▼────────┐
│ TPM 2.0 Hardware│
└─────────────────┘
Implementation Phases
Phase 1: Core TPM Sealing/Unsealing ✅ (Prerequisite: IdentityBundle refactor)
Blockers:
- IdentityBundle must use
DidKeyenum instead of embeddingKeyPair - This is being implemented in Phase A (IdentityBundle refactor)
Tasks:
Implement TPM context initialization
- Connect to TPM resource manager (
/dev/tpmrm0) - Handle TCTI (TPM Command Transmission Interface) setup
- Error handling for missing TPM hardware
- Connect to TPM resource manager (
Implement key sealing
fn seal_key( context: &mut Context, secret_bytes: &[u8; 32], pcr_selection: &[u32], ) -> Result<SealedData>- Generate sealing key in TPM
- Seal Ed25519 private key with TPM
- Bind to platform PCRs (0, 7 for boot integrity)
- Store sealed blob to disk
Implement key unsealing
fn unseal_key( context: &mut Context, sealed_data: &SealedData, ) -> Result<Zeroizing<[u8; 32]>>- Load sealed data from disk
- Verify PCR values match sealing time
- Unseal key using TPM
- Return private key (zeroized)
Add PCR management
- Read current PCR values
- Verify against expected measurements
- Handle PCR mismatch (unsealing fails)
Deliverables:
TpmContextstruct wrapping tss-esapi context- Seal/unseal functions with PCR binding
- Unit tests with TPM simulator (swtpm)
- Error handling for missing TPM hardware
Testing:
- Use swtpm (software TPM simulator) for CI
- Manual testing with real TPM 2.0 hardware
- Test PCR binding (unseal fails after PCR change)
- Test persistent storage (seal, reboot, unseal)
Phase 2: TpmDidSigner Implementation
Tasks:
Implement
TpmDidSignerstructpub struct TpmDidSigner { did: Did, verifying_key: VerifyingKey, tpm_context: Arc<Mutex<TpmContext>>, sealed_data_path: PathBuf, unsealed_key: Option<Zeroizing<[u8; 32]>>, // Cached unsealed key }Implement
DidSignertraitimpl DidSigner for TpmDidSigner { fn did(&self) -> &Did; fn verifying_key(&self) -> &VerifyingKey; fn sign(&self, message: &[u8]) -> Result<Signature>; fn is_hardware_backed(&self) -> bool { true } fn backend_type(&self) -> &str { "tpm" } }Implement signing flow
- Check if key is unsealed (cached)
- If not, unseal from TPM
- Sign with unsealed key in software
- Optionally re-seal (or keep cached for session)
- Zeroize unsealed key on drop
Add session management
- Keep key unsealed for duration of session
- Lock/unlock API for explicit control
- Auto-lock on timeout (configurable)
Deliverables:
TpmDidSignerimplementingDidSigner- Session management for unsealed keys
- Integration tests with sign operations
- Performance benchmarks (seal/unseal overhead)
Testing:
- Sign messages with TPM-backed key
- Verify signatures with public key
- Test session caching (unseal once, sign many)
- Test auto-lock timeout
Phase 3: TpmBackend Integration
Tasks:
Refactor
TpmBackendto use real sealingimpl KeyStoreBackend for TpmBackend { fn init(&mut self, passphrase: &[u8]) -> Result<()> { // Generate new Ed25519 keypair // Seal private key to TPM // Store sealed data to disk // Return DidKey::Hardware with public key } fn unlock(&mut self, passphrase: &[u8]) -> Result<()> { // Load sealed data from disk // Unseal key from TPM (PCR verification) // Cache unsealed key in TpmDidSigner } fn signing_backend(&self) -> Option<&dyn DidSigner> { // Return TpmDidSigner instance } }Add persistent storage
- Store sealed data blob to
~/.icn/tpm-sealed.dat - Store TPM handle/context info
- Handle multiple identities (one sealed blob per identity)
- Store sealed data blob to
Add configuration
[identity.tpm] enabled = true pcrs = [0, 7] # Boot integrity measurements device = "/dev/tpmrm0" auto_lock_timeout_secs = 300Error handling
- TPM not available → fallback to software or error
- PCR mismatch → require manual intervention
- Sealed data corruption → recovery via backup
Deliverables:
- Complete
TpmBackendimplementation - Configuration file support
- Persistent storage for sealed data
- Error handling and recovery procedures
Testing:
- End-to-end: init → unlock → sign → verify
- Persistence: seal → restart → unseal
- PCR binding: seal → modify PCR → unseal fails
- Multiple identities (if supported)
Phase 4: Platform Binding & Attestation
Tasks:
Implement PCR measurement
- Read TPM PCRs during sealing
- Store expected PCR values with sealed data
- Verify PCRs match during unsealing
Add attestation support
pub struct TpmAttestation { pub pcr_values: HashMap<u32, Vec<u8>>, pub quote: Vec<u8>, // TPM quote signature pub quote_sig: Vec<u8>, pub attestation_key: Vec<u8>, } fn attest_identity(tpm: &TpmContext, did: &Did) -> Result<TpmAttestation>- Generate TPM quote over PCRs
- Sign quote with attestation key
- Return attestation proof
Add verification API
fn verify_attestation( attestation: &TpmAttestation, expected_pcrs: &HashMap<u32, Vec<u8>>, ) -> Result<()>Document attestation flow
- When to request attestation
- How to verify attestation
- Trust model for attestation keys
Deliverables:
- TPM attestation generation
- Attestation verification
- Documentation for attestation usage
- Integration with network handshake (optional)
Testing:
- Generate attestation for TPM-sealed identity
- Verify attestation with expected PCRs
- Test attestation with modified PCRs (should fail)
Phase 5: Production Hardening
Tasks:
Add comprehensive error handling
- TPM hardware failures
- PCR measurement changes
- Sealed data corruption
- Resource exhaustion
Add monitoring & observability
- Metrics: seal/unseal operations, latency
- Logging: TPM operations, PCR values
- Alerts: PCR mismatches, unsealing failures
Add recovery procedures
- Backup sealed data (encrypted)
- Emergency unsealing procedure
- PCR update workflow (BIOS updates)
- Identity migration (old TPM → new TPM)
Security audit
- Code review for key material handling
- Verify zeroization of unsealed keys
- Test for timing attacks (constant-time operations)
- Fuzz testing for malformed sealed data
Performance optimization
- Minimize unseal operations (session caching)
- Batch signing operations when possible
- Profile TPM command latency
Deliverables:
- Production-ready error handling
- Comprehensive documentation
- Monitoring integration
- Recovery procedures
- Security audit report
Testing:
- Fault injection (TPM failures, PCR changes)
- Load testing (many sign operations)
- Recovery testing (backup restore)
- Security testing (fuzzing, timing attacks)
Phase 6: Documentation & Examples
Tasks:
Write setup guide
- Hardware requirements (TPM 2.0)
- Linux kernel configuration
- TPM software stack installation
- swtpm setup for testing
Write usage guide
- Initializing TPM-backed identity
- Signing operations
- PCR management
- Backup and recovery
Write migration guide
- Age-encrypted → TPM-sealed migration
- TPM → TPM migration (hardware replacement)
- Emergency recovery procedures
Add examples
- Simple TPM identity creation
- Signing and verification
- Attestation generation
- Configuration examples
Deliverables:
docs/tpm-setup.md- Setup guidedocs/tpm-usage.md- Usage guidedocs/tpm-migration.md- Migration guideexamples/tpm-identity.rs- Example code- Configuration templates
Dependencies
Rust Crates
tss-esapi(^7.4) - TPM2 Software Stack bindingszeroize(^1.7) - Secure memory wiping
System Requirements
- TPM 2.0 hardware (or swtpm for testing)
- Linux kernel 4.12+ with TPM 2.0 support
/dev/tpmrm0device (TPM resource manager)
Optional for Development
swtpm- Software TPM simulator for CItpm2-tools- Command-line TPM utilitiesibmswtpm2- IBM TPM 2.0 simulator
Security Considerations
Threat Model
Attacks TPM Sealing Mitigates:
- ✅ Memory extraction (keys sealed in TPM)
- ✅ Offline key extraction (PCR binding)
- ✅ Key material in swap (never in memory long)
- ✅ Cold boot attacks (keys not in DRAM)
Attacks TPM Sealing Does NOT Mitigate:
- ❌ Runtime attacks (after unsealing)
- ❌ TPM physical access (with advanced tools)
- ❌ Supply chain attacks (compromised TPM)
- ❌ Side-channel attacks on unsealing
Best Practices
PCR Selection
- PCR 0: BIOS/UEFI firmware
- PCR 7: Secure Boot policy
- Avoid PCRs that change frequently
Key Material Handling
- Zeroize unsealed keys immediately after use
- Minimize time key is unsealed
- Use session caching sparingly
Backup Strategy
- Encrypt sealed data backup
- Store backup off-system
- Test recovery procedures
PCR Management
- Document expected PCR values
- Test unsealing before BIOS updates
- Have recovery procedure for PCR changes
Testing Strategy
Unit Tests
- TPM context initialization
- Key sealing/unsealing
- PCR binding
- Error handling
Integration Tests
- End-to-end signing flow
- Persistence across restarts
- Multiple identity management
- Session caching
System Tests
- Real TPM hardware testing
- Performance benchmarks
- Fault injection
- Security testing
CI/CD
- Use swtpm in CI for automated testing
- Manual hardware testing on release
- Security scanning (fuzzing, static analysis)
Success Criteria
Functionality
- ✅ Keys sealed to TPM 2.0 hardware
- ✅ PCR binding for platform integrity
- ✅ Signing operations work correctly
- ✅ Attestation generation
Performance
- ✅ Seal operation < 1 second
- ✅ Unseal operation < 1 second
- ✅ Sign operation < 100ms (after unseal)
- ✅ Session caching reduces unseal overhead
Security
- ✅ Keys never in cleartext on disk
- ✅ PCR mismatch prevents unsealing
- ✅ Zeroization of unsealed keys
- ✅ Constant-time operations
Reliability
- ✅ Graceful handling of TPM failures
- ✅ Recovery from sealed data corruption
- ✅ Migration support for hardware changes
- ✅ Comprehensive error messages
Usability
- ✅ Clear documentation
- ✅ Simple configuration
- ✅ Good error messages
- ✅ Example code
Timeline Estimate
- Phase 1: Core Sealing/Unsealing - 2 weeks
- Phase 2: TpmDidSigner - 1 week
- Phase 3: Backend Integration - 1 week
- Phase 4: Attestation - 1 week
- Phase 5: Production Hardening - 2 weeks
- Phase 6: Documentation - 1 week
Total: ~8 weeks (with one developer)
Current Status
Phase 0: Scaffolding ✅ Complete
- DidSigner trait defined
- DidKey enum defined
- TpmBackend placeholder exists
- Feature flag
tpm-experimentalin place
Next: Phase A (Prerequisite) 🔄 In Progress
- Refactor IdentityBundle to use DidKey
- Remove embedded KeyPair from IdentityBundle
- Enable hardware backend support
Next: Phase 1 ⏳ Not Started
- Implement real TPM sealing/unsealing
- Depends on Phase A completion
Open Questions
PCR Selection: Which PCRs should we bind to by default?
- Recommendation: PCR 0 (firmware) + PCR 7 (secure boot)
Session Management: How long should we cache unsealed keys?
- Recommendation: 5 minutes default, configurable
Backup Strategy: How should users backup TPM-sealed keys?
- Recommendation: Encrypted backup with recovery passphrase
Migration: How to migrate between TPM hardware?
- Recommendation: Export/import with intermediate encryption
Attestation: Should attestation be required for network handshake?
- Recommendation: Optional, policy-driven
References
- TPM 2.0 Specification
- tss-esapi Documentation
- TCG TPM2 Software Stack
- Intel TPM2 Tools
- swtpm Software TPM
Pathway from Current Scaffolding
The current TPM scaffolding (marked tpm-experimental) provides:
- Backend trait abstraction ✅
- Basic structure (TpmBackend, TpmSigningBackend) ✅
- Placeholder implementations (all bail!) ✅
- Feature flag isolation ✅
To reach Phase 1, we need:
- IdentityBundle refactor (Phase A) - In Progress
- Replace placeholder sealing with real tss-esapi calls
- Implement PCR binding logic
- Add persistent storage for sealed data
- Handle TPM initialization errors gracefully
This document serves as the roadmap from scaffolding to production.