Caesar

For this lab, we’ll implement a program that encrypts messages using Caesar’s cipher, per the below.

$ python encrypt.py
key: 13
plaintext:  HELLO
ciphertext: URYYB

Background

Supposedly, Caesar (yes, that Caesar) used to “encrypt” (i.e., conceal in a reversible way) confidential messages by shifting each letter therein by some number of places. For instance, he might write A as B, B as C, C as D, …, and, wrapping around alphabetically, Z as A. And so, to say HELLO to someone, Caesar might write IFMMP. Upon receiving such messages from Caesar, recipients would have to “decrypt” them by shifting letters in the opposite direction by the same number of places.

The secrecy of this “cryptosystem” relied on only Caesar and the recipients knowing a secret, the number of places by which Caesar had shifted his letters (e.g., 1). Not particularly secure by modern standards, but, hey, if you’re perhaps the first in the world to do it, pretty secure!

Unencrypted text is generally called plaintext. Encrypted text is generally called ciphertext. And the secret used is called a key.

To be clear, then, here’s how encrypting HELLO with a key of 1 yields IFMMP:

plaintext H E L L O
+ key 1 1 1 1 1
= ciphertext I F M M P

More formally, Caesar’s algorithm (i.e., cipher) encrypts messages by “rotating” each letter by k positions. More formally, if p is some plaintext (i.e., an unencrypted message), pi is the ith character in p, and k is a secret key (i.e., a non-negative integer), then each letter, ci, in the ciphertext, c, is computed as

ci = (pi + k) % 26

wherein % 26 here means “remainder when dividing by 26.” This formula perhaps makes the cipher seem more complicated than it is, but it’s really just a concise way of expressing the algorithm precisely. Indeed, for the sake of discussion, think of A (or a) as 0, B (or b) as 1, …, H (or h) as 7, I (or i) as 8, …, and Z (or z) as 25. Suppose that Caesar just wants to say Hi to someone confidentially using, this time, a key, k, of 3. And so his plaintext, p, is Hi, in which case his plaintext’s first character, p0, is H (aka 7), and his plaintext’s second character, p1, is i (aka 8). His ciphertext’s first character, c0, is thus K, and his ciphertext’s second character, c1, is thus L. Can you see why?

Let’s write a program in a file called encrypt.py that enables you to encrypt messages using Caesar’s cipher. The program should first prompt the user for a non-negative integer as a key; if the user doesn’t provide such, they should be prompted again and again until they do. The program should then prompt the user for a string of plaintext. And the program should then print the corresponding ciphertext that results from encrypting the plaintext with the key.

Here are a few examples of how the program might work. For example, if the user inputs a key of 1 and a plaintext of HELLO:

$ python encrypt.py
key: 1
plaintext:  HELLO
ciphertext: IFMMP

Here’s how the program might work if the user provides a key of 13 and a plaintext of hello, world:

$ python encrypt.py
key: 13
plaintext:  hello, world
ciphertext: uryyb, jbeyq

Notice that neither the comma nor the space were “shifted” by the cipher. Only rotate alphabetical characters!

How about one more? Here’s how the program might work if the user provides a key of 13 again, with a more complex plaintext:

$ python encrypt.py
key: 13
plaintext:  be sure to drink your Ovaltine
ciphertext: or fher gb qevax lbhe Binygvar

For readability’s sake, notice how we’ve deliberately lined up the plaintext and ciphertext, as by printing two spaces after plaintext: and just one space after ciphertext:.

Notice, too, that the case of the original message has been preserved. Lowercase letters remain lowercase, and uppercase letters remain uppercase.

And what if a user doesn’t cooperate?

$ python encrypt.py
key: -1
key: 1
plaintext:  HELLO
ciphertext: IFMMP

Or really doesn’t cooperate?

$ python encrypt.py
key: cat
key: dog
key: 1
plaintext:  HELLO
ciphertext: IFMMP

Specification

Design and implement a program, caesar, that encrypts messages using Caesar’s cipher.

  • Implement your program in a file called encrypt.py.
  • Your program must prompt the user for a non-negative integer. Let’s call it k for the sake of discussion.
  • If any of the characters of k is not a decimal digit, your program should prompt the user for k again.
  • Do not assume that k will be less than or equal to 26. Your program should work for all non-negative integral values of k.
  • Your program must output plaintext: (without a newline) and then prompt the user for a str of plaintext (using input).
  • Your program must output ciphertext: (without a newline) followed by the plaintext’s corresponding ciphertext, with each alphabetical character in the plaintext “rotated” by k positions; non-alphabetical characters should be outputted unchanged.
  • Your program must preserve case: capitalized letters, though rotated, must remain capitalized letters; lowercase letters, though rotated, must remain lowercase letters.
  • After outputting ciphertext, you should print a newline.

How to begin? Let’s approach this problem one step at a time.

Pseudocode

First, write some pseudocode in a file called caesar.txt that implements this program, even if not (yet!) sure how to write it in code. There’s no one right way to write pseudocode, but short English sentences suffice. Odds are your pseudocode will use (or imply using!) one or more functions, conditions, Boolean expressions, loops, and/or variables.

Spoiler

There’s more than one way to do this, so here’s just one!

  1. Prompt the user for k
  2. Iterate over k to make sure all characters are digits
  3. If not, go back to step 1
  4. Convert k from a str to an int
  5. Prompt user for plaintext
  6. Iterate over each character of the plaintext:
    1. If it is an uppercase letter, rotate it, preserving case, then print out the rotated character
    2. If it is a lowercase letter, rotate it, preserving case, then print out the rotated character
    3. If it is neither, print out the character as is
  7. Print a newline

It’s okay to edit your own after seeing this pseudocode here, but don’t simply copy/paste ours into your own!

Hints

  • Recall that the ASCII value of A is 65. The ASCII value of a, meanwhile, is 97.
  • Read up on ord, which converts characters to their corresponding ASCII (or Unicode) values.
  • Read up on chr, which converts ASCII (or Unicode) values to their corresponding characters.
  • Best to use the modulo (i.e., remainder) operator, %, to handle wraparound from Z to A! But how?

Extra Credit

If finished with encrypt.py, implement a program in a file called decrypt.py that decrypts ciphertext (that’s been encrypted by Caesar’s algorithm) via brute force.

  • Your program should first prompt the user for a string of ciphertext.
  • Your program should then proceed to output every possible plaintext for that ciphertext, as by iterating over all possible keys from 1 through 25.

Testing Your Code

Test your program on, at least, these ciphertexts. If your code is correct, odds are your program will output, for each string of ciphertext, at least one line of plaintext that makes sense?

  • LRF
  • Xnt'qd cnhmf fqdzs!
  • Hyl fvb zbyl fvby jvkl pz jvyylja?
  • Bml'r umppw, gr gq.