Pick a depth. Each prompt opens in your AI pre-loaded with the lesson. Click a row to preview the prompt.
The one-time pad is the only encryption scheme in this course that is provably unbreakable — Shannon proved in 1949 that with a uniformly random key as long as the message, used exactly once, ciphertext gives an attacker zero information about the plaintext. So why doesn't everyone just use it? Because the conditions are brutal: keys must be truly random, as long as the message, never reused, and securely distributed before communication — which is the same problem encryption was supposed to solve. The OTP is therefore the theoretical north star against which all practical ciphers are measured. AES with a 256-bit key approximates the OTP: it stretches a short key into a long pseudorandom keystream and XORs. The entire field of symmetric cryptography is the search for ciphers that are 'OTP-like enough' under realistic assumptions about computational hardness.
The OTP encrypts by XORing the plaintext with a key of equal length. Decryption applies the same XOR. Because XOR is its own inverse and a uniformly random key bit makes the ciphertext bit uniformly random, no information about the plaintext leaks.
// main.go
package main
import (
"crypto/rand"
"fmt"
)
func otpEncrypt(plaintext, key []byte) []byte {
if len(key) != len(plaintext) {
panic("key must equal plaintext length")
}
out := make([]byte, len(plaintext))
for i := range plaintext {
out[i] = plaintext[i] ^ key[i]
}
return out
}
func otpDecrypt(ciphertext, key []byte) []byte {
return otpEncrypt(ciphertext, key) // XOR is its own inverse
}
func main() {
// Generate a fresh, truly random key
pt := []byte("ATTACK AT DAWN")
key := make([]byte, len(pt))
rand.Read(key)
ct := otpEncrypt(pt, key)
fmt.Printf("ciphertext: %x\n", ct)
fmt.Printf("recovered : %s\n", otpDecrypt(ct, key))
// DANGER: key reuse breaks security completely
pt1 := []byte("HELLO WORLD123")
pt2 := []byte("ATTACK AT DAWN")
ct1 := otpEncrypt(pt1, key)
ct2 := otpEncrypt(pt2, key)
// An attacker who sees both can compute pt1 XOR pt2 without the key:
xorOfPlaintexts := make([]byte, len(ct1))
for i := range ct1 {
xorOfPlaintexts[i] = ct1[i] ^ ct2[i]
}
fmt.Printf("pt1 XOR pt2 leaked: %x\n", xorOfPlaintexts)
}go run main.go