← Secret messages

Capture the flag

Solve three puzzles that use the ciphers and checks you have built. Each answer is a flag you type in to prove you solved it.

Capture-the-flag contests are a favourite way for people to learn security: puzzle after puzzle, each hiding a flag. Solving them is how you get a feel for what makes a code strong or weak.

A flag is a secret answer, in the form WP{...}, that proves you solved a puzzle. Each puzzle uses something you built in this course. The page keeps only a scrambled fingerprint of each right answer, so you have to solve them.

PredictRead the code and say what it will do before you run anything. A wrong guess is fine: it shows you what to look for.

Predict

The program as blocks
Show as code
toolkit.ts
let ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
let guess = 0
let number = 0
let which = 0
let codes = [26, 12, 10, 27, 12, 29]
let messages = ["HELLO", "MEET", "KEY", "LOCK"]
function shift(letter: string, key: number): string {
    let place = ALPHABET.indexOf(letter)
    if (place < 0) {
        return letter
    }
    return ALPHABET.charAt((place + key) % 26)
}
function decrypt(message: string, key: number): string {
    let result = ""
    for (let i = 0; i < message.length; i++) {
        result = result + shift(message.charAt(i), 26 - key)
    }
    return result
}
function checksum(message: string): number {
    let total = 0
    for (let i = 0; i < message.length; i++) {
        total += ALPHABET.indexOf(message.charAt(i))
    }
    return total % 10
}
// A tries the next Caesar key on puzzle 1
input.onButtonPressed(Button.A, function () {
    guess += 1
    if (guess > 25) {
        guess = 1
    }
    basic.showNumber(guess)
    basic.showString(decrypt("RIZIV WIRH OICW", guess))
})
// B shows the next puzzle 2 number after XOR with a key (change the 9 to try another key)
input.onButtonPressed(Button.B, function () {
    number = codes[which] ^ 9
    which = (which + 1) % codes.length
    basic.showNumber(number)
})
// A+B shows the checksum of the next puzzle 3 message
input.onButtonPressed(Button.AB, function () {
    basic.showString(messages[which])
    basic.showNumber(checksum(messages[which]))
    which = (which + 1) % messages.length
})
The toolkit's checksum adds each letter's place (A is 0) and keeps the last digit. Press A+B on the first message, HELLO. What checksum does it show?

RunRun it and compare the result with your prediction.

Run

The toolkit has a tool for each puzzle: A tries the next Caesar key on puzzle 1; B shows the next puzzle 2 number after XOR with a key; A+B shows the checksum of the next puzzle 3 message.

The program as blocks
Show as code
toolkit.ts
let ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
let guess = 0
let number = 0
let which = 0
let codes = [26, 12, 10, 27, 12, 29]
let messages = ["HELLO", "MEET", "KEY", "LOCK"]
function shift(letter: string, key: number): string {
    let place = ALPHABET.indexOf(letter)
    if (place < 0) {
        return letter
    }
    return ALPHABET.charAt((place + key) % 26)
}
function decrypt(message: string, key: number): string {
    let result = ""
    for (let i = 0; i < message.length; i++) {
        result = result + shift(message.charAt(i), 26 - key)
    }
    return result
}
function checksum(message: string): number {
    let total = 0
    for (let i = 0; i < message.length; i++) {
        total += ALPHABET.indexOf(message.charAt(i))
    }
    return total % 10
}
// A tries the next Caesar key on puzzle 1
input.onButtonPressed(Button.A, function () {
    guess += 1
    if (guess > 25) {
        guess = 1
    }
    basic.showNumber(guess)
    basic.showString(decrypt("RIZIV WIRH OICW", guess))
})
// B shows the next puzzle 2 number after XOR with a key (change the 9 to try another key)
input.onButtonPressed(Button.B, function () {
    number = codes[which] ^ 9
    which = (which + 1) % codes.length
    basic.showNumber(number)
})
// A+B shows the checksum of the next puzzle 3 message
input.onButtonPressed(Button.AB, function () {
    basic.showString(messages[which])
    basic.showNumber(checksum(messages[which]))
    which = (which + 1) % messages.length
})
Try it yourselfYour edits are saved in this browser.
It takes a few seconds to load. Your work in this step is kept.

InvestigateTrace how each line of code connects to what you see.

Investigate

The program as blocks
Show as code
toolkit.ts
let ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
let guess = 0
let number = 0
let which = 0
let codes = [26, 12, 10, 27, 12, 29]
let messages = ["HELLO", "MEET", "KEY", "LOCK"]
function shift(letter: string, key: number): string {
    let place = ALPHABET.indexOf(letter)
    if (place < 0) {
        return letter
    }
    return ALPHABET.charAt((place + key) % 26)
}
function decrypt(message: string, key: number): string {
    let result = ""
    for (let i = 0; i < message.length; i++) {
        result = result + shift(message.charAt(i), 26 - key)
    }
    return result
}
function checksum(message: string): number {
    let total = 0
    for (let i = 0; i < message.length; i++) {
        total += ALPHABET.indexOf(message.charAt(i))
    }
    return total % 10
}
// A tries the next Caesar key on puzzle 1
input.onButtonPressed(Button.A, function () {
    guess += 1
    if (guess > 25) {
        guess = 1
    }
    basic.showNumber(guess)
    basic.showString(decrypt("RIZIV WIRH OICW", guess))
})
// B shows the next puzzle 2 number after XOR with a key (change the 9 to try another key)
input.onButtonPressed(Button.B, function () {
    number = codes[which] ^ 9
    which = (which + 1) % codes.length
    basic.showNumber(number)
})
// A+B shows the checksum of the next puzzle 3 message
input.onButtonPressed(Button.AB, function () {
    basic.showString(messages[which])
    basic.showNumber(checksum(messages[which]))
    which = (which + 1) % messages.length
})

Puzzle 1. A Caesar cipher: RIZIV WIRH OICW. Find the message. The flag is WP{ then the message in lower case, then }.

Puzzle 2. These numbers were each locked with XOR and the key 9: 26, 12, 10, 27, 12, 29. Unlock them (B does this). Then turn each number into a letter, where 1 is A, 2 is B and so on. The flag is WP{ then the word in lower case, then }.

Puzzle 3. Four messages arrived with their checksums: HELLO 7, MEET 9, KEY 3, LOCK 7. One was damaged on the way. Which? The flag is WP{ then that word in lower case, then }.

MakeBuild something new using what you have learned. Expect a few tries; that is normal.

Make

Make a puzzle for a partner: A shows a secret message that you encrypted with a Caesar key. Keep the key to yourself. Then solve your partner's.

Try it yourselfYour edits are saved in this browser.
It takes a few seconds to load. Your work in this step is kept.
Need a hint?

Use encrypt with a key you choose, and show the cipher text with basic.showString. Make sure your puzzle has a clear answer.

A puzzle for a partner: A shows a message you have encrypted.

This ticks by itself when your blocks do what is asked.

For teachers

Students solve three capture-the-flag puzzles (Caesar, XOR, checksum), typing flags that the page checks without storing the answers, then write a puzzle for a partner. About 45 minutes.

Open the teacher guide: answers, solutions, slides and a worksheet.

Students will be able to

Before class

What this is: a puzzle session that revisits the course's three tools. Students decrypt messages written for them, as in a puzzle book or a CTF contest. No one is breaking into anything.

How flags work: the page stores only a SHA-256 fingerprint (a hash) of each right answer, so reading the page source does not give the answer away. Answers are checked after trimming spaces and ignoring capitals. Because this repository is public, the flags are also written here for you:

Pacing: puzzles can be done in any order. Pairs can race; the flag checks give instant feedback so nobody has to wait for you.

Running the lesson

  1. Puzzle 2 is the slowest because of the second step (numbers to letters). Hint: A is 1.
  2. Puzzle 3: ask which message's own checksum disagrees with the number it came with.

The hard part (on purpose)

Choosing the right tool for each puzzle. Ask which weakness the puzzle shows before they reach for code.

When students get stuck

Stretch and support

Stretch: Write a longer puzzle that uses two steps (Caesar, then XOR).

Support: Share puzzle 1 as a class, then let pairs do the others.

Where it leads

Next: defend a system against all three weaknesses.

What each step is for

Now you can
Next lesson10. Defend the door lockA radio door lock has three weaknesses. Find each one, name the tool from this course that fixes it, and build the fixes in milestones.