Snippets from a computing educator and researcher.

Caesar ciphers

This post starts a series on writing programs to use and break different ciphers. I'm concentrating on ciphers used before World War II, as they're relatively easy to understand and relatively easy to program.

The Caesar cipher

This is a simple cipher where each plaintext letter is shifted some positions along the alphabet to give the ciphertext letter. It can be used with two concentric wheel, like in the photo. You align the inner wheel with the appropriate part of the outer wheel. You can then find the plaintext letter in the inner wheel and the ciphertext letter is the corresponding one in the outer wheel.

How do we encipher a character? We need to be a bit defensive here, and account for the fact that we may be passed non-letters. We also have to think about how we want to handle upper- and lower-case letters. Should non-letters be removed from the result, kept unchanged, cause an error, or something else? Should all letters be converted to upper-case, or all converted to lowercase?

I'll be conservative here, and say that non-letters are retained unchanged, and the case of letters should be retained.

This means I can write caesar-encipher-letter as:

to caesar-encipher-letter(character, shift):
if character is a letter:
cipherletter = letter + shift
if character is uppercasea:
return uppercase(cipherletter)
else:
return cipherletter
else:
return character

The only complication is the letter + shift bit: most programming languages will reject this statement as characters and numbers can't be added together.

As we'll be doing a lot of this sort of thing in future, let's define a couple of utility functions which convert letters to their positions in the alphabet, and back again. Because we'll be using these functions a lot, I give them deliberately short names. pos finds the position of a letter in the alphabet, ignoring case, and raiseing an error if it's given something other than a letter.

def pos(letter):
"""Return the position of a letter in the alphabet (0-25)"""
if letter in string.ascii_lowercase:
return ord(letter) - ord('a')
elif letter in string.ascii_uppercase:
return ord(letter) - ord('A')
else:
raise ValueError('pos requires input of {} to be an ascii letter'.format(letter))

This uses the string module from Python's standard library. That module defines some useful constants, such as string.ascii_letters, string.ascii_lowercase and string.ascii_uppercase.

unpos does the reverse, convering a number into a (lowercase) letter. If the number is outside the range 0–25, the modulus operator, %, trims the number into that range.

def unpos(number):
"""Return the letter in the given position in the alphabet (mod 26)"""
return chr(number % 26 + ord('a'))

def caesar_encipher(message, shift):
"""Encipher a message with the Caesar cipher of given shift"""
enciphered = ""
for character in message:
enciphered.append(caesar_encipher_letter(l, shift))
return enciphered
def caesar_decipher(message, shift):
"""Decipher a message with the Caesar cipher of given shift"""
return caesar_encipher(message, -shift)

However, all those append calls in caesar_encipher seem a bit wasteful. It's better if we use a list comprehension to walk along the characters in the message, enciphering each one as we go.

def caesar_encipher(message, shift):
"""Encipher a message with the Caesar cipher of given shift"""
enciphered = [caesar_encipher_letter(l, shift) for l in message]
return cat(enciphered)

Because this returns a list of characters where we want a string, let's define a little function do the conversion: