v150 · Web Cryptography

Hybrid session key

Real messaging apps don't encrypt payloads with a KEM directly — they use the KEM to establish a symmetric session key, then encrypt with that. encapsulateKey() does this in one step: it takes an ML-KEM public key and hands back an AES-GCM CryptoKey, ready to use. The recipient decapsulateKey()s the same key. Watch a whole message travel end-to-end below.

Type a message and run the exchange.
1Alice — generateKey(ML-KEM-768)
Alice creates an ML-KEM keypair and publishes the public key.
2Bob — encapsulateKey(…, {name:"AES-GCM", length:256})
Bob turns Alice's public key into a fresh AES-GCM session key + a ciphertext to return.
3Bob — encrypt(AES-GCM) with the session key
Bob encrypts the message with the session key and sends {KEM ciphertext, nonce, AES ciphertext}.
4Alice — decapsulateKey(…) → same AES-GCM key
Alice recovers the identical AES-GCM key from the KEM ciphertext with her private key.
5Alice — decrypt(AES-GCM) → plaintext
Alice decrypts the message.
// Alice
const alice = await crypto.subtle.generateKey(
  { name: "ML-KEM-768" }, true, ["encapsulateKey", "decapsulateKey"]);

// Bob: one call turns Alice's public key into an AES-GCM session key
const { ciphertext, sharedKey: bobKey } = await crypto.subtle.encapsulateKey(
  "ML-KEM-768", alice.publicKey,
  { name: "AES-GCM", length: 256 }, true, ["encrypt", "decrypt"]);

const iv = crypto.getRandomValues(new Uint8Array(12));
const msgCt = await crypto.subtle.encrypt({ name: "AES-GCM", iv }, bobKey, data);

// Alice: recover the identical AES-GCM key, then decrypt
const aliceKey = await crypto.subtle.decapsulateKey(
  "ML-KEM-768", alice.privateKey, ciphertext,
  { name: "AES-GCM", length: 256 }, true, ["encrypt", "decrypt"]);
const plaintext = await crypto.subtle.decrypt({ name: "AES-GCM", iv }, aliceKey, msgCt);

see also

implementation reference

Need the exact API surface, compatibility boundaries, errors, lifecycle, and source links? Read the matching gendn reference ↗