One of the simplest ciphers is the Caesar cipher, also called the shift cipher. It works by shifting each letter in the alphabet n positions to the right, mapping it to a different letter. For example, using ‘rotation 13’, a is shifted 13 positions to the right, corresponding to the letter n.
What happens to the last letters in the alphabet? For example, shifting z 13 positions to the right maps it outside of the 26 letters of the English alphabet. In this case we have to use a bit of math, namely modular arithmetic. Whenever the result of the shift is bigger than the size of the alphabet (called the modulus), it wraps around and starts counting from the beginning. So z rotation 13 would become letter number 13 (26+13-26), or the letter m. Mathematically this is expressed as:
x ≡ (26 + 13) mod 26 ≡ 13
In Python, the modulus operator is designated as %.
With these basics, how would we implement a Caesar cipher in Python? First, we need the letter number for each letter in the supplied string (let’s call it num), and then sum the rotation (rot) modulus the number of letters in the alphabet (26).
Mathematically, this would be written as:
x ≡ (num + rot) mod 26
For the first part, getting the letter number, we can either supply a table or, even simpler, get the ASCII value of the letter and subtract 97, since ASCII(‘a’) = 97. Remember, in computer science we almost always start counting at 0, which is why we subtract 97 and not 96 (97-97=0).
Getting the ASCII value is simple in Python, we just use ord() on the letter.
We can now shift the letter with the rotation value using modular arithmetic (to not get out of bounds of the alphabet), and finally change the resulting number back to ASCII using chr(). These 3 steps can be done in Python like this:
num = ord(char)
cypher = (num - 97 + rot) % 26
cypher = chr(cypher + 97)
Doing this for each letter gives us our encrypted string, the cipher text, which we can send to our friends and allies content in the knowledge that nobody can break our state-of-the-art cipher(!).
How then does the recipient decrypt the cipher? Apart from going to one of the countless online breaking tools or breaking it mathematically or using letter analysis, we can of course use Python again to do the decryption. Basically it’s the opposite of what we just did for encryption:
num = ord(char)
plain = (num - 97 - rot) % 26
plain = chr(plain + 97)
As you can see, we now subtract the rotation instead of adding it like in the encryption phase. We again use modulus to wrap around the alphabet, this time when we go lower than 0, or a.
Finally we can wrap the code in a loop so it works on the whole plainstring, import argparse so we can supply the string and rotation directly on the command line, and specify whether we want to encrypt or decrypt. The full script can be found on my GitHub repo or at the bottom of this post.
A few things to note: the script only encrypts letters, not symbols, and is hardcoded for the English alphabet (26 letters). Also, all letters will be changed to lowercase before encryption.
I hope you enjoyed this post. I intend to bring more crypto-related posts in the future since it is something I’m currently studying.
[cc lang=”python”]#!/usr/bin/python # Takes a string and shift encrypts or decrypts it using the # supplied rotation import argparse # Add arguments parser = argparse.ArgumentParser(description=”Encrypt/decrypt Caesar cyphers”) parser.add_argument(“mode”, help=”Encrypt or decrypt”, nargs=”?”, choices=(“encrypt”, “decrypt”)) parser.add_argument(“string”, help=”String to encrypt/decrypt”) parser.add_argument(“rot”, help=”Rotation to use”) args = parser.parse_args() # Definitions mode = args.mode string = args.string.lower() rot = int(args.rot) def encrypt(string, rot): “””Caesar encryption function””” cypherstr = “” for char in string: if not char.isalpha(): cypher = char elif char.isalpha(): num = ord(char) cypher = (num – 97 + rot) % 26 cypher = chr(cypher + 97) cypherstr += cypher return cypherstr def decrypt(string, rot): “””Caesar decryption function””” plainstr = “” for char in string: if not char.isalpha(): plain = char elif char.isalpha(): num = ord(char) plain = (num – 97 – rot) % 26 plain = chr(plain + 97) plainstr += plain return plainstr # Either encrypt or decrypt if mode == “encrypt”: print(encrypt(string, rot)) elif mode == “decrypt”: print(decrypt(string, rot)) [/cc]