225 lines
7.3 KiB
Python
Executable File
225 lines
7.3 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Visual Cryptograpy Experiment.
|
|
|
|
Create two paper cards (credit card size) which reveal the plaintext message when put over each other.
|
|
"""
|
|
|
|
import argparse
|
|
import secrets
|
|
from HersheyFonts import HersheyFonts
|
|
import secrets
|
|
import svgwrite
|
|
from svgwrite import cm, mm
|
|
from bdfparser import Font
|
|
import sys
|
|
import string
|
|
import math
|
|
import mnemonic
|
|
|
|
import xml.etree.ElementTree as ET
|
|
|
|
STROKE_WIDTH = 0.2
|
|
CARD_WIDTH = 85.6
|
|
CARD_HEIGHT = 53.98
|
|
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("output")
|
|
parser.add_argument("--stroke-width", type=float, default=STROKE_WIDTH)
|
|
parser.add_argument("--header-text", default="3x3 Glyph Table Page {page}")
|
|
parser.add_argument("--header-mnemonic", action="store_true")
|
|
parser.add_argument("--card-width", type=float, default=CARD_WIDTH)
|
|
parser.add_argument("--card-height", type=float, default=CARD_HEIGHT)
|
|
parser.add_argument("--font", default="3x3.bdf")
|
|
args = parser.parse_args()
|
|
|
|
|
|
STROKE_WIDTH = args.stroke_width
|
|
|
|
data = {}
|
|
|
|
|
|
dwg = svgwrite.Drawing(
|
|
args.output, ("210mm", "297mm"), profile="tiny", viewBox="0 0 210 297"
|
|
)
|
|
|
|
|
|
|
|
card_offset = 5*9 #((args.card_height + 10) // 10) * 10
|
|
|
|
y = 0
|
|
|
|
thefont = HersheyFonts()
|
|
thefont.load_default_font("futural")
|
|
thefont.normalize_rendering(5)
|
|
|
|
# do not use a word twice
|
|
used_words = set()
|
|
|
|
for page in range(1, 4):
|
|
margin_left = 10
|
|
|
|
for i in range(2):
|
|
binstr = f"{page:02b}"
|
|
r = dwg.rect(
|
|
(2 + i * 4, y+5),
|
|
(3, 3),
|
|
stroke="black",
|
|
stroke_width=STROKE_WIDTH,
|
|
fill="none" if binstr[i] == "0" else "black",
|
|
)
|
|
dwg.add(r)
|
|
|
|
thefont.normalize_rendering(2)
|
|
offsetx = 0.5
|
|
offsety = 7.8
|
|
text = args.header_text
|
|
|
|
for stroke in thefont.strokes_for_text(text.format(page=page)):
|
|
for j, point in enumerate(stroke):
|
|
stroke[j] = (margin_left + offsetx + point[0], y + offsety - point[1])
|
|
pl = dwg.polyline(stroke, stroke="black", stroke_width=STROKE_WIDTH, fill="none")
|
|
dwg.add(pl)
|
|
|
|
w = 9
|
|
h = 9
|
|
|
|
if args.header_mnemonic:
|
|
words = set()
|
|
m = mnemonic.Mnemonic("english")
|
|
for word in m.wordlist:
|
|
if len(word) <= 5:
|
|
words.add(word.upper())
|
|
for i in range(8):
|
|
choice = secrets.choice(list(words - used_words))
|
|
used_words.add(choice)
|
|
|
|
minx = 9999
|
|
maxx = 0
|
|
for stroke in thefont.strokes_for_text(choice):
|
|
for j, point in enumerate(stroke):
|
|
minx = min(minx, point[0])
|
|
maxx = max(maxx, point[0])
|
|
width = (maxx - minx) + STROKE_WIDTH
|
|
for stroke in thefont.strokes_for_text(choice):
|
|
for j, point in enumerate(stroke):
|
|
stroke[j] = (margin_left + (i*w) + ((w-width)/2) + point[0], y + offsety - point[1])
|
|
pl = dwg.polyline(stroke, stroke="black", stroke_width=STROKE_WIDTH, fill="none")
|
|
dwg.add(pl)
|
|
|
|
font = Font(args.font)
|
|
for row in range(4):
|
|
|
|
for i in range(2):
|
|
binstr = f"{row:02b}"
|
|
r = dwg.rect(
|
|
(2 + (i*4), y + 4.25 + ((row+1)*h)),
|
|
(3, 3),
|
|
stroke="black",
|
|
stroke_width=STROKE_WIDTH,
|
|
fill="none" if binstr[i] == "0" else "black",
|
|
)
|
|
dwg.add(r)
|
|
|
|
words = set()
|
|
m = mnemonic.Mnemonic("english")
|
|
for word in m.wordlist:
|
|
if len(word) <= 4:
|
|
words.add(word.upper())
|
|
|
|
choice = secrets.choice(list(words - used_words))
|
|
used_words.add(choice)
|
|
|
|
for stroke in thefont.strokes_for_text(choice):
|
|
for j, point in enumerate(stroke):
|
|
stroke[j] = (2 + point[0], y + 3.75 + ((row+1)*h) - point[1])
|
|
pl = dwg.polyline(stroke, stroke="black", stroke_width=STROKE_WIDTH, fill="none")
|
|
dwg.add(pl)
|
|
|
|
|
|
for col in range(8):
|
|
char = chr((page * 32) + ((row) * 8) + col)
|
|
glyph = font.glyph(char)
|
|
|
|
pw = 1
|
|
ph = 1
|
|
offsetx = 1
|
|
offsety = 1
|
|
tl = (margin_left + offsetx + col * w -0.5, y + offsety + ((row+1) * h) -0.5)
|
|
lr = (margin_left + offsetx + col * w + (3*pw)+0.5, y + offsety + ((row+1) * h) + (3*ph) +0.5 )
|
|
lw = 0.6
|
|
for coords in [(tl, (tl[0] + lw, tl[1])), (tl, (tl[0], tl[1] + lw)), (lr, (lr[0] - lw, lr[1])), (lr, (lr[0], lr[1]-lw)),
|
|
((lr[0], tl[1]), (lr[0]-lw, tl[1])),
|
|
((lr[0], tl[1]), (lr[0], tl[1]+lw)),
|
|
((tl[0], lr[1]), (tl[0]+lw, lr[1])),
|
|
((tl[0], lr[1]), (tl[0], lr[1]-lw)),
|
|
]:
|
|
l = dwg.line(
|
|
coords[0],
|
|
coords[1],
|
|
stroke="black",
|
|
stroke_width=STROKE_WIDTH,
|
|
)
|
|
dwg.add(l)
|
|
if glyph:
|
|
data = glyph.draw().todata()
|
|
for i, line in enumerate(data):
|
|
for j, pixel in enumerate(line):
|
|
if i <= 2 and j <= 2:
|
|
if pixel == "1":
|
|
p = dwg.rect(
|
|
(margin_left + offsetx + col * w + (pw * j), y + offsety + ((row+1) * h) + (ph * i)),
|
|
(pw, ph),
|
|
stroke="none",
|
|
fill="black" if pixel == "1" else "none",
|
|
)
|
|
dwg.add(p)
|
|
|
|
# char
|
|
thefont.normalize_rendering(4)
|
|
offsetx = 5
|
|
offsety = 3
|
|
for stroke in thefont.strokes_for_text(char):
|
|
for j, point in enumerate(stroke):
|
|
stroke[j] = (margin_left + offsetx + col * w + point[0], y + offsety + + ((row+1) * h) + h/2 - point[1])
|
|
|
|
pl = dwg.polyline(stroke, stroke="black", stroke_width=STROKE_WIDTH, fill="none")
|
|
dwg.add(pl)
|
|
|
|
# decimal value of char
|
|
thefont.normalize_rendering(2)
|
|
offsetx = 0.5
|
|
offsety = 0
|
|
for stroke in thefont.strokes_for_text(str(ord(char))):
|
|
for j, point in enumerate(stroke):
|
|
stroke[j] = (margin_left + offsetx + col * w + point[0], y + offsety + ((row+1) * h) + h - point[1])
|
|
|
|
pl = dwg.polyline(stroke, stroke="black", stroke_width=STROKE_WIDTH, fill="none")
|
|
dwg.add(pl)
|
|
|
|
# char box
|
|
r = dwg.rect(
|
|
(margin_left + col * w, y + ((row+1) * h)),
|
|
(w, h),
|
|
stroke="black",
|
|
stroke_width=STROKE_WIDTH,
|
|
fill="none",
|
|
)
|
|
dwg.add(r)
|
|
|
|
if row == 3:
|
|
for i in range(3):
|
|
binstr = f"{col:03b}"
|
|
r = dwg.rect(
|
|
(margin_left + (col*w) + 1 + (i*2.5), y + 10 + ((row+1)*h)),
|
|
(2, 2),
|
|
stroke="black",
|
|
stroke_width=STROKE_WIDTH,
|
|
fill="none" if binstr[i] == "0" else "black",
|
|
)
|
|
dwg.add(r)
|
|
|
|
y += card_offset
|
|
|
|
dwg.save()
|