Here's a cute little encipher/decipher program with a Tkinter GUI I wrote a while back. It's an implementation of a derivative of the Vigenere algorithm; the algorithm is taken from Laurence Smith's Cryptography: The Science of Secret Writing, Amazon link here. It's a dated book (and the technique itself is very old), so don't expect any serious security from this - just something fun to think about.
# vigenere.py, a modification of the Vigenere ciphering program
# Uses Tkinter
import sys, string, tkMessageBox
from Tkinter import *
# -- The application class
class Vigenere:
# Initialize new instances of Vigenere
def __init__(self, root):
self.root = root
self.makeStringVars()
self.makeWidgets()
# Initialize all widget-bound StringVars
def makeStringVars(self):
self.sv_message = StringVar(); self.sv_message.set("")
self.sv_keyword = StringVar(); self.sv_keyword.set("")
self.sv_alphakey = StringVar(); self.sv_alphakey.set("")
# Initialize all Tkinter widgets
def makeWidgets(self):
# Set the application window's title and make it unresizable
self.root.title("Vigenere")
self.root.resizable(0, 0)
# Make the main Frame for all widgets
fr_main = Frame(self.root); fr_main.pack()
# Make the Frame for the message widgets
fr_message = Frame(fr_main); fr_message.pack()
# Make the Frames for all keyword and alphakey widgets
fr_keys = Frame(fr_main); fr_keys.pack(pady=10)
fr_labels = Frame(fr_keys); fr_labels.pack(side=LEFT)
fr_entries = Frame(fr_keys); fr_entries.pack(side=LEFT)
# Make the Frame for the 'Encipher' and 'Decipher' Buttons
fr_buttons = Frame(fr_main); fr_buttons.pack(pady=10)
# Make the message Label and Entry
lb_text = Label(fr_message, text="Text or Ciphertext")
lb_text.pack()
self.en_message = Entry(fr_message, width=60,
textvariable=self.sv_message)
self.en_message.pack()
self.en_message.focus_set()
# Make the keyword and alphakey Labels and Entries
lb_keyword = Label(fr_labels, text="Keyword:")
lb_alphakey = Label(fr_labels, text="Alphabet Key:")
lb_keyword.pack(anchor="e"); lb_alphakey.pack(anchor="e")
en_keyword = Entry(fr_entries, width=40,
textvariable=self.sv_keyword)
en_alphakey = Entry(fr_entries, width=40,
textvariable=self.sv_alphakey)
en_keyword.pack(); en_alphakey.pack()
# Make the 'Encipher' and 'Decipher' Buttons
bt_encipher = Button(fr_buttons, text="Encipher",
command=self.encipher)
bt_decipher = Button(fr_buttons, text="Decipher",
command=self.decipher)
bt_encipher.pack(side=LEFT); bt_decipher.pack(side=LEFT)
# Callback which wraps the encipher function
def encipher(self):
# Grab the message, the keyword, and the alphakey
message = self.sv_message.get()
keyword = self.sv_keyword.get()
alphakey = self.sv_alphakey.get()
# If anything is missing, popup an error dialog
# Otherwise, replace the current message with the
# enciphered message
if len(message) == 0 or len(keyword) == 0 or len(alphakey) == 0:
self.popError(message, keyword, alphakey)
else:
self.en_message.delete(0, END)
self.en_message.insert(END,
encipher(message, keyword, alphakey))
# Callback which wraps the decipher function
def decipher(self):
# Grab the message, the keyword, and the alphakey
message = self.sv_message.get()
keyword = self.sv_keyword.get()
alphakey = self.sv_alphakey.get()
# If anything is missing, popup an error dialog
# Otherwise, replace the current message with the
# deciphered message
if len(message) == 0 or len(keyword) == 0 or len(alphakey) == 0:
self.popError(message, keyword, alphakey)
else:
self.en_message.delete(0, END)
self.en_message.insert(END,
decipher(message, keyword, alphakey))
# Popup a dialog which informs the user not to leave fields blank
def popError(self, message, keyword, alphakey):
error = "These boxes can't be left blank:\n"
if len(message) == 0: error = error+"Text or Ciphertext, "
if len(keyword) == 0: error = error+"Keyword, "
if len(alphakey) == 0: error = error+"Alphakey, "
error = error[0:len(error)-2]
tkMessageBox.showerror(title="Error", message=error)
# -- Module functions
# -- Encipher a text using keyword and alphakey
def encipher(text, keyword, alphakey):
# Clean up the text, keyword, and alphakey
text, keyword, alphakey = clean(text, keyword, alphakey)
# Build the major alphabet and the cipher alphabets
major = makeMajor(keyword)
ciphers = makeCiphers(major, alphakey)
# For each character in text, add the substitution character to
# the ciphertext
ciphertext = ""
for i in range(len(text)):
cipher = ciphers[i%len(alphakey)]
substitute = cipher[string.find(major, text[i])]
ciphertext = ciphertext+substitute
return ciphertext
# -- Decipher a text using keyword and alphakey
def decipher(ciphertext, keyword, alphakey):
# Clean up the text, keyword, and alphakey
ciphertext, keyword, alphakey = clean(ciphertext, keyword,
alphakey)
# Build the major alphabet and the cipher alphabets
major = makeMajor(keyword)
ciphers = makeCiphers(major, alphakey)
# For each character in ciphertext, add the correct character to
# the text
text = ""
for i in range(len(ciphertext)):
cipher = ciphers[i%len(alphakey)]
correct = major[string.find(cipher, ciphertext[i])]
text = text+correct
return text
# -- Clean up the text, keyword, and alphakey
def clean(text, keyword, alphakey):
# Get rid of quotes
text = text.replace("\"", "").replace("\'", "")
keyword = keyword.replace("\"", "").replace("\'", "")
alphakey = alphakey.replace("\"", "").replace("\'", "")
# Remove redunant characters from the keyword and alphakey
keyword, alphakey = collapse(keyword), collapse(alphakey)
# Return all three strings
return text, keyword, alphakey
# -- Remove all duplicate letters from keys
def collapse(key):
ret = ""
for i in range(len(key)):
letter = key[i]
duplicate = False
for j in range(i+1, len(key)):
if letter == key[j]: duplicate = True
if not duplicate: ret = ret+letter
return ret
# -- Build the major cipher alphabet around the input keyword
def makeMajor(keyword):
ret = keyword
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"+ \
":.;,?!@#$%&()+=-*/_<> []{}`~^"+ \
"abcdefghijklmnopqrstuvwxyz"
for i in range(len(alphabet)):
if not alphabet[i] in ret: ret = ret+alphabet[i]
return ret
# -- Build the cipher alphabets using the major alphabet and alphakey
def makeCiphers(major, alphakey):
ret = []
for i in range(len(alphakey)):
index = string.index(major, alphakey[i])
ret.append(major[index:]+major[:index])
return ret
# -- Main function
def main(args):
root = Tk()
v = Vigenere(root)
root.mainloop()
# -- The following code executes upon command-line invocation
if __name__ == "__main__": main(sys.argv)
Do you see how it works? Ignore the Tkinter stuff - do you see what the enciphering and deciphering algorithms are doing?
To encipher text, first you must:
Strip the text, keyword, and alphakey of quotes (I couldn't get quotes to work), then remove redundant characters from the keyword and the alphakey. Create a major cipher alphabet by pulling the characters of the keyword out of the alphabet and sticking the keyword in front. In other words, if your alphabet looks like:
ABCDEFGHIJKLMNOPQRSTUVWXYZ
- and your keyword is PANTS, your major cipher alphabet will look like:
PANTSBCDEFGHIJKLMOQRUVWXYZ
Then, create a list of cipher alphabets based on your alphabet key (alphakey). Basically, for every character in alphabet key, "rotate" the alphabet so that character is in front. In other words, if your major cipher alphabet is the one we just saw, and your alphabet key is OMG, your list of cipher alphabets will be:
OQRUVWXYZPANTSBCDEFGHIJKLM
MOQRUVWXYZPANTSBCDEFGHIJKL
GHIJKLMOQRUVWXYZPANTSBCDEF
And that's just the groundwork - we haven't even started enciphering yet!
Now, here's where it gets complicated. This is a substitution cipher, which means that it works by exchanging characters for other characters. The central weakness of substitution ciphers is that they are easily broken by letter-frequency analyses - in other words, a smart cryptanalyst would look through the enciphered text for the frequencies of certain characters, and based on these frequencies, make guesses about which characters they represent in normal English. Our approach throws them off (a little) because there isn't a one-to-one map between our plain text characters and our enciphered text characters - we employ letter positions and multiple alphabets to do the substitution, as we shall see.
The enciphering algorithm is:
For each character in your plain text message, look at the character's string index. Divide this number by the length of the alphabet key and take the remainder. The remainder will be the list index of the cipher alphabet you are going to use. Then, find the index of the character in the major cipher alphabet. Find the character at the same index in the cipher alphabet you have just chosen. This is the character you will substitute.
So, if our message is MESSAGE, and we're on the first character (M):
The string index is 0. The length of the alphabet key (OMG) is three. The reminder of 0/3 is 0. This means we are going to use the first cipher alphabet, OQRUVWXYZPANTSBCDEFGHIJKLM, because it is the alphabet at index 0 in our alphabets list. Next, we try to find the index of M in the major cipher alphabet. We see that M is the seventeenth character, so the string index is 16. We take the character at the same position in our cipher alphabet: D. This is the character we will substitute for M.
Understanding the deciphering algorithm is left as an exercise to the reader. ;)