-
Notifications
You must be signed in to change notification settings - Fork 1
Adopt Post-Quantum Cryptography with Hybrid Approach #22
Description
Overview
OpenTDFKit should adopt post-quantum (PQ) cryptography to protect against future quantum computing threats. This issue proposes an incremental migration strategy using a hybrid classical + PQ approach for the Swift implementation of OpenTDF.
Current Cryptographic Architecture
OpenTDFKit currently uses (via CryptoKit and CryptoSwift):
- Symmetric encryption: AES-256-GCM (payload encryption) ✅ Quantum-resistant
- Asymmetric crypto: NIST elliptic curves for ECDH and ECDSA
⚠️ Vulnerable to quantum attacks- P-256 (secp256r1) - Most common, default curve
- P-384 (secp384r1) - Higher security level
- P-521 (secp521r1) - Highest security level
- ECDH key agreement for deriving symmetric keys
- ECDSA signatures for policy binding
- Key derivation: HKDF-SHA256 ✅ Quantum-resistant
- Authentication: GMAC (via AES-GCM AAD) ✅ Quantum-resistant
Key Components Using ECC
-
NanoTDF Header (
NanoTDF.swift):- Ephemeral public key (P-256/P-384/P-521)
- Policy binding (GMAC or ECDSA signature)
-
Key Agreement (
CryptoHelper.swift):customECDH()methods for P-256, P-384, P-521- Derives shared secret via ECDH
- Uses HKDF to derive AES symmetric key
-
KAS Rewrap (
KASRewrapClient.swift):- Client ephemeral key generation
- Key unwrapping via ECDH with KAS session public key
-
Signatures (
CryptoHelper.swift):- ECDSA signatures for NanoTDF integrity
- Policy binding verification
Quantum Threat Assessment
Vulnerable to quantum attacks (Shor's algorithm):
- ❌ P-256/P-384/P-521 ECDH - Used for key agreement
- ❌ ECDSA - Used for signatures and policy binding
Already quantum-resistant:
- ✅ AES-256-GCM - 256-bit symmetric keys provide adequate post-quantum security
- ✅ GMAC - Hash-based authentication remains secure
- ✅ HKDF-SHA256 - Key derivation is hash-based
Why OpenTDFKit Needs PQ Crypto
- Long-term data protection: NanoTDF files may be stored long-term (harvest-now-decrypt-later threat)
- Mobile security: iOS/macOS devices are high-value targets for quantum decryption
- Zero Trust alignment: PQ crypto strengthens data-centric security
- Future compliance: Regulatory requirements likely to mandate PQ
- Ecosystem leadership: First Swift TDF implementation with PQ support
Proposed Timeline
Near-term (6-12 months): Research & Planning
- Monitor Swift crypto ecosystem for PQ support
- Evaluate available Swift packages:
- swift-crypto - Apple's CryptoKit additions
- PQClean-swift - Swift bindings
- Native C/C++ PQ libraries with Swift bridging
- Study NanoTDF spec extensions for PQ
- Propose PQ extension to OpenTDF standard (coordinate with opentdf-rs)
Medium-term (1-2 years): Hybrid Implementation
- Implement hybrid ML-KEM + ECDH for key encapsulation
- Implement hybrid ML-DSA + ECDSA for signatures
- Maintain backward compatibility with classical-only NanoTDF
Long-term (2+ years): PQ-Only Mode
- Transition to pure post-quantum when ecosystem matures
- Deprecate classical-only mode
Implementation Strategy
Phase 1: Hybrid Key Encapsulation
Replace ECDH with ML-KEM-768 + ECDH(P-256)
public enum KasKeyCurve: UInt8, Sendable, CaseIterable {
case secp256r1 = 0x00
case secp384r1 = 0x01
case secp521r1 = 0x02
// Add new hybrid curves
case mlkem768_secp256r1 = 0x10 // Hybrid: ML-KEM-768 + P-256
case mlkem1024_secp384r1 = 0x11 // Hybrid: ML-KEM-1024 + P-384
}
// Key agreement combines both mechanisms
func deriveSharedSecret(
pqKeyPair: MlKemKeyPair,
classicalKeyPair: EphemeralKeyPair,
recipientPqPublicKey: MlKemPublicKey,
recipientClassicalPublicKey: Data
) throws -> Data {
// 1. ML-KEM encapsulation
let pqSharedSecret = try mlKemEncapsulate(
publicKey: recipientPqPublicKey,
using: pqKeyPair
)
// 2. ECDH agreement
let ecdhSharedSecret = try customECDH(
privateKey: classicalKeyPair.privateKey,
publicKey: recipientClassicalPublicKey
)
// 3. Combine: concat(pq_secret || ecdh_secret) -> HKDF
return try deriveSymmetricKey(
sharedSecrets: [pqSharedSecret, ecdhSharedSecret]
)
}Phase 2: Hybrid Signatures
Replace ECDSA with ML-DSA-65 + ECDSA-P256 for policy binding
public struct PolicyBinding {
let classicalSignature: Data // ECDSA
let pqSignature: Data? // ML-DSA (optional for hybrid)
let algorithm: String // "ECDSA-P256" or "HYBRID-MLDSA65-P256"
}
func createHybridSignature(
data: Data,
classicalKey: P256.Signing.PrivateKey,
pqKey: MlDsaPrivateKey
) throws -> PolicyBinding {
let ecdsaSig = try classicalKey.signature(for: data)
let mldsaSig = try pqKey.signature(for: data)
return PolicyBinding(
classicalSignature: ecdsaSig.rawRepresentation,
pqSignature: mldsaSig.rawRepresentation,
algorithm: "HYBRID-MLDSA65-P256"
)
}Phase 3: NanoTDF Format Extension
Extend NanoTDF header to support hybrid keys:
public struct Header {
// Existing fields
public var kas: KasMetadata
public var ephemeralKey: Data // Now can be classical OR hybrid
// New fields for hybrid mode
public var pqEphemeralKey: Data? // ML-KEM public key
public var pqPolicyBinding: Data? // ML-DSA signature
// Version negotiation
public static let versionV12: UInt8 = 0x4C // "L1L" - classical
public static let version: UInt8 = 0x4D // "L1M" - classical
public static let versionV14Hybrid: UInt8 = 0x4E // "L1N" - hybrid PQ
}Swift Ecosystem Challenges
Challenge 1: Limited PQ Libraries
- Issue: Swift lacks mature PQ crypto libraries compared to Rust
- Mitigation:
- Use C/C++ PQ libraries with Swift bridging (e.g., liboqs via C interop)
- Contribute to Swift crypto ecosystem
- Consider wrapping Rust libcrux via Swift/Rust FFI
Challenge 2: CryptoKit API Constraints
- Issue: Apple's CryptoKit doesn't support PQ algorithms yet
- Mitigation:
- Use CryptoSwift or custom implementations for PQ
- Monitor Apple's roadmap for CryptoKit PQ support
- Abstract crypto operations behind protocol for easy swapping
Challenge 3: Performance on Mobile
- Issue: PQ operations are more expensive than ECC
- ML-KEM-768: ~1200 byte public key vs ~33 byte P-256 key
- Slower key generation and encapsulation
- Mitigation:
- KAS operations are already network-bound
- Implement async/await efficiently
- Cache PQ keys where appropriate
- Optimize hot paths with profiling
Recommended Approach
Option A: C Library Bridging (Fastest Path)
// Use liboqs via C interop
import liboqs
public class PQCrypto {
static func mlKemKeypair() throws -> (publicKey: Data, privateKey: Data) {
// Call liboqs C functions via Swift C interop
}
}Pros: Proven library, well-tested
Cons: C bridging complexity, potential safety issues
Option B: Pure Swift Implementation (Long-term)
// Implement ML-KEM in pure Swift
public struct MlKem768 {
// Pure Swift implementation
}Pros: Type-safe, no FFI overhead
Cons: Requires extensive crypto expertise, slower initial development
Option C: Rust FFI via swift-bridge (Hybrid)
// Use libcrux-ml-kem from Rust via FFI
@_cdecl("ml_kem_keypair")
public func mlKemKeypair() -> KeyPair { ... }Pros: Leverage proven libcrux, good performance
Cons: Complex build setup, two language toolchains
Recommendation: Start with Option A (liboqs) for prototyping, migrate to Option B or C for production.
Action Items
- Research Swift PQ crypto libraries (liboqs, PQClean, swift-crypto roadmap)
- Create spike: Prototype ML-KEM-768 key agreement in Swift
- Benchmark PQ operations on iOS/macOS (target devices)
- Design NanoTDF v1.4 hybrid format specification
- Propose PQ extension to OpenTDF standard (coordinate with opentdf-rs team)
- Implement C bridging for liboqs ML-KEM and ML-DSA
- Add hybrid key agreement to CryptoHelper
- Add hybrid signatures to signature generation
- Update NanoTDF header parser for hybrid format
- Implement backward compatibility layer
- Add integration tests for hybrid mode
- Benchmark performance on target platforms
- Documentation and migration guide
- Gradual rollout: optional PQ → default hybrid → PQ-only
Performance Targets
Based on current OpenTDFKit benchmarks:
| Operation | Current (P-256) | Target (Hybrid) | Notes |
|---|---|---|---|
| Key generation | ~1.1ms | <5ms | Acceptable for async operations |
| Key agreement | ~1.3ms | <10ms | Network-bound, tolerable overhead |
| Signature | ~1.6ms | <8ms | Policy binding, infrequent |
| NanoTDF size | ~250 bytes | <2KB | Acceptable for mobile data transfer |
References
- NIST Post-Quantum Cryptography Standards
- liboqs - Open Quantum Safe
- Signal's SPQR (Rust reference)
- Swift Crypto
- PQClean
- OpenTDF Specification
Related Work
- Signal SPQR: Production PQ ratcheting using ML-KEM (Rust)
- opentdf-rs: Sister project also adopting PQ (Issue Swift 6 #9)
- Apple CryptoKit: Monitor for potential future PQ support
Cross-Platform Considerations
OpenTDFKit targets iOS, macOS, tvOS, watchOS - ensure PQ implementation works across:
- Different CPU architectures (ARM64, x86_64)
- Minimum OS versions (iOS 18+, macOS 14+)
- Watchdog constraints on watchOS (performance budget)
Priority: Medium-High
Complexity: High
Timeline: 12-24 months for full hybrid implementation