From bd2b9107f8383846731a12fd2a53c45a2d3fa046 Mon Sep 17 00:00:00 2001 From: "Peter D. Gray" Date: Wed, 28 Apr 2021 14:28:31 -0400 Subject: [PATCH] add PDF worksheet --- README.md | 19 ++++-- pdf-worksheet.py | 166 +++++++++++++++++++++++++++++++++++++++++++++++ worksheet.pdf | 86 ++++++++++++++++++++++++ xor-example.md | 80 +++++++++++------------ xor.py | 9 +-- 5 files changed, 310 insertions(+), 50 deletions(-) create mode 100644 pdf-worksheet.py create mode 100644 worksheet.pdf diff --git a/README.md b/README.md index d9d6201..2a54a22 100644 --- a/README.md +++ b/README.md @@ -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. + diff --git a/pdf-worksheet.py b/pdf-worksheet.py new file mode 100644 index 0000000..10ab632 --- /dev/null +++ b/pdf-worksheet.py @@ -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
{hex}', 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() diff --git a/worksheet.pdf b/worksheet.pdf new file mode 100644 index 0000000..da4b068 --- /dev/null +++ b/worksheet.pdf @@ -0,0 +1,86 @@ +%PDF-1.4 +%“Œ‹ž ReportLab Generated PDF document http://www.reportlab.com +1 0 obj +<< +/F1 2 0 R /F2 3 0 R /F3 4 0 R /F4 5 0 R +>> +endobj +2 0 obj +<< +/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font +>> +endobj +3 0 obj +<< +/BaseFont /Symbol /Name /F2 /Subtype /Type1 /Type /Font +>> +endobj +4 0 obj +<< +/BaseFont /Courier-Bold /Encoding /WinAnsiEncoding /Name /F3 /Subtype /Type1 /Type /Font +>> +endobj +5 0 obj +<< +/BaseFont /Courier /Encoding /WinAnsiEncoding /Name /F4 /Subtype /Type1 /Type /Font +>> +endobj +6 0 obj +<< +/Contents 10 0 R /MediaBox [ 0 0 792 612 ] /Parent 9 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +7 0 obj +<< +/PageMode /UseNone /Pages 9 0 R /Type /Catalog +>> +endobj +8 0 obj +<< +/Author (\(anonymous\)) /CreationDate (D:20210428142813+05'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20210428142813+05'00') /Producer (ReportLab PDF Library - www.reportlab.com) + /Subject (\(unspecified\)) /Title (\(anonymous\)) /Trapped /False +>> +endobj +9 0 obj +<< +/Count 1 /Kids [ 6 0 R ] /Type /Pages +>> +endobj +10 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 7389 +>> +stream +Gat=q:Qm$DEQ.DD.H']L;OAT<.^Q/02cA`5:Y:t%D1m&iajs@aCs(]O("1-H"/sGdc9]1_9JE7_9Ko*W^Ng@Ch,W95:7(pojnA`Sp#G=H@n[GFRr:8UkJ,Jc5s8LL95CWOpB;Z8i3.:StFW8*Ls-qd-k?^npe*W[WdYXIVe/dB#&cs*)A/oQ&:Hcra1H.0+V7"a>aKAT(LhaF6QM+2h*N?6KOBt^A71=Nd(ct'YV>N8Y]gGR'EI;*_:7HIe?WGoV/p/(e_oXBmHD*@VnQ-3[bFqBqZU88i/2,Ng/7=f2:W&T$gbQW3qE%V1SAiQ--F#TU^^ptE0-/`#]kLMN)f45)h[G3qom^`W(\Kpj:e6>i.E4qL3c:NH'osuDk9'Q^I9))9#NtQo+nDf0D-i>s_f+NNaiR\]k4lP!+IQd9E>qL6Ca?P.Olfm^WIeU`U6$ViOF;K]mMTRG%Dn%>I%-pPa01ii(F\:qhe.[("6>oHb/IB1Tn1+g[P40t.^QS(:$,;Ym.g`tRn1+g[Rdd?a^RFYm$,;quWsQP0n>cl1Rdd?a5>(]J_N,=YPjkH$+!ph5iC=2=9))9#NtZu4nDf0D-i>s_`sFt*pi403'7QJ@CC.bAn7r?FQLJYr^R"A)$,;eqCC.bAnEUCqT()%P^Rjr\$,<)$le0QVej&jk5g;e3=dfCaa&E19$RMIC5W%[eJ8!;T!7@J2!2;dB!CVRc",I/P#7sh.o)PLNk5du'c3&k,SHf]84:`S)c-?D"?$HJbf5"9&8U\MLYm2?%3N93l55&CE3[OHP+!ph5iC=2=9)*DCNtZu4n7r?FQLJYr^Rjr\$,<)-O*6HTkKa5=ELcl1Rdd?a^RFYeKHDN<.,dZ&5>(]J_N,=YPjkH$+!phEiPu6h:AAhGNtZu4nDf0D-i>s_`sKMHn7r?FQLJYr^R"A)$,;eqCC.bAn7r?FT()%P^Rjr\$,<)$lNt=tmCZu%J+_%_NY:#&!FNJmLK+JLI9/'a.`_2cdE."q.#Om@E-]Fa%@:Xe.]b9p]d39>e1%A<;GZ4&$,%Y$[j:lD_QP6_9#@bWr(=i0,lC9OY9a$<_QP6_C;R/$r+a*P-N%VqY9s0^_QPsKHDN<.,dZ&5>Lun_N,@ZSFGQl+"-tGiPu6h:AAhGY9j*M_QP9`>/IHhr*$t@-2^r`Y9j*M_QP?bHGZj5r-H5`-iA;-YOd&mKls$O2o41tp69CNP?RCPF+8c'=n\DDRjanA&LEr[&J:UI&J^mM&J^mM&J^mM&J^mM&J^mM&J^mM&J^[G;5.Hi75\JE75\JE75\JE75\JE75\JE75\JE75\JE75\JE75\JE784*N=]>Lc=]>Lc=]>Lc=]C%:=dD&M$KkR[](gLqXOj9)P59q!Z9KD]3N93l0)Mt`SW\<9NtQo+n=tXY-2]a]`sFt*ppGc^9DGoJ?VgIiKO<('8#P`<(&??9b8*h8_!29-)m#&"L?/T*$SGLc-Cr+I69W_];"]Hq=>/A\qVH,CYJuKJYX^GlI;lc9h`RHB7<\_">WDuWo@VQWVX-d)0e-Jtt9T:_`_UJii,>=(N28[iK;^4tq0f?u#(fOp/A\qVH,CYJuKJYX^GlI;_+5=_:0s3<\_">WDuWo@VQWVX-d)0e1.=r0f?ht#ZDsANA*6,e-Jtt9T:_`_UJii@n_k:29+,_;e&L\0f?u#(fOp77I\:hA_K32KLo*sUFbS?TU$DpiX[=Y@m9W_];"]Hq=>/A\qVH,FZMQ'TLX^Z#[;lc9h`RHB7<\_">W=c!sQl9CJKW,Q\7[P*&C4rPuVFHpk@qpt%0rDsX)ImN@WA19iQl9ILN2\[.X^PrR;eqb(_pfU%ehFLdWARAO@;6f]l^1kre2jJX0t!%@ojkNVq-7.L[@H[.2U:LWCZd*t,P_.thuXG!^&p"V^oKQdiXWW0"#'&W.m*7;%_SqH0C-ES3q/jR]VYcal+ef1n:D._Sc^Y/^oKKb@Lg&:"#&cO.lm)c%_Rf(0B9jK3:J+%]S6MAiP6s'n,a*4IKM7\^oKE`iXNPD"#&KG.lZt7%D6Q\0AF:C2XmFM]Oh7!ft]*rn:D+^?3;k4^oK?^@L]tN"#&3?.lHf_%D5F<0@R_;2"2[u]LDuVdD.7hn,a'34p*Ia^oK9\if(*fT/X:KLEr&k01FJSbQ%25^H]@mn#>$pEq96Zq[\&33r/^`(OkrS)Inmbm(!NkB^UtDO$6Hju^/J"@R*,D9<[sN[r]mo2]j:Rfljr_;bI5^=otd`2AcUEfnmY6JgV5r!!h%;?+3`=dB)C0bZZ<>*^T@9@S([Wr2r;ff]6W^Y2U,jWeSMb(3E3W@_jfM4^]/(VqDOLPmI6-R&?a7M:W*$1A+rK/>%u$.1jL0@R08sVeCo4r^&&nZ\NdOmc>d':DTgMsAm>9'h9EC@Yu]]3lJD'.Q0gPgD2r(HUY*d=2E-N%1inY9u=4MGZMr&hG9^US,W*p=B[0CG`h);=eB(ao[G+7$#ZeB7s^J10O,gTkQ@Ji+*_e)$ta2<'_m)R"e4RC*EA%&n;/LG.h:YK9Q[eQpU2Je:IgG7ft]_JFsnJFL9nZ6=bWuoZ&a0YX4XEH_,+3tAPif^d[JtX0brT6MJZ),pSMDh;t8\Cbkr3B^LHb8I/]#J?h*qB2Li^/YKZp(+")\%F&H;9WhrQlhl!LA;q:WACAV!'IB(TjlrlM=I;IB*--?B"+8Pi^bTHZZ7%-+Zf;H^^.IqnECnDp/Vo\Q\npQC-rlF2^9`E,rO)4g:kDsHYaQJUrrlFOUd``$.5"2>EOmc4Zq\8ii7CH!Smo)(77kT:%njblnAFi;TIq8.@5(>E\7t*[bAkpG?.5H=/I%MV;Vo\T]Z@.UXT;%`,F^pgq;[l-pA9\/F2G(UZ0>3Rq\7dK`P,-Qm8NBK'[Q]^b9*5OZ-ONHr3=Qig\e>&+4'kihbW=$'6\e]9qPnPVqC\lp3hg8T46]UfUTku6sG'\bgj^/)$$;saAqcht&k+.N25cQCp>Z-ONHr3=Qicht'*:M_g7c1S&JqGd'7WnBSdI%P/^:Gq%-g<S'oD=A]k+oOOeP)TM^/$W@Vjd'Qc1\.!qc''\CG(+]jKJCW24MACo@IgXRa5?1q.hY0)U@&po7gj>0@(Wln[pu.BZK01<1E]!/q;.,YPJ)RS%n+Wf51GTcX31;COSLQc($WBVjcjKc1e2Lqc*08WnBSdI%T[[J)04iS,@E$^M`YpIK$h&?haB^1On6L-h`slmeTmAIB*0.eSUrD^/&n+Vn2=qIHI`Fr,n4RIspI_qu+GsF4+?dlD@@"hl"'Q;r.2IDYmE-IB(WkFdKj\o%/lht++0(;e]W=]hZdBs6G$dpp_"+g'A\Eg0D/Iiq9]2>]&5%UY4k/[l@MZ?g0gkji-LfVmsep!7@/I/0(+]%(FEGNUlOmh\30#emd0^LJr>j/lL)=gQRXS6d#N#k\6s%8X*ZY=[mVlrr/P\BqAQ?Vk2&dV+%19j-Fum4(ADFT$&&1Gp$p;>%kOuHZ/RQ*B_ra>#^(ILSmQU3-@2a<.XLnT1'K_FLYIaOm.Xf72?b_3*BdSiJ3I_(7B6R=CdQILPJ;6(fMD*ZG>\`?]g&o?h<5.6ma#4'j:%".aAd^2m;jjhfmHhH/UdH7aLebT18*3?`m:?#[s7eRN4X"^ikpfN5II)O,VN'9JFb:8He.W/9qA:*jQ$A01pUK&.ZIYt%'(thnoXhr0YibK9XrCU_ka&hYjkFQaD82J`Ru`"+s>03@J'@Kc;rmW?Kl:[*ZlmqkNqEchV"oemJnu3Xh);Uib[-KNYQ3-/kCLR>D5A=eCc=1DGVP&eK24.C[L**mW3]J@fLqd;_T8t@C_]DO&8/$tl2os]'h18MibT?Yoh&lKp@uGe3G1k.pjF<;$=hj.Z=cdc;n?f!HGOT"2h.F<>^/$MR-;")-390_C,KFC_]EpVW)D,IecTKQ\XIGrLC&55M*NY^i.I5:D`eChW!`#^Wg!\>k#4]Mjk+RRW7lgO@>HO-P'>o+n>)a7A$I:iZX5].3:,2]1(5c6P_P.naDQ.T;(?%+c@m#hAroCio8`:gKs.!l^/dil8e#sch?3q?$%TriEk%&L!aPGpf`C;l^YYra%$D2)*W2R=B]j0XbNTN(\r.i>'5ttLS%Q_-#)"7F\suNRoL88?-KC]i3Pe`XLdUZKYts4Mf(sCS?0:QNp(g2!DiJ3Oq(E%<3=`okcLQPRQ25=SjeHL[D,HEFPkV0=-,/Xp9X"LthjtO++?L7#f,POjYZKe/OaC/W0]i\6rXi>U5jRM;?%"-^%CGGJ"!=<&6B#HGUTVMM#EX!E7^"/20Scq:`OrXpX81"-Jp]*j./+DM^lrDsg8):.8<^Y"n7C'L&Ft&U)R^,GGXT"G)MI\TDlq\n-;e:.s3Js!5$%1*u3tqB+<;.]%6UoaoG]3f^b57R^(@C#Jk:nV9O@qejVXi_aAk"dLKZR#ANc(;p/n@35*"k#=>.<9LB63PlI@iJ@5t`O;JYtPgO).:LS+r8V*(QgWXI$M)%7H\0jH*C&9C\9-n_0rVW4AR&'>h(iu^BPB8(bM9/Yd[f-`aQUUAQK=jYP+N9XEUh?Q?=*P=AChdlb0]546[`4SF2&5dIkeO2QO.O,I&N*!Gjb$m5l>cNf[IQl9%f_!$O3kk*n)8Cq[E57MQeS_4=8@[5EZ3R#llF(#j8#>Hp"t8pm>!@pmDIhR^ARu.O.T0srmSp&48n?75Q1-EQQL\QrTX2$5(3>Jo-nk=mciR-".S=rdf~>endstream +endobj +xref +0 11 +0000000000 65535 f +0000000073 00000 n +0000000134 00000 n +0000000241 00000 n +0000000318 00000 n +0000000428 00000 n +0000000533 00000 n +0000000727 00000 n +0000000795 00000 n +0000001078 00000 n +0000001137 00000 n +trailer +<< +/ID +[<807392c73ac4a44365403987194c69a5><807392c73ac4a44365403987194c69a5>] +% ReportLab generated PDF document -- digest (http://www.reportlab.com) + +/Info 8 0 R +/Root 7 0 R +/Size 11 +>> +startxref +8618 +%%EOF diff --git a/xor-example.md b/xor-example.md index da73248..55951cf 100644 --- a/xor-example.md +++ b/xor-example.md @@ -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 = ⊕ = Exclusive OR [Wikipedia](https://en.wikipedia.org/wiki/Exclusive_or) +- values in table are: x ⊕ 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⊕b)=X then (X⊕c)=answer + - right to A, down to B ... take that number, and go to that column + - down to C, that is answer: a ⊕ b ⊕ 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 = ⊕ = Exclusive OR [Wikipedia](https://en.wikipedia.org/wiki/Exclusive_or) -- values in table are: x ⊕ 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⊕b)=X then (X⊕c)=answer - - right to A, down to B ... take that number, and go to that column - - down to C, that is answer: a ⊕ b ⊕ c - diff --git a/xor.py b/xor.py index f84e04d..12d438a 100644 --- a/xor.py +++ b/xor.py @@ -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() +