Skip to content

Commit 0ead172

Browse files
authored
Merge pull request #6 from djosix/allow-parallel-encryption
Allow parallel encryption
2 parents 9c05a26 + 1fcb1a6 commit 0ead172

File tree

6 files changed

+44
-19
lines changed

6 files changed

+44
-19
lines changed

README.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,17 @@ plaintext = decrypt(
6464

6565
### Encryption
6666

67-
Below is an example demonstrating how to encrypt arbitrary bytes. For a detailed understanding of the process, please refer to [this Pull Request](https://github.com/djosix/padding_oracle.py/pull/4). Keep in mind that, unlike the decryption process, this functionality cannot be parallelized.
67+
Below is an example demonstrating how to encrypt arbitrary bytes. For a detailed understanding of the process, please refer to [this Pull Request](https://github.com/djosix/padding_oracle.py/pull/4).
6868

6969
```python
7070
from padding_oracle import encrypt
7171

72-
ciphertext = encrypt(b'YourTextHere', block_size=16, oracle=oracle)
72+
ciphertext = encrypt(
73+
b'YourTextHere',
74+
block_size=16,
75+
oracle=oracle,
76+
num_threads=16,
77+
)
7378
```
7479

7580
### Customized Logging
@@ -106,6 +111,10 @@ The script also includes PHP-like encoding and decoding functions:
106111
from padding_oracle.encoding import urlencode, urldecode, base64_encode, base64_decode
107112
```
108113

114+
### TODO
115+
116+
- [ ] Support more padding schemes
117+
109118
## License
110119

111120
This script is distributed under the MIT license.

src/padding_oracle/padding_oracle.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ def encrypt(
6565
plaintext: bytes | str,
6666
block_size: int,
6767
oracle: OracleFunc,
68+
num_threads: int = 1,
6869
logger: Logger = default_logger,
6970
) -> bytes:
7071
plaintext = to_bytes(plaintext)
@@ -81,7 +82,7 @@ def encrypt(
8182
iv + cipher_blocks[i],
8283
block_size,
8384
oracle,
84-
1,
85+
num_threads,
8586
block_error_logger(logger),
8687
encrypt_progress_logger(logger, i+1, len(plain_blocks)),
8788
)

src/padding_oracle/solve.py

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,22 @@
3030
cast,
3131
)
3232

33+
__all__ = [
34+
'solve',
35+
]
3336

34-
class BlockResult(NamedTuple):
35-
block_index: int
36-
solved: list[int | None] | None = None
37-
error: None | str = None
37+
38+
class BlockResult:
39+
def __init__(
40+
self,
41+
block_index: int,
42+
*,
43+
solved: list[int | None] | None = None,
44+
error: None | str = None,
45+
):
46+
self.block_index = block_index
47+
self.solved = solved
48+
self.error = error
3849

3950

4051
OracleFunc = Callable[[bytes], bool]
@@ -60,7 +71,7 @@ def solve(
6071
ciphertext: bytes,
6172
block_size: int,
6273
oracle: OracleFunc,
63-
parallel: int,
74+
num_threads: int,
6475
block_callback: Callable[[BlockResult], None],
6576
progress_callback: Callable[[list[int | None]], None],
6677
) -> list[int | None]:
@@ -70,7 +81,7 @@ def solve(
7081
ciphertext,
7182
block_size,
7283
oracle,
73-
parallel,
84+
num_threads,
7485
block_callback,
7586
progress_callback,
7687
)
@@ -82,7 +93,7 @@ async def solve_async(
8293
ciphertext: bytes,
8394
block_size: int,
8495
oracle: OracleFunc,
85-
parallel: int,
96+
num_threads: int,
8697
block_callback: Callable[[BlockResult], None],
8798
progess_callback: Callable[[list[int | None]], None],
8899
) -> list[int | None]:
@@ -95,7 +106,7 @@ async def solve_async(
95106
ciphertext,
96107
block_size,
97108
oracle,
98-
parallel,
109+
num_threads,
99110
block_callback,
100111
progess_callback,
101112
)
@@ -115,10 +126,10 @@ async def solve_async(
115126
if result.solved is None:
116127
continue
117128

118-
if len(result.solved) >= ctx.solved_counts[result.block_index]:
129+
if len(result.solved) > ctx.solved_counts[result.block_index]:
119130
update_solved(ctx, result.block_index, result.solved)
120131
ctx.solved_counts[result.block_index] = len(result.solved)
121-
ctx.progress_callback(list[int | None](ctx.plaintext))
132+
ctx.progress_callback(ctx.plaintext)
122133

123134
if len(ctx.tasks) == 0:
124135
break
@@ -140,7 +151,7 @@ def create_solve_context(
140151
ciphertext: bytes,
141152
block_size: int,
142153
oracle: OracleFunc,
143-
parallel: int,
154+
num_threads: int,
144155
block_callback: Callable[[BlockResult], None],
145156
progress_callback: Callable[[list[int | None]], None],
146157
) -> Context:
@@ -154,7 +165,7 @@ def create_solve_context(
154165

155166
plaintext = [None] * (len(cipher_blocks) - 1) * block_size
156167

157-
executor = ThreadPoolExecutor(parallel)
168+
executor = ThreadPoolExecutor(num_threads)
158169
loop = asyncio.get_event_loop()
159170
ctx = Context(
160171
block_size,
@@ -198,7 +209,7 @@ async def solve_block(ctx: Context, block_index: int, C0: list[int],
198209
if result is not None and result.error is not None:
199210
return result
200211

201-
return BlockResult(block_index, P1_suffix)
212+
return BlockResult(block_index, solved=P1_suffix)
202213

203214

204215
async def exploit_oracle(ctx: Context, block_index: int,
@@ -217,7 +228,7 @@ async def exploit_oracle(ctx: Context, block_index: int,
217228
invalid |= len(X1_suffix) > 0 and len(hits) != 1
218229
if invalid:
219230
message = f'invalid number of hits: {len(hits)} (block: {block_index}, byte: {index})'
220-
return BlockResult(block_index, message)
231+
return BlockResult(block_index, error=message)
221232

222233
for byte in hits:
223234
X1_test = [byte ^ padding, *X1_suffix]

src/padding_oracle/utils.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,10 @@ def to_bytes(data: str | bytes | list[int]) -> bytes:
4040

4141

4242
def to_bytes_with_default(maybe_bytes: list[int | None], default: bytes = b' ') -> bytes:
43-
return [default if b is None else b for b in maybe_bytes]
43+
return bytes([
44+
ord(default) if (b is None or b not in range(256)) else b
45+
for b in maybe_bytes
46+
])
4447

4548

4649
def to_bytes_ensure_complete(maybe_bytes: list[int | None]) -> bytes:

tests/test_padding_oracle.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ def test_padding_oracle_encrypt():
2727
plaintext,
2828
cryptor.block_size,
2929
cryptor.oracle,
30+
num_threads=4,
3031
)
3132

3233
assert cryptor.decrypt(encrypted) == plaintext

tests/test_solve.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def nop_callback(*args, **kwargs):
2121
ciphertext,
2222
crypter.block_size,
2323
crypter.oracle,
24-
parallel=4,
24+
num_threads=4,
2525
block_callback=nop_callback,
2626
progress_callback=nop_callback,
2727
)

0 commit comments

Comments
 (0)