def test_41(self): public, oracle = get_message_recovery_oracle() plain = randbelow(2**64) cipher = ck.cipher_RSA(plain, public) _ = oracle(cipher) s = 2 e, n = public new_cipher = ck.cipher_RSA(s, public) * cipher % n new_plain = oracle(new_cipher) s_inv = ck.modinv(s, n) recovered = s_inv * new_plain % n self.assertEqual(plain, recovered)
def test_46(self): plain = cu.base64_to_bytes( 'VGhhdCdzIHdoeSBJIGZvdW5kIHlvdSBkb24ndCBwbGF5IGFyb3VuZCB3aXRoIHRoZSBGdW5reSBDb2xkIE1lZGluYQ==' ) plain_int = int.from_bytes(plain, 'big') public, is_even = get_parity_oracle() e, n = public cipher = ck.cipher_RSA(plain_int, public) # constant which will allow us to double the plaintext double_plain = pow(2, e, n) max_iter = ceil(log(n, 2)) """casting the bounds as Decimal type, and setting the precision to have as many bits as the modulus, is *super* important. If we don't do this rounding errors will screw us, and trying to do this with integer division without messing up the last byte is annoying""" lower_bound, upper_bound = Decimal(0), Decimal(n) getcontext().prec = max_iter for _ in range(max_iter): cipher = double_plain * cipher % n if is_even(cipher): upper_bound = (lower_bound + upper_bound) / 2 else: lower_bound = (lower_bound + upper_bound) / 2 if int(upper_bound) == int(lower_bound): break decrypted = cu.int_to_bytes(int(upper_bound)) self.assertEqual(plain, decrypted)
def test_message_recovery_oracle(self): public, oracle = get_message_recovery_oracle() plain = randbelow(2**64) cipher = ck.cipher_RSA(plain, public) decipher = oracle(cipher) self.assertEqual(plain, decipher) self.assertRaises(ValueError, oracle, cipher)
def test_PKCS(self): N = 1024 public, oracle = get_PKCS_oracle(N=N) plain = randbelow(2**64) padded = PKCS1v1p5(plain, N) self.assertEqual(len(cu.int_to_bytes(padded)), ceil(N / 8) - 1) cipher = ck.cipher_RSA(padded, public) self.assertTrue(oracle(cipher))
def sign_RSA(message_hash, private, N=1024): """Sign a message hash with a private key, with N-bit RSA, PKCS#1.5 padding""" prepad = bytes([0x01]) postpad = bytes([0x00]) + SHA1_ASN pad_length = N // 8 - len(prepad) - len(postpad) - len(message_hash) pad = prepad + bytes([0xff] * pad_length) + postpad plain = pad + message_hash signature = ck.cipher_RSA(plain, private) return signature, plain
def test_47(self): N = 256 public, oracle = get_PKCS_oracle(N=N) plain = int.from_bytes(b'kick it, CC', 'big') padded = PKCS1v1p5(plain, N) cipher = ck.cipher_RSA(padded, public) self.assertTrue(oracle(cipher)) decrypted = ck.Bleichenbacher_attack(cipher, public, N, oracle) self.assertEqual(decrypted, padded)
def test_48(self): N = 1024 public, oracle = get_PKCS_oracle(N=N) plain = int.from_bytes(b'Can it be that it was all so simple then?', 'big') padded = PKCS1v1p5(plain, N) cipher = ck.cipher_RSA(padded, public) self.assertTrue(oracle(cipher)) decrypted = ck.Bleichenbacher_attack(cipher, public, N, oracle) self.assertEqual(decrypted, padded)
def test_40(self): plain = randbelow(2**128) public_keys = [] ciphertexts = [] for _ in range(3): public, _ = ck.gen_RSA_keys() cipher = ck.cipher_RSA(plain, public) public_keys.append(public) ciphertexts.append(cipher) decipher = ck.RSA_broadcast_attack(public_keys, ciphertexts) self.assertEqual(plain, decipher)
def verify_RSA(message_hash, signature, public): """Return true if a given RSA signature and public key matches a message hash. Note that this is deliberately implemented poorly so we can attack it """ padded_hash = ck.cipher_RSA(signature, public) if padded_hash[:2] != bytes([0x01, 0xff]): raise ValueError('Incorrect padding') stripped = None for idx in range(2, len(padded_hash)): """This is the problem! We check that we have a string of 0xff followed by the right ASN.1 and hash, but don't check that the number of 0xffs is such that the hash is right-justified.""" if padded_hash[idx] != 0xff: stripped = padded_hash[idx:] break # hard-coded to use SHA1 if stripped[:16] != bytes([0]) + SHA1_ASN: raise ValueError('Incorrect padding') sig_hash = stripped[16:36] return sig_hash == message_hash
def oracle(cipher): plain = ck.cipher_RSA(cipher, private) return not (plain % 2)
def oracle(cipher): if cipher in previous: raise ValueError('Ciphertext previously submitted') previous.append(cipher) return ck.cipher_RSA(cipher, private)
def oracle(cipher): plain = ck.cipher_RSA(cipher, private) return (plain >= 2 * B) and (plain < 3 * B)
def test_39(self): public, private = ck.gen_RSA_keys() plain = 42 cipher = ck.cipher_RSA(plain, public) decipher = ck.cipher_RSA(cipher, private) self.assertEqual(plain, decipher)