The goal of this assignment is to learn a bit about encryption and decryption, to do real work with functions, and to get more practice with loops, conditionals, and data representations. It is also designed to give you practice developing a well-structured large program.
Create a directory named cipher that will contain your code for this project.
It is strongly recommended that you work on this assignment with a partner. Don't be shy; find somebody new to work with! If you'd like to work in a pair, please figure out who you'd like to work with via Piazza or email, and then email me once your pair is decided. Make sure to put both people's names at the top of each file. Write your code by pair-programming: code together, at the same computer, and switch off who's at the keyboard. Only one of you should ultimately submit your team's code via Moodle.
An important class of algorithms that some computer scientists concern themselves with are encryption and decryption techniques. How can text be encoded so that no one can read it apart from the desired recipient? This is critical for trade secrets and government messages, but even more critical for such common needs as secure web-based purchases, being able to send your password over the web without others listening in, and so on.
An old trick is the Caesar cipher. Pick a special value (called the “key”) between 1 and 25; then for each character in your message, shift each letter forward by the key, wrapping around the end of the alphabet. (Why wouldn't you want to use 26?) For example, if your original message (called the “plaintext”) is “helloyou”, and your key is 2, your encrypted message (the “ciphertext”) is “jgnnqaqw”. If you know the key, you can then later decrypt the ciphertext, and get back the original plaintext.
Supposedly, Julius Caesar used this technique to communicate with his generals. It is very easy to crack (you just have to try decrypting with all 25 possible keys, until you get something that makes sense), but it's still interesting to code up.
The program you write will do the following:
Here is an example run of the finished program (user's input is in red and underlined):
> python simple-caesar.py Encode, decode, or quit? encode What is your plaintext? follow the elk What shift do you want to use? 3 Your ciphertext is: iroorz wkh hon Encode, decode, or quit? alaska Sorry, I didn't understand your input. Encode, decode, or quit? decode What is your ciphertext? iroorz wkh hon What shift do you want to use? 3 Your ciphertext is: follow the elk Encode, decode, or quit? quit Okay, bye!
This part of the assignment will walk you through writing this program. Then, in the second half of the assignment, you'll develop a more sophisticated version of it on your own.
Create a Python file called simple-cipher.py. When you hand in this file, it must have at least the following five functions in it: main()
, interactiveEncode()
, interactiveDecode()
, caesarEncode()
, and caesarDecode()
, defined in that order. The assignment guides you through filling these in.
Type out the following skeleton code in your file:
# Simple Caesar-cipher encoding and decoding, by YOUR_NAME_HERE. def main(): """An infinite-loop prompt for Caesar-cipher encoding and decoding.""" # TODO: Replace this stand-in function body with code for the user prompt. print " Main function is not yet implemented." def interactiveEncode(): """Gets user input to pass to the Caesar-cipher encoder.""" # TODO: Get encoding input (plaintext and shift factor) from the user. print " Encoding is not yet implemented." def interactiveDecode(): """Gets user input to pass to the Caesar-cipher decoder.""" # TODO: Get decoding input (ciphertext and shift factor) from the user. print " Decoding is not yet implemented." def caesarEncode(plaintext, shift): """Returns the ciphertext obtained by transforming every letter in the plaintext by a Caesar cipher with the specified shift. Non-letter characters remain unchanged in the ciphertext.""" # TODO: Fill in the logic to encode the plaintext. print ' I don\'t yet know how to encode "%s".' % plaintext ciphertext = "[the correct ciphertext, of course]" return ciphertext def caesarDecode(ciphertext, shift): """Returns the plaintext obtained by un-transforming every letter in the ciphertext by a Caesar cipher with the specified shift. Non-letter characters remain unchanged.""" # TODO: Fill in the logic to decode the ciphertext. print ' I don\'t yet know how to decode "%s".' % ciphertext plaintext = "[the correct plaintext, of course]" return plaintext main()
There are a few important things to notice about this code, and the program that it embodies:
main()
.This code demonstrates a few very important software-development practices that you should keep in mind in your work from here on out:
main()
).The first feature we'll add to the program is the prompt that asks the user what to do. Fill in the main()
function so that it makes calls to interactiveEncode()
and interactiveDecode()
in response to user input, as in the following example run:
> python simple-caesar.py Encode, decode, or quit? encode Encoding is not yet implemented. Encode, decode, or quit? alaska Sorry, I didn't understand your input. Encode, decode, or quit? decode Decoding is not yet implemented. Encode, decode, or quit? quit Okay, bye!
Get your program working in this way before moving on to the next step.
Now modify interactiveEncode()
and interactiveDecode()
to make calls to caesarEncode()
and caesarDecode()
, respectively:
> python simple-caesar.py Encode, decode, or quit? encode What is your plaintext? follow the elk What shift do you want to use? 3 I don't yet know how to encode "follow the elk". Your ciphertext is: [the correct ciphertext, of course] Encode, decode, or quit? quit Okay, bye!
Now modify caesarEncode()
so that it encodes the given plaintext and returns the ciphertext. Note that this function should not print anything!
> python simple-caesar.py Encode, decode, or quit? encode What is your plaintext? follow the elk What shift do you want to use? 3 Your ciphertext is: iroorz wkh hon Encode, decode, or quit? quit Okay, bye!
To do this step, you'll need the ord()
and chr()
functions (remember what they do?), as well as some clever loops and conditionals.
Non-letter characters should just be passed into the ciphertext unchanged. You can choose how to handle capital letters: you may either convert capitals to capitals, or just convert everything to lowercase if you'd like. Numbers are “non-letter characters”, so they should just pass through like anything else. (I know this makes your math secrets way less secure, but it's not worth the hassle for you to do that for this assignment.)
Finally, modify caesarDecode()
so that it does the decoding. The body of this function should consist of only a single line of code after the docstring. There is no need to write complicated new logic; you can make just a single function call to get the correct plaintext, and you can return it directly without saving it to a variable first!
Congratulations! You've now written an encoder and decoder for the simple Caesar cipher, and wrapped them up in an interactive program! You'll turn in your simple-caesar.py file at the end of this assignment.
A slightly harder, but dramatically more effective variation of the Caesar cipher is called the “double-Caesar” cipher. It works just like the Caesar cipher, but each letter in your message gets shifted by a different amount. How much? It is based on another string of text, called the key. Each letter in the key tells you how many letters to advance: an “a” is 0, a “b” is 1, and so on.
For example, if your original message is “hello you” and your key is “dog”, the “d” means that you shift the first letter by 3, the “o” means you shift the second letter by 14, and the “g” means you shift the third letter by 6. You then repeat the pattern: the fourth letter of the plaintext (“hello you”) gets shifted by 3, the fifth letter gets shifted by 14, and so on. This produces the encrypted message "ksroc eri". Double-Caesar ciphers are actually quite hard to crack.
Just to make it clear, here's that same encoding again:
Original: h e l l o y o u Repeated key: d o g d o g d o Encrypted: k s r o c e r i
Create a Python file called double-caesar.py with the same five methods as the last program (you might want to just copy over the skeleton code from Part 1, but remember to update the docstrings so they reflect your new task!). Here is an example session with the program you should produce:
> python double-caesar.py Encode, decode, or quit? encode What is your plaintext? hello you What is your key? dog Your ciphertext is: ksroc eri Encode, decode, or quit? decode What is your ciphertext? ksroc eri What is your key? dog Your plaintext is: hello you Encode, decode, or quit? quit Okay, bye!
Go through the whole development process again, following the software-development practices outlined above. Make sure your code is always in a working state. Implement one feature at a time, and be sure it's working correctly before you move on to the next thing.
(For this program, the one-line restriction on caesarDecode()
no longer applies. Keep in mind, though, that part of the point of functions is to avoid repeating the same code twice in the same program, so you should still be trying to make efficient use of logic that you've already implemented elsewhere in your code. If you'd like to try, with the assistance of a new helper function it is still possible to write caesarDecode()
in a single line.)
Once you've got the double-Caesar program working, there's just one last step...
All the parts up until this point were worth a total of 39 points. For the full 40, create one more program (in the file double-caesar-spaces.py) that is a variation of your double-Caesar program that also encrypts and decrypts spaces between words (instead of just leaving them unchanged). Spaces should be encoded just like any regular letter; this way, someone looking at the encrypted message can’t tell how many letters were in each word in the plain text.
To do this, the simplest way is to change your setup so that you think of your alphabet as having 27 letters: the original 26 letters, plus a space at the end. So a space character in the plaintext, if shifted by 3, should result in a “c” in the ciphertext; the letter “x” in the plaintext, if shifted by 3, should result in a space character in the ciphertext. So your encoded message may have spaces in it, but they won’t correspond to positions where spaces were in the plain text! How diabolical!
Put your programs (simple-caeser.py, double-caesar.py, and double-caesar-spaces.py) in a zip file called hw8.zip. Remember, the command to do this is:
zip hw8.zip simple-caesar.py double-caesar.py double-caesar-spaces.py
Then submit your zip file via Moodle.