Skip to content
hashkey.bond

Archivecryptography

X25519 and the Shared Secret You Never Use Directly

The raw Diffie-Hellman output is not a key. Feed it through a KDF and bind the transcript.

Published
Reading time
1 min read

X25519 computes shared = X25519(private, peer_public) and returns 32 bytes. Those bytes are a group element encoding, not uniformly random material, and must not be used as an encryption key directly.

The correct pipeline

shared = X25519(sk, pk_peer)
salt   = client_random || server_random
prk    = HKDF-Extract(salt, shared)
key    = HKDF-Expand(prk, "handshake key" || transcript, 32)

The salt and info bind the key to this specific handshake. A replay of the same curve point in another session produces a different key.

Hygiene

  • Reject all-zero outputs (small-order points). X25519 implementations do this, but check.
  • Use ephemeral keys per session for forward secrecy; static-static only when you need a long-term shared secret.
  • Never mix public keys from different curves without length prefixes.

Takeaway: Diffie-Hellman gives you a secret, not a key. KDF it, label it, bind it to the transcript.