#!/usr/bin/env python
import argparse
import uuid
import sys
from termcolor import colored

"""
Script to brute force the 2 elements that are used to compute a UUID v3 or v5.

See https://www.uuidtools.com/uuid-versions-explained#version-3-version-5

The UUID specification establishes 4 pre-defined namespaces.
The pre-defined namespaces are: 
    DNS      — 6ba7b810-9dad-11d1-80b4-00c04fd430c8
    URL      — 6ba7b811-9dad-11d1-80b4-00c04fd430c8
    OID      — 6ba7b812-9dad-11d1-80b4-00c04fd430c8
    X.500 DN — 6ba7b814-9dad-11d1-80b4-00c04fd430c8

Dependencies:
    pip3 install termcolor
"""

UUID_NAMESPACES = [uuid.NAMESPACE_DNS, uuid.NAMESPACE_OID, uuid.NAMESPACE_URL, uuid.NAMESPACE_X500]

def test(target_uuid, version_uuid, name_to_test):
    result = None
    for namespace in UUID_NAMESPACES:
        if version_uuid == 3 and str(uuid.uuid3(namespace, name_to_test)) == target_uuid:
            result = {"NAMESPACE": str(namespace), "NAME": name_to_test, "VERSION": version_uuid}
            break
        elif str(uuid.uuid5(namespace, name_to_test)) == target_uuid:
            result = {"NAMESPACE": str(namespace), "NAME": name_to_test, "VERSION": version_uuid}
            break
    return result        

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Script to brute force the 2 elements that are used to compute a UUID v3 or v5.")
    required_params = parser.add_argument_group("required arguments")
    required_params.add_argument("-d", action="store", dest="dict_name", help="Path to dict to use for the 'NAME' part of the HASH.", required=True)
    required_params.add_argument("-u", action="store", dest="uuid", help="UUID targeted by the brute force operation.", required=True)
    args = parser.parse_args()
    target_uuid = args.uuid
    target_version =  uuid.UUID(target_uuid).version
    name_dict = args.dict_name
    if(target_version not in [3,5]):
        print(colored(f"[!] Version of the UUID is not 3 or 5, it is {target_version}.", "red"))
        sys.exit(1)
    print(colored(f"[+] Execution context:", "magenta"))
    print(f"UUID      : {target_uuid}")
    print(f"Version   : {target_version}")
    print(f"Name dict : {name_dict}")
    print(colored(f"[+] Perform the brute force operation:", "magenta"))
    # Read dict line by line in order to be able to support large dict
    found = False
    with open(name_dict, "r", encoding="utf-8") as fh:
       for name_to_test in fh:
           tested_value = name_to_test.strip()
           print(f"\rTesting 'NAME' to {tested_value:<50}", end="", flush=True)
           result = test(target_uuid, target_version, tested_value)
           if result is not None:
               found = True
               print(colored(f"\n[!] Found: {result}", "green"))
               break
    if not found:
        print(colored(f"\n[!] Not found.", "yellow"))
    print(colored(f"[+] Operation finished.", "magenta"))
