One-Time Pad
January 9, 2015
Let’s begin with the random number generation. Blum-Blum-Shub requires two large primes, each congruent to 3 (mod 4), and a seed coprime to the two primes; the random sequence squares the seed modulo the product of the two primes, keeping only the final bit:
(define *n*
(let ((p 18446744073709551359)
(q 18446744073709551427))
(* p q)))
(define *s* 20150106)
(define (bbs)
((set! *s* (modulo (* *s* *s*) *n*))
((modulo *s* 2))
We used two 64-bit primes; in practice, those would likely be 512 bits or larger, and the seed would be of a similar size. Given the random bits, a random letter can be generated like this:
(define (rand-letter)
(let loop ((i 26) (ds (list)))
(if (positive? i)
(loop (- i 1) (cons (bbs) ds))
(let ((m (undigits ds 2)))
(if (char
(+ (modulo m 26) 65)))))))
The largest multiple of 26 less than 2^26 is 67108860. We can check that the random number generator works by looking at a histogram of the frequency counts of the output from rand-letter
:
> (map cdr (uniq-c char=? (sort char<? (list-of (bbs) (x range 2600000)))))
(99962 100017 100553 100101 99494 99809 100031 100192 100004
100176 99955 99378 100306 100148 100188 99734 100098 100181
99890 100182 100035 100233 99802 100227 99276 100028)
Building a one-time pad is just a simple nest of three loops, for the rows, columns and letter-groups:
(define (one-time-pad rows cols)
(for (r 0 rows)
(for (c 0 cols)
(for (j 0 5)
(display (rand-letter)))
(display #\space))
(newline)))
And here is a one-time pad:
> (one-time-pad 50 10)
RJPFX OLNUW UFSXH XTKCL GEYMV VSSNU VHVSY ZUBUC XVKRY YHFEF
XYMSZ JRIFU RTIYH SUSQG APQDW VHRDC XIYRF XZFTJ UFRUF UIAKW
HIJQS HCIYP GUGOE FTZAG XZSVM DDRHV MGBOY CFSKX LHCCM LADMP
NRAJN QMZMX PKOBY FMAZO GDFQX CBPBO WQDCQ WDLYQ CWAHM NGQHC
KCNPI KKFBN KHJRR YEQCY ZBWDP FMTNJ FYEHX QIRPP YDLKJ ROMEK
ZOXVV PPFOI GWDGS FPJKZ AYIMB MLISK IKDTV IMWZE GZJUL CVNKF
YWNPM GWRHR YEQGY ZWCNT QNOEV ZYBHT ONPWL EJYSH XFRQO XYSLG
IRIXU MPKAO UJTXP RNJBU ATDDB LHCFB EQWZJ MTVMA DYVRM XBXYG
HAGQM BDXOW QSXBR UDJQD AGDJW QQGPJ YHFAI JUPMJ XKQJJ WVHBI
GSBNT FCTXW MTUBZ WGCXL TMIPA WUKMF RMZTM GYKPJ QOYZE MJTOL
MXXRI NDNBT KXTOI YQONE VUMPE DWMFX DQPYB IPTVW OJZGA JIRMU
HFTZZ HYSAD QPDIM EGVAY ADYPT NMEHD VRYCI GNZNV XWOTV OOLCS
KWWJN CYKPJ LKOVS DMAFL DMJXE JVTZB LSPFX LSWMU NYEDN WONTP
QETKW SULQA WEQWE PLEHN NZTVD MGJER KKKIM DITTV MDOUR QKYTB
QWVMJ IXUST DZUAV RCDDT JSXDY QHDSZ RFIDL BCVRJ UOEGZ JDESI
MDDMK TJQNW ZKZBY VOSHY YPGEH YEJWQ JHYWM QFNQR HNWYV QQVYY
PCKUT LQASO BKIYY MSCWN JKWDL EKGND CBQUM VKQGZ MEPOB HNRVF
OJHIN NWHRC RFIJU SDNHZ CHBFW INFEF VIAPM KFBQB YOVIF NQQNW
DSOQA CWMMO SXELL WSCLU RDVCJ YFUXN UBXJM AZSCS FYZIK MRTRN
BNAYM QYMZP TNUZB IWYZU IGTQA GUTUR LHNCN UUDCT XJBCS HIRXF
ZIZSQ BHGHF WOOUR IUVNI KYBVV LUTBR MHDHS BSYYU LQJDN RBHNN
KJLGU NGVES EFVEB KASJM DYTTV HLNSZ VYAZX RWPHQ DRMHA CTYKA
QUWPG HPJJX HWUEB GTQHV KPSXX YSHMM LOADK DYDOY WIALH GFGJL
SUETF STDOJ ADKCL HTLJR GBBGW ZLLQS TVDQW FGTHP EJASO ZCRMR
TUEST GKDHR UBHVX BXGJD MBCDH XUBWT WTTNG SVEDJ JWXTV XCUPD
DHVTE MOIGQ PDYZR FEUOZ OGIPU RXKBA UKDKH RQULZ KKVKH GJXLA
DQQUQ VBCHW MOETP DYTXJ UVXRS LCELV WDGMN TLSJI ULADO WAESF
KDZIC TKVHV QORWU ZONPD QDRZX DBSAA PTRXT RZUPK BEKKV IBHDB
YOQVM KQBCY XEAAB OLCXV XGRGO JZKSP NNRWL XJMJR HOFJN IBUVY
EVRIU CJWZY RGPDL BSJJA KNHNG ISBGA BJTED GQJQE NUTNT FTSIG
JPRTY ATTXQ MOXWN BPRBN NQTKN NZBGV SDOCV UYBDQ EVOGX BRBAH
BIRMK LKBUR ZOTGO EOCPI BBQAH PKMIJ VKPQX PAPEK AYEED UQJRE
KPZSY IRPPT EUTKR EIOYQ YARZJ QRIVM SRIQN AMAOF PPRTF DLMQE
GGTWN CQUVK TLVUD XJFXK MIDTY LYRMP TMWVC MGGHK KIJPL MOJRW
TNSJR VEXWS FKFRE RSTII FLKTS GZMXK ZMFMP XCEGA BJSBS XADAR
MTCAR DMWIY ZJQAN UUODZ DMXSJ IWXUT RIWBE KNCFQ FVRYV YPXXC
TLADG UCNDL YIJWT GHBOE RAGYI XZFWS MKLHW YJTED WJEUI UYENQ
OAEOG ZVGRF KEKVN YFQCI TWQQM DEVRV JKXIY BINAJ ZRMEJ XWEIB
HKQYL IWPMP IAYNU OCBJD JBXPU JYDGH PIJAW BHTPN SXIUU BGPQQ
IECKJ PYVHW YSEUV PNPSK FPEAM THRHZ VNROD FUADM UAAXN YOSTQ
AETHB FFSCN DQEBY VWPIW PYVHD UPBTO WFHET WIYOD FLVKN KASNU
HLOKN FCROX ECXHG YBCPS ELIIC UCQWT KCDYV FYNUX TNQGA PDETF
SUGUX VMWMV TLFLA SDAYJ ATAVV RKHQU AZOSA JQAAX JIRNL CKHYF
EORRY QMAWI EWIHR KWWQE REFVJ VTUIC CNJHK OKHFH ZDQYV ZANGE
OHWRV CWJYW XXDVK IPEVH UKSNY JASAP BVIKH EHOUN QSSFB EVLBM
FYYBP CEXIO MYFHA NKOVU PLPOK XRWVU BRFRY CZIZG CXPOT PFAHX
NGJTV XJJQI VTWXH YWYNX CMHNG JIWBF TZBMU KUJRH ANKOU QVWZK
JVMCH XIVQO VCKTR GFQAV OZJJN SQYOS SBZIB ATUBP IACSW GFBCU
VCJMC GSCMI VKGIE DHTWJ MLQSJ YJQES WTDFM FTCZH TNXOL WCONO
YBDIU DGHPH MEAUZ TNURE GIRKE TWQAY DYPEB QALQS WAAGT ZHDGQ
Cut that out, paste it to one side of a 3 by 5 index card, paste your trigraph on the other side, give a copy to a friend, and you’re ready for some secret communications.
We used the for
macro and undigits
function from the Standard Prelude, plus a few other functions from the Standard Prelude to build the histogram. You can run the program at http://programmingpraxis.codepad.org/fFmsqWJQ.
I should point out the cryptographic weakness of a one-time pad, which is that it quickly runs out, and then you have to figure out a way to get more of it to the party you’re communicating with; if this is intercepted, your plaintext can be eavesdropped upon.
You’re consuming 26 bits per letter; can’t you get away with 5?