feat(cipher): add manual OTP decryption instructions and pad generator
- Expand instructions.md with step-by-step manual encryption/decryption example using modular addition. - Add padgen.py script to generate random one-time pads from /dev/urandom, formatted in groups. - Add cleanup of temporary audio file in numbers_station.sh. - Minor title update in site/index.html for clarity.
This commit is contained in:
parent
33c9b16267
commit
135cf1ff01
@ -1,6 +1,41 @@
|
|||||||
## HOW TO DECRYPT — STEP BY STEP (using your 8-bit machine)
|
### HOW TO DECRYPT — STEP BY STEP (by hand)
|
||||||
|
|
||||||
1. Type in the NERDLETTER DECRYPTOR BASIC program (see below)
|
1. **Key Generation**
|
||||||
|
- You generate a **truly random key** that is **exactly as long as the plaintext message**.
|
||||||
|
- The key must be purely random (each bit or character has equal probability and no patterns).
|
||||||
|
- The key is shared securely between sender and receiver **in advance** (this is the hard part in practice).
|
||||||
|
|
||||||
|
2. **Encryption**
|
||||||
|
- Take the plaintext message.
|
||||||
|
- Combine it with the key using a reversible operation. The two most common methods are:
|
||||||
|
- **Bitwise XOR** (for binary data — the standard modern way)
|
||||||
|
- **Modular addition** (for letters, e.g., A=0, B=1, ..., Z=25 — the classic "pad" method)
|
||||||
|
|
||||||
|
**Example with letters (Vigenère-style addition mod 26):**
|
||||||
|
|
||||||
|
Plaintext: `H E L L O`
|
||||||
|
→ Numbers: `7 4 11 11 14`
|
||||||
|
|
||||||
|
One-time pad: `X M C K L`
|
||||||
|
→ Numbers: `23 12 2 10 11`
|
||||||
|
|
||||||
|
Ciphertext: `(7+23) (4+12) (11+2) (11+10) (14+11) mod 26`
|
||||||
|
→ `4 16 13 21 25`
|
||||||
|
→ Letters: `E Q N V Z`
|
||||||
|
|
||||||
|
3. **Decryption**
|
||||||
|
- The receiver does the **inverse operation** with the same key.
|
||||||
|
- For modular addition: subtract the key (mod 26).
|
||||||
|
- For XOR: XOR the ciphertext with the key again (XOR is its own inverse).
|
||||||
|
|
||||||
|
Ciphertext: `E Q N V Z`
|
||||||
|
Key: `X M C K L`
|
||||||
|
Plaintext: `H E L L O`
|
||||||
|
|
||||||
|
|
||||||
|
### HOW TO DECRYPT — STEP BY STEP (using your 8-bit machine)
|
||||||
|
|
||||||
|
1. Type in the NERDLETTER DECRYPTOR BASIC program (provided with your subscription)
|
||||||
2. RUN
|
2. RUN
|
||||||
3. When it asks, type the PAD groups from the appropriate page:
|
3. When it asks, type the PAD groups from the appropriate page:
|
||||||
|
|
||||||
@ -12,7 +47,7 @@
|
|||||||
|
|
||||||
5. The machine will instantly print the plaintext!
|
5. The machine will instantly print the plaintext!
|
||||||
|
|
||||||
## RESULT YOU SHOULD SEE:
|
#### RESULT YOU SHOULD SEE:
|
||||||
|
|
||||||
```
|
```
|
||||||
MARYHADALITTLELAMB
|
MARYHADALITTLELAMB
|
||||||
|
|||||||
34
cipher/padgen.py
Normal file
34
cipher/padgen.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Generate a one-time pad of uppercase letters from /dev/urandom."""
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def generate_daybook(weeks: int = 52):
|
||||||
|
for week in range(1, weeks + 1):
|
||||||
|
with open(f"daybook_week_{week:02d}.txt", "w") as f:
|
||||||
|
f.write(f"WEEK {week:02d}\n\n")
|
||||||
|
f.write(format_pad(generate_pad(500)))
|
||||||
|
|
||||||
|
def generate_pad(num_letters: int) -> str:
|
||||||
|
"""Read raw bytes from /dev/urandom and map them uniformly to A-Z."""
|
||||||
|
letters = []
|
||||||
|
with open("/dev/urandom", "rb") as f:
|
||||||
|
while len(letters) < num_letters:
|
||||||
|
b = f.read(1)[0]
|
||||||
|
# Reject bytes >= 234 to avoid modulo bias.
|
||||||
|
# 234 = 9 * 26, so bytes 0..233 map uniformly to 0..25.
|
||||||
|
if b < 234:
|
||||||
|
letters.append(chr((b % 26) + 65))
|
||||||
|
return "".join(letters)
|
||||||
|
|
||||||
|
|
||||||
|
def format_pad(text: str, group_size: int = 5, groups_per_line: int = 10) -> str:
|
||||||
|
"""Format as 5-letter groups, 10 groups per line — classic OTP layout."""
|
||||||
|
groups = [text[i:i + group_size] for i in range(0, len(text), group_size)]
|
||||||
|
lines = [" ".join(groups[i:i + groups_per_line])
|
||||||
|
for i in range(0, len(groups), groups_per_line)]
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
n = int(sys.argv[1]) if len(sys.argv) > 1 else 500
|
||||||
|
print(format_pad(generate_pad(n)))
|
||||||
@ -119,4 +119,6 @@ drawtext=font='Courier':fontsize=44:fontcolor=lime@0.95:text='${MESSAGE}':x=(w-t
|
|||||||
drawtext=font='Courier':fontsize=30:fontcolor=green@0.75:text='DESTROY PAGE AFTER USE — SUBSCRIBERS ONLY':x=(w-text_w)/2:y=640" \
|
drawtext=font='Courier':fontsize=30:fontcolor=green@0.75:text='DESTROY PAGE AFTER USE — SUBSCRIBERS ONLY':x=(w-text_w)/2:y=640" \
|
||||||
-c:v libx264 -pix_fmt yuv420p -c:a aac -shortest "$OUTFILE" -y
|
-c:v libx264 -pix_fmt yuv420p -c:a aac -shortest "$OUTFILE" -y
|
||||||
|
|
||||||
|
rm "${TMPDIR}/final_audio.wav"
|
||||||
|
|
||||||
echo "Done: ${OUTFILE}"
|
echo "Done: ${OUTFILE}"
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>The Nerdletter • Retro-Modern Fusion</title>
|
<title>The Nerdletter • Retro-Modern Fusion Newsletter</title>
|
||||||
<style>
|
<style>
|
||||||
@import url('https://fonts.googleapis.com/css2?family=VT323&display=swap'); /* Perfect CRT font */
|
@import url('https://fonts.googleapis.com/css2?family=VT323&display=swap'); /* Perfect CRT font */
|
||||||
body {
|
body {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user