RSA Algorithm to establish a random secret key. Go to cocalc to run the code. You can also install sage on your local machine.


reset()
        
# Return x^a mod n
def pow_mod(x,a,n) :
    L = []
    m = a
    while (m != 0) :
        L.append(m%2)
        m = m >> 1
    M = []
    for i in [0,..,len(L)-1] :
        if (i==0) : M.append(x)
        else : M.append( (M[-1]*M[-1]) % n )
    result = 1
    for i in [0,..,len(L)-1] :
        if ( L[i] == 1) :
            result *= M[i]
            result = result % n
    return result
        
        
# Return the GCD of integers a and b
def GCD(a,b) :
    while (b!=0) :
        r = (a%b)
        a=b
        b=r
    return a
        
        
# Return (gcd, alpha, beta) such that gcd = alpha.a + beta.b
def EGCD(a,b) :
    stack = []
    while (b!=0) :
        r = (a%b)
        m = (a-r)/b
        stack.append([m,-1,-1])
        a=b
        b=r
    gcd=a
    stack.append([-1,1,0])
    for i in range(len(stack)-2,-1,-1) :
        stack[i][1] = stack[i+1][2]
        stack[i][2] = stack[i+1][1] - stack[i][0]*stack[i+1][2]
    return (gcd, stack[0][1], stack[0][2])
        
        
# Basic Miller-Rabin with confidence 1-2^{-t}
def miller_rabin(N,t) :
    for i in [1,..,t] :
        x = 1 + ZZ.random_element(N-1)
        val = pow(x,N-1,N)
        #print val
        if ( val != 1 ) : return False
    return True
        
        
# Generate an n-bit prime
def gen_prime(n,t) :
    for i in [1,..,n*t] :
        p = 2^(n-1) + ZZ.random_element(2^(n-1))
        if miller_rabin(p,t) : return p
    return -1
        
        
# Find a random exponenet e
def find_exponent(phi_N, num) :
    for i in [1,..,num] :
        e = 2 + ZZ.random_element(phi_N-2)
        g = GCD(e,phi_N)
        if (g==1) : return e
    return -1
        
        
        
### MAIN CODE
### The RSA key-agreement protocol
        
# The number of bits in the primes
n=4096
        
# Generate the first prime
p = gen_prime(n,100)
print "p =", p
        
# Generate the second prime (different from the first one)
q = gen_prime(n,100)
while (q == p) : q = gen_prime(n,100)
print "q =", q
        
# Compute the modulus (the product of the two primes)
N = p*q
print "N =" , N
        
# Compute the size of the group (Z_N^*,X), i.e. \phi(N)
phi_N = (p-1) * (q-1)
print "phi(N) =", phi_N
        
# Compute a random exponent that is relatively prime to \phi(N)
e = find_exponent(phi_N,100)
print "e =", e

#Generate a random mask r
r = ZZ.random_element(N)
while (GCD(r,N) != 1) : r = ZZ.random_element(N)
print "r =", r
        
# Encode the mask r into y
y=pow_mod(r,e,N)
print "y =", y
        
# Calcualte the trapdoor d such that ed = 1 mod \phi(N)
d = EGCD(e,phi_N)[1]%phi_N
print "d =", d
        
# Decrypt y to recover r using the trapdoor d
r_tilde = pow_mod(y,d,N)
print "r_tilde =", r_tilde
        
# Check if the random mask was successfully recovered
print "Was Decoding Successful?" , r==r_tilde
        
# Compute the inverse of the decrypted result in preparation for RSA Decryption algorithm
inv_r_tilde = EGCD(r_tilde,N)[1]%N
print "Inverse of r_tilde =", inv_r_tilde