-
Notifications
You must be signed in to change notification settings - Fork 0
/
aes.py
191 lines (175 loc) · 6.37 KB
/
aes.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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
import os
import base64
import xor
import pkcs7
from Crypto.Cipher import AES
def decrypt_ecb_128(ciphertext, key, no_unpad = False):
plaintext = AES.new(key, AES.MODE_ECB, "").decrypt(ciphertext)
if no_unpad:
return plaintext
else:
return pkcs7.unpad(plaintext)
def encrypt_ecb_128(plaintext, key):
return AES.new(key, AES.MODE_ECB, "").encrypt(pkcs7.pad(plaintext, 16))
def decrypt_cbc_128(ciphertext, iv, key, no_unpad = False):
cipher = AES.new(key, AES.MODE_ECB, "")
plaintext = ""
for i in range(0, len(ciphertext) / 16):
block = ciphertext[16 * i : 16 * (i + 1)]
if i == 0:
plaintext = plaintext + xor.xor(cipher.decrypt(block), iv)
else:
prev_block = ciphertext[16 * (i - 1): 16 * i]
plaintext = plaintext + xor.xor(cipher.decrypt(block), prev_block)
if no_unpad:
return plaintext
else:
return pkcs7.unpad(plaintext)
def encrypt_cbc_128(plaintext, iv, key):
cipher = AES.new(key, AES.MODE_ECB, "")
ciphertext = ""
plaintext = pkcs7.pad(plaintext, 16)
for i in range(0, len(plaintext) / 16):
block = plaintext[16 * i : 16 * (i + 1)]
if i == 0:
ciphertext = ciphertext + cipher.encrypt(xor.xor(block, iv))
else:
prev_block = ciphertext[16 * (i - 1): 16 * i]
ciphertext = ciphertext + cipher.encrypt(xor.xor(block, prev_block))
return ciphertext
#detects the score of a ciphertext. Higher score means
#more chance that this text was encrypted by AES-128 in ECB mode
def score_ecb(ciphertext, block_size = 16):
score = 0
while len(ciphertext) > block_size:
for j in range(1, len(ciphertext) / block_size):
if ciphertext[:block_size] == ciphertext[block_size * j:block_size * (j + 1)]:
score += 1
ciphertext = ciphertext[block_size:]
return score
def encryption_oracle(plaintext):
key = os.urandom(16)
alg = ord(os.urandom(1)) % 2
prefix = os.urandom(5 + (ord(os.urandom(1)) % 6))
postfix = os.urandom(5 + (ord(os.urandom(1)) % 6))
plaintext = prefix + plaintext + postfix
if(alg == 0):
return (encrypt_ecb_128(plaintext, key), 0)
else:
iv = os.urandom(16)
return (encrypt_cbc_128(plaintext, iv, key), 1)
#returns pair: (block_size, pad_size)
#pad_size - the length of padding at the end of secret data
def identify_block_size(func_sample):
feed = ""
start_len = len(func_sample(feed))
cur_len = start_len
while cur_len == start_len:
feed = feed + "A"
cur_len = len(func_sample(feed))
return (cur_len - start_len, len(feed))
def ecb_byte_at_a_time_attack(func_sample):
#1. Identify block size
(block_size, pad_size) = identify_block_size(func_sample)
secret_size = len(func_sample("")) - pad_size
secret = ""
#2. Make sure that we are working with AES-ECB
score = score_ecb(func_sample("A" * block_size * 4), block_size)
if score == 0:
raise Exception("Looks like func_sample is not AES-ECB-based")
#3. For each secret text byte create dictionary and find corresponding
# plain text byte
prefix = "A" * (block_size - 1)
filling = prefix
for k in range(0, secret_size):
block_dict = {}
for i in range(0, 0x100):
key = filling + chr(i)
block_dict[func_sample(key)[:block_size]] = chr(i)
offset = len(secret) / block_size
byte_key = func_sample(prefix)[offset * block_size: (offset + 1) * block_size]
secret = secret + block_dict[byte_key]
if prefix == "":
prefix = "A" * (block_size - 1)
else:
prefix = prefix[:-1]
filling = filling[1:] + secret[-1]
return secret
def _ecb_break_byte_at_a_time_sample(plaintext):
secret_str = ( "Um9sbGluJyBpbiBteSA1LjAKV2l0aCBteSByYWctdG9wIGRvd24gc28gbXkg"
"aGFpciBjYW4gYmxvdwpUaGUgZ2lybGllcyBvbiBzdGFuZGJ5IHdhdmluZyBq"
"dXN0IHRvIHNheSBoaQpEaWQgeW91IHN0b3A/IE5vLCBJIGp1c3QgZHJvdmUg"
"YnkK" )
key = _ecb_break_byte_at_a_time_sample.secret_key
return encrypt_ecb_128(plaintext + base64.decode(secret_str), key)
_ecb_break_byte_at_a_time_sample.secret_key = os.urandom(16)
def _ecb_break_byte_at_a_time_sample2(plaintext):
secret_str = ( "Um9sbGluJyBpbiBteSA1LjAKV2l0aCBteSByYWctdG9wIGRvd24gc28gbXkg"
"aGFpciBjYW4gYmxvdwpUaGUgZ2lybGllcyBvbiBzdGFuZGJ5IHdhdmluZyBq"
"dXN0IHRvIHNheSBoaQpEaWQgeW91IHN0b3A/IE5vLCBJIGp1c3QgZHJvdmUg"
"YnkK" )
key = _ecb_break_byte_at_a_time_sample2.secret_key
prefix = _ecb_break_byte_at_a_time_sample2.prefix
return encrypt_ecb_128(prefix + plaintext + base64.decode(secret_str), key)
_ecb_break_byte_at_a_time_sample2.secret_key = os.urandom(16)
_ecb_break_byte_at_a_time_sample2.prefix = os.urandom(ord(os.urandom(1)))
def _solve_set1_ch7():
file = open("./input/7.txt", "r")
data = file.read().replace("\n", "")
file.close()
print decrypt_ecb_128(base64.decode(data), "YELLOW SUBMARINE")
def _solve_set1_ch8():
max_score = 0
score = 0
aes_ciphertext = ""
file = open("./input/8.txt", "r")
data = file.read().split("\n")
file.close()
for hex_ciphertext in data:
score = score_ecb(hex_ciphertext.decode("hex"))
if score > max_score:
max_score = score
aes_ciphertext = hex_ciphertext
print aes_ciphertext, max_score
def _solve_set2_ch10():
file = open("./input/10.txt", "r")
data = file.read().replace("\n", "")
file.close()
return decrypt_cbc_128(base64.decode(data), "\x00" * 16, "YELLOW SUBMARINE")
def _solve_set2_ch11():
plaintext = _solve_set2_ch10()
for i in range(0, 10):
(ciphertext, alg) = encryption_oracle(plaintext)
if alg == 0:
alg = "ECB"
else:
alg = "CBC"
if score_ecb(ciphertext) > 0:
print str(i) + " guessed alg is ECB (actual is {:s})".format(alg)
else:
print str(i) + " guessed alg is CBC (actual is {:s})".format(alg)
def _solve_set2_ch12():
return ecb_byte_at_a_time_attack(_ecb_break_byte_at_a_time_sample)
#This function finds secret prefix length. Knowing it, we can append corresponding
#number of bytes to the prefix to make it's length is a multiple of the block size
#AFter that we can use the same techic we used in challenge 12 to get secret postfix
def _solve_set2_ch14():
func_sample = _ecb_break_byte_at_a_time_sample2
block_size = identify_block_size(func_sample)[0]
data = "A"
enc_data1 = func_sample("A")
enc_data2 = func_sample("B")
for i in range(len(enc_data1)):
if enc_data1[i] != enc_data2[i]:
eq_blocks = i / block_size
break
for prefix_len in range(1, block_size):
enc_data1 = func_sample(data + "A")
enc_data2 = func_sample(data + "B")
data = data + "A"
for i in range(len(enc_data1)):
if enc_data1[i] != enc_data2[i]:
eq_blocks2 = i / block_size
break
if eq_blocks < eq_blocks2:
return block_size - prefix_len + eq_blocks * block_size