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 us: support@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))
|
Automox for Easy IT Operations
Automox is the cloud-native IT operations platform for modern organizations. It makes it easy to keep every endpoint automatically configured, patched, and secured – anywhere in the world. With the push of a button, IT admins can fix critical vulnerabilities faster, slash cost and complexity, and win back hours in their day.
Grab your free trial of Automox and join thousands of companies transforming IT operations into a strategic business driver.