"""
Clubhouse crypto: simple substitution, easily cracked
(but useful for learning purposes).
Permutation class S appended to facilitate group theory
approach to permutations as per this thread archived
at the Math Forum:
http://www.mathforum.com/epigone/mathedu/swexprundwand
"""
import string, random
def permute(pool):
"""
Randomly permute whatever list is passed in as pool
"""
oldlist = []+list(pool)
newlist = []
for i in range(len(oldlist)):
randitem = random.choice(oldlist)
oldlist.remove(randitem)
newlist.append(randitem)
return newlist
def mkdict(n=0):
"""
Pair uppercase alphabet or integers 1...n with
randomly permuted version of same
"""
if n>0: pool = range(1,n+1)
else: pool = string.uppercase
tuples = zip(pool,permute(pool))
codedict = {}
for pair in tuples:
codedict[pair[0]]=pair[1]
return codedict
def mkkey(codedict):
"""
Return key as a list of cyclic tuples, e.g.
[('A','R','C'),('D','Q')...]
"""
dictkeys = codedict.keys()
result = []
for i in dictkeys[:]:
newtuple = ()
if i in dictkeys and not codedict[i]==i:
initval,nextval = i,i
while 1:
newtuple += (nextval,)
dictkeys.remove(nextval)
nextval = codedict[nextval]
if nextval==initval:
break
result.append(newtuple)
return result
def gendict(keytuples):
"""
generate a dictionary from a list of cyclic tuples
"""
result = {}
for cycle in keytuples:
rot1 = (cycle+(cycle[0],))[1:]
for i in range(len(cycle)):
result[cycle[i]]=rot1[i]
return result
def revkey(keytuples):
"""
Reverse all the cyclic tuples in a list of tuples
"""
result = []
for i in keytuples:
temp = list(i)
temp.reverse()
result.append(tuple(temp))
return result
def encrypt(filename):
"""
Use a permuted alphabet to encrypte a .txt file --
saving the key and file separately. Save file
as filename.cpt, the codekey as filename.key
"""
encfile = filename[:string.rfind(filename,".")]+".cpt"
keyfile = filename[:string.rfind(filename,".")]+".key"
print "Writing to " + encfile
print "Saving key as " + keyfile
txtfileobj = open(filename,'r')
encfileobj = open(encfile,'w')
keyfileobj = open(keyfile,'w')
thedict = mkdict()
for line in txtfileobj.readlines():
uline = string.upper(line)
encline = ""
for char in uline:
if char in string.uppercase:
encline = encline + thedict[char]
else:
encline = encline + char
encfileobj.write(encline)
keyfileobj.write(str(mkkey(thedict)) + "\n")
txtfileobj.close()
encfileobj.close()
keyfileobj.close()
def decrypt(filename):
"""
Use a codekey to decrypt a file (see encrypt)
"""
txtfile = filename[:string.rfind(filename,".")]+".dcp"
keyfile = filename[:string.rfind(filename,".")]+".key"
print "Reading from " + filename
print "Writing to " + txtfile
print "Using key " + keyfile
print ""
encfileobj = open(filename,'r')
txtfileobj = open(txtfile,'w')
keyfileobj = open(keyfile,'r')
temp = eval(keyfileobj.readline())
thekey = revkey(temp)
thedict = gendict(thekey)
for line in encfileobj.readlines():
dcpline = ""
for char in line:
if char in thedict.keys():
dcpline = dcpline + thedict[char]
else:
dcpline = dcpline + char
print dcpline[:-1]
txtfileobj.write(dcpline)
encfileobj.close()
txtfileobj.close()
keyfileobj.close()
def gcd(a,b):
"""Return greatest common divisor using Euclid's Algorithm."""
while b:
a, b = b, a % b
return a
def lcm(a,b):
"""
Return lowest common multiple."""
return (a*b)/gcd(a,b)
def LCM(terms):
"Return lcm of a list of numbers."
return reduce(lambda a,b: lcm(a,b), terms)
class S:
"""
Defines a permuation and operations therewith
"""
def __init__(self,n=0,dict={}):
if len(dict.keys())>0:
self.dict = dict
elif n==0:
self.dict = mkdict()
else:
self.dict = mkdict(n)
def __mul__(self,other):
newdict = {}
for i in other.dict.keys():
newdict[i] = self.dict[other.dict[i]]
return S(dict=newdict)
def __repr__(self):
return str(mkkey(self.dict))
def __pow__(self,n):
r = S(dict=self.dict)
if n==0:
return r*r.inv()
elif abs(n)>1:
for i in range(1,abs(n)):
r *= S(dict=self.dict)
if n<0:
r = r.inv()
return r
def __eq__(self,other):
rtn = 1
for i in self.dict.keys():
if self.dict[i] != other.dict[i]:
rtn = 0
break
return rtn
def len(self):
return len(self.dict)
def inv(self):
newdict = {}
for i in self.dict.keys():
newdict[self.dict[i]] = i
return S(dict=newdict)
def order(self):
tuples = mkkey(self.dict)
return LCM(map(len,tuples))
# code highlighted using py2html.py version 0.8