demo · v133
Two-party ECDH handshake — using only WebCrypto
Alice and Bob each generate an X25519 keypair, exchange public keys, derive a shared secret. Both sides should agree. Before 133, X25519 wasn't in WebCrypto — you had to ship a wasm/JS BoringSSL port. Now navigator.subtle.deriveBits({ name: "X25519" }) just works.
Alice
Bob
why X25519 specifically
X25519 (Curve25519 in Diffie-Hellman role) became the default ECDH curve in TLS 1.3 because it's faster than NIST P-256, doesn't need point-validation tricks to avoid invalid-curve attacks, and has constant-time implementations that don't fight the compiler. End-to-end-encrypted apps in the browser had to embed BoringSSL or compile sodium/tweetnacl to wasm to get it — 50–200 KiB of bytecode that did one thing. The WebCrypto addition removes the ship-it-yourself tax. Combined with Ed25519 (signing, already shipped) the modern Curve25519 family is finally a first-class WebCrypto citizen.
const {publicKey, privateKey} = await crypto.subtle.generateKey(
{ name: "X25519" }, false, ["deriveBits"]
);
const shared = await crypto.subtle.deriveBits(
{ name: "X25519", public: peerPublicKey }, privateKey, 256
);