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. ;)

Recommended Answers

All 2 Replies

G-Do very nice indeed! This would make a good contribution to the Python Code Snippets on DaniWeb. Right now there is mostly my stuff there, new blood and new ideas are always welcome! Just a thought.

Whoa, I didn't even know that existed! Great, I have some other stuff I'd like to upload, too. :cheesy:

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.