diff --git a/data/set6c47.py b/data/set6c47.py index c05695a..884b1ca 100644 --- a/data/set6c47.py +++ b/data/set6c47.py @@ -1,4 +1,6 @@ +import sys from math import log, ceil, floor +from fractions import Fraction e = 3 @@ -22,6 +24,7 @@ def add_padding(m: int, k: int) -> int: r.append(0xff) r.append(0x0) r += m.to_bytes(from_len, 'big') + assert(len(r) == k) r = int.from_bytes(r, byteorder='big') return r @@ -52,7 +55,6 @@ def decrypt(c: int) -> int: def test(): m = int.from_bytes(b"kick it, CC", byteorder='big') - m = 129852745126415640677073731 assert(m == 129852745126415640677073731) k = bytes_needed(n) @@ -74,6 +76,7 @@ def main(): k = bytes_needed(n) m = int.from_bytes(b"kick it, CC", byteorder='big') + m_orig = m c = encrypt(add_padding(m, k)) assert(m == remove_padding(decrypt(c), k)) @@ -86,7 +89,8 @@ def main(): # Step 2: Searching for PKCS conforming messages. while True: - print(f"{i=}\n{s=}\n{m=}") + # print("========") + # print(f"{i=} {s=} {m=}") if i == 1: # Step 2.a: Starting the search. s = ceil(n / (3 * B)) @@ -99,12 +103,13 @@ def main(): # Step 2.c: Searching with one interval left. a, b = m[0] found = False - r = ceil(2 * ((b * s - 2 * B) / n)) + r = ceil(Fraction(2 * (b * s - 2 * B), n)) while not found: - s_lower = ceil((2 * B + r * n) / b) - s_upper = ceil((3 * B + r * n) / a) - assert(s_lower < s_upper + 1) - for s in range(s_lower, s_upper): + s_lower = Fraction(2 * B + r * n, b) + s_upper = Fraction(3 * B + r * n, a) + s_lower_ceil = ceil(s_lower) + s_upper_ceil = ceil(s_upper) + for s in range(s_lower_ceil, s_upper_ceil): if oracle(c * pow(s, e, n) % n): found = True break @@ -115,25 +120,32 @@ def main(): # Step 3: Narrowing the set of solutions. m_new = [] for (a, b) in m: - lower = ceil((a * s - 3 * B + 1) / n) - upper = ceil((b * s - 2 * B) / n) - for r in range(lower, upper): - a_new = max(a, ceil((2 * B + r * n) / s)) - b_new = min(b, floor((3 * B - 1 + r * n) / s)) + lower = Fraction((a * s - 3 * B + 1), n) + upper = Fraction((b * s - 2 * B), n) + lower_ceil = ceil(lower) + upper_ceil = ceil(upper) + + # If upper is an integer we have to increment it to include it into + # the range. + if upper == upper_ceil: + upper_ceil += 1 + + for r in range(lower_ceil, upper_ceil): + a_new = max(a, ceil(Fraction(2 * B + r * n, s))) + b_new = min(b, floor(Fraction(3 * B - 1 + r * n, s))) m_new.append((a_new, b_new)) m = m_new # Step 4: Computing the solutions. if len(m) == 1 and m[0][0] == m[0][1]: - m = a * pow(s0, -1, n) % n + m = m[0][0] break i = i + 1 - print(m) - - - - + assert(m == 5300541194335152988749892502228755547482451611528547105226896651010982723) + m = remove_padding(m, k) + assert(m == m_orig) + print("[okay] Challenge 47: Bleichenbacher's PKCS 1.5 Padding Oracle (Simple Case)") main()