This is an old revision of the document!


Lab 04 - Hashes, Public Key Encryption

Exercise 1

In this exercise you will implement the Birthday attack on SHA-1. The goal is to obtain a collision in the first four bytes of the hash.

You can start from this code:

import hashlib
import random
 
N_MESSAGES = 2**16 # 2^16 random messages
N_COLL_BYTES = 4
 
def generate_messages():
    msgs = []
 
    for i in xrange(N_MESSAGES):
        msgs.append("Ana are %d mere si %d pere" % (i, random.randint(0, 100000)))
 
    return msgs
 
def find_collision(msgs):
    hashes = dict() # will keep first N_COLL_BYTES bytes of each hash
 
    # TODO - find collision and return it as a tuple (msg1, msg2)
 
    return None # return None if no collision is found
 
def main():
    while True:
        # Generate N_MESSAGES (2^16) different random messages
        msgs = generate_messages()
 
        # Find a hash collision
        collision = find_collision(msgs) # collision = (msg1, msg2)
 
        if collision:
            # print collision (the two messages and their hash) in a readable format
            print collision[0] # msg1
            print hashlib.sha1(collision[0]).hexdigest() # msg1 hash
            print collision[1] # msg2
            print hashlib.sha1(collision[1]).hexdigest() # msg2 hash
            break
 
if __name__ == '__main__':
    main()

Can you also find a collision in the first 5 bytes of the hash?

In order to compute a SHA-1 hash, you can use the hashlib module:

hashlib.sha1(msg).hexdigest()

Exercise 2: RSA parity oracle (4p)

Generate a 1024 bit RSA key pair.

Write an oracle function that uses the private key to answer the question “is the plaintext of this message even or odd” (is the last bit of the message 0 or 1). Imagine for instance a server that accepted RSA-encrypted messages and checked the parity of their decryption to validate them, and spat out an error if they were of the wrong parity.

Anyways: function returning true or false based on whether the decrypted plaintext was even or odd, and nothing else.

Take the following string and un-Base64 it in your code (without looking at it!) and encrypt it to the public key, creating a ciphertext:

VGhhdCdzIHdoeSBJIGZvdW5kIHlvdSBkb24ndCBwbGF5IGFyb3VuZCB3aXRoIHRoZSBGdW5reSBDb2xkIE1lZGluYQ==

With your oracle function, you can trivially decrypt the message.

Here's why:

  • RSA ciphertexts are just numbers. You can do trivial math on them. You can for instance multiply a ciphertext by the RSA-encryption of another number; the corresponding plaintext will be the product of those two numbers.
  • If you double a ciphertext (multiply it by (2**e)%n), the resulting plaintext will be either even or odd, depending on whether the multiplication result was larger than the modulus or not.
  • If the plaintext after doubling is even, doubling the plaintext didn't wrap the modulus — the modulus is a prime number.

That means the plaintext is less than half the modulus.

You can repeatedly apply this heuristic, once per bit of the message, checking your oracle function each time.

Your decryption function starts with bounds for the plaintext of [0,n].

Each iteration of the decryption cuts the bounds in half; either the upper bound is reduced by half, or the lower bound is.

After log2(n) iterations, you have the decryption of the message.

Print the upper bound of the message as a string at each iteration; you'll see the message decrypt “hollywood style”.

Decrypt the string (after encrypting it to a hidden private key) above.

parity_oracle.zip

ac/laboratoare/04.1539798495.txt.gz · Last modified: 2018/10/17 20:48 by tiberiu.iorgulescu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0