In [1]:
from fidelio_functions import *
Before sending any messages, we must agree on a way to represent characters as numbers.
Fidelio comes with 3 pre-defined character encodings: ALL_CAPS
, CAPS_PLUS
, and DEFAULT_100
.
Each of these is a tuple for converting int -> char.
char_to_num()
creates a dictionary for converting char -> int.
In [2]:
print(ALL_CAPS)
In [3]:
for key, val in sorted(char_to_num(ALL_CAPS).items()):
print(key,val)
In [4]:
message = "WHERE IS RPT WHERE IS TASK FORCE THIRTY FOUR RR THE WORLD WONDERS?"
print(message)
In [5]:
# Default alphabet
ints = text_to_ints(message)
print(ints,'\n')
test_text = ints_to_text(ints)
print(test_text)
In [6]:
# ALL_CAPS alphabet has capital letters only, no punctuation or spaces
ints = text_to_ints(message,ALL_CAPS)
print(ints,'\n')
test_text = ints_to_text(ints,ALL_CAPS)
print(test_text)
The Caesar cipher shifts each letter in the alphabet back 3 places.
The alphabet "wraps around," meaning the letters ABC
are mapped to XYZ
.
To reproduce the Caesar cipher with Fidelio, first use ALL_CAPS
to convert text to integers.
Then subtract 3 using base 26 modular arithmetic and convert back to text.
To decrypt, do the same, but with a shift of +3 instead of -3.
In [7]:
ciphertext = caesar(message,-3,ALL_CAPS)
print(ciphertext)
plaintext = caesar(ciphertext,3,ALL_CAPS)
print(plaintext)
ROT13 is like the classic Caesar cipher, but it shifts each letter 13 characters forward: $m \rightarrow (m + 13) \% 26$.
Shifting each letter 13 characters backward gives the same effect: $m \rightarrow (m-13)\%26 = (m+13)\%26$.
The ROT13 transformation is its own inverse.
In [8]:
ciphertext = caesar(message,13,ALL_CAPS)
print(ciphertext)
plaintext = caesar(ciphertext,-13,ALL_CAPS)
print(plaintext)
# With a 26-character alphabet, +13 and -13 are the same shift
plaintext = caesar(ciphertext,13,ALL_CAPS)
print(plaintext)
In [9]:
# With the default 100-character alphabet, ROT50 is its own inverse
caesar(caesar(message,50),50)
Out[9]:
In [10]:
ciphertext = caesar(message,13,CAPS_PLUS)
print(ciphertext)
plaintext = caesar(ciphertext,-13,CAPS_PLUS)
print(plaintext)
In [11]:
for x in range(42):
print( caesar(ciphertext,x,CAPS_PLUS) )
This polyalphabetic cipher shifts characters using modular arithmetic, but characters are not all shifted by the same amount. There are many possible passwords, so brute-force attacks are much harder.
Choose a password, and be sure to use characters which are in the selected alphabet. The password is then repeated until it is the same length as the plaintext. Each integer $m_k$ in the plaintext is shifted $$ m_k \rightarrow (m_k+x_k) \% 26 $$ where $x_k$ is the corresponding integer in the extended password.
In [12]:
ints = text_to_ints('MEETMEATDAWN',ALL_CAPS)
print(ints,'\n')
extended_password = text_to_ints('FIDELIOFIDEL',ALL_CAPS)
print(extended_password,'\n')
cipher = [ (ints[k] + extended_password[k]) % 26 for k in range(len(ints)) ]
print(cipher,'\n')
ciphertext = ints_to_text(cipher,ALL_CAPS)
print(ciphertext,'\n')
decipher = [ (cipher[k] - extended_password[k]) % 26 for k in range(len(cipher)) ]
print(decipher,'\n')
plaintext = ints_to_text(decipher,ALL_CAPS)
print(plaintext)
In [13]:
# Try the original message and default alphabet
ciphertext = dodgson(message,'FIDELIO')
print(ciphertext)
plaintext = dodgson(ciphertext,'FIDELIO',decrypt=True)
print(plaintext)
In [14]:
# Let's try guessing the password
dodgson(ciphertext,'12345',decrypt=True)
Out[14]:
In [15]:
# Caution: a partially-correct password can recover parts of the message
ciphertext = dodgson(message,'Passw0rd123')
print(ciphertext)
plaintext = dodgson(ciphertext,'password123',decrypt=True)
print(plaintext)