<jupyter_start><jupyter_text>**What is a One Time-Pad?****The one-time pad is an encryption technique that transforms plaintext to ciphertext.****Given a plaintext message, the one-time pad encrypts the plaintext with a randomly generated key that is at least as long as the length of the plaintext, if not longer. In order for the one-time pad to stay secure, a key that is generated randomly cannot be used more than once, hence the name of the encryption technique.** $\text{One-Time Pad Encryption}: E(k, p) \rightarrow c$$\text{One-Time Pad Decryption}: De(k, c) \rightarrow p$$\text{Where: } E = \text{Encryption}, D = \text{Decryption}, k = \text{randomly generated key}, p = \text{plaintext}, c = \text{ciphertext}$ Source: Lecture - [Introduction to Cryptography](https://www.cs.purdue.edu/homes/ninghui/courses/Fall05/lectures/355_Fall05_lect09.pdf) from Purdue University by Ninghui LiSource: Lecture - [Perfect Secrecy, One Time Pad, Randomness](https://www.cs.umd.edu/~gasarch/COURSES/456/F18/lecps/lecps.pdf) University of Maryland Course by <NAME>  **Security of a One-Time Pad****One way of measuring the security of a cipher is using Claude Shannon's definition of secrecy. If a hacker captures the ciphertext of an encryption method, they should not be able to decipher the plaintext from the ciphertext alone.** General Proof of Secrecy  $P[E(k, p_1) = c] = P[E(k, p_2) = c] \ \ \forall \ \ p_1, p_2, ... p_n \in \mathcal{P} \ \ \wedge \ \  \forall \ \ c \in \mathcal{C}$ $\text{Where}: \mathcal{P} = \text{Set of all plaintexts}, \mathcal{K} = \text{Set of all possible keys}, \mathcal{C} = \text{Set of all possible ciphertexts}$ One-Time Pad Proof of Secrecy - $\forall \ \ p, c: P[E(k,p) = c] = \frac{\text{ of keys in }\mathcal{K} \ s.t. \ E(k,p) = c}{\mathcal{|K|}}$$\text{Given $k$ chosen randomly}: P[E(k,p_0)=c] = P[E(k,p_1)=c] \ \ \forall \ \ c \in \mathcal{C} \ \ \wedge \ \ p \in \mathcal{P}$-  $\text{length(k) $\geq$ length(p)}$ **We can also take a look at an example of a one-time pad encryption. If we have a five letter word, "hello" that we want to encrypt using the one-time pad, if our encryption method is truly random and we can encrypt using each letter of the alphabet in English, there are: $26 * 26 * 26 * 26 * 26 \approx 12$ million combinations!** Source: Lecture - [Cryptography I](https://www.coursera.org/learn/crypto) Stanford Course by <NAME>Source: Video - [The one-time pad](https://www.youtube.com/watch?v=FlIG3TvQCBQ&t=32s) from Khan Academy  **One Time-Pad Encryption and Decryption** $c = (p+k) \  \% \text{ length(English Alphabet)}$ Source: Website - [One-time pad](https://en.wikipedia.org/wiki/One-time_pad) from Wikipedia<jupyter_code>def otp(message):
  plain_dict = {index: letter for index, letter in enumerate(string.ascii_lowercase)}
  inv_dict = {letter:index for index, letter in plain_dict.items()}
  message = message.lower()
  message = ''.join(letter for letter in message if letter.isalnum())
  key = []

  while len(key) < len(message):
    key.append(secrets.choice(range(0,len(plain_dict))))

  encrypt_list = [(inv_dict[let]+ind)%len(plain_dict) for let, ind in zip(message,key)]

  return [''.join([plain_dict[ind] for ind in encrypt_list]), key]
otp_encryption = otp('Hello world my name is adrian!')

otp_cipher = otp_encryption[0]
otp_key = otp_encryption[1]

print(f'The one time pad cipher text: {otp_cipher}')<jupyter_output>The one time pad cipher text: zjjlpxnhapfzcgjblqzjtmol<jupyter_text>$p = (c-k) \  \% \text{ length(English Alphabet)}$ Source: Website - [One-time pad](https://en.wikipedia.org/wiki/One-time_pad) from Wikipedia<jupyter_code>def otp_decryption(cipher_text, key):
  plain_dict = {index: letter for index, letter in enumerate(string.ascii_lowercase)}
  inv_dict = {letter:index for index, letter in plain_dict.items()}
  cipher_list = [inv_dict[let] for let in cipher_text]
  
  return ''.join([plain_dict[(c_index-key_index)%26] for c_index,key_index in zip(cipher_list, key)])
otp_decryption(otp_cipher, otp_key)
len(otp_key) == len(otp_decryption(otp_cipher, otp_key))<jupyter_output><empty_output><jupyter_text>**Problems with the One-Time Pad**  **Insecurity of Multiple Time Pad**<jupyter_code>plain_dict = {index: letter for index, letter in enumerate(string.ascii_lowercase)}
inv_dict = {letter:index for index, letter in plain_dict.items()}
message_1 = 'himom'
message_2 = 'hidad'
key = [11,4,6,3,6,17]
en_m1 = ''.join([plain_dict[ind] for ind in [(inv_dict[let]+ind)%len(plain_dict) for let, ind in zip(message_1,key)]])
en_m2 = ''.join([plain_dict[ind] for ind in [(inv_dict[let]+ind)%len(plain_dict) for let, ind in zip(message_2,key)]])

print(f'Encrypted Message 1: {en_m1}\nEncrypted Message 2: {en_m2}')
dict_attack_2l = ['of', 'to', 'in', 'it', 'is', 'be', 'as', 'at', 'so', 'we', 'he', 'hi', 'by', 'or', 'on', 'do', 'if', 'me', 'my', 'up', 'an', 'go', 'no', 'us', 'am']
dict_attack_3l = ['all', 'and', 'are', 'but', 'dad', 'mom', 'way']
bigram_list = ['no way', 'to all', 'to dad', 'to mom', 'in all', 'is all', 'so are', 'we are', 'he and', 'hi all', 'hi mom', 'hi dad', 'by all', 'do all', 'do and', 'if all', 'if and', 'my all',
               'my dad', 'my mom']
cracking_list_2l = []

for i in range(0,26):
  for j in range(0,26):
    cracking_list_2l.append([plain_dict[i] + plain_dict[j], [i,j]])

possible_2l_word = []

for i in cracking_list_2l:
  if i[0] in dict_attack_2l:
    possible_2l_word.append([i[0], i[1]])
  else:
    pass
cracking_list_3l = []

for i in range(0,26):
  for j in range(0,26):
    for k in range(0,26):
      cracking_list_3l.append([plain_dict[i] + plain_dict[j] + plain_dict[k], [i,j,k]])

possible_3l_word = []

for i in cracking_list_3l:
  if i[0] in dict_attack_3l:
    possible_3l_word.append([i[0], i[1]])
  else:
    pass
possible_2l_word
possible_3l_word
possible_phrases = []

for word_1 in possible_2l_word:
  for word_2 in possible_3l_word:
    if (word_1[0] + " " + word_2[0]) in bigram_list:
      possible_phrases.append(word_1[0] + word_2[0])
decrypt_list = []

for word in possible_phrases:
  decrypt_list.append([inv_dict[letter] for letter in word])
decrypt_list
en_m1_ind = [inv_dict[letter] for letter in en_m1]
en_m2_ind = [inv_dict[letter] for letter in en_m2]
possible_keys = []

for num_list in decrypt_list:
  possible_keys.append([x1 - x2 for x1, x2 in zip(en_m1_ind, num_list)])
possible_keys
final_list = []

for key in possible_keys:
  if any(num < 0 for num in key):
    pass
  else:
    final_list.append(key)

for key in final_list:
  try:
    print(''.join([plain_dict[ind] for ind in [x1 - x2 for x1, x2 in zip(en_m1_ind, key)]]), ''.join([plain_dict[ind] for ind in [x1 - x2 for x1, x2 in zip(en_m2_ind, key)]]), key)
  except:
    pass<jupyter_output>himom hidad [11, 4, 6, 3, 6]<jupyter_text>**Verify that the cipher has been cracked**$C_1 \oplus C_2 = P_1 \oplus P_2$<jupyter_code>#check to see if we cracked it
for i,j in zip(en_m1, en_m2):
  print((inv_dict[i]-inv_dict[j])%26)
for i,j in zip('himom', 'hidad'):
  print((inv_dict[i]-inv_dict[j])%26)<jupyter_output>0
0
9
14
9
{'fraction_non_alphanumeric': 0.15611754732975416, 'fraction_numerical': 0.021051144391070923, 'mean_word_length': 4.769356153219234, 'pattern_counts': {'":': 0, '<': 18, '<?xml version=': 0, '>': 16, 'https://': 6, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '4', 'dir': 'jupyter-structured-clean-dedup', 'id': '364529', 'license': 'MIT', 'n_tokens_mistral': 2751, 'n_tokens_neox': 2643, 'n_words': 774, 'path': 'Cryptography Workshops/Cryptography_with_Python!_One_Time_Pad.ipynb', 'repo_name': 'tudev/Workshops-2020-2021', 'size': '7090'}