Big Data/Analytics Zone is brought to you in partnership with:

I have been programming in Python since 2006 and writing about Python almost as long on my blog. I also enjoy apologetics, reading, and photography. Mike is a DZone MVB and is not an employee of DZone and has posted 67 posts at DZone. You can read more from them at their website. View Full User Profile

Fun with Python and Silly Ciphers

04.27.2013
| 2995 views |
  • submit to reddit

When I was a kid, I was really into secret codes and ciphers. I thought they were all kinds of fun. My mom thought it would be fun to use some of the ciphers I was so enamored with in treasure hunts for special occasions, like birthdays. She would take something like a Cryptograph Wheel and create codes with it that my brother and I would have to decode with our own wheel to find a gift or another clue. We used stuff where numbers would represent letters (a=1, b=2, c=3) or we would use a sliding scale where you move the alphabet one letter over so A=B, C=D, D=E, etc. Sometimes we’d create a code stick where you get a long string of paper and wrap it around a pencil and then write a message of the paper. It’s pretty much impossible to read when it’s unwrapped.

Anyway, I decided to create a silly cipher program with wxPython where I could input a string and have it convert it to something else. I also wanted my program to decode it too. Now you can’t really make a program that can use a Cryptograph Wheel or a code stick, but for number codes or slide scales, that’s extremely easy.

Creating an Encoding GUI

cipher_trans

Creating the actual GUI is a piece of cake. It takes a little more work to code up the back end where you have to actually parse the string and change it into something else. For this exercise, I created 4 encoders and 3 decoders. The 5 encoders encode strings into numbers, ASCII (which is a different set of numbers), L33t (just for fun), and Hex. For the decoders, I decode everything except for L33t. Let’s take a moment to study the code for the GUI:

import controller
import wx
 
########################################################################
class CipherPanel(wx.Panel):
    """"""
 
    #----------------------------------------------------------------------
    def __init__(self, parent):
        """Constructor"""
        wx.Panel.__init__(self, parent=parent)
        choices = ["", "Alpha2Num", "ASCII", "L33t", "Hex"]
        decode_choices = ["", "ASCII", "Hex", "Num2Alpha"]
        self.mainSizer = wx.BoxSizer(wx.VERTICAL)
 
        toLbl = wx.StaticText(self, label="Translate into:")
        self.toCbo = wx.ComboBox(self, value=choices[1], choices=choices)
        self.add2HSizer([toLbl, self.toCbo])
 
        fromLbl = wx.StaticText(self, label="Translate from:")
        self.fromCbo = wx.ComboBox(self, value="", choices=decode_choices)
        self.add2HSizer([fromLbl, self.fromCbo])
 
        txtLbl = wx.StaticText(self, label="Enter text to encode or decode:")
        self.originalTxt = wx.TextCtrl(self, style=wx.TE_MULTILINE)
 
        self.translatedTxt = wx.TextCtrl(self, style=wx.TE_MULTILINE)
 
        encodeBtn = wx.Button(self, label="Encode")
        encodeBtn.Bind(wx.EVT_BUTTON, self.onEncode)
        decodeBtn = wx.Button(self, label="Decode")
        decodeBtn.Bind(wx.EVT_BUTTON, self.onDecode)
 
        # layout widgets
        self.mainSizer.Add(txtLbl, 0, wx.ALL, 5)
        self.mainSizer.Add(self.originalTxt, 1, wx.EXPAND|wx.ALL, 5)
        self.mainSizer.Add(self.translatedTxt, 1, wx.EXPAND|wx.ALL, 5)
        self.add2HSizer([encodeBtn, decodeBtn])
 
        self.SetSizer(self.mainSizer)
 
    #----------------------------------------------------------------------
    def add2HSizer(self, widgets):
        """
        Add widgets to a horizontal sizer
        """
        sizer = wx.BoxSizer(wx.HORIZONTAL)
        for widget in widgets:
            sizer.Add(widget, 0, wx.ALL|wx.CENTER, 5)
 
        if isinstance(widgets[0], wx.Button):
            self.mainSizer.Add(sizer, 0, wx.CENTER)
        else:
            self.mainSizer.Add(sizer)
 
    #----------------------------------------------------------------------
    def onDecode(self, event):
        """
        Decodes what's in the original text box to the encoding 
        specified and puts the result in the bottom text box
        """
        from_value = self.fromCbo.GetValue()
        value_to_decode = self.originalTxt.GetValue()
        if from_value == "Hex":
            new_value = value_to_decode.decode("hex")
        elif from_value == "ASCII":
            value_to_decode = [int(i) for i in value_to_decode.split()]
            new_value = controller.convertFromASCII(value_to_decode)
        elif from_value == "Num2Alpha":
            value_to_decode = value_to_decode.split()
            new_value = controller.convertFromNumbers(value_to_decode)
        else:
            return
 
        self.translatedTxt.SetValue(new_value)
 
    #----------------------------------------------------------------------
    def onEncode(self, event):
        """
        Encodes what's in the original text box to the encoding 
        specified and puts the result in the bottom text box
        """
        to_value = self.toCbo.GetValue()
        value_to_encode = self.originalTxt.GetValue()
        if to_value == "Hex":
            new_value = value_to_encode.encode("hex")
        elif to_value == "ASCII":
            new_value = controller.convertToASCII(value_to_encode)
        elif to_value == "L33t":
            new_value = controller.convertToLeet(value_to_encode)
        elif to_value == "Alpha2Num":
            new_value = controller.convertToNumbers(value_to_encode)
 
        self.translatedTxt.SetValue(new_value)
 
########################################################################
class CipherFrame(wx.Frame):
    """"""
 
    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        title = "Cipher Creator / Translator"
        size = (800,600)
        wx.Frame.__init__(self, None, title=title, size=size)
        panel = CipherPanel(self)
        self.Show()
 
#----------------------------------------------------------------------
if __name__ == "__main__":
    app = wx.App(False)
    frame = CipherFrame()
    app.MainLoop()

We should probably take a couple moments and break this down. As you probably noticed in the screenshot earlier, this GUI has a couple of ComboBoxes, a couple multiline TextCtrls and two buttons. The ComboBoxes control what we’re encoding or decoding to. We have imported a mysterious module called “controller” which houses all the code that does the encoding. We’ll look at that in a minute. First, we need to look at a couple of the functions in this piece. In the onEncode method, we should how to grab the value we’re going to encode and pass it to the appropriate controller function. For the onDecode method, we sometimes have to do a little pre-processing before we pass the data on to the controller. You can see an example in the ASCII portion of the conditional where we have to create a list of integers or in the Num2Alpha section where we need to create a list of numbers. Once you understand what’s going on here, feel free to continue to the next piece of code below.

#----------------------------------------------------------------------
def convertToASCII(string):
    """"""
    output = []
    for letter in string:
        output.append( ord(letter) )
 
    return " ".join([str(i) for i in output])
 
#----------------------------------------------------------------------
def convertToCeasar(string):
    """




http://www.wikihow.com/Create-Secret-Codes-and-Ciphers




    Shifts the alphabet 3 places such that A becomes X,
    B becomes Y, etc
    """
    ceasar_dict = {"a": "x", 
                   "b": "y",
                   "c": "z",
                   "d": "a",
                   "e": "b",
                   "f": "c",
                   "g": "d",
                   "h": "e",
                   "i": "f",
                   "j": "g",
                   "k": "h",
                   "l": "i",
                   "m": "j",
                   "n": "k",
                   "o": "l",
                   "p": "m",
                   "q": "n",
                   "r": "o",
                   "s": "p",
                   "t": "q",
                   "u": "r",
                   "v": "s",
                   "w": "t",
                   "x": "u",
                   "y": "v",
                   "z": "w"}
    new_string = ""
    for char in string:
        if char == ' ':
            new_string += ' '
        else:
            new_string += ceasar_dict[char.lower()]
    return new_string
 
#----------------------------------------------------------------------
def convertToLeet(string):
    """"""
    leet_dict = {"a":"4", "b":"8", "e":"3", "l":"1",
                 "o":"0", "s":"5", "t":"7"}
    new_string = ""
    for letter in string:
        if letter.lower() in leet_dict:
            letter = leet_dict[letter.lower()]
        new_string += letter
    return new_string
 
#----------------------------------------------------------------------
def convertToNumbers(string):
    """
    Convert a string to numbers where a=1, b=2, c=3, etc
    """
    keys = "abcdefghijklmnopqrstuvwxyz"
    values = ['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']
    num_dict = dict(zip(keys,values))
    new_string = ""
    for letter in string:
        if letter.lower() in num_dict:
            new_string += num_dict[letter.lower()] + " "
    return new_string
 
#----------------------------------------------------------------------
def convertFromASCII(data):
    """
    Convert from ASCII
    """
    s = ""
    if isinstance(data, str):
        for item in data.split():
            s += chr(int(item))
    else:
        # can also convert a list of integers
        for item in data:
            s += chr(item)
    return s
 
#----------------------------------------------------------------------
def convertFromCeasar(string):
    """
    Converts string from Ceasar to normal
    """
    unceasar_dict = {"x": "a",
                     "y": "b",
                     "z": "c",
                     "a": "d",
                     "b": "e",
                     "c": "f",
                     "d": "g",
                     "e": "h",
                     "f": "i",
                     "g": "j",
                     "h": "k",
                     "i": "l",
                     "j": "m",
                     "k": "n",
                     "l": "o",
                     "m": "p",
                     "n": "q",
                     "o": "r",
                     "p": "s",
                     "q": "t",
                     "r": "u",
                     "s": "v",
                     "t": "w",
                     "u": "x",
                     "v": "y",
                     "w": "z"}
    new_string = ""
    for char in string:
        if char == ' ':
            new_string += ' '
        else:
            new_string += unceasar_dict[char.lower()]
    return new_string
 
#----------------------------------------------------------------------
def convertFromNumbers(data):
    """
    Convert a list of numbers to letters where 1=a, 2=b, 3=c, etc
    """
    keys = ['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']
    values = "abcdefghijklmnopqrstuvwxyz"
    num_dict = dict(zip(keys,values))
 
    new_string = ""
    for char in data:
        val = num_dict[char]
        new_string += val
    return new_string
 
#----------------------------------------------------------------------
if __name__ == "__main__":
    x = convertToASCII("I love you")
    y = convertToLeet("I love you")
    print x
    new_x = [int(i) for i in x.split()]
    print convertFromASCII(new_x)
    print convertFromASCII(x)
 
    x = convertToCeasar("Meeting tomorrow at station")
    print "Meeting tomorrow at station =>", x
    print "%s =>" % x, convertFromCeasar(x)
 
    t = convertToNumbers("Python rules")
    print "Python rules => " + t
    print "%s => " % t, convertFromNumbers(t.split())

You may notice that I have included other examples in the controller that aren’t currently hooked into the GUI. For example, I have a converter function in there to convert strings to Ceasar, which is a popular cipher where the alphabet is shifted 3 places in one direction or the other. Anyway, one of the nicest bits about this code is that we didn’t need to import anything. It all works with just normal Python! To convert to ASCII, we use Python’s builtin ord. For most of the others, we use dictionaries to map the values. At the bottom of the script, we have a few test cases to check and make sure it is converting the strings as we expect it to. These should probably be made into real unit tests, but for a quick and dirty check, these work great!

Wrapping Up

There are several other codes I’d like to add to this at some point, such as Morse code. I also like the codes where the message is hidden in the text, such as every first letter (or last) in a poem spells our something or where every letter at the beginning of a sentence spells something. Those were always fun. I’ve included a few links at the end of this article so you can read about other fun codes and ciphers. Have fun coding!

Additional Reading

Download the Source!

Published at DZone with permission of Mike Driscoll, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)