My favorites | Sign in
Project Home Downloads Wiki Issues Source
Repository:
Checkout   Browse   Changes   Clones    
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#!/usr/bin/env python
########################################################################################################
# Chaocipher v0.1
#
# invented by John F. Byrne, 1918
# this script by Paul Makowski (C) 2010, GPLv3
# http://paulmakowski.wordpress.com; my.hndl@gmail.com
#
# ref: http://www.mountainvistasoft.com/chaocipher/ActualChaocipher/Chaocipher-Revealed-Algorithm.pdf
# ^ steps mentioned in code reference the above ^
# blog: http://paulmakowski.wordpress.com/2010/07/07/chaocipher-now-with-ascii-support/
#
#########################################################################################################
import random, os, time, base64
from sys import exit

# parse options
from optparse import OptionParser
parser = OptionParser()
parser.add_option("-k", "--keyfile", dest="keyfile", type="string", help="specify keyfile (required for decryption) (format: <initial CT alphabet><NULL byte><initial PT alphabet>)")
parser.add_option("-e", "--encrypt", dest="pt", type="string", help="plaintext (PT) string to encrypt")
parser.add_option("-d", "--decrypt", dest="ct", type="string", help="ciphertext (CT) binary to decrypt (expects base64 encoding)")
(options, args) = parser.parse_args()
if (options.ct): options.ct = base64.b64decode(options.ct)
if (options.ct and not options.keyfile): parser.error("keyfile required for decryption; exiting...")
if (options.ct and options.pt) or (not options.ct and not options.pt): parser.error("either encrypt (-e \"foo\") or decrypt (-d \"bar\") must be specified; exiting...")


# this should really only be used in cases of encrypting
# ...or in brute forcing a ciphertext... poorly :)
alphabetCT = []; alphabetPT = []
if not options.keyfile:

# generate random alphabets
alphabet = [chr(i) for i in range(9, 126+1)] # default to 118 printable ASCII characters, numbered from 9 to 125 (decimal) (this includes tabs and newlines)
random.seed() # debugging be damned
random.shuffle(alphabet); alphabetCT = alphabet[:] # shuffle the alphabets...
random.shuffle(alphabet); alphabetPT = alphabet[:] # ...and point alphabetCT & alphabetPT to copies of the shuffled alphabets

# write alphabets to timestamped file for future decryption use
lt = time.localtime(time.time())
timestamp = "chaocipher_%02d-%02d-%04d_%02d-%02d-%02d" % (lt[2], lt[1], lt[0], lt[3], lt[4], lt[5])
newFilePath = os.getcwd() + "/" + timestamp
print "[*] no keyfile specified; generating one at " + newFilePath
newKeyFile = open(newFilePath, 'w'); newKeyFile.write("".join(alphabetCT) + '\0' + "".join(alphabetPT)); newKeyFile.close()


# keyfile specified; load it and parse it
else:
hKeyfile = open(options.keyfile, "r"); keyfile = hKeyfile.read(); hKeyfile.close()
[alphabetCTstring, alphabetPTstring] = keyfile.split('\0')

# TODO: check for duplicate characters in alphabets (they make me a sad panda)
if (len(alphabetCTstring) % 2 != 0): print "ERROR: alphabets must be of even length; exiting..."; exit(1);
if (len(alphabetCTstring) != len(alphabetPTstring)): print "ERROR: alphabets must be of equal length; exiting..."; exit(1);
alphabetCT = list(alphabetCTstring); alphabetPT = list(alphabetPTstring)

alphabetLen = len(alphabetCT) # optimization; theres no point in continually finding this; len(alphabetCT) = len(alphabetPT)
zenith = 0 # less code if this is global


# simulate left wheel permutation
def permuteLeft():
global zenith
# step 1: (already done by updating the zenith pointer in main)
extractedChar = alphabetCT[(zenith+1) % alphabetLen] # step 2: extract character at the zenith+1 position
alphabetCT[(zenith+1) % alphabetLen] = "*" # ...and replace with a flag
# step 3: (covered by modulo arithmetic)
nadir = (zenith + alphabetLen/2) % alphabetLen # step 4: point nadir to the "opposite" position of the zenith...
alphabetCT.insert(nadir+1, extractedChar) # ...insert removed character at this position...
alphabetCT.remove("*") # ...and remove flagged list entry


# simulate right wheel permutation
def permuteRight():
global zenith
# step 1: (already done by updating the zenith pointer in main)
alphabetPT.append(alphabetPT.pop(0)) # step 2: shift entire alphabet one to the left
extractedChar = alphabetPT[(zenith+2) % alphabetLen] # step 3: extract character at the zenith+2 position
alphabetPT[(zenith+2) % alphabetLen] = "*" # ...and replace with a flag
# step 4: (covered by modulo arithmetic)
nadir = (zenith + alphabetLen/2) % alphabetLen # step 5: point nadir to the "opposite" position of the zenith...
alphabetPT.insert(nadir+1, extractedChar) # ...insert removed character at this position...
alphabetPT.remove("*") # ...and remove flagged list entry



# ENCRYPTION
if options.pt:

plaintext = options.pt; ciphertext = ""
for character in plaintext:

# encrypt character
zenith = alphabetPT.index(character)
ciphertext += alphabetCT[zenith]

# permute "wheels"
permuteLeft()
permuteRight()


# DECRYPTION
if options.ct:

ciphertext = options.ct; plaintext = ""
for character in ciphertext:

# decrypt character
try: zenith = alphabetCT.index(character)
except: print "ERROR: Could not find a required character in the provided ciphertext alphabet. Usually this means you are using an incorrect keyfile during decryption."; exit(1)
plaintext += alphabetPT[zenith]

# permute "wheels"
permuteLeft()
permuteRight()


# RESULTS
print "PT: " + plaintext
print "CT: " + base64.b64encode(ciphertext) + " (base64 encoded)"






Change log

c22e1a150180 by myhndl on Jul 7, 2010   Diff
add blog url
Go to: 
Project members, sign in to write a code review

Older revisions

536b63398464 by myhndl on Jul 7, 2010   Diff
initial
All revisions of this file

File info

Size: 5599 bytes, 127 lines
Powered by Google Project Hosting