The key-distribution problem

The symmetric ciphers in 0.3 are fast and secure, but both sides need the same secret key first. Two people in the same room can share it on paper. A browser talking to a server it has never seen before cannot: every byte it sends travels over networks that anyone may read.

For centuries this was the hard part of cryptography. Governments shipped code books by courier. In 1976 Whitfield Diffie and Martin Hellman published a way to agree on a key using only public messages. That idea, and the family of techniques that grew from it, is public-key cryptography: each party has a private key it never shares and a public key it can show to anyone.

One-way functions

Public-key cryptography needs operations that are easy in one direction and hard in the other. Mixing paint is a good picture: combining yellow and blue is instant, but given the green you cannot easily say how much of each went in. In mathematics the standard examples are:

Some one-way functions have a trapdoor: a piece of secret information that makes the reverse direction easy. RSA is built on such a trapdoor. Diffie–Hellman does not need one.

A two-minute warm-up: arithmetic mod p

All of these schemes compute with remainders. Working modulo pp means you keep only the remainder after dividing by pp, like hours on a clock: 10 o’clock plus 5 hours is 3 o’clock, because 15 mod 12=315 \bmod 12 = 3. The same works for multiplication and powers. With p=23p = 23:

52=25≡2,53=125≡10,56=15625≡8(mod23).5^2 = 25 \equiv 2, \qquad 5^3 = 125 \equiv 10, \qquad 5^6 = 15625 \equiv 8 \pmod{23}.

The powers of 5 modulo 23 jump around: 5, 2, 10, 4, 20, 8, 17, 16, 11, 9, … and they visit every number from 1 to 22 before repeating. A number with this property is called a generator. The notation a≡b(modp)a \equiv b \pmod p and the other symbols are collected in 8.1 Numbers, sets and modular arithmetic.

Diffie–Hellman key exchange

Diffie–Hellman

  1. Everyone agrees on a public prime pp and a generator gg.
  2. Alice picks a secret aa and sends A=ga mod pA = g^a \bmod p. Bob picks a secret bb and sends B=gb mod pB = g^b \bmod p.
  3. Alice computes Ba=gabB^a = g^{ab}. Bob computes Ab=gabA^b = g^{ab}. Both now hold the same number s=gab mod ps = g^{ab} \bmod p.

Check it by hand with p=23p = 23, g=5g = 5, a=6a = 6, b=15b = 15. Alice sends A=56 mod 23=8A = 5^6 \bmod 23 = 8, Bob sends B=515 mod 23=19B = 5^{15} \bmod 23 = 19. Then Alice computes 196 mod 23=219^6 \bmod 23 = 2 and Bob computes 815 mod 23=28^{15} \bmod 23 = 2. The shared secret is 2, and it never crossed the network.

p, g = 23, 5          # public: a small prime and a generator
a, b = 6, 15          # private: Alice's and Bob's secrets

A = pow(g, a, p)      # Alice sends A
B = pow(g, b, p)      # Bob sends B

print("A =", A, " B =", B)
print("Alice computes", pow(B, a, p), " Bob computes", pow(A, b, p))
A = 8  B = 19
Alice computes 2  Bob computes 2

An eavesdropper sees 23, 5, 8 and 19. To get the secret they would have to find aa with 5a≡8(mod23)5^a \equiv 8 \pmod{23}. For such a small prime you can simply try all 22 values. Real Diffie–Hellman uses primes of 2048 bits or more, where the best known algorithms would take longer than the age of the universe on all the world’s computers. This is the discrete-logarithm assumption: nobody has proved it, but decades of work have not broken it for large, well-chosen groups.

Diffie–Hellman: two people, one secret, no meeting

Pick Alice's and Bob's private numbers. Only A and B are sent in the open. Then try the real thing with X25519.

Public for everyone: p = 23, g = 5.

Alice sends A = 5^6 mod 23
8
Bob sends B = 5^15 mod 23
19
Alice computes B^6 mod 23
2
Bob computes A^15 mod 23
2

Shared secret: 2. Both equal g^(a·b) mod p = 2.

What to notice: Anyone watching sees p, g, A and B, yet both sides land on the same secret. Recovering a or b from A or B is the discrete-logarithm problem: trivial for p = 23, infeasible for the 255-bit group behind X25519.

RSA: a trapdoor you can publish

RSA (Rivest, Shamir and Adleman, 1977) works differently. Instead of agreeing on a secret, anyone can encrypt a short message to your public key, and only you can decrypt it. The trapdoor is the factorization of a large number.

RSA with small numbers

Pick primes p=61p = 61 and q=53q = 53, so n=pq=3233n = pq = 3233. Choose the public exponent e=17e = 17. The private exponent is the inverse of ee modulo (p−1)(q−1)=3120(p-1)(q-1) = 3120, which is d=2753d = 2753 because 17⋅2753=46801=15⋅3120+117 \cdot 2753 = 46801 = 15 \cdot 3120 + 1.

The public key is (n,e)=(3233,17)(n, e) = (3233, 17). To encrypt m=65m = 65, compute c=6517 mod 3233=2790c = 65^{17} \bmod 3233 = 2790. To decrypt, compute 27902753 mod 3233=652790^{2753} \bmod 3233 = 65. Finding dd requires knowing (p−1)(q−1)(p-1)(q-1), and that requires factoring nn.

The toy version is useful for understanding and dangerous to use. “Textbook” RSA is deterministic, so encrypting the same message twice gives the same ciphertext, and it leaks structure an attacker can exploit. Real RSA encryption always adds randomized padding first (RSA-OAEP, standardized in RFC 8017), and real keys use a modulus of 2048 or 3072 bits. Most new protocols no longer use RSA for key exchange at all; they use elliptic curves.

Elliptic curves without the heavy algebra

An elliptic curve is the set of points (x,y)(x, y) that satisfy an equation such as y2=x3+ax+by^2 = x^3 + ax + b, with all arithmetic done modulo a prime. The key fact is that you can add two points on the curve and get a third point on the curve, using a fixed geometric rule: draw the line through them, find where it meets the curve again, and reflect.

Adding a starting point GG to itself kk times gives kGkG. On the small curve y2=x3+2x+2y^2 = x^3 + 2x + 2 modulo 17 with G=(5,1)G = (5, 1), the multiples go 2G=(6,3)2G = (6, 3), 3G=(10,6)3G = (10, 6), 4G=(3,1)4G = (3, 1), and after 19 steps return to the starting point. Computing kGkG for a 255-bit kk takes about 255 doublings and additions. Finding kk from kGkG is the elliptic-curve discrete logarithm, and for well-chosen curves the best known method needs about 21282^{128} operations.

Elliptic-curve Diffie–Hellman (ECDH) is Diffie–Hellman with points instead of powers: Alice sends aGaG, Bob sends bGbG, and both compute abGabG. The two curves you will meet most often are:

from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey

alice_private = X25519PrivateKey.generate()
bob_private = X25519PrivateKey.generate()

# Each side sends only its public key (32 bytes) over the network.
alice_public = alice_private.public_key()
bob_public = bob_private.public_key()

alice_shared = alice_private.exchange(bob_public)
bob_shared = bob_private.exchange(alice_public)

print(len(alice_shared), "bytes")
print(alice_shared == bob_shared)
32 bytes
True

How big should keys be?

Security is measured in bits: kk-bit security means the best known attack needs about 2k2^k operations. Elliptic-curve keys are much shorter than RSA keys for the same level, because the best attacks on elliptic curves are much slower than the best factoring algorithms.

SecuritySymmetricRSA / finite-field DHElliptic curve
112 bits3-key TDEA2048224–255
128 bitsAES-1283072256–383 (P-256, X25519)
192 bitsAES-1927680384–511 (P-384)
256 bitsAES-25615360512+ (P-521)

The table follows NIST SP 800-57 Part 1. Sizes are key lengths in bits. All of these numbers assume classical computers; 0.7 explains why a large quantum computer would break RSA, Diffie–Hellman and elliptic curves at every size.

Key exchange needs authentication

Diffie–Hellman protects against someone who only listens. Someone who can change traffic can sit in the middle: they run one exchange with Alice while pretending to be Bob, and another with Bob while pretending to be Alice. Each side ends up with a key shared with the attacker, and nothing in the math looks wrong.

The fix is to authenticate the public keys. In TLS the server signs its key-exchange message with a key that a certificate authority has vouched for, so the browser can check it really came from the site. That is the job of digital signatures, covered in 0.6.

Putting it together: hybrid encryption

Public-key operations are thousands of times slower than symmetric encryption and handle only short inputs. So real systems use each for what it is good at:

  1. A key exchange (or a key-encapsulation mechanism, see 0.7) produces a shared secret.
  2. A key-derivation function (0.4) turns that secret into symmetric keys.
  3. An AEAD cipher such as AES-GCM or ChaCha20-Poly1305 (0.3) encrypts and authenticates the actual data.

Every HTTPS connection you open follows this pattern. The same pattern appears in the post-quantum world: ML-KEM replaces the key exchange, and in hybrid TLS it runs alongside X25519.

Common mistakes

Further reading

Diffie and Hellman, “New Directions in Cryptography” (1976), the original paper. NIST SP 800-57 Part 1 Rev. 5, “Recommendation for Key Management”, for the security-level table. David Wong, Real-World Cryptography (Manning, 2021), chapters 5 and 6, for key exchange and asymmetric encryption in practice. These references are cited from memory.

Next: 0.6 Digital signatures, which solve the authentication problem.

Check Your Understanding

  1. Q1. In Diffie–Hellman with p = 23 and g = 5, Alice picks a = 6 and Bob picks b = 15. What does an eavesdropper see?

  2. Q2. Why does a plain Diffie–Hellman exchange not protect you from an attacker who can modify messages?

  3. Q3. Which pair gives roughly the same security level according to NIST SP 800-57?

  4. Q4. In practice, how is a large file encrypted to someone’s public key?