Skip to content

Adopt Post-Quantum Cryptography with Hybrid Approach #22

@arkavo-com

Description

@arkavo-com

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

  1. NanoTDF Header (NanoTDF.swift):

    • Ephemeral public key (P-256/P-384/P-521)
    • Policy binding (GMAC or ECDSA signature)
  2. 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
  3. KAS Rewrap (KASRewrapClient.swift):

    • Client ephemeral key generation
    • Key unwrapping via ECDH with KAS session public key
  4. 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

  1. Long-term data protection: NanoTDF files may be stored long-term (harvest-now-decrypt-later threat)
  2. Mobile security: iOS/macOS devices are high-value targets for quantum decryption
  3. Zero Trust alignment: PQ crypto strengthens data-centric security
  4. Future compliance: Regulatory requirements likely to mandate PQ
  5. 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:
  • 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

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions