Caesar (Shift) Cipher¶
The Caesar cipher, also called the shift cipher, takes a message and “shifts” each letter by a fixed number of positions in the alphabet. For a positive key, encryption shifts the letters forward, while decryption shifts them backward.
Encryption¶
We restrict our attention to capital letters only, A through Z. Shifting each letter forward by one, wrapping Z back to A, defines a rule for changing plaintext into ciphertext.
HELLO WORLD # plaintext
IFMMP XPSME # ciphertext
Alternatively, we could choose to shift each letter forward by two positions.
HELLO WORLD # plaintext
JGNNQ YQTNF # ciphertext
In general, each letter can be shifted by \(n\) positions for some \(n \in \mathbb{Z}\). First, assign the letters to consecutive integers, for example,
With \(n\) fixed, add \(n\) to each letter’s integer value and reduce the result modulo \(26\); that is, take the remainder after dividing by \(26\). This reduction produces an integer from \(0\) through \(25\).
Finally, translate the result back to a letter. The integer \(n\) is the cipher key: a different choice of \(n\) leads to a different cipher. Only its value modulo \(26\) matters: \(n\) and \(n + 26k\) produce the same cipher for any integer \(k\in\mathbb{Z}\).
For example, let \(n = 12\). The letter \(\text{T}\) is initially assigned the value of \(19\), and
Because \(\text{F}\) corresponds to the value \(5\), the key \(n = 12\) gives the rule \(\text{T}\to\text{F}\).
The String Module
The standard-library string module defines several strings of characters that are useful for processing text.
Constant |
Contents |
|---|---|
|
|
|
|
|
|
|
|
Punctuation characters, such as |
|
Whitespace characters, including space, tab |
For the cipher described above, string.ascii_uppercase is the alphabet the cipher is defined over.
import string
print(string.ascii_uppercase)
print(len(string.ascii_uppercase))
ABCDEFGHIJKLMNOPQRSTUVWXYZ
26
Problem 3
Write a function named shift_encrypt_letter() that takes a single-character string and an integer \(n\).
If the character is an uppercase letter A-Z, encrypt the letter using the Caesar cipher with key \(n\).
Otherwise, return the character unchanged.
Test Cases
print(shift_encrypt_letter("A", 12))
print(shift_encrypt_letter("D", -3))
print(shift_encrypt_letter("T", 0))
print(shift_encrypt_letter("A", 7))
print(shift_encrypt_letter("!", 5))
M
A
T
H
!
Problem 4
Write a function named shift_encrypt() that accepts a string of plaintext (with one or more characters) and an integer \(n\).
Encrypt the plaintext using the Caesar cipher with key \(n\).
Leave characters other than A-Z unchanged.
Hint: Join the results of a list comprehension.
Test Cases
print(shift_encrypt("HELLO WORLD!", 1))
print(shift_encrypt("HELLO WORLD!", 2))
print(shift_encrypt("HELLO WORLD!", 18))
IFMMP XPSME!
JGNNQ YQTNF!
ZWDDG OGJDV!
print(shift_encrypt(seuss.upper(), 3))
L DP VDP. L DP VDP. VDP L DP.
WKDW VDP-L-DP! WKDW VDP-L-DP! L GR QRW OLNH WKDW VDP-L-DP!
ZRXOG BRX OLNH JUHHQ HJJV DQG KDP?
L GR QRW OLNH WKHP, VDP-L-DP.
L GR QRW OLNH JUHHQ HJJV DQG KDP.
Decryption¶
Decrypting a Caesar-ciphered message undoes the shift applied during encryption. Since encryption shifts every letter forward by \(n\), decryption shifts every letter backward by the same amount.
Problem 5
Write a function named shift_decrypt() that accepts a string of ciphertext (with one or more characters) and an integer \(n\).
Decrypt the ciphertext using the Caesar cipher with key \(n\).
Hint: Can you do this with a call to shift_encrypt() from Problem 4?
Test Cases
print(shift_decrypt("JGNNQ YQTNF!", 1))
print(shift_decrypt("JGNNQ YQTNF!", 2))
print(shift_decrypt("JGNNQ YQTNF!", 3))
IFMMP XPSME!
HELLO WORLD!
GDKKN VNQKC!
The following test checks that decryption undoes encryption for a random key \(n\).
import random
x = "THE QUICK BROWN FOX JUMPED OVER THE LAZY DOG"
n = random.randint(0, 25)
print(shift_decrypt(shift_encrypt(x, n), n) == x)
True
Problem 6
When the key \(n\) used to encrypt a message is unknown, one approach to decryption is to try each of the \(26\) distinct shifts until the decrypted result looks like readable English. This so-called brute-force approach is practical for the Caesar cipher because the number of possibilities is small.
Decrypt the coded message KYV TRBV ZJ R CZV by calling shift_decrypt() with different values of the key \(n\) and reading each result.
Store the value of the key \(n\) that leads to decryption in a variable named problem6_key.
Using the approach from Problem 6, can you decrypt the messages from the beginning of the lab?