import argparse
import sys

from math import gcd

sys.setrecursionlimit(1500)

parser = argparse.ArgumentParser(description='RSA Common modulus attack')
required_named = parser.add_argument_group('required named arguments')
required_named.add_argument('-n', '--modulus', help='Common modulus', required=True)
required_named.add_argument('-e1', '--e1', help='First exponent', required=True)
required_named.add_argument('-e2', '--e2', help='Second exponent', required=True)
required_named.add_argument('-ct1', '--ct1', help='First ciphertext', required=True)
required_named.add_argument('-ct2', '--ct2', help='Second ciphertext', required=True)

def egcd(a, b):
    if a == 0:
        return (b, 0, 1)
    else:
        g, y, x = egcd(b % a, a)
        return (g, x - (b // a) * y, y)

def modinv(a, m):
    g, x, y = egcd(a, m)
    if g != 1:
        raise ValueError('Modular inverse does not exist.')
    else:
        return x % m

def attack(c1, c2, e1, e2, N):
    if gcd(e1, e2) != 1:
        raise ValueError("Exponents e1 and e2 must be coprime")
    s1 = modinv(e1,e2)
    s2 = int((gcd(e1,e2) - e1 * s1) / e2)
    temp = modinv(c2, N)
    m1 = pow(c1,s1,N)
    m2 = pow(temp,-s2,N)
    return (m1 * m2) % N

def main():
    args = parser.parse_args()
    n = int(args.modulus, 16)
    ct1 = int(args.ct1, 16)
    ct2 = int(args.ct2, 16)
    e1 = int(args.e1, 16)
    e2 = int(args.e2, 16)
    print(n)
    print(ct1)
    print(ct2)
    print(e1)
    print(e2)
    print('[+] Started attack...')
    try:
        message = attack(ct1, ct2, e1, e2, n)
        print('[+] Attack finished!')
        print(type(message))
        print('\nPlaintext message:\n%s' % format(message, 'x'))
    except Exception as e:
        print('[+] Attack failed!')
        print(e.message)

main()

