Skip to content
The Lumenary
Grades 3–5Ages 8–11 35 min

Secret codes

Hide a message with an alphabet wheel, find out why Z wraps round to A, and then break the code — by trying every key, or by counting letters.

Formally verified · 16 LeanComputationally tested · 2 PythonAI-drafted · awaiting human reviewMathematically reviewed: Awaiting human review

Your guides: Chloe, Margaret, Gideon

MotivationH

A note across the classroom

Chidi and Ngozi sit at opposite ends of their classroom in Lagos. Chidi wants to pass Ngozi a note about the surprise party they are planning for their friend Tobi — but the note will pass through many hands, and Tobi might see it. So Chidi writes:

PHHW DW QRRQ

Ngozi knows the secret: every letter has been moved 3 places along the alphabet. She moves each letter 3 places back — P to M, H to E, W to T — and reads: MEET AT NOON.

More than two thousand years ago, the Roman general Julius Caesar hid his letters in exactly this way. The Roman writer Suetonius tells us that Caesar replaced each letter by the one three places later: D for A, E for B, and so on. That is why this kind of code is called a Caesar shift.

IntuitionH

The alphabet wheel

Write the alphabet round a circle, and a second copy on a smaller wheel inside it. Turn the inside wheel 3 places. Now every letter on the outside sits next to its code letter on the inside: A next to D, B next to E, …

ADBECFDGEHFIGJHKILJMKNLOMPNQORPSQTRUSVTWUXVYWZXAYBZCplain: outsidecode: inside
An alphabet wheel for a shift of 3. Find a letter of your message on the outside; its code letter is just inside it. The gold sector shows A → D. Near the top, X, Y and Z sit next to A, B and C: the alphabet wraps round.

On a wheel, the alphabet has no end. After Z comes A again, just as after 12 o'clock on a clock comes 1 o'clock. So with a shift of 3, X becomes A, Y becomes B and Z becomes C. That is what wrapping round means.

DefinitionsH

Big ideas

Definition— cipher

A cipher is a rule for turning a message into a disguised form and back again. The message is the plain text; the disguised form is the coded text (or cipher text). Turning plain text into coded text is encoding; turning it back is decoding.

Definition— key

The key is the secret information needed to decode a message — for a Caesar shift, the size of the shift.

Definition— Caesar shift

A Caesar shift with key kk replaces each letter by the letter kk places later in the alphabet, wrapping round from Z to A.

Definition— substitution cipher

A substitution cipher replaces each letter by another letter using any scrambled alphabet as the key, for example A → Q, B → W, C → E, … Every letter must get a different code letter, so that the message can be decoded. A Caesar shift is a very special substitution cipher.

Worked examplesH

Let's try together

Example— Encoding with a shift of 3

plainMEETATNOON
codePHHWDWQRRQ

Spaces stay as spaces (real code-writers often remove them, to make the code harder to break).

Example— Wrapping round

With a shift of 3, ZEBRA becomes CHEUD: Z wraps round to C, E → H, B → E, R → U, A → D. With numbers: Z is 25, 25+3=2825 + 3 = 28, 28−26=228 - 26 = 2, which is C.

Example— Decoding

To decode a shift of 3, shift each letter 3 places back: PHHW → MEET. Or — surprise! — shift 23 places forward. Going 3 places and then 23 more is 26 places: once all the way round the wheel, back where you started.

Example— Breaking a Caesar cipher by trying every key

Tobi finds a note: WKLV LV IXQ. He does not know the key. But there are only 26 ways to turn the inside wheel, and one of them does nothing at all. So he simply tries them all:

shift back by123…
decodingVJKU KU HWPUIJT JT GVOTHIS IS FUN…

At a shift of 3 the message makes sense. Trying every key like this is called a brute-force attack. A Caesar shift has only 25 keys that change the message, so it can be broken in a few minutes with a pencil.

Example— A substitution cipher

Ngozi and Chidi decide to use a scrambled alphabet instead. The key is a list like this:

plainABCDEF…
codeQWERTY…

How many keys are there? A can be given any of 26 code letters; then B any of the 25 letters left; then C any of the 24 left; and so on. That makes 26×25×24×⋯×2×126 \times 25 \times 24 \times \dots \times 2 \times 1 keys — the number written 26!26!:

26!=403 291 461 126 605 635 584 000 000.26! = 403\,291\,461\,126\,605\,635\,584\,000\,000.

Far too many to try, even for a computer trying a billion keys a second: it would take more than ten billion years. But there is a cleverer way to break it…

ProofH

Why the wheel works

Theorem— Undoing a shift

A shift forward by kk places is undone by a shift forward by 26−k26 - k places.

Proof

Shifting by kk and then by 26−k26 - k moves every letter k+(26−k)=26k + (26 - k) = 26 places along the wheel. The wheel has 26 letters, so 26 places is exactly once round: every letter comes back to where it started.

∎

Theorem— Only 26 shifts

There are exactly 26 different Caesar shifts, and only 25 of them change the message.

Proof

A shift by 26 goes once round the wheel and changes nothing, so a shift by 27 does the same as a shift by 1, a shift by 28 the same as a shift by 2, and so on. So every shift does the same as one of the shifts 0,1,2,…,250, 1, 2, \dots, 25. These 26 shifts are all different, because they send A to 26 different letters (shift kk sends A to letter number kk). The shift by 0 changes nothing, which leaves 25.

∎

Two more facts follow from the wheel. Shifting by aa and then by bb is the same as one shift by a+ba + b (for example, 10 and then 20 is 30, which is once round and 4 more: a shift by 4). And a shift never sends two different letters to the same code letter — the inside wheel has each letter exactly once — which is why every coded message can be decoded.

Why letter-counting breaks substitution ciphers. A substitution cipher changes which letters are used, but not how often they are used. In English, E is the most common letter: in a long text, roughly one letter in every eight is an E. Next come T, A, O, I and N. So if the most common letter of a long coded message is Q, then Q very probably stands for E. A few good guesses like this, plus short words like THE and A, and the whole message starts to appear. This is called frequency analysis.

Explore

Explore: crack the code by counting

Here is a secret message, made with a Caesar shift:

WOOD WO XOKB DRO QBOOX QKDO LOCSNO DRO BSFOB KD COFOX DRSC OFOXSXQ. LBSXQ DRO WKZ, DRO BYZO KXN DRBOO ZOXMSVC. GO GSVV COXN DRO XOHD COMBOD WOCCKQO GROX DRO WYYX SC RSQR.

The chart shows how many times each of its ten most common letters appears.

Letters in the secret message

The tallest bar is probably E. How far round the wheel is it from E?

010203040FrequencyO: 3232OD: 1313DX: 1212XR: 1111RC: 99CS: 99SB: 88BK: 66KQ: 66QW: 55W

What to try:

  • Which letter is most common? If it stands for E, how many places is it from E? That is probably the key.
  • Decode the first few words with your key. Do they make sense? If not, try the second most common letter as E.
  • Look for a short word that appears many times. In English, a common three-letter word is THE.
For grown-ups · the answer

The most common letter is O, which is 10 places after E, so the key is a shift of 10 (decode by shifting 10 back, or 16 forward). The message reads: MEET ME NEAR THE GREEN GATE BESIDE THE RIVER AT SEVEN THIS EVENING. BRING THE MAP, THE ROPE AND THREE PENCILS. WE WILL SEND THE NEXT SECRET MESSAGE WHEN THE MOON IS HIGH. The word DRO, which appears six times, is THE.

Formal mathematicsF

Gideon's true-or-false game

Gideon only plays with statements that are exactly true or exactly false. Decide, and give a reason.

  1. "With a shift of 3, Y becomes B." — True. Y, Z, A, B: three places on, wrapping round.
  2. "A shift forward by 5 is undone by a shift forward by 21." — True. 5+21=265 + 21 = 26, once round the wheel.
  3. "There are 26 Caesar shifts that change a message." — False. There are 26 shifts, but the shift by 0 changes nothing: 25 change the message.
  4. "A message that looks like nonsense is safe." — False. A Caesar message can be cracked by trying 25 keys.
  5. "A shift by 10 and then a shift by 20 is the same as a shift by 4." — True. 30 places is once round and 4 more.
For grown-ups · the same ideas in Lean, a proof checker

Letters are numbered A = 0, …, Z = 25, and the remainder on division by 26 (written % 26) does the wrapping round:

shiftLean 4Formally verified
/-- A Caesar shift by `k` on letter numbers (A = 0, …, Z = 25): move `k` places on, wrapping
round from 25 back to 0. -/
def shift (k n : Nat) : Nat := (n + k) % 26
Axioms used: No axiomsformal/Lumenary/SecretCodes.leanTry it in the Lean web editor
wrap_aroundLean 4Proved here from basic factsFormally verified
/-- Shifting by 3 wraps round the end of the alphabet: X (23) → A (0), Y (24) → B (1),
Z (25) → C (2). And M (12) → P (15) needs no wrapping. -/
theorem wrap_around : shift 3 23 = 0 ∧ shift 3 24 = 1 ∧ shift 3 25 = 2 ∧ shift 3 12 = 15 := by
  decide
Axioms used: No axiomsformal/Lumenary/SecretCodes.leanTry it in the Lean web editor
shift_ltLean 4Applies a library theoremFormally verified
/-- A shift always lands on one of the 26 letters: a remainder on division by 26 is less
than 26. This applies core Lean's `Nat.mod_lt`. -/
theorem shift_lt (k n : Nat) : shift k n < 26 :=
  Nat.mod_lt _ (by decide)
Axioms used: No axiomsformal/Lumenary/SecretCodes.leanTry it in the Lean web editor

The facts proved above, now checked for every letter and every key:

shift_undoLean 4Proved here from basic factsFormally verified
/-- **A shift forward by `k` is undone by a shift forward by `26 − k`**: together they move
26 places, once round the whole wheel. -/
theorem shift_undo (k n : Nat) (hk : k ≤ 26) (hn : n < 26) : shift (26 - k) (shift k n) = n := by
  unfold shift
  omega
Axioms used: propext, Quot.soundformal/Lumenary/SecretCodes.leanTry it in the Lean web editor
shift_shiftLean 4Proved here from basic factsFormally verified
/-- Two shifts, one after the other, are one shift: by `a`, then by `b`, is the same as by
`a + b`. (So the order of two shifts does not matter.) -/
theorem shift_shift (a b n : Nat) : shift b (shift a n) = shift (a + b) n := by
  unfold shift
  omega
Axioms used: propext, Quot.soundformal/Lumenary/SecretCodes.leanTry it in the Lean web editor
shift_add_twenty_sixLean 4Proved here from basic factsFormally verified
/-- Shifting by `k + 26` is the same as shifting by `k`. So every shift is one of the shifts
0, 1, …, 25: there are **at most** 26 different shifts. -/
theorem shift_add_twenty_six (k n : Nat) : shift (k + 26) n = shift k n := by
  unfold shift
  omega
Axioms used: propext, Quot.soundformal/Lumenary/SecretCodes.leanTry it in the Lean web editor
shifts_differLean 4Proved here from basic factsFormally verified
/-- The shifts 0, 1, …, 25 are all different: shift `k` sends A (0) to letter number `k`. So
there are **exactly** 26 shifts — and only 25 that change the message. -/
theorem shifts_differ (k : Nat) (hk : k < 26) : shift k 0 = k := by
  unfold shift
  omega
Axioms used: propext, Quot.soundformal/Lumenary/SecretCodes.leanTry it in the Lean web editor
shift_injectiveLean 4Proved here from basic factsFormally verified
/-- A shift never sends two different letters to the same letter. That is why a coded message
can always be decoded. -/
theorem shift_injective (k n m : Nat) (hn : n < 26) (hm : m < 26)
    (h : shift k n = shift k m) : n = m := by
  unfold shift at h
  omega
Axioms used: propext, Quot.soundformal/Lumenary/SecretCodes.leanTry it in the Lean web editor

Real messages. A computer stores the letter A as the number 65 and Z as 90, so a character is shifted by turning it into a letter number, shifting, and turning it back. Lean checks the lesson's messages, breaks WKLV LV IXQ by trying all 26 keys, and proves that decoding undoes encoding for every message:

shiftCharLean 4Formally verified
/-- Shift one character. Computers store `A` as the number 65, …, `Z` as 90. A capital letter
is turned into its letter number (subtract 65), shifted, and turned back (add 65); spaces and
other characters are left as they are. -/
def shiftChar (k : Nat) (c : Char) : Char :=
  if 65 ≤ c.toNat ∧ c.toNat ≤ 90 then Char.ofNat ((c.toNat - 65 + k) % 26 + 65) else c
Axioms used: No axiomsformal/Lumenary/SecretCodes.leanTry it in the Lean web editor
caesarLean 4Formally verified
/-- The Caesar cipher with key `k`: shift every letter of the message by `k`. -/
def caesar (k : Nat) (message : String) : String :=
  String.ofList (message.toList.map (shiftChar k))
Axioms used: propext, Classical.choice, Quot.soundformal/Lumenary/SecretCodes.leanTry it in the Lean web editor
meet_at_noonLean 4Proved here from basic factsFormally verified
/-- Julius Caesar's shift of 3: MEET AT NOON becomes PHHW DW QRRQ. -/
theorem meet_at_noon : caesar 3 "MEET AT NOON" = "PHHW DW QRRQ" := by
  decide
Axioms used: propext, Classical.choice, Quot.soundformal/Lumenary/SecretCodes.leanTry it in the Lean web editor
zebraLean 4Proved here from basic factsFormally verified
/-- Wrapping round: with a shift of 3, ZEBRA becomes CHEUD (Z → C). -/
theorem zebra : caesar 3 "ZEBRA" = "CHEUD" := by
  decide
Axioms used: propext, Classical.choice, Quot.soundformal/Lumenary/SecretCodes.leanTry it in the Lean web editor
decode_by_23Lean 4Proved here from basic factsFormally verified
/-- To decode a shift of 3, shift forward by 26 − 3 = 23. -/
theorem decode_by_23 : caesar 23 "PHHW DW QRRQ" = "MEET AT NOON" := by
  decide
Axioms used: propext, Classical.choice, Quot.soundformal/Lumenary/SecretCodes.leanTry it in the Lean web editor
try_every_shiftLean 4Proved here from basic factsFormally verified
/-- Breaking a Caesar cipher by trying every key: among the 26 shifts of WKLV LV IXQ, one
reads THIS IS FUN. -/
theorem try_every_shift : "THIS IS FUN" ∈ (List.range 26).map (fun k => caesar k "WKLV LV IXQ") := by
  decide
Axioms used: propext, Classical.choice, Quot.soundformal/Lumenary/SecretCodes.leanTry it in the Lean web editor
caesar_undoLean 4Proved here from basic factsFormally verified
/-- **Decoding undoes encoding, for every message and every key**: the Caesar cipher with key
`26 − k` decodes the Caesar cipher with key `k`. -/
theorem caesar_undo (k : Nat) (hk : k ≤ 26) (message : String) :
    caesar (26 - k) (caesar k message) = message := by
  unfold caesar
  rw [String.toList_ofList, List.map_map]
  have : (shiftChar (26 - k) ∘ shiftChar k) = id := funext (shiftChar_undo k hk)
  rw [this, List.map_id, String.ofList_toList]
Axioms used: propext, Classical.choice, Quot.soundformal/Lumenary/SecretCodes.leanTry it in the Lean web editor

And the number of substitution keys:

substitution_keysLean 4Proved here from basic factsFormally verified
/-- A substitution key chooses a code letter for A (26 choices), then for B (25 left), and so
on: 26 × 25 × ⋯ × 1 keys — more than 400 million million million million. -/
theorem substitution_keys : factorial 26 = 403291461126605635584000000 := by
  decide
Axioms used: No axiomsformal/Lumenary/SecretCodes.leanTry it in the Lean web editor

What is not formalised: frequency analysis. It is not a theorem that E is the most common letter of a message — it is usually true of long English texts, and false for some short ones (try counting the letters of "ZIGZAG"). That is a fact about language, which is why the lesson treats it as evidence, not proof.

ComputationC

Margaret breaks the codes

Caesar shifts: encode, decode, and try every keyPythonExact computationExhaustive finite checkComputationally tested
"""Caesar shifts: encoding, wrapping round, and breaking by trying every key.

Letters are numbered A = 0, ..., Z = 25, and a Caesar shift by k sends
letter n to (n + k) mod 26, so Z wraps round to A. The program encodes and
decodes the lesson's examples, then breaks a secret message by trying all
26 shifts. To pick out the right one automatically, it gives each attempt a
score that measures how far its letter counts are from typical English
letter frequencies (smaller is more English-like).
"""

import textwrap

ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

# Typical percentages of each letter in long English texts (A to Z).
ENGLISH = [8.2, 1.5, 2.8, 4.3, 12.7, 2.2, 2.0, 6.1, 7.0, 0.15, 0.8, 4.0, 2.4,
           6.7, 7.5, 1.9, 0.1, 6.0, 6.3, 9.1, 2.8, 1.0, 2.4, 0.15, 2.0, 0.07]

SECRET = "ESP ECPLDFCP TD FYOPC ESP XLYRZ ECPP"

def shift_letter(letter, k):
    """Shift one capital letter k places along the alphabet, wrapping round."""
    return ALPHABET[(ALPHABET.index(letter) + k) % 26]

def caesar(message, k):
    """Shift every capital letter of the message; leave other characters."""
    return "".join(shift_letter(c, k) if c in ALPHABET else c
                   for c in message)

def english_score(text):
    """How far the letter counts are from English (chi-squared statistic)."""
    letters = [c for c in text if c in ALPHABET]
    total = len(letters)
    score = 0.0
    for i, letter in enumerate(ALPHABET):
        expected = total * ENGLISH[i] / 100
        observed = letters.count(letter)
        score += (observed - expected) ** 2 / expected
    return score

def best_shift(secret):
    """The decoding shift whose result has the most English-like letters."""
    return min(range(26), key=lambda k: english_score(caesar(secret, k)))

def main():
    for message, k, note in [("MEET AT NOON", 3, ""),
                             ("ZEBRA", 3, "  (Z wraps round to C)"),
                             ("PHHW DW QRRQ", 23, "  (23 undoes 3)")]:
        label = f"{message}, shift {k}:"
        print(f"{label:<25}{caesar(message, k)}{note}")
    print()
    print(f"Secret message: {SECRET}")
    print("Every possible decoding (shift forward by k):")
    best = best_shift(SECRET)
    for k in range(26):
        attempt = caesar(SECRET, k)
        mark = "  <- most English-like" if k == best else ""
        print(f"  k = {k:>2}: {attempt}{mark}")
    print(f"The sender shifted by {(26 - best) % 26};"
          f" shifting by {best} undoes it.")
    print()
    print(textwrap.fill(
        "Epistemic status: exact and exhaustive - every one of the 26 shifts "
        "was tried, so the message cannot hide from this search. The "
        "'English-like' score is only a heuristic: for very short or unusual "
        "messages it can point to the wrong shift, so a person still reads "
        "the answer to check that it makes sense.",
        width=78,
    ))

if __name__ == "__main__":
    main()
Runs entirely in your browser, in an isolated worker. Nothing is sent to a server.

Trying every key is exhaustive: the message cannot hide from it. The score is only a helpful guess, so a person still reads the answer to check it makes sense.

Breaking a substitution cipher by counting lettersPythonExperimental evidenceExact computationComputationally tested
"""Breaking a substitution cipher by counting letters.

A substitution cipher replaces each letter by another letter, using a
scrambled alphabet as the key. There are 26 x 25 x ... x 1 keys - far too
many to try - but the cipher keeps the pattern of how often each letter is
used. The program encodes a paragraph of English with a random key (from a
seeded random number generator), counts the letters of the secret message,
and compares the most common ones with the most common letters of English.
Then it tries the simplest possible attack - match the letters by rank -
and reports how much of the message that alone gets right.
"""

import math
import random
import textwrap
from collections import Counter

ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
ENGLISH_ORDER = "ETAOINSHRDLCUMWFGYPBVKJXQZ"

PLAIN = (
    "PEOPLE HAVE SENT SECRET MESSAGES FOR THOUSANDS OF YEARS. A SIMPLE WAY "
    "TO HIDE A MESSAGE IS TO REPLACE EVERY LETTER WITH ANOTHER LETTER. THE "
    "MESSAGE THEN LOOKS LIKE NONSENSE, BUT A CLEVER READER CAN STILL BREAK "
    "THE CODE. IN ENGLISH THE LETTER E APPEARS MORE OFTEN THAN ANY OTHER "
    "LETTER, AND THE WORD THE IS VERY COMMON. SO THE CODE BREAKER COUNTS "
    "THE LETTERS IN THE SECRET MESSAGE AND GUESSES THAT THE MOST COMMON ONE "
    "STANDS FOR E. THEN SHE LOOKS FOR SHORT WORDS AND REPEATED PATTERNS, "
    "AND THE HIDDEN MESSAGE SLOWLY APPEARS."
)

def random_key(seed):
    """A scrambled alphabet: plain letter ALPHABET[i] becomes key[i]."""
    letters = list(ALPHABET)
    random.Random(seed).shuffle(letters)
    return "".join(letters)

def substitute(text, key):
    """Replace each capital letter using the key; keep other characters."""
    table = dict(zip(ALPHABET, key))
    return "".join(table.get(c, c) for c in text)

def letter_counts(text):
    """How many times each capital letter appears, most common first."""
    return Counter(c for c in text if c in ALPHABET).most_common()

def rank_guess(secret):
    """Guess a decoding key by matching letters by frequency rank."""
    ranked = [letter for letter, _ in letter_counts(secret)]
    unused = [c for c in ALPHABET if c not in ranked]
    return dict(zip(ranked + unused, ENGLISH_ORDER))

def main():
    key = random_key(1586)
    secret = substitute(PLAIN, key)
    print(f"Possible keys: 26! = {math.factorial(26):,}")
    print("Secret message (start): " + secret[:52] + "...")
    counts = letter_counts(secret)
    total = sum(n for _, n in counts)
    decode = {code: plain for plain, code in zip(ALPHABET, key)}
    print(f"Most common letters in the secret message ({total} letters):")
    print("  code letter  count  share  really stands for  English rank")
    for rank, (code, n) in enumerate(counts[:6]):
        line = (f"  {code:^11}  {n:>5}  {100 * n / total:>4.0f}%"
                f"  {decode[code]:^17}  {ENGLISH_ORDER[rank]:^12}")
        print(line.rstrip())
    guess = rank_guess(secret)
    attempt = "".join(guess.get(c, c) for c in secret)
    right = sum(1 for p, a in zip(PLAIN, attempt) if p in ALPHABET and p == a)
    print(f"Matching letters by rank alone gets {right} of {total} letters"
          f" right ({100 * right / total:.0f}%):")
    print("  " + attempt[:60] + "...")
    print("A code breaker then fixes it using words such as THE and patterns.")
    print()
    print(textwrap.fill(
        "Epistemic status: exact counting, but one experiment - one "
        "paragraph and one random key. Long English texts usually have E as "
        "their most common letter, but short or unusual texts may not, so "
        "frequency analysis gives good guesses, not certainties. Here the "
        "guesses are checked against the real key.",
        width=78,
    ))

if __name__ == "__main__":
    main()
Runs entirely in your browser, in an isolated worker. Nothing is sent to a server.

The most common code letter really did stand for E. But matching letters by rank alone got well under half of the message right: the counts in one paragraph are not exactly the same as in English as a whole. Real code breakers use the counts to make a start, and then use short words, repeated letters and patterns to finish the job. This was one experiment, with one paragraph and one key — evidence, not a law.

Exercises

Your turn

Make your own alphabet wheel from two paper circles and a split pin — it makes encoding and decoding quick. You can ask for a hint whenever you like.

1. Secret codes

Warm-upHuman

This message was made with a Caesar shift of 3 (each letter moved 3 places forward): KHOOR. What does it say?

Choose one

2. Secret codes

Warm-upHuman

Encode ZEBRA with a Caesar shift of 3.

Choose one

3. Secret codes

CoreHuman

A message was encoded by shifting every letter 7 places forward. Ngozi's alphabet wheel only turns forward. By how many places forward must she shift to decode it? (Give a number from 1 to 25.)

4. Secret codes

CoreHuman

Tobi wants to break a Caesar cipher by trying every possible shift. How many different shifts does he need to try that actually change the message?

5. Secret codes

CoreHuman

Chidi shifts a message 10 places forward, and then shifts the result 20 places forward. Which single shift (a number from 0 to 25) would have done the same?

6. Secret codes

CoreHuman

In a long message made with a Caesar shift, the most common letter is K. If K stands for E, how many places forward was each letter shifted?

7. Secret codes

CoreHuman

A long English message was coded with a substitution cipher (a scrambled alphabet). In the coded message the most common letter is Q. Which plain letter is Q most likely to stand for?

Choose one

8. Secret codes

StretchHuman

Kai sends KHOOR ZRUOG using a Caesar shift. "Nobody can read it," he says. "It looks like complete nonsense!" Is his message safe?

Choose one

9. Secret codes

StretchFormal

In Lean, caesar k message encodes a message with a Caesar shift by k. Which statement says that shifting by 26 − k decodes every message encoded with key k (for any key k from 0 to 26)?

Choose one

10. Secret codes

StretchComputational

Write a function shift_letter(letter, k) that shifts one capital letter k places forward, wrapping round after Z. For example shift_letter("A", 3) is "D" and shift_letter("Y", 3) is "B". Use the string ALPHABET: ALPHABET.index(letter) gives the letter's number (A = 0), and ALPHABET[n] gives the letter with number n. In Python, n % 26 is the remainder when n is divided by 26.

Runs entirely in your browser, in an isolated worker. Nothing is sent to a server.
Common misconceptionsH

Things that trip people up

Misconception

"It looks scrambled, so it's safe." A Caesar shift looks like nonsense, but it has only 25 keys that change anything, and anyone can try them all. A substitution cipher has a huge number of keys, but letter counting breaks it anyway. Looking scrambled and being safe are very different things.

Misconception

Forgetting to wrap round. With a shift of 3, Z does not become a mystery symbol — it becomes C. On the alphabet wheel, after Z comes A again.

Misconception

Decoding in the wrong direction. To decode a shift of 3 forward, go 3 places back (or 23 places forward). Going 3 more places forward just encodes the message twice.

Misconception

"The method must be secret." Everyone knows how Caesar shifts work, and that is fine: what must be secret is the key. Since the 19th century, code designers have aimed for systems that stay safe even when the enemy knows exactly how they work — as long as the key is secret. A Caesar shift fails that test because its keys are too few to hide.

ViewpointsH

Other ways of seeing

Number theory— The number theorist: arithmetic on a wheel

A Caesar shift is addition on a wheel of 26 numbers: you add the key and keep only the remainder after dividing by 26. Mathematicians call this modular arithmetic, and you can meet it in clock arithmetic. It leads to new ciphers. Try multiplying the letter numbers by 3 (and keeping the remainder): A stays A, B becomes D, C becomes G, … — and you can check that the 26 letters all get different code letters, so this cipher can be decoded. Multiplying by 2 does not work: N is 13, and 2×13=262 \times 13 = 26, which is the same as 0, so A and N would both become A. The multipliers that work are exactly the numbers that share no factor with 26 except 1.

Combinatorics— Counting keys

Counting the keys tells you how a cipher can be attacked. A Caesar shift has 26 keys, so you can try them all. A substitution cipher has 26×25×⋯×1=26!26 \times 25 \times \dots \times 1 = 26! keys — counted with the product rule from systematic listing — far too many to try. That is why its weakness had to be found somewhere else: in the letter counts. Modern ciphers have so many keys and hide the letter counts so well that the best known attacks are hopeless.

Set-theoretic connectionH

A cipher is a perfect pairing

A cipher's key pairs up the alphabet with itself, so that every letter has exactly one code letter, and every code letter comes from exactly one letter. In the language of sets and functions, it is a one-to-one correspondence (a bijection) from the set of 26 letters to itself. Decoding is the inverse pairing: follow every arrow backwards. And doing one shift and then another is a chain of actions, just like those in One thing after another; undoing a shift by kk with a shift by 26−k26 - k is like undoing a quarter turn with three more quarter turns.

Wonder Trails

Wonder Trails

History & global context

History and global context

  • Rome. The writer Suetonius (about 121 CE) reports that Julius Caesar wrote secret letters with a shift of three, and that the emperor Augustus used a shift of one.
  • Baghdad. In the 9th century the scholar Abū Yūsuf Yaʿqūb al-Kindī wrote A Manuscript on Deciphering Cryptographic Messages, the earliest known description of breaking codes by counting how often letters appear — frequency analysis.
  • Scotland and England. In 1586 Mary, Queen of Scots, wrote coded letters from prison. Thomas Phelippes, a code breaker working for Queen Elizabeth I's government, used frequency analysis to read them. The letters were used as evidence against her, and she was executed in 1587.
  • A secret that need not be secret. In 1883 the Dutch-born cryptographer Auguste Kerckhoffs, working in France, wrote that a military cipher should stay safe even if the enemy captures the method: only the key should need to be secret.
  • Codes that were never broken. In the Second World War, Navajo Code Talkers in the United States Marine Corps sent messages in a code based on the Navajo language. It was never broken.
  • Machines. The German Enigma machine was first broken in 1932 by the Polish mathematicians Marian Rejewski, Jerzy Różycki and Henryk Zygalski. In 1939 they shared their methods with Britain and France, and at Bletchley Park in England a team including Alan Turing broke Enigma messages throughout the war.
  • Codes for everyone. In 1976 Whitfield Diffie and Martin Hellman, and in 1977 Ron Rivest, Adi Shamir and Leonard Adleman, published new kinds of ciphers whose safety comes from number theory — ideas found a few years earlier, in secret, by James Ellis, Clifford Cocks and Malcolm Williamson at Britain's GCHQ. These ideas protect messages and payments on the internet today; see Cryptography.
Further reading

For grown-ups: further reading

  • Simon Singh, The Code Book (1999), and its version for young readers, The Code Book: How to Make It, Break It, Hack It, Crack It (2001) — the history of codes from Caesar to the internet.
  • Janet Beissinger and Vera Pless, The Cryptoclub: Using Mathematics to Make and Break Secret Codes (2006) — a book written for children aged about 10–14, with activities.
  • David Kahn, The Codebreakers (1967) — the classic, detailed history of cryptography.