Before any messages are exchanged, both peers in p2p-chat perform a Diffie-Hellman key exchange to derive a shared 32-byte key. This key is never transmitted over the network — each side computes it independently from their own private key and the other peer’s public key. The exchange happens once per connection, and the resulting key is used for all message encryption during that session.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/Project516/p2p-chat/llms.txt
Use this file to discover all available pages before exploring further.
How the key exchange works
Both peers run the sameExchangeKeys function. There is no separate client-side or server-side logic for the crypto — the code path is identical regardless of which peer initiated the connection.
Generate an ephemeral key pair
Each peer generates a fresh Curve25519 key pair using
box.GenerateKey. The randomness comes from crypto/rand, the operating system’s cryptographically secure random source. Both the public and private keys are 32 bytes.Send the local public key
The local public key (32 bytes) is written directly to the connection. No framing or encoding is applied — it is a raw byte sequence.
Read the peer's public key
The peer’s public key is read from the connection using
io.ReadFull, which blocks until exactly 32 bytes have been received. This ensures the key is complete before proceeding.Derive the shared key
box.Precompute performs the X25519 Diffie-Hellman operation: it multiplies the peer’s public key by the local private key to produce a 32-byte shared secret. Both peers arrive at the same value without ever sending the secret over the wire."Encryption established." is printed to stdout.Ephemeral keys
The key pair generated in step 1 exists only for the duration of theExchangeKeys call. It is never written to disk, never logged, and never reused. Each new connection produces a completely independent key pair.
Ephemeral keys are generated from
crypto/rand, which reads from /dev/urandom (or the equivalent OS source) on Linux. This is appropriate for cryptographic key material.The shared key
After the exchange completes,ExchangeKeys returns a pointer to a 32-byte array. This value is the session key — it is passed to crypto.Encrypt and crypto.Decrypt for every message sent or received on that connection.
The shared key is computed identically by both peers:
- Peer A computes:
DH(A_private, B_public) - Peer B computes:
DH(B_private, A_public)
Forward secrecy
Because keys are generated fresh for every connection and never persisted, a compromise of one session’s key material does not affect any other session — past or future.What forward secrecy means in practice
What forward secrecy means in practice
If an attacker records encrypted traffic today and later obtains the shared key for that session (through compromise of a peer’s memory, for example), they can decrypt only that session. They cannot decrypt any other session because each session uses an independent ephemeral key pair.
Why ephemeral keys are essential for forward secrecy
Why ephemeral keys are essential for forward secrecy
Long-lived keys — keys stored on disk and reused across connections — do not provide forward secrecy. If such a key is compromised, all past sessions encrypted with it are retroactively exposed. Generating fresh keys per connection eliminates this risk.
Limitations
p2p-chat does not verify the identity of the peer during the key exchange. There is no PKI, no certificate chain, and no fingerprint comparison mechanism. The protocol follows a trust-on-first-use (TOFU) model. Because both peers run the sameExchangeKeys code with no client/server distinction, there is no structural way to assign a persistent identity to either side. Adding identity verification would require an out-of-band mechanism — such as comparing key fingerprints over a separate channel, or integrating a PKI.
Related pages
Message encryption
How the shared key is used to encrypt and authenticate every message with XSalsa20-Poly1305.