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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions