-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathchallenge_25.py
69 lines (49 loc) · 2.28 KB
/
challenge_25.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
"""
Orel Ben-Reuven
https://cryptopals.com/sets/4/challenges/25
Break "random access read/write" AES CTR
Back to CTR. Encrypt the recovered plaintext from this file (the ECB exercise) under CTR with a random key
(for this exercise the key should be unknown to you, but hold on to it).
Now, write the code that allows you to "seek" into the ciphertext, decrypt, and re-encrypt with different plaintext.
Expose this as a function, like, "edit(ciphertext, key, offset, newtext)".
Imagine the "edit" function was exposed to attackers by means of an API call that didn't reveal the key
or the original plaintext; the attacker has the ciphertext and controls the offset and "new text".
Recover the original plaintext.
Food for thought.
A folkloric supposed benefit of CTR mode is the ability to easily "seek forward" into the ciphertext;
to access byte N of the ciphertext, all you need to be able to do is generate byte N of the keystream.
Imagine if you'd relied on that advice to, say, encrypt a disk.
"""
import base64
from Crypto.Random import get_random_bytes
from Utils.BytesLogic import xor_bytes
from Utils.AES import aes_ecb_decrypt, AesCtr
# globals
AES_BLOCK_SIZE = 16
class EditOracle:
def __init__(self):
self.key = get_random_bytes(AES_BLOCK_SIZE)
self.ctr_obj = AesCtr(self.key)
def get_cipher(self):
# load cipher and decode base64 to bytes
with open('25.txt', 'r') as fh:
source = base64.b64decode(fh.read())
key = b"YELLOW SUBMARINE"
plaintext = aes_ecb_decrypt(ciphertext=source, key=key, remove_padding=True)
# encrypt under CTR mode
ciphertext = self.ctr_obj.encrypt(plaintext)
return ciphertext
def edit(self, ciphertext: bytes, offset: int, new_text: bytes):
key_stream = self.ctr_obj.generate_key_stream(len(ciphertext))
key_stream = key_stream[offset: offset + len(new_text)]
new_cipher = xor_bytes((key_stream, new_text))
out = ciphertext[:offset] + new_cipher + ciphertext[offset+len(new_cipher):]
return out
def main():
oracle = EditOracle()
ciphertext = oracle.get_cipher()
# attack
recovered_plaintext = oracle.edit(ciphertext=ciphertext, offset=0, new_text=ciphertext)
print(recovered_plaintext)
if __name__ == '__main__':
main()