Modification of Vigenere en/deciphering algorithm

G-Do 0 Tallied Votes 215 Views Share

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.

For a fairly complete description of how it works, see this post:
http://daniweb.com/techtalkforums/post155767-1.html

NOTE: Apparently, the CGI that controls "contribution" submissions inserts newlines around comments, leaving the code looking very sparse. I think it looks ugly. For a more compact version of the same, see the original post.

# 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)