X-Wing KEM
X-Wing is a general-purpose hybrid post-quantum / traditional key encapsulation mechanism: it runs ML-KEM-768 and X25519 side by side and binds their shared secrets with SHA3-256, so the resulting key stays secure as long as either ML-KEM-768 or X25519 holds. That is the conservative shape for migrating off classical ECDH today — a harvest-now- decrypt-later attacker must break the lattice KEM, and a hypothetical ML-KEM break still leaves X25519 standing.
The construction follows draft-connolly-cfrg-xwing-kem. The combiner is frozen — it hashes the ML-KEM shared secret, the X25519 shared secret, the X25519 ephemeral public key, the recipient's X25519 public key, and a fixed six-byte label — but the document is still an IETF Internet-Draft, so this primitive is marked experimental and sits beside the other pre-RFC post-quantum drafts (b.crypto.hpke.pq). The wire sizes are fixed: a 1216-byte public key (ML-KEM-768 1184 ‖ X25519 32), a 1120-byte ciphertext (ML-KEM-768 1088 ‖ X25519 32), a 32-byte decapsulation seed, and a 32-byte shared secret.
X-Wing composes the framework's vendored ML-KEM-768 and X25519 plus SHA3 — it adds no new cryptographic core, only the standard combiner and wire framing.
b.crypto.xwing.combiner(ssM, ssX, ctX, pkX) #
The X-Wing combiner: SHA3-256(ssM ‖ ssX ‖ ctX ‖ pkX ‖ label), where the label is the fixed six bytes the draft defines. Exposed for advanced use and known-answer testing; encapsulate and decapsulate call it internally. Each input must be 32 bytes.
var ss = b.crypto.xwing.combiner(ssMlkem, ssX25519, ephPub, recipientPub);
// → 32-byte shared secret
b.crypto.xwing.keygen(seed?) #
Generate an X-Wing keypair. The decapsulation key is a 32-byte seed (store this); the encapsulation key is the 1216-byte public key to publish. Pass a 32-byte seed for deterministic generation, or omit it for a random key.
var kp = b.crypto.xwing.keygen();
kp.publicKey.length; // → 1216
kp.secretKey.length; // → 32 (the seed — keep it secret)
b.crypto.xwing.encapsulate(publicKey, eseed?) #
Encapsulate to a 1216-byte X-Wing public key. Returns the 1120-byte ciphertext to send and the 32-byte sharedSecret to key a symmetric cipher with. Pass a 64-byte eseed (X25519 ephemeral scalar ‖ ML-KEM coins) for deterministic encapsulation, or omit it for fresh randomness.
var enc = b.crypto.xwing.encapsulate(recipientPublicKey);
enc.ciphertext.length; // → 1120
enc.sharedSecret.length; // → 32
b.crypto.xwing.decapsulate(secretKey, ciphertext) #
Recover the 32-byte shared secret from a 1120-byte X-Wing ciphertext using the 32-byte decapsulation seed. ML-KEM-768's implicit-rejection means a tampered ciphertext yields a different (still 32-byte) secret rather than an error, so never branch on success — derive keys and let the AEAD tag fail.
var ss = b.crypto.xwing.decapsulate(kp.secretKey, enc.ciphertext);
ss.equals(enc.sharedSecret); // → true
Last updated 2026-08-08T16:39:49.652Z by seeder.