Enigma
Calvin (Deutschbein)
W12Mon: 11 Nov
Announcements
- Midterm Sketch
- Project 5 Posted
- Guest Lecture Wednesday
Today
- Enigma
- Cryptography
- Rotations
- Ciphers
- Starter Code
Cryptography
- The essence of P4: Enigma is cryptography.
- Here are words I use to do cryptography.
- "Encryption" takens a "plaintext" and produces a "cypertext".
- A "cyphertext" is "encryped."
- Here is a minimal example, encryption through reversal.
def encrypt(plaintext:str) -> str:
cyphertext = plaintext[::-1]
return cyphertext
- We imagine this as a way to keep something secret.
>>> encrypt("hello world")
'dlrow olleh'
Rotations
- Reversal is a little to boring and too easy.
- A "rotation" (also called a "Caesar cipher") is more sophisticated.
- It works by taking each letter and advancing it through the alphabet by some amount.
- "D" is 3 further through the alphabet than "A".
>>> _ = [print(i,c) for i,c in enumerate("ABCD")]
0 A
1 B
2 C
3 D
Rotations
- Rotations are not too bad because we can solve them one letter at a time.
- Basically we imagine it looks like this:
- The core insight here is we can consider each letter separately.
- So we could write a function from one letter to another.
def rotate(letter:str) -> str:
pass
Rotations
- We write a function from one letter to another.
def rotate(letter:str) -> str:
pass
- To go from "A" to "D" we:
- Found the index of "A" in the alphabet
- Added 3
- Found the letter of that index in the alphabet.
Rotations
- To go from "A" to "D" we:
- Found the index of "A" in the alphabet
>>> s = "ABCD"
>>> s.index("A")
0
- "A" is at index 0 in the alphabet.
- On Enigma, the full alphabet is defined in "EnigmaConstants.py"
ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Rotations
- We write a function from one letter to another.
ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
def rotate(letter:str) -> str:
ind_in_abcs = ALPHABET.index(letter)
- To go from "A" to "D" we:
- ✓ Found the index of "A" in the alphabet
- Added 3
- Found the letter of that index in the alphabet.
Rotations
- We write a function from one letter to another.
ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
def rotate(letter:str) -> str:
ind_in_abcs = ALPHABET.index(letter)
ind_in_abcs += 3
- To go from "A" to "D" we:
- ✓ Found the index of "A" in the alphabet
- ✓ Added 3
- Found the letter of that index in the alphabet.
Rotations
- We write a function from one letter to another.
ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
def rotate(letter:str) -> str:
ind_in_abcs = ALPHABET.index(letter)
ind_in_abcs += 3
return ALPHABET[ind_in_abcs]
- To go from "A" to "D" we:
- ✓ Found the index of "A" in the alphabet
- ✓ Added 3
- ✓ Found the letter of that index in the alphabet.
Rotations
- This is good:
def rotate(letter:str) -> str:
ind_in_abcs = ALPHABET.index(letter)
ind_in_abcs += 3
return ALPHABET[ind_in_abcs]
- But it breaks if we try to rotate "Z"
>>> rotate("Z")
Traceback (most recent call last):
IndexError: string index out of range
Rotations
- It breaks if we try to rotate "Z"
>>> rotate("Z")
Traceback (most recent call last):
IndexError: string index out of range
- We need to "loop back" from index 25 ("Z" + 1) to 0 ("A").
>>> (25 + 1) % 26
0
Rotations
- This is good:
def rotate(letter:str) -> str:
ind_in_abcs = ALPHABET.index(letter)
ind_in_abcs += 3
ind_in_abcs %= len(ALPHABET)
return ALPHABET[ind_in_abcs]
- Now we can rotate "Z"
>>> rotate("Z")
'C'
Rotations
- Really though, we want to be able to specify how far we rotate..
def rotate(letter:str, offset:int) -> str:
ind_in_abcs = ALPHABET.index(letter)
ind_in_abcs += offset
ind_in_abcs %= len(ALPHABET)
return ALPHABET[ind_in_abcs]
- Now we can rotate "Z"
>>> rotate("A",3)
'D'
>>> rotate("A",-1)
'Z'
Rotations
- Then we can encrypt (and decrypt)
def encrypt(letter:str) -> str:
return rotate(letter, 17)
def decrypt(letter:str) -> str:
return rotate(letter, -17)
- Now we can rotate "Z"
>>> encrypt("A")
'R'
>>> decrypt(encrypt("A"))
'A'
Today
- Enigma
- ✓ Cryptography
- ✓ Rotations
- Ciphers
- Starter Code
Rotations
- Let's look back at fixed offset rotate:
def rotate(letter:str) -> str:
ind_in_abcs = ALPHABET.index(letter)
ind_in_abcs += 3
ind_in_abcs %= len(ALPHABET)
return ALPHABET[ind_in_abcs]
- We can think of this a rotation, or as a letter substitution.
Rotations
- Let's look back at fixed offset rotate:
CIPHER = ALPHABET[3:] + ALPHABET[:3]
# or 'DEFGHIJKLMNOPQRSTUVWXYZABC'
def rotate(letter:str) -> str:
ind_in_abcs = ALPHABET.index(letter)
return CIPHER[ind_in_abcs]
- We can use a string to specify how we encrypt!
- The string is of length 26
- Only contains capital letters, and
- Contains no duplicates!
Rotations
- Ciphers don't only have to be rotations!
ROTOR_PERMUTATIONS = [
"EKMFLGDQVZNTOWYHXUSPAIBRCJ", # Permutation for slow rotor
"AJDKSIRUXBLHWTMCQGZNPYFVOE", # Permutation for medium rotor
"BDFHJLCPRTXVZNYEIWGAKMUSQO" # Permutation for fast rotor
]
- Here are three historically significant rotations.
- They were discovered by cool gay people that defeated the Nazis.
- "Be gay, stop war crime."
- They provided with Project 4.
Ciphers
- We can imagine encryption other than rotation.
- We call these "letter-substitution ciphers" or "cryptograms"
- I just call them ciphers (shorter)
CIPHER = "BDFHJLCPRTXVZNYEIWGAKMUSQO"
def encrypt(letter:str) -> str:
ind_in_abcs = ALPHABET.index(letter)
return CIPHER[ind_in_abcs]
- This allows us to create codes!
- We use a cipher, and some plaintext, to create encrypted "cyphertext"
Ciphers
- We may want to be able to use different ciphers, like the 3 from the assignment.
def encrypt(cipher:str, letter:str) -> str:
ind_in_abcs = ALPHABET.index(letter)
return cipher[ind_in_abcs]
- This way we can encrypt any letter with any cipher.
Ciphers
- To decrypt, we go backwards, but in a novel way.
- Rather than finding index in the alphabet, find index in the cipher!
- Rather than finding a corresponding letter in the cipher, find it in the alphabet!
def encrypt(cipher:str, letter:str) -> str:
ind_in_abcs = ALPHABET.index(letter)
return cipher[ind_in_abcs]
def decrypt(cipher:str, letter:str) -> str:
ind_in_ciph = cipher.index(letter)
return ALPHABET[ind_in_ciph]
- This way we can encrypt any letter with any cipher.
Ciphers
- To decrypt, we go backwards, but in a novel way.
>>> encrypt(CIPHER, "M")
'Z'
>>> decrypt(CIPHER, "Z")
'M'
- I also call these "forward and backward"
Today
- Enigma
- ✓ Cryptography
- ✓ Rotations
- ✓ Ciphers
- Starter Code
Starter code
- I found the starter code a bit confusing.
- Basically, you need to know what certain methods are intended to do in EngimaModel.py.
- I ignored:
- On milestone 0, I updated:
- key_pressed
- key_released
- is_key_down
- These are essentially parts of event listeners that attached to a graphics window in another file.
Starter code
- On milestone 1, I updated:
- key_pressed
- key_released
- is_lamp_on
- The key corresponds to the letter being encrypted, and the lamp corresponds to the return value of the encryption.
- On milestone 1, you haven't written any encrpytion, so they are the same!
Starter code
- On milestone 2, I updated:
- get_rotor_letter
- rotor_clicked
- I also created some new attributes, which could be set during __init__
- We'll get to that soon!
Starter code
- On milestone 3, I updated:
- This is encryption with a single cipher.
Debug Print
- Starting in milestone 3, I wrote very detailed print statements.
- This code is from milestone 3 part c, an optional milestone I made for myself.
- A letter is thrice encryped by all three rotors.
- Having this made the next milestone not only easier, but possible!
Beginning loop with i = 2
before forward: letter = A letter's index in abcs = 0
after forward: letter = B letter's index in cipher = 0
Beginning loop with i = 1
before forward: letter = B letter's index in abcs = 1
after forward: letter = J letter's index in cipher = 1
Beginning loop with i = 0
before forward: letter = J letter's index in abcs = 9
after forward: letter = Z letter's index in cipher = 9
Starter code
- On milestone 4, I updated:
- On this milestone...
- A letter is "forward" encrypted 3 times by 3 ciphers
- This letter is encrypted once more with a "reflector"
- The letter is "reverse" encryped backwards through the ciphers
- The output result is a fully encrypted letter that lights up a corresponding lamp.
Starter code
- On milestone 5, I updated:
- On this milestone...
- A letter previously based through 3 ciphers forward and backward.
- Now, I rotate the letter before and after passing through the cipher.
- We can think of this as 2 times 3 times 3 ciphers, or just as two for loops.
- Not really understandable without Milestones 2 and 4, so just do those first!
- Then you're done!
Visualizing
- Right-to-left-to-right.