Cryptopals Set 1 Basic Walkthrough

My walkthrough solutions of Cryptopals set 1 challenges. Written in Python.

The challenge can be accessed here, and my solutions can be accessed here.

Challenge 1: Convert hex to base64

1
2
3
4
5
6
7
8
9
10
11
12
13
import base64

from utils import is_strs_equal

original_str = "49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d"

raw_str = bytes.fromhex(original_str)

str_to_byte = base64.b64encode(raw_str).decode()

verify_str = "SSdtIGtpbGxpbmcgeW91ciBicmFpbiBsaWtlIGEgcG9pc29ub3VzIG11c2hyb29t"

is_strs_equal(verify_str, str_to_byte)

Challenge 2: Fixed XOR

1
2
3
4
5
6
7
from utils import hex_xor, is_strs_equal

h1 = "1c0111001f010100061a024b53535009181c"
h2 = "686974207468652062756c6c277320657965"
verify = "746865206b696420646f6e277420706c6179"

is_strs_equal(hex_xor(h1, h2).hex(), verify)

Challenge 3: Single-byte XOR cipher

1
2
3
4
from utils import str_xor_char_attack

input_str = "1b37373331363f78151b7f2b783431333d78397828372d363c78373e783a393b3736"
print(str_xor_char_attack(input_str))

Output result:

1
(("Cooking MC's like a pound of bacon", 'X'), 205.57999999999998)

Challenge 4: Detect single-character XOR

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from utils import str_xor_char_attack

f = open('4.txt', 'r')
strings = f.readlines()
f.close()

result_dict = {}

for index in range(len(strings)):
    try:
        str_decrypted = str_xor_char_attack(strings[index])
        result_dict[(index, str_decrypted[0])] = str_decrypted[1]
    except:
        pass

dict_ordered = sorted(result_dict.items(), key=lambda x:x[1], reverse=True)
print("((line index, ('decrypted messege', 'key')), score)")
print(dict_ordered[0])

Output result:

1
2
((line index, ('decrypted messege', 'key')), score)
((170, ('Now that the party is jumping\n', '5')), 179.53999999999996)

Challenge 5: Implement repeating-key XOR

1
2
3
4
5
6
7
8
9
from utils import key_word_encryption, is_strs_equal

input_str = "Burning 'em, if you ain't quick and nimble\nI go crazy when I hear a cymbal"
verify_str = "0b3637272a2b2e63622c2e69692a23693a2a3c6324202d623d63343c2a26226324272765272a282b2f20430a652e2c652a3124333a653e2b2027630c692b20283165286326302e27282f"

key_word = "ICE"

output_str = key_word_encryption(key_word, input_str)
is_strs_equal(output_str, verify_str)

utils.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
from math import floor
import pandas as pd
import string

# verify 2 strings equal
def is_strs_equal(s1, s2):
if s1 == s2:
print("Correct!")
else:
print("Wrong answer.")

# input: byte strings b1, b2
# output: byte string b1 xor b2
def bytes_xor(b1, b2):
return bytes([bit1 ^ bit2 for (bit1, bit2) in zip(b1, b2)])

# input: hex stirng h1, h2
# output: h1 xor h2 in bytes
def hex_xor(h1, h2):
h1_bytes = bytes.fromhex(h1)
h2_bytes = bytes.fromhex(h2)
h1_xor_h2 = bytes_xor(h1_bytes, h2_bytes)
return h1_xor_h2

# input: hex h1, str char1
# output: h1 xor char1 in bytes
def hex_xor_single_letter(h1, char1):
h1_bytes = bytes.fromhex(h1)
char1_bytes = bytes(char1, 'utf-8') * len(h1_bytes)
h1_xor_char1 = bytes_xor(h1_bytes, char1_bytes)
return h1_xor_char1

# input: none
# output: dict of letter frequency. 'a': 0.
def get_ENfreq_table():
freq_table = pd.read_csv("freq_table.csv")
letter = []
frequency = []
for i in freq_table['letter']:
letter.append(i)
for freq_str in freq_table['frequency']:
freq_float = float(freq_str.replace('%',''))
frequency.append(freq_float)
freq_dict = dict(zip(letter, frequency))
freq_dict[' '] = 10.0
return freq_dict

# input: string
# output: scoring in float
def single_letter_scoring(some_str):
freq_dict = get_ENfreq_table()
score = 0
for char in some_str:
if char.upper() in freq_dict.keys():
score += freq_dict[char.upper()]
return score

# input: a encrypted str by a single char
# output: highest scoring one
def str_xor_char_attack(some_string):
ascii_str = string.printable
result_dict = {}
for letter in ascii_str:
letter_xor_input = hex_xor_single_letter(some_string, letter).decode('utf-8')
result_score = single_letter_scoring(letter_xor_input)
result_dict[(letter_xor_input, letter)] = result_score

dict_ordered = sorted(result_dict.items(), key=lambda x:x[1], reverse=True)
return dict_ordered[0]

# input: a key word and a string
# output: an encrypted string
def key_word_encryption(keyword, pt):
keyword_bytes = bytes(keyword,'utf-8')
pt_bytes = bytes(pt, 'utf-8')
len_keyword = len(keyword_bytes)
len_pt = len(pt_bytes)
keyword_ext = keyword_bytes * floor(len_pt/len_keyword)
len_ext = len(keyword_ext)
if len_ext != len_pt:
diff = len_pt - len_ext
keyword_ext = keyword_ext + keyword_bytes[:diff]
if len(keyword_ext) == len_pt:
output_bytes = bytes_xor(keyword_ext, pt_bytes)
return output_bytes.hex()
else:
print("Error!")
return