Quantum Security Programme · Technical brief

Cryptographic discovery in regulated firms — finding every TLS endpoint, every signed artifact, every long-lived key

← Q-Security overview

1. The exact problem this technology solves (real attack model)

Crypto discovery is the prerequisite for everything else in a PQ migration. You cannot replace what you have not catalogued. The threat model is harvest-now-decrypt-later (HNDL): an adversary captures TLS sessions, IPsec tunnels, encrypted email, signed software artifacts, or VPN traffic today and stores the ciphertext against the day a cryptographically-relevant quantum computer (CRQC) can run Shor against the key-establishment material. RSA-2048, RSA-3072, ECDSA-P256, X25519, DH-2048 — all break under Shor. AES-256-GCM data keys are safe (Grover gives a quadratic speed-up, not exponential), but the session keys that wrap them are typically derived from an ECDHE handshake and are therefore harvestable.

The discovery problem in a regulated Irish firm — bank, insurer, payments processor, utility — is that nobody has a complete inventory. A 2024 Ponemon study put the average enterprise figure at 61 % of cryptographic assets unknown to the security team. Typical gaps:

  • TLS terminators on internal microservices behind a service mesh, using mTLS with certificates issued by an internal CA whose root is on a USB stick in a safe in Sandyford.
  • SSH host keys (ssh-rsa, ssh-ed25519) on every Linux box, every network appliance, every jump host, generated at provisioning time and never rotated.
  • Signed artifacts: container images signed with cosign using ECDSA, JAR files signed with jarsigner against an RSA-2048 keystore, RPM/DEB packages signed with GPG, firmware images signed with vendor RSA keys.
  • Long-lived keys: HSM-resident RSA wrapping keys, KMIP master keys, database TDE keys, S/MIME identities for board correspondence, code-signing certs valid through 2031.
  • DNSSEC zone-signing keys (ZSK) and key-signing keys (KSK) — almost universally RSASHA256 or ECDSAP256SHA256.

Without this inventory, you cannot build a migration plan, prioritise by exposure window, or attest to the Central Bank that you have a defensible PQ roadmap under the DORA Article 9 cryptography control.

2. The cryptographic primitive in technical detail

Discovery is not itself a cryptographic operation, but you are inventorying primitives, so you must classify each finding by what replaces it.

Key establishment — anything performing Diffie-Hellman (TLS ECDHE, IKEv2 DH groups, SSH KEX, Signal X3DH) is replaced by ML-KEM (FIPS 203, formerly Kyber). ML-KEM is IND-CCA2 secure under the Module-LWE assumption. The three parameter sets — ML-KEM-512, ML-KEM-768, ML-KEM-1024 — target NIST levels 1, 3, 5. Public keys are 800/1184/1568 bytes; ciphertexts 768/1088/1568 bytes. In practice you deploy hybrid: X25519MLKEM768 (codepoint 0x11EC) per draft-kwiatkowski-tls-ecdhe-mlkem and now widely adopted.

Digital signaturesRSA-PSS, RSA-PKCS#1v1.5, ECDSA, Ed25519 are replaced by ML-DSA (FIPS 204, formerly Dilithium) for general use, and SLH-DSA (FIPS 205, formerly SPHINCS+) for cases where you cannot tolerate a lattice assumption — typically root CAs and firmware signing with decade-plus validity. Both are EUF-CMA secure. ML-DSA-65 has 1952-byte public keys and 3309-byte signatures; SLH-DSA-SHA2-128s has 32-byte public keys but 7856-byte signatures and very slow signing. Choose accordingly.

Symmetric and hashesAES-256-GCM, ChaCha20-Poly1305, SHA-256, SHA-3 stay. Flag anything weaker (AES-128 in long-retention contexts, SHA-1 anywhere) for replacement before PQ migration, not after.

3. Reference implementations in 2026

  • liboqs 0.12 (Open Quantum Safe) — reference implementations of all FIPS 203/204/205 parameter sets, plus the older NIST round-3 candidates for backwards interop testing. Production-grade for ML-KEM and ML-DSA; SLH-DSA is correct but slow.
  • OpenSSL 3.5 ships ML-KEM and ML-DSA natively; the oqs-provider remains useful for hybrid groups not yet upstreamed and for SLH-DSA.
  • BoringSSL — Google enabled X25519MLKEM768 in Chrome stable from version 131 (late 2024). Server-side support across Cloudflare, Google Front End, and a growing share of CDNs.
  • OpenSSH 9.9+mlkem768x25519-sha256 KEX is the default in 10.0 (April 2025). SSH host keys remain Ed25519/RSA pending FIPS 204 finalisation in OpenSSH itself; expect ssh-mldsa65 in 10.x during 2026.
  • BIND 9.20 — experimental ML-DSA DNSSEC algorithm support; not yet IETF-allocated codepoints, so do not deploy to production zones. Watch draft-ietf-dnsop-dnssec-pqc.
  • AWS KMSpq-hybrid mode on the KMS TLS endpoint since 2020 (initially Kyber-512, now ML-KEM-768). KMS-resident signing keys are still classical.
  • GCP Cloud KMS / Azure Key Vault — TLS to the control plane is hybridised; customer-managed PQ signing keys are in preview.
  • Cloudflare — PQ TLS on by default for any origin that negotiates it; about 38 % of human Cloudflare traffic was PQ-protected by end of 2025.

4. The deployment pattern

A discovery sweep has four passes: network, filesystem, certificate stores, and signed-artifact registries. The output is a single normalised inventory keyed by SHA-256 of the public key, with primitive, parameter, location, expiry, and exposure window. Example skeleton (Python, pseudocode):

import ssl, socket, hashlib, json
from cryptography import x509
from cryptography.hazmat.primitives import serialization

def probe_tls(host, port=443):
    ctx = ssl.create_default_context()
    ctx.check_hostname = False
    ctx.verify_mode = ssl.CERT_NONE
    with socket.create_connection((host, port), timeout=5) as s:
        with ctx.wrap_socket(s, server_hostname=host) as tls:
            der = tls.getpeercert(binary_form=True)
            cipher = tls.cipher() # (suite, proto, bits)
            group = tls.group() # 3.12+: e.g. 'X25519MLKEM768'
    cert = x509.load_der_x509_certificate(der)
    pub = cert.public_key()
    spki = pub.public_bytes(
        serialization.Encoding.DER,
        serialization.PublicFormat.SubjectPublicKeyInfo)
    return {
        "host": host,
        "tls_version": cipher[1],
        "kex_group": group,
        "sig_alg": cert.signature_algorithm_oid.dotted_string,
        "spki_sha256": hashlib.sha256(spki).hexdigest(),
        "not_after": cert.not_valid_after_utc.isoformat(),
        "pq_ready": group and "MLKEM" in group,
    }

# Feed it from your asset inventory, not a /16 sweep.
for host in load_asset_inventory():
    try: print(json.dumps(probe_tls(host)))
    except Exception as e: log_failure(host, e)

Then run parallel passes: find / -name 'id_*' -o -name '*.pem' -o -name '*.p12' across managed estate via Ansible; cosign tree against every OCI registry; gpg --list-keys --with-colons on signing hosts; dig +dnssec against every zone; KMIP Locate calls against every HSM partition.

5. What goes wrong in production

  • Middlebox intolerance. ClientHello with the X25519MLKEM768 group exceeds the 1500-byte single-packet threshold. Older F5, Palo Alto, and Cisco gear silently drops the second TLS record. You discover this only when 2 % of

    Schedule a sovereign-cryptography assessment

    Book a security call →