Alright, are you ready to write a script that does what you just did by hand?
Before getting started, think about this set of steps as a conversion between by-hand to pseudo code for programming.
Now let's go!
A script that does the Caesar cipher has to start by finding a character in an alphabet. In this example, you will write a script that creates an alphabet string and finds the index of a certain character. In other words, it will tell you that A has an index of 0, B has an index of 1, and so on, up through Z, which has an index of 25.
# find_letter_in_alphabet from microbit import * sleep(1000) alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" letter = "M" index = alpha.find(letter) print("letter =", letter, "index =", index) print()
When the script runs, the terminal should print index = 12 since that is the index of M in the string alpha.
Every cipher begins with an alphabet, so the script starts by creating a string named alpha, which contains all the uppercase letters in the alphabet.
alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
The .find() method is something available to each string in Python. .find() searches the string for whatever text is passed to it from inside the parentheses, and it returns the index where the text is first located. Remember that the index numbers in a string start at zero. So, the first character A has an index of 0, B has an index of 1, and so on, through Z with an index of 25. In this counting system, M has an index of 12 in the alpha string. So, letter = "M" followed by alpha.find(letter) returns 12.
“…and now, for something completely different!”
You might have already experimented with the .find method in Try This: Find the Substring [1]. It had examples of finding the starting index values of words like "Arthur" and "run", and even used the index to decide what to do next. In this encryption example, the alpha string’s .find() method is instead used to locate a single letter within a string.
This next script will encrypt individual letters with the Caesar cipher. As-written it encrypts the letter M with a key of 5.
# caesar_encrypt_letter from microbit import * sleep(1000) key = 5 letter = "M" alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" index = alpha.find(letter) print("key =", key) print("letter =", letter, ", index =", index) index = index + key index = index % 26 result = alpha[index] print("new index =", index, ", result =", result) print()
What if a script receives a Caesar encrypted character and a key? Do you need to write another script? The answer is no. To decrypt a ciphertext letter to plaintext, just use the negative of the key that encrypted it. So, if a ciphertext character was encrypted with 5, it can be decrypted with -5.
Remember how a plaintext letter M encrypted to a ciphertext letter R when the key was 5? To decrypt a ciphertext letter of R with a key of 5, just run it through the same Caesar cipher with a key of -5. The result will be the plaintext character M.
Once your script knows the index of a character in a string, it can simply add the key to the index and use the result to get the ciphertext character.
The first four statements are the same as the previous activity. So when key = 5 and the plaintext letter = M, alpha.find(letter) returns 12, which gets stored in index. Now for the Caesar shift. index = index + key evaluates to index = 12 + 5, which is 17. With result = alpha[index], that’s result = alpha[17]. Since alpha[17] returns "R", that’s the ciphertext character the result variable stores.
Remember the circular shift? That’s when the next character after Z in a Caesar shift wraps around to A. For example, with a plaintext letter of X and a key of 5, we want a ciphertext letter of C.
This circular shift is made possible by index = index % 26. The % operator is called the modulus operator, and it returns what’s left over from an integer division operation. Unlike floating point division where 19 / 5 = 3.8, integer division’s quotient result would be 3 with a remainder of 4. Integer division always rounds down, so 19 // 5 = 3. The remainder is 19 % 5 = 4. That remainder of 4 is another way of expressing the 0.8 part of 3.8 because 0.8 * 5 = 4. Another way to think about the remainder is that 3 * 5 = 15, which is 4 below the original numerator of 19.
After index = index + key, 24 is still Y, and 25 is still Z, but 26 isn’t in the alphabet. In fact, alpha[26] would cause an exception in the script by making it look for a character that’s not there. 26 needs to be changed to 0, 27 needs to be changed to 1, 28 needs to be changed to 2 and so on… That’s exactly what index = index % 26 does, as you can see from the right column in this table. It also works fine for values below 26.
Integer quotient, integer remainder
24 // 26 = 0 24 % 26 = 24
25 // 26 = 0 25 % 26 = 25
26 // 26 = 1 26 % 26 = 0
27 // 26 = 1 27 % 26 = 1
28 // 26 = 1 28 % 26 = 2
29 // 26 = 1 29 % 26 = 3
Here is an example with a key of 5 and the plaintext letter X. See how the result of 28 % 26 is 2? The letter C in the alpha string has an index of 2, and that’s the correct result if the script starts with X and a key of 5.
Let’s say your script receives the ciphertext letter R and an encryption key of 5. How would it decrypt to get back to the plaintext letter M?
The answer is to use the same algorithm, but reverse the sign of the key so that it’s -5. The index of R is 17, so index = index + key would be index = 17 + -5 = 12. That’s the index of M, and that’s how the script can use the negative value of a key to decrypt from a ciphertext character back to the original plaintext character.
The ROT-13 cipher is a special case of the Caesar cipher where the characters are shifted right 13 places. Are you ready to create a ROT-13 cipher? Hint: key = 13!
Links
[1] https://learn.parallax.com/tutorials/robot/cyberbot/strings-characters-primer/compare-find-check/try-find-substring