OpenPGP Internals — An Implementer's Series

PQC & The Schism

Article 6 of 7 · RFC 9980, LibrePGP, implementations

Sep 9, 2026 · security · 13 min read · 2500 words advanced

Post-quantum OpenPGP, and the schism.

security openpgp pgp post-quantum ml-kem

Two things happened to OpenPGP in the 2020s. It got a credible post-quantum story — RFC 9980, published June 2026, specifying composite ML-KEM+ECDH encryption, composite ML-DSA+EdDSA signatures, and standalone SLH-DSA. And it split into two incompatible standards, with GnuPG pursuing LibrePGP and the IETF working group and Sequoia pursuing RFC 9580. This article covers the cryptographic constructions in detail — including why hybrid rather than pure PQC, and what a KEM combiner must actually bind — then treats the schism as the concrete interoperability problem it is, and ends with an implementation comparison for people who have to choose a library.

Prerequisites. Article 2 for key packets and the v6 explicit-length field that makes new algorithms addable, Article 3 for PKESK and SEIPDv2.

1. The threat model, stated precisely

A cryptographically relevant quantum computer running Shor's algorithm breaks RSA, DSA, and every elliptic-curve scheme OpenPGP currently uses. No such machine is known to exist. The reason to act now is harvest-now-decrypt-later: an adversary who records ciphertext today and stores it can decrypt it whenever such a machine arrives.

This threat is unusually severe for OpenPGP specifically. Email and file encryption produce artifacts with very long confidentiality requirements — archives, legal documents, journalistic source material, medical records. A TLS session key protects data for the lifetime of the connection; an encrypted archive may need to stay confidential for decades.

Signatures are a different, milder problem. A quantum adversary can forge signatures once the machine exists, but cannot retroactively forge a signature that was verified in the past by a party that recorded the result. There is no "harvest-now-forge-later" — forgery is only useful going forward. This asymmetry is why encryption migration is more urgent than signature migration, and why RFC 9980 makes ML-KEM-768+X25519 mandatory-to-implement while the signature side has more latitude.

2. Why hybrid, not pure post-quantum

RFC 9980 does not replace ECC. It combines ECC and PQC, so that the construction stays secure if either component survives. The reasoning is explicit in the RFC: deploy post-quantum in combination with traditional schemes such that security is retained even if all schemes but one in the combination are broken.

ConcernPure PQCHybrid
Classical cryptanalysis of new lattice assumptionsTotal loss if brokenFalls back to ECC security
Implementation bugs in new, less-exercised codeTotal lossECC path still holds
Quantum adversarySecureSecure (PQ component holds)
Key and ciphertext sizeLargeLarger
Compute costModerateBoth operations

The lattice assumptions underlying ML-KEM and ML-DSA are roughly a decade old in their current form and have had far less cryptanalytic attention than RSA or the discrete log problem. Hybrid is the conservative response to that asymmetry in scrutiny, not a hedge against NIST's judgment.

3. Composite encryption: ML-KEM + ECDH

RFC 9980 defines two composite encryption algorithms:

AlgorithmComponentsRequirement level
ML-KEM-768 + X25519NIST security category 3 + Curve25519MUST implement
ML-KEM-1024 + X448NIST security category 5 + Curve448SHOULD implement

3.1 The KEM combiner

Both KEMs are run independently, producing two shared secrets, which are then combined through a KDF into the key that actually wraps the session key:

Composite KEM: two independent encapsulations, one combined key ML-KEM-768 encaps → ct_pq , ss_pq X25519 ECDH → ct_ecc , ss_ecc KEM combiner (KDF) binds both ct + both pk KEK wraps session key breaking one component leaves the KEK secure — that is the entire design goal the combiner must bind ciphertexts and public keys, or the composition is not robust

Fig. 1 — Concatenating shared secrets is not sufficient. The combiner's KDF input must include both ciphertexts and both public keys.

The binding requirement is the subtle part and worth stating explicitly. A naive combiner \(K = \text{KDF}(ss_{pq} \| ss_{ecc})\) is not robust: it fails to bind the derived key to the specific ciphertexts and public keys involved, which opens the door to re-encapsulation and key-substitution attacks where an adversary swaps one component's ciphertext for one of their own. RFC 9980's combiner includes both ciphertexts, both public keys, and a domain-separation label, which is what makes the composition secure rather than merely convenient.

3.2 Size consequences

QuantityX25519 aloneML-KEM-768 + X25519
Public key32 B~1,216 B
Ciphertext / PKESK payload32 B~1,120 B
Private key32 B~2,432 B

Roughly a 35× growth in key size. For most OpenPGP use this is irrelevant — a 1 KB PKESK on a 50 KB email is noise. It matters in three places: constrained hardware tokens (many smartcards cannot store keys this size), keyserver and WKD storage and bandwidth at scale, and any protocol that embeds OpenPGP certificates in size-limited fields — DNS records for DANE being the obvious casualty.

This is where Article 2's v6 explicit key-material length pays off. Because a v6 public key packet declares how many octets its key material occupies, an implementation that has never heard of ML-KEM can still parse past the packet correctly. Without that field, adding a new algorithm with a new key size would break every older parser. Forward compatibility was designed in one revision before it was needed.

4. Post-quantum signatures

4.1 Composite ML-DSA + EdDSA

Signatures follow the same hybrid pattern: both signatures are computed over the same message and both must verify. RFC 9980 defines composites pairing ML-DSA-65 with Ed25519 and ML-DSA-87 with Ed448.

\[ \text{verify}(m, \sigma) = \text{verify}_{\text{ML-DSA}}(m, \sigma_{pq}) \ \land \ \text{verify}_{\text{EdDSA}}(m, \sigma_{ecc}) \]

The conjunction matters. An implementation that accepts the signature if either component verifies has built a downgrade: an attacker who breaks one component can forge freely. Both, always, or reject.

4.2 SLH-DSA standalone

SLH-DSA (formerly SPHINCS+) is included as a standalone algorithm rather than as a hybrid component, and the reason is structural: it is hash-based, so its security rests only on the hash function's properties rather than on lattice assumptions. There is no need to hedge it with ECC, because its assumptions are the most conservative available.

The cost is size. SLH-DSA signatures are large — on the order of 8–30 KB depending on parameter set, versus 64 bytes for Ed25519. That makes it unsuitable for routine message signing and well-suited to exactly one thing: long-lived root and code-signing keys, where signature size is irrelevant and the strongest possible assumption is worth paying for.

Use caseRecommendedWhy
Routine message signingML-DSA-65 + Ed25519Reasonable size, hybrid safety
Software release signingSLH-DSA, or compositeVerified rarely, must last decades
Long-lived offline root keySLH-DSAMost conservative assumptions available
Encryption subkeyML-KEM-768 + X25519MTI, harvest-now-decrypt-later urgency

5. The schism

This section is factual rather than partisan. Both sides have coherent positions; the consequence for you is an interoperability matrix you must plan around.

5.1 What happened

The OpenPGP working group spent years revising RFC 4880. In September 2022, GnuPG announced it would implement an earlier draft rather than the direction the rest of the working group had agreed. The working group continued and published RFC 9580 in July 2024, defining version 6 keys, SEIPDv2, and the AEAD design covered in Articles 2 and 3. GnuPG and allied implementers formalized their alternative as LibrePGP, based on the v5 design.

Werner Koch's stated position is that the IETF's changes were harmful to existing deployments and that users need long-term stability over disruptive change. The working group's position, as described in LWN's coverage, is that the changes were substantive security improvements: protection against key-overwriting attacks, a wire-format change to prevent signature aliasing, and downgrade protection.

5.2 The technical disagreements

AreaRFC 9580LibrePGPConsequence
AEAD containerSEIPD version 2 (tag 18)AEAD Encrypted Data, tag 20Ciphertext does not decrypt across implementations
Key versionv6 — SHA-256 fingerprints, explicit lengths, salted sigsv5 — different layout and fingerprint constructionCertificates are not mutually parseable
ChunkingExponent-based chunk sizes in SEIPDv2Different chunking schemeEven where both do AEAD, framing differs
Secret-key AEAD bindingAD binds the public key packetDifferent constructionThe key-overwriting protection differs
Signature saltingv6 signatures saltedNot adoptedDifferent collision-resistance posture
The practical rule for 2026: "OpenPGP-compatible" is no longer a meaningful claim in a spec or a procurement document. You must state which: RFC 4880, RFC 9580, or LibrePGP — and for encryption, whether you emit SEIPDv2 (tag 18 v2) or AEAD Encrypted Data (tag 20). Write it into your interface documentation and your test matrix.

5.3 A defensible engineering posture

Given the split, the interoperability-maximizing configuration for general correspondence in 2026 is unglamorous:

  • v4 keys with Ed25519 signing and X25519 encryption subkeys — parsed by everything.
  • SEIPDv1 with a correctly-enforced MDC for messages to unknown peers, per Article 3's strict-rejection rules.
  • SEIPDv2 AEAD where you know the peer speaks RFC 9580.
  • v6 keys and post-quantum algorithms where you control both ends — internal systems, machine-to-machine, archival encryption with a known toolchain.

For archival encryption specifically, the harvest-now-decrypt-later argument overrides interoperability: if you are encrypting something that must stay confidential for twenty years and you control the decryption side, use ML-KEM-768+X25519 now.

6. The implementation landscape

ImplementationLanguageStandardInterfaceNotes
GnuPGCRFC 4880 + LibrePGPCLI, plus GPGMEThe reference deployment. CLI-parsing integrations carry the SigSpoof bug class (Article 4).
SequoiaRustRFC 9580Library-first; sq CLIMemory-safe, independently audited, strict policy engine. The reference for new work.
RNPC++RFC 4880 + AEADLibraryUsed by Thunderbird. C++ memory-safety caveats apply.
OpenPGP.jsJavaScriptRFC 4880 + parts of 9580LibraryBrowser/Node. Memory-safe runtime; see CVE-2025-47934 for API-shape hazards.
GopenPGPGoRFC 4880 + extensionsLibraryProton's native-app stack; wraps Go's crypto/openpgp lineage.

6.1 Memory safety is not a slogan here

An OpenPGP implementation parses attacker-controlled, deeply-nested, variably-length binary from untrusted sources — the archetypal environment for memory-corruption bugs. Rust and Go eliminate buffer overflows and use-after-free by construction; C and C++ do not. That is a genuine, measurable difference in attack surface for this specific workload, independent of any team's competence.

Memory safety is not sufficient, though, and OpenPGP.js's CVE-2025-47934 makes the point: message-spoofing through an API-shape confusion, in a memory-safe language. Logic bugs in verification interfaces survive any language choice.

6.2 Choosing a library

Decision guide.
  • New system, Rust or FFI-tolerant: Sequoia. RFC 9580, memory-safe, audited, and its explicit policy engine forces you to state your algorithm and version constraints rather than inheriting defaults.
  • Browser or Node: OpenPGP.js — read its verification API documentation carefully and check each result object's verified status individually.
  • Go service: GopenPGP.
  • Must interoperate with existing GnuPG deployments: GPGME, not CLI output parsing. If you must shell out, never merge status and stderr, and escape everything you print.
  • Whatever you choose: pin an explicit algorithm and version policy, and test against the other implementations, because the schism guarantees your assumptions are wrong somewhere.

7. A migration plan

PhaseActionRationale
1 — InventoryEnumerate keys, algorithms, key versions, and which implementation each consumer usesYou cannot plan a migration you cannot describe
2 — Retire the indefensibleRemove RSA <3072, DSA, Elgamal, SED (tag 9) acceptance, S2K usage 255These are weak today, independent of quantum concerns
3 — Modernize classicallyEd25519 + X25519, SEIPDv2 where peers allow, Argon2-protected secret keysBest available classical posture; prerequisite for the rest
4 — Hybrid PQC where you control both endsML-KEM-768+X25519 encryption subkeys for archival and internal useHarvest-now-decrypt-later is the live threat
5 — PQC signatures for long-lived rootsSLH-DSA for offline roots and release signingSize is irrelevant there; assumption strength is not
6 — InstrumentLog algorithm and version usage across your fleetMigration progress is unmeasurable otherwise

FAQ

Should I move to post-quantum OpenPGP now?

For encryption of long-lived data where you control decryption, yes — harvest-now-decrypt-later means waiting has a real cost that arrives retroactively. For general correspondence, not yet; interoperability is poor and the classical algorithms are not broken. For signatures, there is no urgency, because forgery cannot be applied retroactively to already-verified signatures.

Is hybrid just hedging against NIST?

It is hedging against cryptanalytic immaturity, which is a different and more defensible claim. The lattice assumptions are far less studied than RSA or discrete log, and the implementations are new. Hybrid means a break in either the assumption or the implementation is survivable. The cost is size and compute, both of which OpenPGP can afford.

Which side of the schism should I build on?

If you control both ends: RFC 9580, via Sequoia. It is the IETF standard, it has the better security properties discussed in Articles 2 and 3, and it is where the post-quantum work landed. If you must interoperate with the installed GnuPG base: support v4 keys and SEIPDv1 as your baseline, and treat v6/SEIPDv2 as an opportunistic upgrade. Do not build a system that assumes one dialect is universal.

Why is SLH-DSA standalone rather than hybrid?

Its security rests on hash function properties alone — the most conservative assumption in the entire toolkit, and one that has survived far more scrutiny than lattices. Adding ECC would increase size and complexity to hedge against an assumption that does not need hedging.

Takeaways

  • Harvest-now-decrypt-later makes encryption migration urgent and signature migration not. The asymmetry should drive your prioritization.
  • A KEM combiner must bind ciphertexts and public keys, not merely concatenate shared secrets, or the composition loses the robustness that is the whole point of going hybrid.
  • Composite signature verification is a conjunction. Accepting either component is a built-in downgrade.
  • SLH-DSA is for roots, not for messages — 8–30 KB signatures are fine on a key you verify once a year.
  • The schism is concrete, not political: tag 18 v2 versus tag 20 means ciphertext that does not decrypt. Specify your dialect explicitly.
  • Memory safety materially reduces attack surface for this workload, but does not touch the verification-interface bug class that has caused the most damage.

Next in this series

Article 7 — The complete resource map: every RFC, research paper, book, video, course, tool, and mailing list worth your time, plus a structured learning roadmap for implementers and researchers.

References

← prev: Trust & Distribution next: The Resource Map →
© cvam — written in plaintext, served warm