-
Notifications
You must be signed in to change notification settings - Fork 0
/
set3.py
195 lines (153 loc) · 5.54 KB
/
set3.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
192
193
194
195
import os
import random
import time
import constants
import set1
import set2
import mt19937
def challange17_encrypt(plaintext):
iv = os.urandom(16)
ciphertext = set2.aes_cbc_encrypt(plaintext, constants.key, iv)
return ciphertext, iv
def challenge17_decrypt(ciphertext, iv):
plaintext = set2.aes_cbc_decrypt(
ciphertext, constants.key, iv, unpad=False)
try:
set2.pkcs7_unpad(plaintext, 16, True)
return True
except set2.PaddingError:
pass
return False
def challenge17_crack_block(block, block_c):
block_i = [0] * 16
block_p = [0] * 16
for i in range(16):
prefix = b'0'*(15-i)
suffix = bytes([block_i[k] ^ (i+1) for k in range(16-i, 16)])
for j in range(256):
chosen_ct = prefix + bytes([j]) + suffix + block
if challenge17_decrypt(chosen_ct, b'0'*16):
block_i[15-i] = j ^ (i + 1)
block_p[15-i] = block_c[15-i] ^ block_i[15-i]
break
else:
assert False
return bytes(block_p)
def challenge17_crack(ciphertext, iv):
plaintext = b''
for i in range(0, len(ciphertext), 16):
block_c = iv if i == 0 else ciphertext[i-16:i]
plaintext += challenge17_crack_block(ciphertext[i:i+16], block_c)
return set2.pkcs7_unpad(plaintext, 16)
def challenge17(plaintext):
ciphertext, iv = challange17_encrypt(plaintext)
return challenge17_crack(ciphertext, iv)
def aes_ctr(text, key):
resulttext = b''
for i in range(0, len(text), 16):
count = (constants.nonce.to_bytes(8, 'little')
+ (i//16).to_bytes(8, 'little'))
keystream = set2.aes_ecb_encrypt(count, key)
block = text[i:i+16]
resulttext += set1.xor(block, keystream[:len(block)])
return resulttext
def challenge19_adjust(ciphertext, hint, keystream):
for i, j in enumerate(range(len(ciphertext)-len(hint), len(ciphertext))):
keystream[j] = ciphertext[j] ^ hint[i]
def challenge19(ciphertexts):
maxlen = max(map(len, ciphertexts))
keystream = []
for i in range(maxlen):
_, keybyte = max([(sum([constants.charscore[j ^ ct[i]]
for ct in ciphertexts if i < len(ct)]), j) for j in range(256)])
keystream.append(keybyte)
# the last few bytes of the keystream need to be adjusted manually
# because there are too few lines to get the things right
challenge19_adjust(ciphertexts[4], b' head', keystream)
challenge19_adjust(ciphertexts[37], b'rn,', keystream)
keystream = bytes(keystream)
plaintexts = [set1.xor(ct, keystream[:len(ct)]) for ct in ciphertexts]
for pt in plaintexts:
print(pt)
def challenge20(ciphertexts):
maxlen = max(map(len, ciphertexts))
keystream = []
for i in range(maxlen):
_, keybyte = max([(sum([constants.charscore[j ^ ct[i]]
for ct in ciphertexts if i < len(ct)]), j) for j in range(256)])
keystream.append(keybyte)
challenge19_adjust(
ciphertexts[26], b'nd observe the whole scenery', keystream)
keystream = bytes(keystream)
plaintexts = [set1.xor(ct, keystream[:len(ct)]) for ct in ciphertexts]
for pt in plaintexts:
print(pt)
def challenge22():
ts = int(round(time.time()))
ts += random.randint(40, 1000)
seed1 = ts
mt19937.seed(seed1)
rndnum1 = mt19937.randint32()
ts += random.randint(40, 1000)
for i in range(40, 1001):
mt19937.seed(ts-i)
if mt19937.randint32() == rndnum1:
return (seed1, ts-i)
assert False
def getbit(byte, idx):
return (byte & (1 << idx)) != 0
def setbit(byte, idx, bit):
assert bit == 0 or bit == 1
if getbit(byte, idx) != bit:
return byte ^ (1 << idx)
return byte
def undoxorrightshift(num, shiftlen):
assert 0 < shiftlen < 32
for i in range(32-shiftlen):
bit = getbit(num, 31-i) ^ getbit(num, 31-i-shiftlen)
num = setbit(num, 31-i-shiftlen, bit)
return num
def undoxorleftshift(num, shiftlen, magic):
assert 0 < shiftlen < 32
for i in range(shiftlen, 32):
bit = (getbit(num, i-shiftlen) & getbit(magic, i)) ^ getbit(num, i)
num = setbit(num, i, bit)
return num
def untemper(num):
num = undoxorrightshift(num, 18)
num = undoxorleftshift(num, 15, 0xefc60000)
num = undoxorleftshift(num, 7, 0x9d2c5680)
num = undoxorrightshift(num, 11)
return num
def challenge23(rndnums):
assert len(rndnums) >= 624
rndnums = rndnums[:624]
rng = mt19937.MT19937()
rng.mt = list(map(untemper, rndnums))
rng.mti = mt19937.N
return rng
def mt19937_ctr(text, seedvalue=constants.seedvalue):
rng = mt19937.MT19937(seedvalue)
result = b''
for i in range(0, len(text), 4):
keybytes = rng.randint32().to_bytes(4, 'big')[:len(text[i:i+4])]
result += set1.xor(text[i:i+4], keybytes)
return result
def mt19937_ctr_safer(text):
prefix = os.urandom(random.randint(4, 20))
return mt19937_ctr(prefix + text)
def challenge24():
ciphertext = mt19937_ctr_safer(b'A' * 10)
ctlen = len(ciphertext)
prefixlen = len(ciphertext) - 10
for i in range((1 << 8)-1):
if ciphertext[prefixlen:] == mt19937_ctr(b'A'*ctlen, i)[prefixlen:]:
return i
assert False
def passwordtoken():
return mt19937_ctr(b'reset password now', int(time.time()))
def is_token_now(token):
ts = int(time.time())
# allow one second error
return (token == mt19937_ctr(b'reset password now', ts)
or token == mt19937_ctr(b'reset password now', ts-1))