What is a Caesar Cipher?

A Caesar cipher is a method for encoding a message where letters of the alphabet are shifted, thus obfuscating the original message. For example, a Caesar cipher that shifts the alphabet by 13 means that A becomes N, B becomes O, C becomes P, and etc. Shifting the alphabet by 13 using a Caesar shift is also referred to as ROT13. There's no practical security use for Caesar ciphers; they do not provide confidentiality or integrity of messages.

Start Programming

Now that we have some basic knowledge of Caesar ciphers, let's get started on creating our encoder and decoder in Python! I've opted for Python 3.8 in this tutorial. There are many ways to write a Caesar cipher script in Python; this tutorial goes over a script I wrote that works for me.

I created my script in Python 3.8 using PyCharm 2020.1 (Community Edition). I named my script caesar.py.

Create a characters variable

The first thing I did was create a variable that housed all of the possible alphabetic characters (upper and lower case). I created a list in a variable called chars and added an uppercase alphabet and a lowercase alphabet.

chars = ['ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz']

Create an encoder function

Next, I wanted to create a function that would allow me to encode a message. The first line of code defines the function.

def encode(message, offset=13):

The function requires two variables to work. The first variable is the message. This is the plaintext message that you want to encode. The second variable is the offset. This variable tells the function how many characters the message must be shifted. By default, it will shift by 13 characters. This means we can execute the function without even giving it the offset and it will, by default, use ROT13. I added this in to allow for flexibility if I wanted to shift the message by anything other than 13 characters.

Next, I created a variable to tell Python how to transpose the message. This is where the script might start looking a little more intimidating and complex. To do this, I used the str.maketrans() method. This method takes the first argument as the plain alphabet and the second argument as the cipher.

def encode(message, offset=13):
    enc_chars = str.maketrans(
        f'{chars[0]}{chars[1]}',
        f'{chars[0][offset:]}{chars[0][:offset]}{chars[1][offset:]}{chars[1][:offset]}'
    )

The first argument, f'{chars[0]}{chars[1]}', concatenates the two items in the chars list variable. The result is 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.

The second argument,  f'{chars[0][offset:]}{chars[0][:offset]}{chars[1][offset:]}{chars[1][:offset]}', starts the alphabet at the offset position (in this case, the 13th position or N), then concatenates it with the remaining alphabet starting with A and up to the letter before the offset (or, M). The result is 'NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm'.

Finally, I had the function return the value of the transposed (encoded) message using the str.translate() method. Here's what the entire script looks like up to this point:

chars = ['ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz']


def encode(message, offset=13):
    enc_chars = str.maketrans(
        f'{chars[0]}{chars[1]}',
        f'{chars[0][offset:]}{chars[0][:offset]}{chars[1][offset:]}{chars[1][:offset]}'
    )
    return str.translate(message, enc_chars)

Create a decoder function

After creating the encoder, creating a decoder was easy! It's just the reverse of the encoder. I copied the exact same function and just flipped the arguments in the str.maketrans() method. After that was done, that finished everything for the script. Here's what the entire completed script looks like:

chars = ['ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz']


def encode(message, offset=13):
    enc_chars = str.maketrans(
        f'{chars[0]}{chars[1]}',
        f'{chars[0][offset:]}{chars[0][:offset]}{chars[1][offset:]}{chars[1][:offset]}'
    )
    return str.translate(message, enc_chars)


def decode(message, offset=13):
    dec_chars = str.maketrans(
        f'{chars[0][offset:]}{chars[0][:offset]}{chars[1][offset:]}{chars[1][:offset]}',
        f'{chars[0]}{chars[1]}'
    )
    return str.translate(message, dec_chars)

Running the Script

If you've followed along with this blog and ran the script, you might realize it doesn't return anything. I built it specifically to be used with an interactive Python shell. From a command-line terminal, use the -i option with python to enter an interactive shell. From here, you can run the encoder and decoder functions.

python -i caesar.py
>>> encode('Hello')
>>> 'Uryyb'
>>>
>>> encode('Hello', 15)
>>> 'Wtaad'

Running the script through the interpreter

If you don't feel comfortable using command-line to execute Python or you just want to use the script in whatever IDE you're using, I've appended the script with code that will prompt the user to encode and decode messages.

chars = ['ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz']


def encode(message, offset=13):
    enc_chars = str.maketrans(
        f'{chars[0]}{chars[1]}',
        f'{chars[0][offset:]}{chars[0][:offset]}{chars[1][offset:]}{chars[1][:offset]}'
    )
    return str.translate(message, enc_chars)


def decode(message, offset=13):
    dec_chars = str.maketrans(
        f'{chars[0][offset:]}{chars[0][:offset]}{chars[1][offset:]}{chars[1][:offset]}',
        f'{chars[0]}{chars[1]}'
    )
    return str.translate(message, dec_chars)


get_option = input("Choose [e]ncode or [d]ecode (Default: e): ")
if get_option == 'e':
    message = input('Enter your plaintext message: ')
    offset = int(input('Choose the shift (1-26): '))
    if offset < 1 or offset > 26:
        raise Exception(f'Invalid entry: {offset}')
    else:
        print(f'Your encoded message: {encode(message, offset)}')
elif get_option == 'd':
    message = input('Enter your ciphertext message: ')
    offset = int(input('Choose the shift (1-26): '))
    if offset < 1 or offset > 26:
        raise Exception(f'Invalid entry: {offset}')
    else:
        print(f'Your decoded message: {decode(message, offset)}')
else:
    raise Exception(f'Invalid option: {get_option}')

Conclusion

This is just one of the many ways you can build a Caesar cipher in Python. Once you learn how to build an encoder and decoder in a cipher as simple as this one, you'll be able to progress to more difficult ciphers and encryption schemes.