add PDF worksheet

This commit is contained in:
Peter D. Gray 2021-04-28 14:28:31 -04:00
parent cf1e05b222
commit bd2b9107f8
No known key found for this signature in database
GPG Key ID: F0E6CC6AFC16CF7B
5 changed files with 310 additions and 50 deletions

View File

@ -1,7 +1,6 @@
# BIP39 Wordlist on Paper
Uses python's "reportlab" to make a PDF file of 4 pages, containing every English
Use python's "reportlab" to make a PDF file of 4 pages, containing every English
BIP-39 seed word, and their hex value.
Could be cut into squares for drawing from a hat... or for looking up values to do
@ -11,13 +10,23 @@ Hexidecimal number that each words represents is shown in the corner.
Look here for the final PDF ready to go: [wordlist.pdf](wordlist.pdf)
# Seed XOR Worksheets and Explainer
This repo is also holding some code to explain how to XOR two or more
This repo is also holds some code to explain how to XOR two or more
seed phrases together, using your hands... fully airgapped.
See [xor-example.md](xor-example.md)
The worksheet to go with this is still in progress.
To follow along, or to use this paper method yourself, you may want to use this worksheet:
[worksheet.pdf](worksheet.pdf)
You will need the wordlist as reference as well.
# Security
Be sure to completely destroy any paper materials used in your work. They
contain your master secret and could be used to steal your funds! It's best
to burn them after use.

166
pdf-worksheet.py Normal file
View File

@ -0,0 +1,166 @@
# print a large worksheet
from reportlab.lib import colors
from reportlab.lib.pagesizes import letter, landscape
from reportlab.lib.units import inch
from reportlab.lib.enums import *
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from mnemonic import Mnemonic
wordlist = Mnemonic('english').wordlist
styleSheet = getSampleStyleSheet()
labelStyle = ParagraphStyle('llab', fontFace='courier', alignment=TA_RIGHT)
labelStyleCenter = ParagraphStyle('tlab', fontFace='courier', alignment=TA_CENTER)
commentStyle = ParagraphStyle('com', alignment=TA_CENTER)
hex_style = ParagraphStyle('tlab', fontName='Courier-Bold', alignment=TA_LEFT, fontSize=6, spaceAfter=2, spaceBefore=0, leading=4)
word_style = ParagraphStyle('cell', alignment=TA_CENTER, fontSize=8, spaceBefore=0, spaceAfter=0, leading=8)
def cell(w):
assert 0 <= w < 0x800
word = wordlist[w]
hex = ('%03x' % w).upper()
#rv = Paragraph(f'{word}\n<br/><font face="courier">{hex}</font>', cellStyle)
rv = []
rv.append(Paragraph(hex, hex_style))
rv.append(Paragraph(word, word_style))
return rv
def left_label(x):
return Paragraph(x, labelStyle)
def top_label(x):
return Paragraph(x, labelStyleCenter)
def doit(fname='worksheet.pdf'):
doc = SimpleDocTemplate(fname, pagesize=landscape(letter))
doc.leftMargin = doc.rightMargin = \
doc.topMargin = doc.bottomMargin = 0.1 * inch
# container for the 'Flowable' objects
data = []
blanks = ['' for i in range(24*3)]
for part in 'ABCD':
row = [part, 'Word #']
for i in range(24):
row.append(str(i+1))
row.append('')
row.append('')
data.append(row)
data.append(['', 'Word'] + blanks)
data.append(['', 'Hex Digit'] + blanks)
if part != 'A':
if part == 'B':
xor = 'A⊕B'
elif part == 'C':
xor = '(A⊕B)⊕C'
else:
xor = '...⊕' + part
data.append([xor, ''] + blanks[:-2] + ['X', 'X'])
conf = [
#('BACKGROUND',(1,1),(-2,-2),colors.green),
#('TEXTCOLOR',(0,0),(0,-1),colors.red),
('VALIGN', (0,0),(0,-1), 'MIDDLE'),
#('ALIGN', (0,0),(0,-1), 'RIGHT'),
('ALIGN', (0,0),(0,1), 'CENTER'),
('ALIGN', (1,0),(2,-1), 'RIGHT'),
('ALIGN', (2,0),(-1,-1), 'CENTER'),
('VALIGN', (0,0),(0,-1), 'MIDDLE'),
('LEFTPADDING', (0,0),(-1,-1), 2),
('RIGHTPADDING', (0,0),(-1,-1), 2),
#('TOPPADDING', (0,0),(-1,-1), 0),
#('BOTTOMPADDING', (0,0),(-1,-1), 4.25),
('GRID', (0,0),(-1,-1), 0.5, colors.grey),
('LINEBEFORE', (2,0), (2, -1), 1.0, colors.grey),
]
for y in [0, 3, 3+4, 3+4+4]:
conf.extend([
('SPAN', (0,y), (0, y+2)),
('BACKGROUND', (0,y), (0, y+2), colors.lightgrey),
('LINEABOVE', (0,y), (-1, y), 1.0, colors.grey),
])
if y:
conf.extend([
('SPAN', (0,y+3), (1, y+3)),
('ALIGN', (0,y+3),(1,y+3), 'RIGHT'),
('LINEABOVE', (0,y+3), (-1, y+3), 1.0, colors.grey),
])
for x in range(24):
pos = 2 + (x*3)
conf.extend([
('SPAN', (pos,y), (pos+2, y)),
('SPAN', (pos,y+1), (pos+2, y+1)),
])
for x in range(3, 24*3, 6):
conf.extend([
('BACKGROUND', (2+x,0), (2+x+2, -1), colors.lightgrey),
])
W1 = 10
W2 = W1 + 2
t = Table(data, repeatRows=0, colWidths=[W2, None]+[W1 for i in range(24*3)])
t.setStyle(TableStyle(conf))
seed_samples = [[cell(w) for w in
sorted(list(range(0, 0x800, 0x100)) + [0, 1, 2, 3, 0x7fd, 0x7fe, 0x7ff])]]
t2 = Table(seed_samples, colWidths=50)
t2.setStyle(TableStyle([
('LEFTPADDING', (0,0),(-1,-1), 2),
('RIGHTPADDING', (0,0),(-1,-1), 2),
('TOPPADDING', (0,0),(-1,-1), 0),
('BOTTOMPADDING', (0,0),(-1,-1), 4.25),
('GRID', (0,0),(-1,-1), 0.5, colors.grey),
]),
)
hex_table = [[''] + ['%X'%i for i in range(16)]]
for y in range(16):
hex_table.append( ['%X'%y] + ['%X'%(x^y) for x in range(16)] )
t3 = Table(hex_table, colWidths=None)
t3s = TableStyle([
('ALIGN', (1,1),(-1,-1), 'CENTER'),
('ALIGN', (1,1),(-1,-1), 'RIGHT'),
('VALIGN', (0,0),(-1,-1), 'MIDDLE'),
('GRID', (0,0),(-1,-1), 0.5, colors.grey),
('FONT', (0,0), (-1, -1), 'Courier-Bold', 6),
('FONT', (1,1), (-1, -1), 'Courier', 6),
])
for x in range(0, 16, 2):
t3s.add('BACKGROUND', (2+x,0), (2+x, -1), colors.lightgrey)
t3.setStyle(t3s)
# page top-to-bottom
elements = []
elements.append(Paragraph('Seed XOR Worksheet',
ParagraphStyle('tlab2', alignment=TA_LEFT, fontSize=16, spaceAfter=20, spaceBefore=20)))
elements.append(t)
elements.append(Spacer(0, 20))
elements.append(t3)
if 1:
elements.append(Paragraph('IMPORTANT: After use, burn with fire!',
ParagraphStyle('tlab', alignment=TA_LEFT, fontSize=8, spaceAfter=2, spaceBefore=4)))
if 0:
elements.append(Spacer(0, 10))
elements.append(t2)
doc.build(elements)
doit()

86
worksheet.pdf Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,3 +1,42 @@
## XOR Lookup Table
| XOR | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F
|----:|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---
|**0**| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F
|**1**| 1 | 0 | 3 | 2 | 5 | 4 | 7 | 6 | 9 | 8 | B | A | D | C | F | E
|**2**| 2 | 3 | 0 | 1 | 6 | 7 | 4 | 5 | A | B | 8 | 9 | E | F | C | D
|**3**| 3 | 2 | 1 | 0 | 7 | 6 | 5 | 4 | B | A | 9 | 8 | F | E | D | C
|**4**| 4 | 5 | 6 | 7 | 0 | 1 | 2 | 3 | C | D | E | F | 8 | 9 | A | B
|**5**| 5 | 4 | 7 | 6 | 1 | 0 | 3 | 2 | D | C | F | E | 9 | 8 | B | A
|**6**| 6 | 7 | 4 | 5 | 2 | 3 | 0 | 1 | E | F | C | D | A | B | 8 | 9
|**7**| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | F | E | D | C | B | A | 9 | 8
|**8**| 8 | 9 | A | B | C | D | E | F | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7
|**9**| 9 | 8 | B | A | D | C | F | E | 1 | 0 | 3 | 2 | 5 | 4 | 7 | 6
|**A**| A | B | 8 | 9 | E | F | C | D | 2 | 3 | 0 | 1 | 6 | 7 | 4 | 5
|**B**| B | A | 9 | 8 | F | E | D | C | 3 | 2 | 1 | 0 | 7 | 6 | 5 | 4
|**C**| C | D | E | F | 8 | 9 | A | B | 4 | 5 | 6 | 7 | 0 | 1 | 2 | 3
|**D**| D | C | F | E | 9 | 8 | B | A | 5 | 4 | 7 | 6 | 1 | 0 | 3 | 2
|**E**| E | F | C | D | A | B | 8 | 9 | 6 | 7 | 4 | 5 | 2 | 3 | 0 | 1
|**F**| F | E | D | C | B | A | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
- XOR = EOR = &oplus; = Exclusive OR [Wikipedia](https://en.wikipedia.org/wiki/Exclusive_or)
- values in table are: x &oplus; y in hex
- go sideways for first digit, then look down for second digit
- in fact, doesn't matter if you do row or column first
- example: 2 XOR 6 => 4 same as 6 XOR 2 => 4
- any values XOR itself is zero (diagonal on this table)
- alternative view: (x) XOR (y) = flip bits of (x) that are set in (y)
- XOR with zero does nothing (flips no bits)
- XOR with 0xF flips all four bits
- XOR with self flips all set bits, so gives zero
- to XOR three values together, do (a&oplus;b)=X then (X&oplus;c)=answer
- right to A, down to B ... take that number, and go to that column
- down to C, that is answer: a &oplus; b &oplus; c
---
# XOR Seed Example Using 3 Parts
## Seed A (1 of 3)
@ -55,44 +94,3 @@
- The checksum of each of the XOR-parts protects the final result, assuming your XOR
math is correct.
---
## XOR Lookup Table
| XOR | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F
|----:|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---
|**0**| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F
|**1**| 1 | 0 | 3 | 2 | 5 | 4 | 7 | 6 | 9 | 8 | B | A | D | C | F | E
|**2**| 2 | 3 | 0 | 1 | 6 | 7 | 4 | 5 | A | B | 8 | 9 | E | F | C | D
|**3**| 3 | 2 | 1 | 0 | 7 | 6 | 5 | 4 | B | A | 9 | 8 | F | E | D | C
|**4**| 4 | 5 | 6 | 7 | 0 | 1 | 2 | 3 | C | D | E | F | 8 | 9 | A | B
|**5**| 5 | 4 | 7 | 6 | 1 | 0 | 3 | 2 | D | C | F | E | 9 | 8 | B | A
|**6**| 6 | 7 | 4 | 5 | 2 | 3 | 0 | 1 | E | F | C | D | A | B | 8 | 9
|**7**| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | F | E | D | C | B | A | 9 | 8
|**8**| 8 | 9 | A | B | C | D | E | F | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7
|**9**| 9 | 8 | B | A | D | C | F | E | 1 | 0 | 3 | 2 | 5 | 4 | 7 | 6
|**A**| A | B | 8 | 9 | E | F | C | D | 2 | 3 | 0 | 1 | 6 | 7 | 4 | 5
|**B**| B | A | 9 | 8 | F | E | D | C | 3 | 2 | 1 | 0 | 7 | 6 | 5 | 4
|**C**| C | D | E | F | 8 | 9 | A | B | 4 | 5 | 6 | 7 | 0 | 1 | 2 | 3
|**D**| D | C | F | E | 9 | 8 | B | A | 5 | 4 | 7 | 6 | 1 | 0 | 3 | 2
|**E**| E | F | C | D | A | B | 8 | 9 | 6 | 7 | 4 | 5 | 2 | 3 | 0 | 1
|**F**| F | E | D | C | B | A | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
- XOR = EOR = &oplus; = Exclusive OR [Wikipedia](https://en.wikipedia.org/wiki/Exclusive_or)
- values in table are: x &oplus; y in hex
- go sideways for first digit, then look down for second digit
- in fact, doesn't matter if you do row or column first
- example: 2 XOR 6 => 4 same as 6 XOR 2 => 4
- any values XOR itself is zero (diagonal on this table)
- alternative view: (x) XOR (y) = flip bits of (x) that are set in (y)
- XOR with zero does nothing (flips no bits)
- XOR with 0xF flips all four bits
- XOR with self flips all set bits, so gives zero
- to XOR three values together, do (a&oplus;b)=X then (X&oplus;c)=answer
- right to A, down to B ... take that number, and go to that column
- down to C, that is answer: a &oplus; b &oplus; c

9
xor.py
View File

@ -154,10 +154,11 @@ def worked_example(count=3):
if 1:
worked_example()
if 1:
print('\n---\n\n')
print('## XOR Lookup Table\n\n')
xor_table('')
print('---\n')
if 1:
worked_example()