Reading — step 1 of 5
Read
Key Schedule
After Noise handshake, both sides derive multiple keys via HKDF-style chains.
WireGuard's chain:
chaining_key = HASH(b"WireGuard v1 zx2c4 [email protected]")
hash = HASH(chaining_key || init_data)
# Each DH op contributes:
chaining_key, key = HKDF(chaining_key, dh_output, 2)
hash = HASH(hash || ephemeral_pub)
# Send sub-message; chain hash includes message
hash = HASH(hash || encrypted_field)
Initiator and responder follow same chain → same final state.
Final session keys:
- Sending key (initiator → responder): from chain.
- Receiving key (responder → initiator): from chain (different derivation).
Each direction has independent counter starting at 0.
Each AEAD operation:
- nonce = counter (8 bytes) padded to 12 bytes.
- aad = b"".
- key = sending_key or receiving_key.
Forward secrecy: the chain depends on EPHEMERAL keys (generated per-handshake). After the handshake completes, ephemeral private keys are deleted. Even if static keys leak later, past sessions remain confidential.
Identity hiding: in Noise_IK, the initiator's static pub key is encrypted under a key derived from the responder's static pub. So passive observers can't see who's connecting.
Rekeying:
- WireGuard: every 2 minutes OR every 64 GB transferred OR every 2^60 messages.
- Generates fresh ephemeral keys. Keeps cryptographic margin.
Replay defense:
- Counters are ever-increasing.
- Receiver maintains anti-replay window.
- Old session keys discarded after rekey.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…