def p40() -> str: msg = b'Assume you\'re a Javascript programmer. That is, you\'re using a naive handrolled ' \ b'RSA to encrypt without padding.' print(f'Encrypting secret message "{msg.decode()}"') msg = int(hexlify(msg), 16) pairs = [] for _ in range(3): rsa = RSA() c = rsa.enc(msg) pairs.append((c, rsa.N)) print( f'Generated ciphertext = {str(c)[:15]}... for N = {str(rsa.N)[:15]}...' ) c0, c1, c2 = [c for (c, _) in pairs] n0, n1, n2 = [N for (c, N) in pairs] m0, m1, m2 = n1 * n2, n0 * n2, n0 * n1 t0 = (c0 * m0 * invmod(m0, n0)) t1 = (c1 * m1 * invmod(m1, n1)) t2 = (c2 * m2 * invmod(m2, n2)) c = (t0 + t1 + t2) % (n0 * n1 * n2) m = kth_root(c, 3) m = unhexlify(hex(m)[2:]) return f'Recovered message "{m.decode()}"'
def p46() -> str: rsa = RSA() m = b64decode( 'VGhhdCdzIHdoeSBJIGZvdW5kIHlvdSBkb24ndCBwbGF5IGFyb3VuZCB3aXRoIHRoZSBG' 'dW5reSBDb2xkIE1lZGluYQ==') m = int(hexlify(m), 16) c = rsa.enc(m) bounds = [(0, 1), (1, 1)] for _ in range(rsa.N.bit_length()): nm = bounds[0][0] * bounds[1][1] + bounds[1][0] * bounds[0][1] dm = bounds[0][1] * bounds[1][1] * 2 gcd = gcd_func(nm, dm) nm, dm = nm // gcd, dm // gcd c = (pow(2, rsa.e, rsa.N) * c) % rsa.N if rsa.dec(c) % 2 == 0: bounds[1] = (nm, dm) else: bounds[0] = (nm, dm) recovered = bounds[1][0] * rsa.N // bounds[1][1] return f'Recovered message "{unhexlify(hex(recovered)[2:]).decode()}"'
def padding_oracle(ctxt: int, rsa: RSA) -> bool: ptxt = rsa.dec(ctxt) try: unpad_pkcs15(int.to_bytes(ptxt, rsa.bitsize // 8, byteorder='big')) return True except ValueError: return False
def bb98(rsa: RSA) -> bytes: m = pad_pkcs15(b'kick it, CC', rsa.bitsize // 8) debug_print(f'Padded message is {hexlify(m)}, {len(m)} bytes long') m = int(hexlify(m), 16) c = rsa.enc(m) B = 2 ** (rsa.bitsize - 16) M = {(2*B, 3*B - 1)} s = 1 i = 1 while not _solution_found(M): debug_print(f'Starting round with M {M}') if i == 1: s = step2a(c, B, rsa) elif len(M) != 1: s = step2b(c, s, rsa) else: a, b = next(iter(M)) s = step2c(c, s, a, b, B, rsa) debug_print(f'Found s for this round: {s}') M = step3(s, M, B, rsa.N) i += 1 a, _ = next(iter(M)) padded = int.to_bytes(a, length=rsa.bitsize // 8, byteorder='big') debug_print(unpad_pkcs15(padded).decode()) return unpad_pkcs15(padded)
def p47(): rsa = RSA(bitsize=256) m = pkcs15('kick it, CC', 256 / 8) m = int(hexlify(m), 16) c = rsa.enc(m) B = 2**(256 - 16) M = [2 * B, 3 * B - 1] i = 1 def update_ctxt(s): return (c * pow(s, rsa.e, rsa.N)) % rsa.N while not M[0] == M[1]: a, b = M[0], M[1] if i == 1: s = ceiling(rsa.N, 3 * B) c_ = update_ctxt(s) while not pkcs15_padding_oracle(c_, rsa): s += 1 c_ = update_ctxt(s) else: r = ceiling(2 * (b * s - 2 * B), rsa.N) s = ceiling(2 * B + r * rsa.N, b) c_ = update_ctxt(s) while not pkcs15_padding_oracle(c_, rsa): if s >= (3 * B + r * rsa.N) / a: r += 1 s = ceiling(2 * B + r * rsa.N, b) else: s += 1 c_ = update_ctxt(s) r = ceiling((a * s - 3 * B + 1), rsa.N) M[0] = max(a, ceiling(2 * B + r * rsa.N, s)) M[1] = min(b, (3 * B - 1 + r * rsa.N) / s) i += 1 hex_data = hex(M[0])[2:-1] recovered = unhexlify(hex_data.zfill(len(hex_data) + (len(hex_data) % 2))) return 'Recovered message "{}"'.format(validate_pkcs15(recovered))
def p41() -> str: rsa = RSA() N, e = rsa.N, rsa.e ptxt = dumps({ 'time': 1356304276, 'social': '867-00-5309', }) ptxt = int(hexlify(ptxt.encode()), 16) ctxt = rsa.enc(ptxt) s = 2 ctxt_ = pow(s, e, N) * ctxt % N ptxt_ = rsa.dec(ctxt_) recovered = invmod(s, N) * ptxt_ % N recovered = unhexlify(hex(recovered)[2:]).decode() return 'Recovered data {}'.format(loads(recovered))
def p41(): rsa = RSA() N, e = rsa.N, rsa.e ptxt = dumps({ 'time': 1356304276, 'social': '555-55-5555', }) ptxt = int(hexlify(ptxt), 16) ctxt = rsa.enc(ptxt) s = 2 ctxt_ = pow(s, e, N) * ctxt % N ptxt_ = rsa.dec(ctxt_) recovered = invmod(s, N) * ptxt_ % N recovered = unhexlify(hex(recovered)[2:-1]) return 'Recovered data {}'.format(loads(recovered))
def p42(): modsize = 1024 rsa = RSA(modsize) m = b'hi mom' h = sha1(m).digest() mystr = b'\x00\x01\xff\x00' mystr += h mystr += ((modsize // 8) - len(mystr)) * b'\x00' forged = kth_root(int(hexlify(mystr), 16), rsa.e, rounded=True) + 1 verified = _verify_sig(m, forged, rsa) assert verified return f'Message - {m.decode()}\nSignature - {forged}'
def p48(): rsa = RSA(bitsize=768) m = pkcs15('kick it, CC', 768 / 8) m = int(hexlify(m), 16) c = rsa.enc(m) B = 2**(768 - 16) M = [(2 * B, 3 * B-1)] i = 1 def update_ctxt(s): return (c * pow(s, rsa.e, rsa.N)) % rsa.N while not (len(M) == 1 and M[0][0] == M[0][1]): if i == 1: s = ceiling(rsa.N, 3 * B) c_ = update_ctxt(s) while not pkcs15_padding_oracle(c_, rsa): s += 1 c_ = update_ctxt(s) elif len(M) >= 2: s += 1 c_ = update_ctxt(s) while not pkcs15_padding_oracle(c_, rsa): s += 1 c_ = update_ctxt(s) else: a, b = M[0][0], M[0][1] r = ceiling(2 * (b*s - 2*B), rsa.N) s = ceiling(2*B + r*rsa.N, b) c_ = update_ctxt(s) while not pkcs15_padding_oracle(c_, rsa): if s >= (3*B + r*rsa.N) / a: r += 1 s = ceiling(2*B + r*rsa.N, b) else: s += 1 c_ = update_ctxt(s) newM = [] for (a, b) in M: rlow = ceiling((a * s - 3 * B + 1), rsa.N) rhigh = (b * s - 2 * B) / rsa.N for r in range(rlow, rhigh + 1): newa = max(a, ceiling(2 * B + r * rsa.N, s)) newb = min(b, (3 * B - 1 + r * rsa.N) / s) newM.append((newa, newb)) M = list(set(newM)) i += 1 hex_data = hex(M[0][0])[2:-1] recovered = unhexlify(hex_data.zfill(len(hex_data) + (len(hex_data) % 2))) return 'Recovered message "{}"'.format(validate_pkcs15(recovered))
def p48() -> str: mod_size = 768 rsa = RSA(bitsize=mod_size) return bb98(rsa).decode()
# Obtain the N from the public keys of the RSA objects. n0 = rsa0.pub[1] n1 = rsa1.pub[1] n2 = rsa2.pub[1] # Encrypt the integer of the message via all three RSA objects. plainnum = int.from_bytes(message, "big") c0 = rsa0.encryptnum(plainnum) c1 = rsa1.encryptnum(plainnum) c2 = rsa2.encryptnum(plainnum) # Can't do N/n0 for ms0 instead because floating point operations arent accurate N = n0 * n1 * n2 ms0 = n1 * n2 ms1 = n0 * n2 ms2 = n0 * n1 r0 = (c0 * ms0 * mod_inverse(ms0, n0)) r1 = (c1 * ms1 * mod_inverse(ms1, n1)) r2 = (c2 * ms2 * mod_inverse(ms2, n2)) R = (r0 + r1 + r2) % N m = floorRoot(R, 3) return m.to_bytes((m.bit_length() + 7) // 8, "big") message = "This is RSA Broadcast Attack" RSA_Broadcast_Attack(message.encode(), RSA(256), RSA(256), RSA(256)).decode("utf-8")
# Imports import math import base64 import decimal from p39 import RSA # Given given_string = "VGhhdCdzIHdoeSBJIGZvdW5kIHlvdSBkb24ndCBwbGF5IGFyb3VuZCB3aXRoIHRoZSBGdW5reSBDb2xkIE1lZGluYQ==" def check_parity(ciphertext, rsa): return rsa.decryptnum(ciphertext)& 1 rsa = RSA(1024) ciphertext = rsa.encrypt(b"Hello") print(check_parity(ciphertext, rsa)) # 1 def parity_attack(message, rsa): (_, n) = rsa.pub ciphertext = rsa.encryptnum(int.from_bytes(message, "big")) multiplier = rsa.encryptnum(2) lower_bound = decimal.Decimal(0) upper_bound = decimal.Decimal(n) num_iter = int(math.ceil(math.log(n, 2))) decimal.getcontext().prec = num_iter for _ in range(num_iter): ciphertext = (ciphertext * multiplier) % n if check_parity(ciphertext, rsa)& 1:
def p47() -> str: mod_size = 256 rsa = RSA(bitsize=mod_size) return bb98(rsa).decode()