Okay, insomnia got me, so I went ahead and implemented it in Python. It appears to work reasonably well, at least it successfully deciphers their test message. You can specify the key by specifying the -c and -p options, which are the settings for the cipher and plain wheels. You should pass a permutation of the uppercase letters as arguments to those if you want to use other than the default key, which matches Rubin's example.
#!/usr/bin/env python # # _ _ _ # ___| |__ __ _ ___ ___(_)_ __ | |__ ___ _ __ # / __| '_ \ / _` |/ _ \ / __| | '_ \| '_ \ / _ \ '__| # | (__| | | | (_| | (_) | (__| | |_) | | | | __/ | # \___|_| |_|\__,_|\___/ \___|_| .__/|_| |_|\___|_| # |_| # An implementation of John Byrnes' Chaocipher as described in papers # by Moshe Rubin. # # Written by Mark VandeWettering <email@example.com> # No rights are reserved. No warranties are implied. # import sys import random import string import optparse p = optparse.OptionParser() p.add_option("-d", "--decrypt", action="store_true", dest="decrypt", default=False, help="decrypt instead of encrypt") p.add_option("-p", "--pwheel", dest="pw", default="PTLNBQDEOYSFAVZKGJRIHWXUMC", help="plaintext wheel setting") p.add_option("-c", "--cwheel", dest="cw", default="HXUCZVAMDSLKPEFJRIGTWOBNYQ", help="cipher wheel setting") opts, args = p.parse_args() # initialize the code machine... cnt = 0 def output(c): global cnt sys.stdout.write(c) cnt = cnt + 1 if cnt % 50 == 0: sys.stdout.write('\n') cnt = 0 elif cnt % 5 == 0: sys.stdout.write(' ') class Machine: def __init__(self, cw, pw): self.cw = cw # cipher wheel self.pw = pw # plaintext wheel pass def twizzle(self, idx): self.cw = self.cw[idx:] + self.cw[0:idx] self.cw = list(self.cw) + \ self.cw[2:14] + \ list(self.cw) + \ self.cw[14:] # and the plaintext wheel self.pw = self.pw[idx:] + self.pw[0:idx] self.pw = self.pw[1:] + list(self.pw) self.pw = self.pw[0:2] + \ self.pw[3:14] + \ list(self.pw) + \ self.pw[14:] def encrypt(self, d): # find where it is in the plain text wheel... idx = self.pw.index(d) r = self.cw[idx] self.twizzle(idx) return r def decrypt(self, d): idx = self.cw.index(d) r = self.pw[idx] self.twizzle(idx) return r random.seed(0) machine = Machine(list(opts.cw), list(opts.pw)) for arg in args: try: data = open(arg).read() except IOError, msg: print >> sys.stderr, "%s: %s" % (arg, msg) print >> sys.stderr, "continuing..." data = data.upper() # filter out all the non alpha characters... data = filter(lambda x : x in string.uppercase, data) if opts.decrypt: for d in data: output(machine.decrypt(d)) else: for d in data: output(machine.encrypt(d)) print
Addendum: Here is some cipher text that you can decode with the above program (or your own implementation):
TLMAG OONSK JBJYB QVGDQ CDUNW NMZPL OYCWP CWKWQ RBOYA DSLQB KYCDG XJOLO NKTTL RUZZJ QGJBQ NRQHQ RREUI YIDHZ OMVWZ MVYUF QOGSN NUVYT JGQPS QTBRW FHLTC LVVBP MYYQV
Stumbling back through articles in Slashdot, I found a pretty nifty article on one of my favorite subjects: historical cryptography. The story goes that back in 1918, a cipher system/machine was invented by John F. Byrne. Rumor says that it was very strong, and yet could be implemented using a mechanism that would fit in a cigar box. The details of this invention were never publicly released. However, recently the widow of Bryne's son, John Byrnes Jr., has decided to donate his notes to the National Cryptological Museum, and the first publications are beginning to trickle out. Moshe Rubin has a new paper that details the working of the algorithm in sufficient detail that it should be possible to write an implementation in whatever language you like for experimentation. It's too late for me to start today, but expect a Python reference implementation in the next few days:
A cursory glance over the implementation suggests that the key space is basically 26! * 26! or about:
By comparison, the German Army Enigma (three rotors) had a keyspace of only 562,064,881,159,999,426,560, and the Navy Enigma a keyspace which was only 1000x larger. So if all things were equal, we might expect that the Chaocipher was a lot harder to crack. But all things are probably not equal. I'll be pondering this over the next few days.