Hybrid post-quantum
@webbuf/aesgcm-x25519dh-mlkem
Hybrid AES-256-GCM with X25519 ECDH + ML-KEM-768: classical + post-quantum authenticated encryption (Curve25519-flavored sibling of @webbuf/aesgcm-p256dh-mlkem)
Install
npm install @webbuf/aesgcm-x25519dh-mlkem Usage
import {
aesgcmX25519dhMlkemEncrypt,
aesgcmX25519dhMlkemDecrypt,
} from "@webbuf/aesgcm-x25519dh-mlkem";
import { mlKem768KeyPair } from "@webbuf/mlkem";
import { x25519PublicKeyCreate } from "@webbuf/x25519";
import { FixedBuf } from "@webbuf/fixedbuf";
import { WebBuf } from "@webbuf/webbuf";
// Both parties have persistent (static-static) X25519 keypairs.
const senderPriv = FixedBuf.fromRandom<32>(32);
const senderPub = x25519PublicKeyCreate(senderPriv);
const recipientPriv = FixedBuf.fromRandom<32>(32);
const recipientPub = x25519PublicKeyCreate(recipientPriv);
// Recipient holds an ML-KEM-768 keypair too.
const { encapsulationKey, decapsulationKey } = mlKem768KeyPair();
// Sender encrypts using its own X25519 priv, recipient's X25519 pub,
// recipient's ML-KEM encapsulation key.
const plaintext = WebBuf.fromUtf8("hybrid encryption");
const ciphertext = aesgcmX25519dhMlkemEncrypt(
senderPriv,
recipientPub,
encapsulationKey,
plaintext,
);
// Recipient decrypts using its own X25519 priv, sender's X25519 pub,
// own ML-KEM decapsulation key.
const recovered = aesgcmX25519dhMlkemDecrypt(
recipientPriv,
senderPub,
decapsulationKey,
ciphertext,
); API reference (4 exports)
Constants
AESGCM_X25519DH_MLKEM
constAESGCM_X25519DH_MLKEM: { readonly versionByte: 3; readonly kemCiphertextSize: 1088; readonly ivSize: 12; readonly tagSize: 16; readonly fixedOverhead: number; readonly hkdfInfo: "webbuf:aesgcm-x25519dh-mlkem v1"; } Functions
_aesgcmX25519dhMlkemEncryptDeterministic
functionTest/internal-only: encrypt with caller-supplied ML-KEM `m` and AES-GCM `iv`. Used by the KAT regression tests in `test/audit.test.ts` to assert byte-precise output against the fixtures captured in issue 0007 Experiment 4. Application code should use `aesgcmX25519dhMlkemEncrypt`.
_aesgcmX25519dhMlkemEncryptDeterministic(senderPrivKey: FixedBuf<32>, recipientPubKey: FixedBuf<32>, recipientEncapKey: FixedBuf<1184>, plaintext: WebBuf, m: FixedBuf<32>, iv: FixedBuf<12>, aad?: WebBuf): WebBuf aesgcmX25519dhMlkemDecrypt
functionDecrypt an `@webbuf/aesgcm-x25519dh-mlkem` ciphertext. Validates the version byte and minimum length, computes the same hybrid key by combining X25519 ECDH and decapsulated KEM shared secrets, and decrypts. Throws on version-byte mismatch, truncation, or AES-GCM authentication failure (which catches tampered KEM ciphertext, tampered AES ciphertext, tampered IV, AAD mismatch, or any wrong input key — including a wrong X25519 sender pub, wrong X25519 recipient priv, or wrong ML-KEM decapsulation key). Also throws if the X25519 shared secret with the supplied sender pub key is non-contributory (small-order public key). This propagates the `@webbuf/x25519` strict-rejection guarantee through the hybrid layer.
aesgcmX25519dhMlkemDecrypt(recipientPrivKey: FixedBuf<32>, senderPubKey: FixedBuf<32>, decapKey: FixedBuf<2400>, ciphertext: WebBuf, aad?: WebBuf): WebBuf aesgcmX25519dhMlkemEncrypt
functionEncrypt a message with hybrid X25519 ECDH + ML-KEM-768 key exchange. Both parties use persistent (static-static) X25519 keypairs. The sender provides their own private X25519 key and the recipient's public X25519 key plus ML-KEM-768 encapsulation key. The AES key is derived from the concatenation of the raw 32-byte X25519 ECDH shared secret and the 32-byte ML-KEM shared secret via HKDF-SHA-256. **Small-order rejection.** `x25519SharedSecretRaw` throws if the recipient's public key is small-order (the resulting shared secret is non-contributory). This protects the hybrid construction from being collapsed to PQ-only by a malicious peer presenting a small-order public key. `aad` is optional Additional Authenticated Data — bytes that are authenticated by the AES-GCM tag but not encrypted and not transmitted in the output. The recipient must supply the same `aad` bytes the sender used; any mismatch causes `aesgcmX25519dhMlkemDecrypt` to throw. Output layout: [0..1) version byte (0x03) [1..1089) ML-KEM-768 ciphertext (1088 bytes) [1089..1101) AES-GCM IV (12 bytes) [1101..1101+N) AES-GCM ciphertext (N bytes; same length as plaintext) [1101+N..1117+N) AES-GCM authentication tag (16 bytes)
aesgcmX25519dhMlkemEncrypt(senderPrivKey: FixedBuf<32>, recipientPubKey: FixedBuf<32>, recipientEncapKey: FixedBuf<1184>, plaintext: WebBuf, aad?: WebBuf): WebBuf