-
Notifications
You must be signed in to change notification settings - Fork 5
/
paddown.py
91 lines (70 loc) · 3.47 KB
/
paddown.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
import logging
import sys
from abc import ABC, abstractmethod
logger = logging.getLogger(__name__)
class PaddownException(Exception):
pass
class Paddown(ABC):
@abstractmethod
def has_valid_padding(self, ciphertext: bytes) -> bool:
"""
Override this method and send off the ciphertext to check for valid padding.
:param bytes ciphertext: The ciphertext to check, send this to your padding oracle.
:rtype: True for valid padding, False otherwise.
"""
raise PaddownException("Not implemented")
def __init__(self, ciphertext: bytes, blocksize: int = 16):
if not isinstance(ciphertext, bytes):
raise Exception(f"Ciphertext {type(ciphertext)} not an instance of {bytes}")
self.ciphertext = ciphertext
self.blocksize = blocksize
def find_c_prime_at_index(self, ciphertext: bytearray, index: int):
if not isinstance(ciphertext, bytearray):
raise PaddownException(f"ciphertext not an instance of {bytearray}")
# Replace ciphertext at index with a guessed byte
ciphertext_temp = ciphertext
for c_prime in range(256):
ciphertext_temp[index] = c_prime
if self.has_valid_padding(ciphertext_temp):
return c_prime
raise PaddownException(f"No valid padding found, is .has_valid_padding(...) implemented correctly?")
def decrypt_block(self, c_i):
if not isinstance(c_i, bytearray):
raise PaddownException(f"block c_i not an instance of {bytearray}")
c_previous = bytearray(b"\x00" * self.blocksize)
intermediate = bytearray(b"\x00" * self.blocksize)
for i in range(self.blocksize):
self.progress_bar(i, self.blocksize - 1, "Decrypting ")
for j in range(i):
c_previous[(self.blocksize - 1) - j] = intermediate[(self.blocksize - 1) - j] ^ (i + 1)
c_prime = self.find_c_prime_at_index(c_previous + c_i, (self.blocksize - 1) - i)
intermediate[(self.blocksize - 1) - i] = c_prime ^ (i + 1)
logger.debug(f"intermediate: {[hex(x)[2:] for x in intermediate]}")
return intermediate
def get_intermediate(self, ciphertext) -> bytes:
key = b""
blocks = len(ciphertext) // self.blocksize
# Iterate blocks last to first
for i in range(blocks):
block_start = len(ciphertext) - (i + 1) * self.blocksize
block_end = len(ciphertext) - (i * self.blocksize)
key = self.decrypt_block(ciphertext[block_start:block_end]) + key
return key
def decrypt(self) -> bytes:
logger.debug(f"Ciphertext length: {len(self.ciphertext)}")
logger.debug(f"Blocks to decrypt: {len(self.ciphertext) // self.blocksize}")
# Convert self.ciphertext to mutable bytearray
self.ciphertext = bytearray(self.ciphertext)
key = self.get_intermediate(self.ciphertext)
plaintext = bytearray()
for i in range(len(self.ciphertext) - self.blocksize):
b = self.ciphertext[i] ^ key[i + self.blocksize]
plaintext += (b).to_bytes(1, byteorder="big")
print("\n") # print variable on new line from progress bar
return plaintext
def progress_bar(self, i, total_length, post_text):
n_bar = 100 # size of progress bar
j = i / total_length
sys.stdout.write("\r")
sys.stdout.write(f"[{'#' * int(n_bar * j):{n_bar}s}] {int(100 * j)}% {post_text}")
sys.stdout.flush()