CPSC120
Fundamentals of Computer Science

Activity 23

String Comparison

Caesar Cipher

Most of us like to keep secrets. For example, I don't want you to know my credit card number. I'd rather you not be able to buy things with my money. However, communication over the Internet is like trying to shout in a crowded room. Everyone can hear what you are saying. So, to protect our valuable secrets, we rely on encryption schemes. Today, you are going to implement a simplistic encryption scheme known since Roman times: Caesar cipher.

Encryption in the Caesar schema relies on shifting letters in the alphabet. By default, this shift is 3 letters. So, a becomes d, b becomes e, etc. The only issue with this is at the end of the alphabet. The end of the alphabet wraps around to the front. So, x becomes a, y becomes b, and z becomes c.

Details

Create the function encode_caesar(plaintext: str) -> str that encodes text using the Caesar cipher. The parameter plaintext is a string of lowercase characters. The function should return a string of lowercase characters, equivalent to the plaintext, with each character shifted by 3.

Test Cases

import test

def encode_caesar(plaintext: str) -> str:
    # Put your code here

def main() -> None:
    test.equal(encode_caesar("hello"), "khoor")
    # Put more test cases here
    return None

main()

Hint

  • The ord function gives you the numeric representation of a character.

  • Use addition to shift the numeric representation of the character by 3.

  • The numeric representation may be too large after the shift. If it is, subtract 26 to wrap the number around to the beginning.

  • Finally, use the chr function to convert the numeric value back to a character.

Challenge

Encryption for Caesar is useful, but to talk to anyone, they need to decrypt the Caesar encryption. Write the function decode_caesar(ciphertext: str) -> str that decrypts the ciphertext.

Substitution Cipher

Another common technique for encryption is known as the Substitution Cipher. Instead of defining a linear equation between the plaintext and ciphertext, you can specify a map that translates characters. Cryptograms use this technique, for example.

The substitution cipher key is a string with all of the letters of the alphabet. For example, the string "dsgnotpjvizcbqlaeukxymwhrf" is one possible key for the substitution cipher. It maps maps 'a' to 'd', 'b' to 's', 'c' to 'g', and so on. For example, "hello world" would become "joccl wlucn" under this key.

Details

Create the function encode_substitution(plaintext: str, key: str) -> str that encodes text using the substitution cipher. The parameter plaintext is a string of lowercase characters. The parameter key is a string with the lowercase letters of the alphabet in any order. The function should return plaintext encoded using the substitution cipher and key.

Test Cases

import test

def encode_substitution(plaintext: str, key: str) -> str:
    # Put your code here

def main() -> None:
    test.equal(encode_substitution("hello", "swlpndbzkmhueqgtoravyixcfj"), "znuug")
    # Put more test cases here
    return None

main()

Hint

  • For each character in plaintext, compute its index in the alphabet. The character 'a' is 0, 'b' is 1, 'c' is 2, etc.

  • Use the character’s index to get the character at the same index in the key string. This is the encrypted value of the character.

  • Return the accumulated encrypted characters.

Challenge

Write the function decode_substitution(ciphertext: str, key: str) -> str that decrypts the ciphertext with the associated key.

Vigenere Cipher

Cryptography is the science of hiding secrets. A typical cryptographic protocol converts a message into an unreadable ciphertext by anyone who does not know the key.

We observed a very simplistic encryption scheme: Caesar cipher. This encryption scheme has been in use since Roman times and is very easy to break. As such, we likely want a more robust encryption scheme to make our messages even more secure.

The key in the Caesar cipher is 3. To encrypt, you chose the letter 3 spaces further in the alphabet of the entered character. In Vigenere, the key is a word. Each letter in the key encodes a different amount of shift.

Consider the plaintext "hello", with a key of "hi". To encrypt the "h" in hello, you use the letter "h" as the shift. This is a shift of 7 since h is the 7th character of the alphabet. Then, for the letter "e" in hello, you would use the letter "i" as the shift, which is a shift of 8. But, now we have run out of letters in the key. So we wrap back around to the beginning of the key string, using "h" as the key for the first "l" in hello.

Details

Create the function encode_vigenere(plaintext: str, key: str) -> str that encodes text using the Vigenere cipher. Both parameters plaintext and key are strings of lowercase characters. The function should return plaintext encoded using the Vigenere cipher and key.

Test Cases

import test

def encode_vigenere(plaintext: str, key: str) -> str:
    # Put your code here

def main() -> None:
    test.equal(encode_vigenere("hello", "hi"), "omstv")
    # Put more test cases here
    return None

main()

Hint

  • The encryption is similar to the Caesar cipher, with a change to the shift that occurs.

  • Instead of always shifting by 3, get a character from key and convert it to a shift amount.

  • To get a character from the key, use the index operator with the loop variable, i.

  • If the key string is shorter than the plaintext string, use the % operator to wrap the index back to the key’s beginning.

  • Convert the shift character to a number between 0 and 25 by subtracting ord('a') from the character’s ordinal value.

Challenge

The Vigenere cipher is possibly the strongest classical cipher you will encounter. This means that writing a decryption function is of the utmost importance. Write the function decode_vigenere(ciphertext: str, key: str) -> str that decrypts the ciphertext with the associated key.