#! /usr/bin/env python3

import math
import subprocess

def read_signature(fname):
    signature = b""
    with open(fname, "r") as fd:
        for line in fd:
            for c in line.split(" "):
                if c != "":
                    signature += int(c, 16).to_bytes(1, "little")
    return signature


def bytes_to_number(number_bytes):
    number = 0
    for n in number_bytes[::-1]:
        number <<= 8
        number += n
    return number


def read_publickey(fname):
    with open(fname, "r") as fd:
        content = fd.read()
        # keyfile format: N; E
        n, e = content.split(";")
    return int(n, 16), int(e, 16)


def read_privatekey(fname):
    try:
        with open(fname, "r") as fd:
            content = fd.read()
            # keyfile format: P; Q; dP; dQ; invQ; N; D; E
            p, q, dp, dq, invq, n, d, e = content.split(";")
        return int(p, 16), int(q, 16), int(n, 16)
    except FileNotFoundError:
        return 0, 0, 0


def main():
    # for debugging, verify by comparing to the original primes:
    p, q, n = read_privatekey("privatekey.csv")

    # public key is always available (also on the server)
    n, e = read_publickey("publickey.csv")

    # TODO: begin remove solution
    print("[+] Getting correct signature")
    correct_sig = bytes_to_number(read_signature("signature.clean"))
    print("[+] Got it")
    print("[+] Getting faulted signature")
    faulted_sig = bytes_to_number(read_signature("signature.corrupted"))
    print("[+] Got it")

    print(f"[+] Correct Signature: 0x{correct_sig:x}")
    print(f"[+] Faulted Signature: 0x{faulted_sig:x}")
    assert correct_sig != faulted_sig
    # TODO: end remove solution

    print("[+] Recovering Factor...")

    # TODO: begin remove solution
    prime1 = math.gcd((correct_sig - faulted_sig) % n, n)
    assert prime1 != 1
    # TODO: end remove solution

    print(f"[+] Recovered factor: 0x{prime1:x}")
    if prime1 == p and p != 0:
        print("[DBG] SUCCESS: Recovered factor matches P")
    elif prime1 == q and q != 0:
        print("[DBG] SUCCESS: Recovered factor matches Q")

    # TODO: begin remove solution
    prime2 = n // prime1
    assert prime1 * prime2 == n
    assert prime2 != 1
    # TODO: end remove solution
    print(f"[+] Got other factor: 0x{prime2:x}")
    if prime2 == p and p != 0:
        print("[DBG] SUCCESS: Recovered second factor matches P")
    elif prime1 == q and q != 0:
        print("[DBG] SUCCESS: Recovered second factor matches Q")

    # we will check these 3 prints on the server
    print(f"[+] Prime1: 0x{prime1:x}")
    print(f"[+] Prime2: 0x{prime2:x}")
    print(f"[+] N: 0x{n:x}")


if __name__ == "__main__":
    main()
