def challenge_46(): print() # Get message, create RSA keypair, generate ciphertext message = base64.b64decode('VGhhdCdzIHdoeSBJIGZvdW5kIHlvdSBkb24ndCBwbGF5IGFyb3VuZCB3aXRoIHRoZSBGdW5reSBDb2xkIE1lZGluYQ==') pub, priv = set5.set_up_rsa(e=3, keysize=1024) ciphertext = set5.encrypt_rsa(set5.bytes_to_int(message), pub) # Set up our Oracle oracle = lambda x: rsa_oracle_plaintext_even(x, priv) # Verify the keypair works assert set5.int_to_bytes(set5.encrypt_rsa(ciphertext, priv)) == message # Set initial lower and upper bound, as well as intermediate ciphertext lower_bound, upper_bound = 0, pub[1] ciphertext_ = ciphertext # Perform the below until the lower and upper bound converge while upper_bound != lower_bound: # Multiply plaintext by multiplying ciphertext by 2**`e` mod `N` ciphertext_ = (ciphertext_ * set5.modexp(2, pub[0], pub[1])) % pub[1] # If the oracle says True, update the upper bound; if False, update the lower bound if oracle(ciphertext_): upper_bound = floor(upper_bound + lower_bound, 2) else: lower_bound = floor(upper_bound + lower_bound, 2) # Create 'Holywood style' output intermediate_result = str(set5.int_to_bytes(upper_bound))[:os.get_terminal_size().columns - 1] fill = " " * (os.get_terminal_size().columns - 1 - len(intermediate_result)) print(colorama.Cursor.UP(1) + intermediate_result + fill) # Print final outputs print(colorama.Cursor.UP(1) + "Result: {}".format(set5.int_to_bytes(upper_bound))) print("Original: {}".format(message))
def query(self, msg): # Generate hash of given message msg_hash = set4.hmac_sha1(b'', bytes(msg)).hexdigest() # If hash was seen before, ignore request if msg_hash in self.seen_hashes: return None # Add hash to list of seen hashes self.seen_hashes.append(msg_hash) # Return decrypted message return set5.encrypt_rsa(msg, self.__private__)
def verify(self, msg, signature): print('Message to verify: {}'.format(msg)) decrypted_sig = set5.int_to_bytes(int(set5.encrypt_rsa(signature, self.public))) match = re.search(b'\x01\xff*\x00(.{20})', decrypted_sig, re.DOTALL) if match is None: raise ValueError("Invalid padding.") expected_digest = hashlib.sha1(msg).digest() print('Digest expected: {}'.format(expected_digest)) obtained_digest = match.group(1) print('Digest obtained: {}'.format(obtained_digest)) return expected_digest == obtained_digest
def challenge_48(): # Set up new RSA instance, this time with bigger key length pub, priv = set5.set_up_rsa(e=3, keysize=768) # Prepare message message = pad_PKCS(b'I don\'t know, Marge. Trying is the first step towards failure - Homer Simpson', k=(pub[1].bit_length() + 7) // 8) # Get ciphertext using generated RSA instance ciphertext = set5.encrypt_rsa(set5.bytes_to_int(message), pub) # Set up our Oracle oracle = lambda x: rsa_oracle_02(x, priv) assert oracle(ciphertext) # Perform the actual attack: set up bleichenbacher98 instance bb98 = bleichenbacher98(ciphertext, pub, oracle) # Run the attack found_message = bb98.solve() print("Found message:", found_message) # Verify the found message equals our original plaintext assert_true(found_message == message)
def challenge_47(): # Set up new RSA instance pub, priv = set5.set_up_rsa(e=3, keysize=256) # Prepare message message = pad_PKCS(b'kick it, CC', k=(pub[1].bit_length() + 7) // 8) # Get ciphertext using generated RSA instance ciphertext = set5.encrypt_rsa(set5.bytes_to_int(message), pub) # Set up our Oracle oracle = lambda x: rsa_oracle_02(x, priv) assert oracle(ciphertext) # Perform the actual attack: set up bleichenbacher98 instance bb98 = bleichenbacher98(ciphertext, pub, oracle) # Run the attack found_message = bb98.solve() print("Found message:", found_message) # Verify the found message equals our original plaintext assert_true(found_message == message)
def challenge_41(): # Initate a new RSA Oracle oracle = RsaOracle() # Generate a secret secret = random.randrange(2, oracle.public[1] // 2) # Generate a message to send to the RSA oracle msg = set5.encrypt_rsa(secret, oracle.public) # Query 1: try the generated encrypted secret r = oracle.query(msg) # Expected output: the original secret assert r == secret # Query 2: try the generated encrypted secret again r = oracle.query(msg) # Expected output: a None assert r is None e, n = oracle.public s = 2 # Query 3: the attack - try our manipulated value msgp = (set5.modexp(s, e, n) * msg) % n # Expected output: the original secret r = (oracle.query(msgp) // s) % n assert_true(r == secret)
def sign(self, msg): digest = hashlib.sha1(msg).digest() sgn = set5.bytes_to_int(b'\x00\x01' + (b'\xff' * (128 - len(digest) - 3)) + b'\x00' + digest) if sgn > self.public[1]: raise ValueError("Message to big for public key") return set5.encrypt_rsa(sgn, self.__private__)
def rsa_oracle_02(ciphertext, priv): plaintext = set5.int_to_bytes(set5.encrypt_rsa(ciphertext, priv)).rjust((priv[1].bit_length() + 7) // 8, b'\x00') return plaintext[0:2] == b'\x00\x02'
def rsa_oracle_plaintext_even(ciphertext, priv): return set5.encrypt_rsa(ciphertext, priv) % 2 == 0