Students build a challenge-and-response lock so that a heard answer cannot be used again, then add a lockout after three wrong answers.
Load these into Waypoints Prepare: choose Load URL and paste the copied address, or download the file and import it. In Prepare, the worksheet's Teacher edition shows the answers; the student copy hides them.
Predict and Run 10 min · Investigate 10 min · Modify 8 min · Make 18 min
The idea: to prove you know a secret without saying it, the other side sets a random question, the challenge, and you answer it using the secret. A fresh challenge each time means an old answer fits only the old challenge.
Everyday picture: a bank clerk who asks for a random character of your password each time. Where it breaks: if the same question comes up, the answer works again; the challenge must be random and not repeat.
Honest limits: the answer function here, (n × key + 3) mod 100, is a toy: someone who collects enough challenges and answers can work out the key, and with only 90 possible challenges they repeat soon. Real systems use a hash function (a one-way mix) and huge challenges. Tell students the toy is for learning the shape of the idea.
Same program on both micro:bits: the simulator runs two copies. The one where A is pressed acts as the key; the other acts as the lock.
Why a fixed password fails once heard, and a fresh challenge doesn't. Ask what a listener could reuse, and whether the answer ever repeats.
| What you will see | A question to ask | The fix |
|---|---|---|
| The lock always shows a cross. | Do both micro:bits have the same secretKey? Is the answer function the same? | Use the same secretKey and answer function on both. |
| The tick appears on the wrong micro:bit. | Which micro:bit checks the answer? | The micro:bit that picked the challenge also checks the answer; the knocker only answers. |
One possible solution for each task. Other correct programs are fine.

let secretKey = 7
let challenge = 0
let opened = 0
function answer(n: number): number {
return (n * secretKey + 3) % 100
}
radio.setGroup(5)
input.onButtonPressed(Button.A, function () {
radio.sendString("KNOCK")
})
input.onButtonPressed(Button.B, function () {
basic.showNumber(opened)
})
radio.onReceivedString(function (receivedString) {
if (receivedString == "KNOCK") {
challenge = randint(10, 99)
radio.sendValue("chall", challenge)
}
})
radio.onReceivedValue(function (name, value) {
if (name == "chall") {
radio.sendValue("answer", answer(value))
} else if (name == "answer") {
if (value == answer(challenge)) {
opened += 1
basic.showIcon(IconNames.Yes)
} else {
basic.showIcon(IconNames.No)
}
}
})

let secretKey = 7
let challenge = 0
let wrong = 0
function answer(n: number): number {
return (n * secretKey + 3) % 100
}
radio.setGroup(5)
input.onButtonPressed(Button.A, function () {
radio.sendString("KNOCK")
})
radio.onReceivedString(function (receivedString) {
if (receivedString == "KNOCK") {
if (wrong >= 3) {
basic.showIcon(IconNames.Skull)
basic.pause(5000)
wrong = 0
} else {
challenge = randint(10, 99)
radio.sendValue("chall", challenge)
}
}
})
radio.onReceivedValue(function (name, value) {
if (name == "chall") {
radio.sendValue("answer", answer(value))
} else if (name == "answer") {
if (value == answer(challenge)) {
wrong = 0
basic.showIcon(IconNames.Yes)
} else {
wrong += 1
basic.showIcon(IconNames.No)
}
}
})
Stretch: Make the challenge 3 digits and store it as a number. Why does a longer challenge repeat less often?
Support: Give the lockout in outline: students fill in the conditions.
Why does a challenge and response protect a lock better than a fixed password?
The answer depends on a fresh challenge, so an answer that was heard before no longer fits.
Next: capture-the-flag puzzles, then defend a whole system.