Hey Everyone,
Sorry for not posting yesterday. I was planning to, but I got stuck down a deep rabbit hole last night. I wanted to start a new branch of the "Fun with Python" series that could also serve as an introduction to cryptography, which is what today's post is all about.
Intro to Cryptography: The Caesar Cipher
Probably one of the most well-known and simplest cryptography algorithms is the Caesar cipher. It's a type of substitution cipher where each letter in the plaintext is replaced by a letter some fixed number of positions down the alphabet. For example, with a shift of 3, 'A' would become 'D', 'B' would become 'E', and so on.
Its more modern and well-known relative is ROT13, which is just a Caesar cipher with a fixed shift of 13. The fun thing about ROT13 is that it's reciprocal; applying the same operation twice gets you back to your original text.
Here are a couple of simple Python scripts to demonstrate.
caesar.py
This script can handle any shift value, for both encrypting and decrypting.
#!/usr/bin/env python3
import argparse
def caesar_cipher(text, shift, decrypt=False):
"""
Applies the Caesar cipher to a given text with a given shift.
Handles both uppercase and lowercase letters, preserving case.
Non-letter characters are left unchanged.
:param text: The text to be processed.
:param shift: The shift amount (integer).
:param decrypt: If True, shifts backward for decryption.
:return: The processed text.
"""
if not text:
raise ValueError("Text cannot be empty.")
result = []
effective_shift = shift if not decrypt else -shift
for char in text:
if char.isupper():
base = ord("A")
new_char = chr((ord(char) - base + effective_shift) % 26 + base)
result.append(new_char)
elif char.islower():
base = ord("a")
new_char = chr((ord(char) - base + effective_shift) % 26 + base)
result.append(new_char)
else:
result.append(char)
return "".join(result)
def main():
parser = argparse.ArgumentParser(description="Caesar Cipher")
parser.add_argument("text", help="Text to encrypt/decrypt")
parser.add_argument("shift", type=int, help="Shift amount")
parser.add_argument(
"--decrypt", action="store_true", help="Decrypt instead of encrypt"
)
args = parser.parse_args()
try:
result = caesar_cipher(args.text, args.shift, args.decrypt)
print(f"Result: {result}")
except ValueError as e:
print(f"Error: {e}")
if __name__ == "__main__":
main()
rot13.py
And here is a dedicated script just for ROT13.
#!/usr/bin/env python3
import argparse
def rot13_cipher(text):
"""
Applies the ROT13 cipher to a given text.
ROT13 is a Caesar cipher with a fixed shift of 13.
It is reciprocal: applying it again decrypts the text.
Handles both uppercase and lowercase letters, preserving case.
Non-letter characters are left unchanged.
:param text: The text to be processed.
:return: The processed text.
"""
if not text:
raise ValueError("Text cannot be empty.")
result = []
shift = 13
for char in text:
if char.isupper():
base = ord("A")
new_char = chr((ord(char) - base + shift) % 26 + base)
result.append(new_char)
elif char.islower():
base = ord("a")
new_char = chr((ord(char) - base + shift) % 26 + base)
result.append(new_char)
else:
result.append(char)
return "".join(result)
def main():
parser = argparse.ArgumentParser(description="ROT13 Cipher")
parser.add_argument("text", help="Text to process with ROT13")
args = parser.parse_args()
try:
result = rot13_cipher(args.text)
print(f"Result: {result}")
except ValueError as e:
print(f"Error: {e}")
if __name__ == "__main__":
main()
As you can see, running ROT13 is the same as running the Caesar cipher with a shift of 13.
About That Rabbit Hole...
So, what sent me down this rabbit hole? While researching different ciphers we could play with, I got curious: "I've always wondered how the Enigma Machine works. How hard could it be? It was made in the 1930s, right?"
Oof. Boy, do I have some serious respect for the people who created that thing, and for the codebreakers who broke it during WWII. It was a bit of a journey, but the end result was worth it.
Let's just say I have a fully historically accurate recreation of the Enigma I machine... but that's going to have to be a post for another day.
As always, Michael Garcia a.k.a. TheCrazyGM