""" Contains functions common to many exploits """

from sympy import integer_nthroot
from fractions import gcd
from Crypto.Util.asn1 import DerSequence
from Crypto.PublicKey import RSA
from binascii import a2b_base64

def num2string(hexnum):
    """ Interprets a hex number as ASCII """
    if hexnum == None:
        return None
    else:
        string = hex(hexnum)[2:-1]
        if len(string) % 2 != 0:
            string = '0' + string
        return string.decode('hex')

def compute_priv_rsa_exp(key_e, prime_factors_list):
    """ Compute RSA private key

        Computes an RSA private key given a public exponent
        e and a list of the primes factors of the modulus
    """
    totient = totient_from_primes(prime_factors_list)
    key_d = long(int(mod_inv(key_e, totient)))
    return key_d

def totient_from_primes(prime_factors_list):
    """ Compute euler's phi function from prime factors

        Computes the euler's phi function of the number that is the
        product of the provided list of primes
    """
    totient = 1
    for factor in prime_factors_list:
        totient = totient * (factor - 1)
    return totient

def coprime(num1, num2):
    """ Returns True if n1 and n2 are coprime and False otherwise """
    return gcd(num1, num2) == 1

def is_printable(str1, codec='utf8'):
    """ Returns True if s can be decoded using the specified codec """
    try: 
        str1.decode(codec)
    except UnicodeDecodeError: 
        return False
    else: 
        return True

def int_nthroot(num, nthroot):  
    """ Returns the nth root of r if it is an integer and None otherwise """    
    (root, exact) = integer_nthroot(num, nthroot)
    if exact:
        return root
    else:
        return None

def crt(divs, rems):
    '''
    Chinese Remainder Theorem
    ds: array of dividers
    rs: array of remainders
    Return the number s such that s mod ds[i] = rs[i]
    '''
    length = len(divs)
    if not length == len(rems):
        print "The lengths of the two must be the same"
        return None

    p_num = i_num = prod = 1 
    s_num = 0
    for i_num in range(length): 
        prod *= divs[i_num]
    for i_num in range(length):
        p_num = prod / divs[i_num]
        s_num += rems[i_num] * mod_inv(p_num, divs[i_num]) * p_num
    return s_num % prod

def mod_inv(a_num, mod):
    '''
    Return r such that a*r mod m = 1
    '''
    g_num, x_num, y_num = egcd(a_num, mod)
    if g_num != 1:
        print("no inverse")
        return None
    else:
        return x_num % mod

def egcd(a_num, b_num):
    '''
    Extended Euclidean gcd. Return g, x, y such that ax+by=g=gcd(a, b)
    '''
    if a_num == 0: 
        return (b_num, 0, 1)
    else:
        g_num, y_num, x_num = egcd(b_num % a_num, a_num)
        return (g_num, x_num-(b_num//a_num)*y_num, y_num)
        
def extract_key_from_pem(pemfile):
    """ Returns extracted key from a pem file """       
    try:
        f_desc = open(pemfile, 'r')
    except IOError:
        print "Error: PEM file not found"
    else:
        pem = f_desc.read()
        
        lines = pem.replace(" ", '').split()
        der = a2b_base64(''.join(lines[1:-1]))

        # Extract subjectPublicKeyInfo field from X.509 certificate(see RFC3280)
        cert = DerSequence()
        cert.decode(der)
        tbs_certificate = DerSequence()  
        tbs_certificate.decode(cert[0])
        subject_public_key_info = tbs_certificate[6]

        # Initialize RSA key
        rsa_key = RSA.importKey(subject_public_key_info)
        
        f_desc.close()
        return rsa_key      
