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.
Make your prediction first. Help opens once you've locked it in.

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 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.

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
})

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 }.
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.
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.
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.
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.
Choosing the right tool for each puzzle. Ask which weakness the puzzle shows before they reach for code.
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.
Next: defend a system against all three weaknesses.