34 lines
1.3 KiB
Python
34 lines
1.3 KiB
Python
|
|
#!/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)))
|