Skip to content

GAP-Q1: Post-Quantum Signature Hardening - EPCC Implementation Plan

Version: 1.0.0 Created: 2025-12-27 Gap ID: GAP-Q1 Severity: MEDIUM (Future-proofing) Estimated Effort: 16-24 hours Dependencies: GAP-M4 (BIP-322 signatures) Status: ✅ IMPLEMENTED (2025-12-28)


Executive Summary

This plan implements post-quantum cryptographic (PQC) signature hardening for AEGIS governance workflows using NIST-standardized algorithms. The implementation uses a hybrid approach combining classical Ed25519 signatures with ML-DSA (Dilithium) post-quantum signatures for defense-in-depth against future quantum computing threats.

Current State

  • Override workflow uses Ed25519 signatures (128-bit classical security)
  • BIP-322/Schnorr planned for GAP-M4 (also quantum-vulnerable)
  • No quantum-resistant signature capability
  • Signatures stored as 64-byte blobs

Target State

  • Hybrid signature scheme: Ed25519 + ML-DSA-44 (128-bit post-quantum security)
  • Both signatures required for valid override
  • Backward-compatible verification for migration period
  • Future-proofed against cryptographically relevant quantum computers (CRQC)

1. Threat Model

1.1 Quantum Computing Timeline

Timeframe Threat Level Action
2025-2030 Low Monitor, plan migration
2030-2035 Medium Implement hybrid signatures
2035+ High Full PQC required

NIST Recommendation: Begin migration planning now; hybrid deployment by 2030.

1.2 Attack Scenarios

Scenario Classical Risk Post-Quantum Risk Mitigation
Signature forgery Infeasible Shor's algorithm ML-DSA hybrid
Key recovery Infeasible Grover's algorithm 256-bit keys
Harvest-now-decrypt-later N/A High for long-lived data Immediate concern for audit trails
Audit trail tampering Infeasible Could forge historical overrides Hybrid signatures protect history

1.3 Regulatory Drivers

  • NSA CNSA 2.0: Requires PQC for national security systems by 2035
  • NIST SP 800-208: Recommends hybrid signatures during transition
  • PCI-DSS 4.0: Anticipates cryptographic agility requirements
  • EU Cyber Resilience Act: May require quantum-safe cryptography

2. Algorithm Selection

2.1 NIST Standardized Algorithms

Algorithm FIPS Type Security Signature Size Public Key Private Key
ML-DSA-44 204 Lattice 128-bit 2,420 bytes 1,312 bytes 2,560 bytes
ML-DSA-65 204 Lattice 192-bit 3,309 bytes 1,952 bytes 4,032 bytes
ML-DSA-87 204 Lattice 256-bit 4,627 bytes 2,592 bytes 4,896 bytes
SLH-DSA-128f 205 Hash 128-bit 17,088 bytes 32 bytes 64 bytes

2.2 Selected Configuration

Primary: ML-DSA-44 (Dilithium Level 2)

Rationale: 1. 128-bit security matches Ed25519 classical security 2. Smallest signature size among lattice schemes 3. Fast signing/verification (~0.5ms) 4. NIST standardized (FIPS 204) 5. Widely implemented in liboqs

2.3 Hybrid Scheme

HybridSignature = Ed25519(message) || ML-DSA-44(message)

Total size: 64 + 2,420 = 2,484 bytes
Verification: Both must pass

3. Architecture Design

3.1 Component Diagram

┌─────────────────────────────────────────────────────────────────────┐
│                     Override Workflow                                │
│  ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐        │
│  │  DomainLead     │ │   RiskOfficer   │ │  Verification   │        │
│  │  (Signer 1)     │ │   (Signer 2)    │ │                 │        │
│  └────────┬────────┘ └────────┬────────┘ └────────┬────────┘        │
└───────────┼────────────────────┼────────────────────┼───────────────┘
            │                    │                    │
            ▼                    ▼                    ▼
┌─────────────────────────────────────────────────────────────────────┐
│                    HybridSignatureProvider                          │
│  ┌─────────────────────────────────────────────────────────────┐    │
│  │  sign(message, keypair) → HybridSignature                   │    │
│  │  verify(signature, message, public_keys) → bool             │    │
│  │  generate_keypair() → HybridKeyPair                         │    │
│  └─────────────────────────────────────────────────────────────┘    │
└───────────────────────────────────┬─────────────────────────────────┘
            ┌───────────────────────┼───────────────────────┐
            ▼                       ▼                       ▼
┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐
│   Ed25519Provider   │ │   MLDSAProvider     │ │   KeyStorage        │
│   (cryptography)    │ │   (liboqs-python)   │ │   (encrypted)       │
└─────────────────────┘ └─────────────────────┘ └─────────────────────┘

3.2 Data Structures

from dataclasses import dataclass
from enum import Enum
from typing import Optional

class SignatureAlgorithm(str, Enum):
    """Signature algorithm identifiers."""
    ED25519 = "Ed25519"
    ML_DSA_44 = "ML-DSA-44"
    ML_DSA_65 = "ML-DSA-65"
    HYBRID_ED25519_ML_DSA_44 = "Ed25519+ML-DSA-44"

@dataclass
class HybridPublicKey:
    """Combined classical + post-quantum public key."""
    classical: bytes          # Ed25519 public key (32 bytes)
    post_quantum: bytes       # ML-DSA public key (1,312 bytes)
    algorithm: SignatureAlgorithm = SignatureAlgorithm.HYBRID_ED25519_ML_DSA_44

@dataclass
class HybridPrivateKey:
    """Combined classical + post-quantum private key."""
    classical: bytes          # Ed25519 private key (32 bytes)
    post_quantum: bytes       # ML-DSA private key (2,560 bytes)
    algorithm: SignatureAlgorithm = SignatureAlgorithm.HYBRID_ED25519_ML_DSA_44

@dataclass
class HybridKeyPair:
    """Complete hybrid key pair."""
    public: HybridPublicKey
    private: HybridPrivateKey

@dataclass
class HybridSignature:
    """Combined classical + post-quantum signature."""
    classical: bytes          # Ed25519 signature (64 bytes)
    post_quantum: bytes       # ML-DSA signature (2,420 bytes)
    algorithm: SignatureAlgorithm = SignatureAlgorithm.HYBRID_ED25519_ML_DSA_44

    def to_bytes(self) -> bytes:
        """Serialize to bytes for storage."""
        return self.classical + self.post_quantum

    @classmethod
    def from_bytes(cls, data: bytes) -> "HybridSignature":
        """Deserialize from bytes."""
        return cls(
            classical=data[:64],
            post_quantum=data[64:],
        )

3.3 Database Schema Changes

-- Update WorkflowTransition to handle larger signatures
ALTER TABLE workflow_transitions
    ALTER COLUMN signature TYPE BYTEA,
    ADD COLUMN signature_algorithm VARCHAR(50) DEFAULT 'Ed25519';

-- Index for algorithm-based queries
CREATE INDEX idx_transition_algorithm ON workflow_transitions(signature_algorithm);

-- Key storage table (encrypted at rest)
CREATE TABLE governance_keys (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    actor_id VARCHAR(100) NOT NULL UNIQUE,
    public_key_classical BYTEA NOT NULL,        -- 32 bytes
    public_key_pq BYTEA NOT NULL,               -- 1,312 bytes
    encrypted_private_key BYTEA NOT NULL,       -- Encrypted blob
    algorithm VARCHAR(50) NOT NULL DEFAULT 'Ed25519+ML-DSA-44',
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    rotated_at TIMESTAMP WITH TIME ZONE,
    expires_at TIMESTAMP WITH TIME ZONE
);

4. Implementation Tasks

Phase 1: Foundation (8 hours)

4.1 Library Integration

  • [ ] Add liboqs-python>=0.9.0 to requirements.txt
  • [ ] Add oqs type stubs or inline annotations
  • [ ] Create src/crypto/ module directory
  • [ ] Implement src/crypto/__init__.py with exports
  • [ ] Implement src/crypto/pqc.py with ML-DSA wrapper
# src/crypto/pqc.py
"""Post-Quantum Cryptography primitives using liboqs."""

import oqs
from dataclasses import dataclass
from typing import Tuple

ALGORITHM = "Dilithium2"  # ML-DSA-44

def generate_ml_dsa_keypair() -> Tuple[bytes, bytes]:
    """Generate ML-DSA-44 key pair.

    Returns:
        Tuple of (public_key, private_key)
    """
    with oqs.Signature(ALGORITHM) as signer:
        public_key = signer.generate_keypair()
        private_key = signer.export_secret_key()
        return public_key, private_key

def ml_dsa_sign(message: bytes, private_key: bytes) -> bytes:
    """Sign message with ML-DSA-44.

    Args:
        message: Message to sign
        private_key: ML-DSA private key

    Returns:
        Signature bytes (~2,420 bytes)
    """
    with oqs.Signature(ALGORITHM, private_key) as signer:
        return signer.sign(message)

def ml_dsa_verify(signature: bytes, message: bytes, public_key: bytes) -> bool:
    """Verify ML-DSA-44 signature.

    Args:
        signature: Signature to verify
        message: Original message
        public_key: ML-DSA public key

    Returns:
        True if valid, False otherwise
    """
    with oqs.Signature(ALGORITHM) as verifier:
        return verifier.verify(message, signature, public_key)

4.2 Hybrid Signature Provider

  • [ ] Implement src/crypto/hybrid.py
  • [ ] Integrate Ed25519 from existing cryptography library
  • [ ] Implement combined sign/verify logic
  • [ ] Add serialization/deserialization
# src/crypto/hybrid.py
"""Hybrid signature scheme: Ed25519 + ML-DSA-44."""

from cryptography.hazmat.primitives.asymmetric.ed25519 import (
    Ed25519PrivateKey,
    Ed25519PublicKey,
)
from dataclasses import dataclass
from . import pqc

@dataclass
class HybridSignatureProvider:
    """Provider for hybrid classical + post-quantum signatures."""

    def generate_keypair(self) -> HybridKeyPair:
        """Generate new hybrid key pair."""
        # Classical (Ed25519)
        classical_private = Ed25519PrivateKey.generate()
        classical_public = classical_private.public_key()

        # Post-quantum (ML-DSA-44)
        pq_public, pq_private = pqc.generate_ml_dsa_keypair()

        return HybridKeyPair(
            public=HybridPublicKey(
                classical=classical_public.public_bytes_raw(),
                post_quantum=pq_public,
            ),
            private=HybridPrivateKey(
                classical=classical_private.private_bytes_raw(),
                post_quantum=pq_private,
            ),
        )

    def sign(self, message: bytes, private_key: HybridPrivateKey) -> HybridSignature:
        """Create hybrid signature.

        Both Ed25519 and ML-DSA signatures are generated.
        """
        # Classical signature
        ed_key = Ed25519PrivateKey.from_private_bytes(private_key.classical)
        classical_sig = ed_key.sign(message)

        # Post-quantum signature
        pq_sig = pqc.ml_dsa_sign(message, private_key.post_quantum)

        return HybridSignature(
            classical=classical_sig,
            post_quantum=pq_sig,
        )

    def verify(
        self,
        signature: HybridSignature,
        message: bytes,
        public_key: HybridPublicKey,
    ) -> bool:
        """Verify hybrid signature.

        Both signatures must be valid.
        """
        # Verify classical
        try:
            ed_key = Ed25519PublicKey.from_public_bytes(public_key.classical)
            ed_key.verify(signature.classical, message)
            classical_valid = True
        except Exception:
            classical_valid = False

        # Verify post-quantum
        pq_valid = pqc.ml_dsa_verify(
            signature.post_quantum,
            message,
            public_key.post_quantum,
        )

        return classical_valid and pq_valid

Phase 2: Integration (8 hours)

4.3 Override Workflow Updates

  • [ ] Update OverrideRequest to support hybrid signatures
  • [ ] Update _verify_signature() to use HybridSignatureProvider
  • [ ] Add backward compatibility for Ed25519-only verification
  • [ ] Update add_signature() to create hybrid signatures

4.4 Key Management

  • [ ] Implement key generation CLI command
  • [ ] Implement secure key storage (encrypted at rest)
  • [ ] Add key rotation workflow
  • [ ] Document key ceremony procedures

4.5 Database Migration

  • [ ] Create Alembic migration for schema changes
  • [ ] Add signature_algorithm column
  • [ ] Update JSONB serialization for larger signatures

Phase 3: Testing (4 hours)

4.6 Unit Tests

  • [ ] Test ML-DSA key generation
  • [ ] Test ML-DSA sign/verify
  • [ ] Test hybrid signature creation
  • [ ] Test hybrid verification (both valid)
  • [ ] Test hybrid verification (classical invalid)
  • [ ] Test hybrid verification (PQ invalid)
  • [ ] Test serialization round-trip

4.7 Integration Tests

  • [ ] Test override workflow with hybrid signatures
  • [ ] Test backward compatibility with Ed25519-only
  • [ ] Test key rotation during active override
  • [ ] Test database persistence of large signatures

Phase 4: Documentation (4 hours)

4.8 Documentation

  • [ ] Update override.py docstrings
  • [ ] Create docs/security/post-quantum.md
  • [ ] Update ADR for quantum resistance decision
  • [ ] Document key ceremony procedures
  • [ ] Add migration guide for existing deployments

5. Migration Strategy

5.1 Migration Phases

Phase A: Preparation (Week 1-2)
├── Deploy liboqs-python to all environments
├── Generate hybrid key pairs for all governance actors
├── Update database schema
└── Deploy new code (backward compatible)

Phase B: Parallel Operation (Week 3-6)
├── New overrides use hybrid signatures
├── Old overrides remain valid (Ed25519 only)
├── Monitor for issues
└── Validate audit trail integrity

Phase C: Cutover (Week 7+)
├── Require hybrid signatures for new overrides
├── Mark Ed25519-only as deprecated
└── Plan Ed25519 sunset date

5.2 Backward Compatibility

def verify_signature(
    signature: bytes,
    message: bytes,
    public_key: bytes,
    algorithm: SignatureAlgorithm,
) -> bool:
    """Verify signature with algorithm-specific logic."""
    if algorithm == SignatureAlgorithm.ED25519:
        # Legacy Ed25519 only
        return ed25519_verify(signature, message, public_key)

    elif algorithm == SignatureAlgorithm.HYBRID_ED25519_ML_DSA_44:
        # Hybrid verification
        hybrid_sig = HybridSignature.from_bytes(signature)
        hybrid_key = HybridPublicKey.from_bytes(public_key)
        return provider.verify(hybrid_sig, message, hybrid_key)

    else:
        raise ValueError(f"Unknown algorithm: {algorithm}")

6. Performance Considerations

6.1 Benchmarks (Expected)

Operation Ed25519 ML-DSA-44 Hybrid
Key generation 0.03 ms 0.15 ms 0.18 ms
Sign 0.04 ms 0.35 ms 0.39 ms
Verify 0.08 ms 0.30 ms 0.38 ms
Signature size 64 B 2,420 B 2,484 B

6.2 Storage Impact

Component Before After Increase
Signature 64 B 2,484 B 38x
Public key 32 B 1,344 B 42x
Private key 32 B 2,592 B 81x
Override record ~500 B ~5.5 KB 11x

6.3 Mitigations

  1. Compression: Signatures compress well (30-40% reduction)
  2. Batch verification: Verify multiple signatures in parallel
  3. Lazy loading: Only load full signature when needed
  4. Archival: Move old overrides to cold storage

7. Security Considerations

7.1 Key Protection

  • Private keys encrypted at rest (AES-256-GCM)
  • Key derivation using Argon2id
  • HSM support for production (future enhancement)
  • Key rotation every 12 months

7.2 Side-Channel Resistance

  • liboqs uses constant-time implementations
  • No secret-dependent branching
  • Memory is zeroed after use

7.3 Algorithm Agility

  • SignatureAlgorithm enum allows future additions
  • Schema supports algorithm versioning
  • Can add ML-DSA-65/87 if security requirements increase

8. Dependencies

8.1 Library Requirements

# requirements.txt additions
liboqs-python>=0.9.0       # Post-quantum algorithms

8.2 System Requirements

  • liboqs native library (installed via pip)
  • OpenSSL 3.0+ (for Ed25519)
  • 64-bit architecture required

8.3 Gap Dependencies

GAP-M4 (BIP-322) ──────► GAP-Q1 (Post-Quantum)
                         ├── liboqs-python
                         ├── Database schema update
                         └── Key management infrastructure

9. Acceptance Criteria

9.1 Functional

  • [ ] Hybrid signatures can be generated
  • [ ] Hybrid signatures can be verified
  • [ ] Override workflow accepts hybrid signatures
  • [ ] Backward compatibility with Ed25519-only
  • [ ] Key generation produces valid pairs
  • [ ] Database stores/retrieves large signatures

9.2 Non-Functional

  • [ ] Verification < 1ms per signature
  • [ ] No memory leaks in signing loop
  • [ ] 100% test coverage for crypto module
  • [ ] Security audit passed (internal)

9.3 Documentation

  • [ ] API documentation complete
  • [ ] Migration guide written
  • [ ] Key ceremony documented
  • [ ] Security analysis documented

10. Risks and Mitigations

Risk Probability Impact Mitigation
liboqs instability Low High Pin version, monitor CVEs
Performance regression Low Medium Benchmark, lazy loading
Key management complexity Medium High Clear procedures, automation
Algorithm deprecation Low High Algorithm agility in design
HSM incompatibility Medium Medium Software fallback

11. References

Standards

Libraries

Research


Changelog

Version Date Author Changes
1.0.0 2025-12-27 Claude Code Initial implementation plan