Quantcast
Python

Pythonic Caesar Ciphers

By July 11, 2018 No Comments

I was recently having a discussion with a fellow security person who stated that encryption was a ‘black art left to pure mathematicians’. And while some of it certainly is, an encryption scheme which stood up for a thousand years exists, and it can be implemented in Python.

Most folks have heard of the ‘Caesar Cipher’ (or Caesar code). The Caesar cipher uses transposition to hide its data. What that means is you move letters over by X. For example, if you had an offset of 3, the word CAT would be represented as ‘FDW’.

A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
X Y Z A B C D E F G H I J K L M N O P Q R S T U V W

This method was used by Julius Caesar to send private messages back and forth. However, frequency analysis could be used to crack this cipher. In each language certain letters are most popular, such as E in the English language.

But Caesar was not defeated so easily, and so the keyed Caesar cipher was developed. A key would be something like ‘AUTOMOX’. Once added to the alphabet all unique letters are then used in the beginning, and not used again. Here, CAT is represented as ‘TAP’.

A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
A U T O M O X B C D E F G H I J K L N P Q R S V W Z

With enough combination it makes it much harder to determine. Hack proof it is not, but it lasted for a long time, and is a good way to learn about keys and ciphers in Python.

Prereqs

The only external library we need to install is more_itertools. This package will allow us to get a unique list of letters from the key.

pip3 install more_itertools

The Program

First, let’s get the infrastructure out of the way. The code will require a key, input, and encrypt or decrypt as an option:

import argparse

import string

from  more_itertools import unique_everseen

parser = argparse.ArgumentParser(description=’Caesar Cipher With Keyword’)

parser.add_argument(‘keyword’, help=”Keyword or Phrase”, type=str)

parser.add_argument(‘inputText’, help=”plaintext or ciphertext”, type=str)

parser.add_argument(‘–encrypt’, help=”Encrypt phrase”, action=”store_true”)

parser.add_argument(‘–decrypt’, help=”Decrypt phrase”, action=”store_true”)

args=parser.parse_args()

Next, change all letters to lowercase and remove any whitespace:

def cleanup(x):

   x=x.lower()

   x=x.replace(‘ ‘, ”)

   return x

Then, use a function that prints out the plain alphabet is necessary (we’ll need it a few times):

def plainAlpha():

   letters=[]

   for letter in string.ascii_lowercase:

       letters.append(letter)

   return letters

Next, use a function that will create a cipher alphabet. To do this, prepend the alphabet with unique letters in their original order from the passphrase. Then, add the remaining letters:

def createCipherAlpha(unique):

   i=len(unique)

   cipherAlpha=unique[:]

   for letter in plainAlpha():

       if letter not in unique:

           cipherAlpha.append(letter)

           i+=1

       if i == 26:

           break

   return cipherAlpha

Now, create a dictionary with the plaintext alphabet and the cipher alphabet, or vice versa if you are decrypting:

def createMap(key, value):

    lookupTable=dict(zip(key,value))

    return lookupTable

Last, create a function that creates the message using the cipher alphabet and message:

def createMessage(inputText, lookupTable):

   cipherText=””

   for letter in inputText:

       cipherText=cipherText + lookupTable[letter]

   return cipherText

Use some basic logic to run the program:

inputText=cleanup(args.inputText)

keyword=cleanup(args.keyword)

#Get a unique list of letters preserving order from the keyword

unique=list(unique_everseen(keyword))

#Create a cipher alphabet using the unique letters

cipherAlpha=createCipherAlpha(unique)

if args.encrypt:

   #Create a way to map plain words to cipherAlpha

   lookupTable=createMap(cipherAlpha, plainAlpha())

if args.decrypt:

   #Build an cipherAlphabet using the keyword

   lookupTable=createMap(plainAlpha(),cipherAlpha)

cipherText=createMessage(inputText, lookupTable)

print(“Keyword:{}  \nUnique: {}\nOffset: {}\nOutput: {}\n”.format(keyword, unique, len(unique), cipherText))

So, now you have the logic. Let’s try it:

joes-MacBook-Pro:introcrypto joe$ ./hail.py automox –encrypt “Patch Your Shit”

Keyword:automox  

Unique: [‘a’, ‘u’, ‘t’, ‘o’, ‘m’, ‘x’]

Offset: 6

Output: sachmydbuvmnc

joes-MacBook-Pro:introcrypto joe$ ./hail.py automox –decrypt sachmydbuvmnc

Keyword:automox  

Unique: [‘a’, ‘u’, ‘t’, ‘o’, ‘m’, ‘x’]

Offset: 6

Output: patchyourshit

There you have it! A quick overview of encryption using Python. Now, don’t ask me about an elliptical curve, the math is beyond me! The whole program is included below. As usual, if you have any questions, feel free to email me: mcmanus@automox.com!

#!/usr/bin/env python3

import argparse

import string

from  more_itertools import unique_everseen

parser = argparse.ArgumentParser(description=’Caesar Cipher With Keyword’)

parser.add_argument(‘keyword’, help=”Keyword or Phrase”, type=str)

parser.add_argument(‘inputText’, help=”plaintext or ciphertext”, type=str)

parser.add_argument(‘–encrypt’, help=”Encrypt phrase”, action=”store_true”)

parser.add_argument(‘–decrypt’, help=”Decrypt phrase”, action=”store_true”)

args=parser.parse_args()

#cleanup keyword and input

def cleanup(x):

   x=x.lower()

   x=x.replace(‘ ‘, ”)

   return x

def plainAlpha():

   letters=[]

   for letter in string.ascii_lowercase:

       letters.append(letter)

   return letters

def createCipherAlpha(unique):

   i=len(unique)

   cipherAlpha=unique[:]

   for letter in plainAlpha():

       if letter not in unique:

           cipherAlpha.append(letter)

           i+=1

       if i == 26:

           break

   return cipherAlpha

   

def createMap(key, value):

    #Create a way to map plain words to cipherAlpha

    lookupTable=dict(zip(key,value))

    return lookupTable

def createMessage(inputText, lookupTable):

   cipherText=””

   for letter in inputText:

       cipherText=cipherText + lookupTable[letter]

   return cipherText

#remove spaces and shift to lower from user input

inputText=cleanup(args.inputText)

keyword=cleanup(args.keyword)

#Get a unique list of letters preserving order from the keyword

unique=list(unique_everseen(keyword))

#Create a cipher alphabet using the unique letters

cipherAlpha=createCipherAlpha(unique)

if args.encrypt:

   #Create a way to map plain words to cipherAlpha

   lookupTable=createMap(cipherAlpha, plainAlpha())

if args.decrypt:

   #Build an cipherAlphabet using the keyword

   lookupTable=createMap(plainAlpha(),cipherAlpha)

cipherText=createMessage(inputText, lookupTable)

print(“Keyword:{}  \nUnique: {}\nOffset: {}\nOutput: {}\n”.format(keyword, unique, len(unique), cipherText))



Joe McManus

Author Joe McManus

Joe is a Senior Cyber Security Researcher at CERT and a Professor at the University of Colorado College of Engineering where he teaches graduate courses in information security and forensics. Recently, Joe was the Director of Security at SolidFire, (acquired by NetApp [NTAP]). He is an avid cyclist, climber and leads the Automox security team.

More posts by Joe McManus

Leave a Reply