def bleichenbacher(oracle: Oracle): """ Bleichenbacher's attack Good ideas taken from: http://secgroup.dais.unive.it/wp-content/uploads/2012/11/Practical-Padding-Oracle-Attacks-on-RSA.html """ k, n, e = oracle.get_k(), oracle.get_n(), oracle.get_e() B = pow(2, 8 * (k - 2)) B2 = 2 * B B3 = B2 + B @typecheck def pkcs_conformant(c_param: int, s_param: int) -> bool: """ Helper-Function to check for PKCS conformance. """ pkcs_conformant.counter += 1 return oracle.decrypt(i2osp(c_param * pow(s_param, e, n) % n, k)) pkcs_conformant.counter = 0 cipher = os2ip(oracle.eavesdrop()) assert (pkcs_conformant(cipher, 1)) c_0 = cipher set_m_old = {(B2, B3 - 1)} i = 1 s_old = 0 while True: if i == 1: s_new = ceildiv(n, B3) while not pkcs_conformant(c_0, s_new): s_new += 1 elif i > 1 and len(set_m_old) >= 2: s_new = s_old + 1 while not pkcs_conformant(c_0, s_new): s_new += 1 elif len(set_m_old) == 1: a, b = next(iter(set_m_old)) found = False r = ceildiv(2 * (b * s_old - B2), n) while not found: for s in interval(ceildiv(B2 + r * n, b), floordiv(B3 - 1 + r * n, a)): if pkcs_conformant(c_0, s): found = True s_new = s break r += 1 set_m_new = set() for a, b in set_m_old: r_min = ceildiv(a * s_new - B3 + 1, n) r_max = floordiv(b * s_new - B2, n) for r in interval(r_min, r_max): new_lb = max(a, ceildiv(B2 + r * n, s_new)) new_ub = min(b, floordiv(B3 - 1 + r * n, s_new)) if new_lb <= new_ub: # intersection must be non-empty set_m_new |= {(new_lb, new_ub)} print("Calculated new intervals set_m_new = {} in Step 3".format( set_m_new)) if len(set_m_new) == 1: a, b = next(iter(set_m_new)) if a == b: print("Calculated: ", i2osp(a, k)) print("Calculated int: ", a) print("Success after {} calls to the oracle.".format( pkcs_conformant.counter)) return a i += 1 s_old = s_new set_m_old = set_m_new
def bleichenbacher(oracle: Oracle): """ Bleichenbacher's attack Good ideas taken from: http://secgroup.dais.unive.it/wp-content/uploads/2012/11/Practical-Padding-Oracle-Attacks-on-RSA.html """ k, n, e = oracle.get_k(), oracle.get_n(), oracle.get_e() B = pow(2, 8 * (k - 2)) B2 = 2 * B B3 = B2 + B @typecheck def pkcs_conformant(c_param: int, s_param: int) -> bool: """ Helper-Function to check for PKCS conformance. """ pkcs_conformant.counter += 1 return oracle.decrypt(i2osp(c_param * pow(s_param, e, n) % n, k)) pkcs_conformant.counter = 0 cipher = os2ip(oracle.eavesdrop()) assert(pkcs_conformant(cipher, 1)) c_0 = cipher set_m_old = {(B2, B3 - 1)} i = 1 s_old = 0 while True: if i == 1: s_new = ceildiv(n, B3) while not pkcs_conformant(c_0, s_new): s_new += 1 elif i > 1 and len(set_m_old) >= 2: s_new = s_old + 1 while not pkcs_conformant(c_0, s_new): s_new += 1 elif len(set_m_old) == 1: a, b = next(iter(set_m_old)) found = False r = ceildiv(2 * (b * s_old - B2), n) while not found: for s in interval(ceildiv(B2 + r*n, b), floordiv(B3 - 1 + r*n, a)): if pkcs_conformant(c_0, s): found = True s_new = s break r += 1 set_m_new = set() for a, b in set_m_old: r_min = ceildiv(a * s_new - B3 + 1, n) r_max = floordiv(b * s_new - B2, n) for r in interval(r_min, r_max): new_lb = max(a, ceildiv(B2 + r*n, s_new)) new_ub = min(b, floordiv(B3 - 1 + r*n, s_new)) if new_lb <= new_ub: # intersection must be non-empty set_m_new |= {(new_lb, new_ub)} print("Calculated new intervals set_m_new = {} in Step 3".format(set_m_new)) if len(set_m_new) == 1: a, b = next(iter(set_m_new)) if a == b: print("Calculated: ", i2osp(a, k)) print("Calculated int: ", a) print("Success after {} calls to the oracle.".format(pkcs_conformant.counter)) return a i += 1 s_old = s_new set_m_old = set_m_new
""" tests = [ #{"k": 4, "p": 10007, "q": 10037}, #{"k": 8, "p": 1000000007, "q": 1000000009}, #{"k": 16, "p": 15309168720959725921, "q": 12819619822143804367}, #{"k": 32, "p": 313115142601654954062569328755831304743, "q": 255336707253239299888475776540791782543}, #{"k": 128, "p": 13244628888829977635820637741951867212791935321427723006722254687460420690588887758810928307766709750393036804634467753421052044765639091747501507784485213, "q": 11583722350869317111812024666321163171121150655325205224246499810906481321757898291274629991421369420603573230614994104724581115295807013606297300412089473}, { "k": 256, "p": 167230636094866282461211664159158428279902699551992447152002026086321450931356694020366071860452874936312114743689156451204955885905421523426919810372766672329365549589557666294538091910136590569719360771818561583316969574158815755289605442067349031803550793473499645742177046498728201139371344588674279678939, "q": 153746015991426629737627279764483089360526745536038013938043722107713599542535853191396561623967198520570205780805436781605500829408932188321165836162114053101365153759076284383142329005938490083741368370827739032497065588280706602245858167496933285469691492972704468586486254156236192774677901155398324342253 } ] for test in tests: n = test["p"] * test["q"] B = pow(2, 8 * (test["k"] - 2)) B2 = 2 * B m = b"ABCD" m = B2 + int(m, 16) bleichenbacher_simulation(test["k"], n, m) if __name__ == "__main__": oracle = Oracle() #run_tests() bleichenbacher(oracle), oracle.get_k()
print("No luck for set_m_new = {} in Step 4".format(set_m_new)) @typecheck def run_tests(): """ Tests to validate the algorithm. """ tests = [ #{"k": 4, "p": 10007, "q": 10037}, #{"k": 8, "p": 1000000007, "q": 1000000009}, #{"k": 16, "p": 15309168720959725921, "q": 12819619822143804367}, #{"k": 32, "p": 313115142601654954062569328755831304743, "q": 255336707253239299888475776540791782543}, #{"k": 128, "p": 13244628888829977635820637741951867212791935321427723006722254687460420690588887758810928307766709750393036804634467753421052044765639091747501507784485213, "q": 11583722350869317111812024666321163171121150655325205224246499810906481321757898291274629991421369420603573230614994104724581115295807013606297300412089473}, {"k": 256, "p": 167230636094866282461211664159158428279902699551992447152002026086321450931356694020366071860452874936312114743689156451204955885905421523426919810372766672329365549589557666294538091910136590569719360771818561583316969574158815755289605442067349031803550793473499645742177046498728201139371344588674279678939, "q": 153746015991426629737627279764483089360526745536038013938043722107713599542535853191396561623967198520570205780805436781605500829408932188321165836162114053101365153759076284383142329005938490083741368370827739032497065588280706602245858167496933285469691492972704468586486254156236192774677901155398324342253} ] for test in tests: n = test["p"] * test["q"] B = pow(2, 8 * (test["k"] - 2)) B2 = 2 * B m = b"ABCD" m = B2 + int(m, 16) bleichenbacher_simulation(test["k"], n, m) if __name__ == "__main__": oracle = Oracle() #run_tests() bleichenbacher(oracle), oracle.get_k()