Factoring RSA Moduli

March 24, 2014

We studied RSA encryption in a previous exercise. To recap: select two prime numbers p and q and make their product n = p q the modulus of the crypto system. Choose an encryption key e and a decryption key d such that d e ≡ 1 (mod φ(p q)), where the totient φ(p q) = (p − 1) × (q − 1). Then encrypt a message m as c = me (mod n) and decrypt the cipher test c as m = cd.

In our implementation we chose p and q randomly and kept them secret, even from the user who knows the decryption key. Of course, it is hard for someone who knows who does not know d to factor n and thus compute d; that is the basis of the encryption. But it is fairly easy to recover p and q if you know n, e and d, as Boneh showed (page 205, left column, Fact 1):

  1. [Initialize] Set k d e − 1.
  2. [Try a random g] Choose g at random from {2, …, n − 1} and set tk.
  3. [Next t] If t is divisible by 2, set tt / 2 and xg t (mod n). Otherwise go to Step 2.
  4. [Finished?] If x > 1 and y = gcd(x −1, n) > 1 then set py and q &larrow; n / y, output p and q and terminate the algorithm. Otherwise go to Step 3.

For instance, given n = 25777, e = 3 and d = 16971, we calculate p = 149 and q = 173 when g = 5, noting that 149 × 173 = 25777; on average, it takes no more than two different g to complete the algorithm. Knowledge of p and q, though not necessary, is useful, because a more efficient decryption is possible using p, q and the Chinese remainder theorem, which does arithmetic with numbers the size of p and q instead of the size of their product.

Your task is to write a program that factors RSA moduli. When you are finished, you are welcome to read or run a suggested solution, or to post your own solution or discuss the exercise in the comments below.


Pages: 1 2

3 Responses to “Factoring RSA Moduli”

  1. David said

    In Erlang; gcd and powmod are defined in the module.

    gcd(A, 0) -> A;
    gcd(A, B) -> gcd(B, A rem B).
    powmod(_, 0, _) -> 1;
    powmod(A, N, M) ->
        case N band 1 of
    	0 -> X = powmod(A, N bsr 1, M), X*X rem M;
    	1 -> A*powmod(A, N-1, M) rem M
    try_random(N) -> random:uniform(N-2) + 1.
    rsa_factors(N, E, D) ->
        K = E*D - 1,
        rsa_factors(N, E, D, K, K, try_random(N)).
    rsa_factors(N, E, D, K, T, G) ->
    	T band 1 =:= 1 -> rsa_factors(N, E, D, K, K, try_random(N));
    	true ->
    	    T2 = T bsr 1,
    	    X = powmod(G, T2, N),
    	    Y = gcd(X-1, N),
    		(X > 1) and (Y > 1) -> {Y, N div Y};
    		true -> rsa_factors(N, E, D, K, T2, G)
    9> ex1:rsa_factors(25777, 3, 16971).
  2. matthew said

    Ok, here’s some Python: I’ve rearranged the algorithm a little to hopefully be more efficient – find the smallest possible value of x by repeated halving, then we only need one call to pow per value of g, the rest can be got by squaring. Also I’ve checked that we don’t have a trivial root of unity (and if we do, we can bail out early):

    from random import randrange
    def gcd (a,b):
        while b != 0: a,b = b,a%b
        return a
    def factor(n,d,e):
        k = d*e-1
        while True:
            g = randrange(2,n)
            t = k
            while t%2 == 0: t = t//2
            x = pow(g,t,n)
            while t < k and x != 1 and x != n-1:
                if x*x%n==1:
                    a = gcd(x-1,n)
                    b = n//a
                    if a>b: return (b,a)
                    else: return (a,b)
                x = x*x%n
                t = t*2
    def test(n,d,e):
        (a,b) = factor(n,d,e)
        assert(a != 1)
        assert(b != 1)
        assert(a*b == n)
  3. matthew said

    The Wiener attack described in the Boneh paper is quite fun as well.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: