Problem Set 6 Solutions Math 158, Fall 2016 All exercise numbers

Problem Set 6 Solutions
Math 158, Fall 2016
All exercise numbers from the textbook refer to the second edition.
1. (a) Textbook exercise 3.3 (this shows, as we mentioned in class, that RSA decryption always
works when the modulus is a product of two primes, regardless of whether the original
message was a unit modulo N or not).
(b) Suppose that Bob publishes a public key N = 117, e = 5 (note that this is not an RSA
public key, since 117 isn’t a product of two primes), and he requests that Alice encrypt
her message m (where 0 ≤ m < N ) by sending him c ≡ me (mod N ), as in RSA. Find
two different messages m1 , m2 which give the same ciphertext. This shows that Bob
cannot always decrypt messages unambiguously in this system. (Try to find m1 and m2
using a principle that you would be able to generalize, rather than by guess-and-check).
Note. In general, unique decryption will be possible as long as N is squarefree, meaning it
is not divisible by any square. For example, decryption is always possible if N = pqr, where
p, q, r are distinct prime (see problem 11).
Solution.
(a) Suppose that x ≡ cd (mod pq). We wish to show that xe ≡ c (mod pq). In other words,
we wish to show that cde ≡ c (mod pq). By the Chinese remainder theorem, it suffices to
prove that both cde ≡ c (mod p) and cde ≡ c (mod q). Since de ≡ 1 (mod (p − 1)(q − 1))
by assumption, we may write de = 1 + k(p − 1)(q − 1) for some integer k.
Consider first the congruence modulo p. There are two cases: p | c and p - c. First,
if p - c, then Fermat’s little theorem implies that cp−1 ≡ 1 (mod p). Therefore cde =
c · (cp−1 )k(q−1) ≡ c · 1k(q−1) ≡ c (mod p), as desired. The second case is p | c. In this
case, both c and cde are congruent to 0 modulo p, so certainly cde ≡ c (mod p).
The congruence modulo q is established in exactly the same way. So cde − c is divisible
by both p and q, hence it is divisible by pq, and cde ≡ c (mod pq).
Note. All that was needed was that de − 1 was divisible by both (p − 1) and (q − 1),
which is slightly weaker than being divisible by their product.
(b) Note that 117 = 32 · 13. Observe that by the Chinese remainder theorem, two numbers
are congruent modulo 117 if and only if they are congruent modulo 9 and also congruent
modulo 13.
One option is to choose two integers m1 , m2 which are congruent modulo 13, both
divisible by 3, yet not congruent modulo 9. Then it is guaranteed that m51 ≡ m52
(mod 13) (since m1 ≡ m2 (mod 13)), and both m1 , m2 will be divisible by 35 , hence
both congruent to 0 (mod 9) (and congruent to each other). For example, one could
solve
m1 ≡ 1 (mod 13)
m1 ≡ 3 (mod 9)
m2 ≡ 1 (mod 13)
m2 ≡ 6 (mod 9)
to obtain m1 ≡ 66 (mod 117) and m2 ≡ 105 (mod 117). Indeed these satisfy m51 ≡
m52 ≡ 27 (mod 117).
Note. In fact, one can show that this is the only way to construct such pairs: one must
choose numbers that are congruent modulo 13, but with different remainder modulo 9,
both chosen from {0, 3, 6} (so that both are divisible by 3).
2. Textbook exercise 3.6.
Due the night of Thursday 10/27 (hard deadline 4am on 10/28).
page 1 of 13
Problem Set 6 Solutions
Math 158, Fall 2016
Solution.
(a) As explained in class, we can solve this congruence by finding d ≡ e−1 (mod φ(N ))
(which exists since we are assuming gcd(e, φ(N )) = 1), and then computing cd (mod φ(N )).
The reason given in class is that: since c is a unit modulo N , any solution x must be a
unit as well (any common factor of x and N would be a common factor of xe and N ), so
Euler’s formula implies cφ(N ) ≡ 1 (mod N ). Since de = 1 + kφ(N ) for some integer k,
it follows that cd ≡ xde ≡ x · (xφ(N ) )k ≡ x (mod N ). So any solution x satisfies x ≡ cd
(mod N ). Conversely, if x ≡ cd (mod N ), then xe ≡ cde ≡ c (mod N ) by the same
reasoning.
(b)
i. N = 1463 factors into primes as 7 · 11 · 19, so φ(N ) = (7 − 1)(11 − 1)(19 − 1) = 1080.
The inverse of e = 577 modulo 1080 is 73. So the answer is 6073 (mod 1463), which
simplifies to 1390 (mod 1463).
1
ii. N = 1625 factors into primes as 53 · 13, so φ(N ) = N (1 − 15 )(1 − 13
) = 53 · 13 · 45 · 12
13 =
2
5 · 4 · 12 = 1200. The inverse of 959 modulo 1200 is 239. Therefore we compute
1583239 ≡ 147 (mod 1625).
iii. N = 2134440 factors into primes as 23 · 32 · 5 · 72 · 112 . Therefore φ(N ) = (22 · 1)(3 ·
2)(4)(7 · 6)(11 · 10) = 443520 (in the second step of this computation, I have used
the fact that pe (1 − p1 ) = pe−1 (p − 1), applied to each prime individually, to simplify
the expression in exercise 3.5(d)). The inverse of 133957 modulo 443520 is 326413.
Therefore we compute 224689326413 ≡ 1892929 (mod 2134440)
3. Textbook exercise 3.10.
Solution.
(a) The way we discussed finding decryption exponents in class, one would have d ≡ e−1
(mod φ(N )), hence φ(N ) | (de − 1). So one can employ the following idea: construct
several encryption-decryption pairs with your magic box, and compute the greatest
common divisor of the various numbers de − 1. Hopefully, this will be a small multiple of
φ(N ) (or better yet, φ(N ) itself). Once we know φ(N ) and N , we can factor N using the
quadratic formula (see Remark 3.11). As long as we take encryption-decryption pairs,
its unlikely that the various numbers de − 1 will have a common factor larger than φ(N )
(see Homework 2, problem 1).
Unfortunately, there is a small wrinkle: this is not the only way to find decryption
exponents. Indeed, Remark 3.6 points out that it’s enough to make sure de ≡ 1
(mod (p − 1)(q − 1)/g), where g = gcd(p − 1, q − 1). In fact, this is both necessary
and sufficient1 . It’s possible that your magic box is giving exponents satisfying this
weaker condition (indeed, the examples in parts (b) through (d) are of this form).
The good news is that this only adds a bit of complication: if we choose enough key
pairs, the gcd of the numbers de − 1 will be some multiple of (p − 1)(q − 1)/g, and very
possibly (p − 1)(q − 1)/g itself. Then we can try to guess φ(N ) by taking a multiple of
this gcd that is close to N (note that φ(N ) = N − p − q + 1, so its order of magnitude
will be close to N ).
1
One way to prove this is to show that de must be congruent to 1 modulo p − 1 and modulo q − 1, which shows
that de − 1 is divisible by the least common multiple of p − 1 and q − 1; one can then argue that this least common
multiple is (p − 1)(q − 1)/g.
Due the night of Thursday 10/27 (hard deadline 4am on 10/28).
page 2 of 13
Problem Set 6 Solutions
Math 158, Fall 2016
This strategy may seem a big loose (and doesn’t carry guarantees), but the following
examples shows that is can be made to work.
(b) We begin by finding the gcd of the numbers de − 1.
>>> from fractions import gcd
>>> N = 38749709
>>> e = [10988423,25910155]
>>> d = [16784693,11514115]
>>> gcd(d[0]*e[0]-1,d[1]*e[1]-1)
19368558
This is too small to be φ(N ), but it could very plausibly be φ(N )/2 (e.g. if gcd(p −
1, q − 1) = 2), since it is just under half of N (and we know that φ(N ) is only a little
less than N in relative terms). Going with this assumption, let’s attempt to compute
p, q as outlined in Remark 3.11.
>>> M = gcd(d[0]*e[0]-1,d[1]*e[1]-1)
>>> phi = 2*M
>>> b = N - phi + 1 # This should be p+q. We’ll solve X^2 - bX + N = 0.
>>> import math # So that I can use sqrt
>>> p = (b-math.sqrt(b*b - 4*N))/2
>>> q = (b+math.sqrt(b*b - 4*N))/2
>>> p,q
(5347.0, 7247.0) # Reported with decimals since sqrt was used.
Indeed, this worked! We can check that 5347 · 7247 = N .
(c) We can proceed similarly to before, but now we take the gcd of three numbers. Here is
the transcript from the python terminal.
>>> N = 225022969
>>> e = [70583995,173111957,180311381]
>>> d = [4911157,7346999,29597249]
>>> M = gcd( gcd(d[0]*e[0]-1,d[1]*e[1]-1),d[2]*e[2]-1)
>>> float(N)/M
6.000841978863939
>>> phi = 6 * M # Seems like a reasonable guess
>>> b = N - phi + 1 # Should be p+q
>>> p = (b-math.sqrt(b*b-4*N))/2
>>> q = (b+math.sqrt(b*b-4*N))/2
>>> print p,q
10867.0 20707.0
>>> p*q
225022969.0 #Success!
(d) Similar to before:
>>> N = 1291233941
>>> e = [1103927639,1022313977,387632407]
>>> d = [76923209,106791263,7764043]
>>> M = gcd( gcd(d[0]*e[0]-1,d[1]*e[1]-1), d[2]*e[2]-1 )
>>> float(N)/M
10.00085538680072
Due the night of Thursday 10/27 (hard deadline 4am on 10/28).
page 3 of 13
Problem Set 6 Solutions
Math 158, Fall 2016
>>> phi = 10*M # Seems like a good guess
>>> b = N - phi + 1
>>> p = (b + math.sqrt(b*b-4*N) )/2
>>> q = (b - math.sqrt(b*b-4*N) )/2
>>> print p,q
97151.0 13291.0
>>> p*q
1291233941.0
4. Textbook exercise 3.17.
Solution.
(a) The primes less than 100 are
2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97
There are 25 total. Of these, the first 8 are less than 20, and two more are less than 30.
So π(20) = 8, π(30) = 10, π(100) = 25.
(b) There are a number of ways to count primes that will be fast enough to count up to
100000. For the sake of variety, the code below illustrates the sieve of Eratosthenes,
which is a very old and reasonably quick way to do it (search online to find details and
history).
def primesToBound(N):
isPrime = [True]*(N+1)
primes = []
for p in xrange(2,N+1):
if not(isPrime[p]): continue
primes += [p]
k = p
while k<=N/p:
isPrime[k*p] = False
k += 1
return primes
def pi(N):
return len(primesToBound(N))
Using the function pi(N), we can empirically examine the ratio π(X)/(X/ ln(X)) as
follows.
>>> from math import log
>>> for X in [100,1000,10000,100000]:
...
print X,pi(X),pi(X)/(X/log(X))
...
100 25 1.1512925465
1000 168 1.16050288687
10000 1229 1.13195083172
100000 9592 1.1043198106
Due the night of Thursday 10/27 (hard deadline 4am on 10/28).
page 4 of 13
Problem Set 6 Solutions
Math 158, Fall 2016
From these data, the prime number theorem looks plausible enough, although one might
conjecture that the limit is closer to 1.1 than to 1 itself. More data would be needed to
clearly see the convergence to 1.
5. Textbook exercise 3.18.
Solution.
(a) We can sort the primes up to 100 into congruence classes modulo 4 as follows.
0
(mod 4) :
(none)
1
(mod 4) :
5, 13, 17, 29, 37, 41, 53, 61, 73, 89, 97
2
(mod 4) :
2
3
(mod 4) :
3, 7, 11, 19, 23, 31, 43, 47, 59, 67, 71, 79, 83
Just by counting, we obtain:
π1 (10) = 1 π1 (25) = 3 π1 (100) = 11
π3 (10) = 2 π3 (25) = 5 π3 (100) = 13
Note that these are are fairly close to each other, but π3 is always one or two larger than
π1 .
(b) Using a previously written function primesToBound(N) that gives a list of all primes
≤ N , there are various ways to count the number of elements in a given congruence
class. Here is a particularly pythonic one-liner for this.
def pi(N,rem,modulus):
return len([p for p in primesToBound(N) if p%modulus == rem])
Using this function, we can interrogate the relationship between π1 and π3 as follows.
>>> for X in [100,1000,10000,100000]:
...
print X, pi(X,1,4), pi(X,3,4), float(pi(X,3,4))/pi(X,1,4)
...
100 11 13 1.18181818182
1000 80 87 1.0875
10000 609 619 1.01642036125
100000 4783 4808 1.00522684508
(c) These data suggest that, asymptotically speaking π1 (X) and π3 (X) are the same –
their ratio seems to go to 1. Furthermore, the data suggest that this ratio approaches
1 from above, not below, i.e. that more primes are 3 (mod 4) than 1 (mod 4) (by
a shrinking margin in relative terms). One might conjecture that π3 (X) > π1 (X)
for all X. The investigation of this conjecture (and related questions) is described
in Granville and Martin’s very interesting article “Prime Number Races,” which you
can find here: http://www.maa.org/sites/default/files/pdf/upload_library/22/
Ford/granville1.pdf, The conjecture turns out to be false, but one needs to look at
more data to determine this.
6. For each integer n between 1,000,000 and 1,000,009 inclusive, determine the proportion of the
numbers from 1 to n − 1 inclusive that are Miller-Rabin witnesses. Which of these numbers
are prime? (The figures you obtain should convince you that the 75% figure from Rabin’s
theorem is rather conservative, and explains why most people are not worried about using
only a few Miller-Rabin trials to test primality).
Due the night of Thursday 10/27 (hard deadline 4am on 10/28).
page 5 of 13
Problem Set 6 Solutions
Math 158, Fall 2016
Solution. We first require a function is witness(a,n) that determines whether or not a
is a witness modulo n. See the solution to problem 9 for one implementation. With this in
hand, we can determine these proportions as follows.
>>> for
...
...
...
...
...
1000000
1000001
1000002
1000003
1000004
1000005
1000006
1000007
1000008
1000009
n in xrange(1000000,1000010):
wits = 0
for a in xrange(1,n):
if is_witness(a,n): wits += 1
print n, float(wits)/(n-1)
0.999998999999
0.99625
0.999999000001
0.0
0.999999000003
0.999998000008
0.999997000015
0.999902000588
0.999999000007
0.999994000048
One of these numbers is prime: 1000003 (since it has no witnesses to compositness). The
others are composite. Note that in all cases, at least 99.5% of the candidates are witnesses.
So even a single Miller-Rabin trial is quite conclusive (at least for these choices of n).
7. Textbook exercise 3.22. (We will discuss Pollard’s algorithm in class on Monday).
(a) Here’s what happens when I arbitrarily choose a = 2, and start raising it to larger and
larger powers as prescribed by Pollard. At each step, I check whether gcd(a − 1, n) has
become interesting yet.
>>> n = 1739
>>> a = 2
>>> gcd(a-1,n)
1
>>> a = pow(a,2,n)
>>> gcd(a-1,n)
1
>>> a = pow(a,3,n)
>>> gcd(a-1,n)
1
>>> a = pow(a,5,n)
>>> gcd(a-1,n)
1
>>> a = pow(a,6,n)
>>> gcd(a-1,n)
37
After 6 tries, a factor is found: p = 37. The other is 1739/37 = 47. The reason that this
worked so quickly is that p − 1 = 36 = 22 32 ; these are both very small primes, so very
quickly (for small k) 2k! ≡ 1 (mod 37).
Due the night of Thursday 10/27 (hard deadline 4am on 10/28).
page 6 of 13
Problem Set 6 Solutions
Math 158, Fall 2016
(b) This time, I’ll automate the process a bit. To keep track of how things evolve, I’ll print
the value of a at each step, and stop once gcd(a − 1, n) > 1. As before, I’ll use a = 2 for
simplicity.
>>> n = 220459
>>> i = 1
>>> a = 2
>>> while True:
...
print i,a,gcd(a-1,n)
...
if gcd(a-1,n) > 1: break
...
i += 1
...
a = pow(a,i,n)
...
1 2 1
2 4 1
3 64 1
4 22332 1
5 85054 1
6 4046 1
7 43103 1
8 179601 449
Again, this didn’t take too long. We found the factor p = 449, because 28! ≡ 1 (mod n).
This worked because 449 − 1 = 448 factors into 28 · 7, which divides 8!.
(c) We proceed like before.
>>> n = 48356747
>>> a = 2
>>> i = 1
>>> while True:
...
print i,a,gcd(a-1,n)
...
if gcd(a-1,n) > 1: break
...
i += 1
...
a = pow(a,i,n)
...
1 2 1
2 4 1
3 64 1
4 16777216 1
5 29007256 1
6 6497326 1
7 11540770 1
8 13320680 1
9 2119447 1
10 32129514 1
11 4931912 1
12 35410324 1
13 46845551 1
14 45774461 1
15 46983891 1
Due the night of Thursday 10/27 (hard deadline 4am on 10/28).
page 7 of 13
Problem Set 6 Solutions
Math 158, Fall 2016
16 8398521 1
17 9367160 1
18 17907956 1
19 13944673 6917
So after getting to 219! (mod n), we found one factor: p = 6917. The other is n/p = 6991.
This worked because 6917 − 1 factors into small primes: it is 22 · 7 · 13 · 19. This divides
19!, which is why we found the factor once 219! (mod n) was computed.
8. Suppose that p is a large prime (e.g. 1024 bits), g is a primitive root modulo p, and Alice
has an Elgamal public key A corresponding to a private key a (that is, A ≡ g a (mod p), and
Alice knows the number a). Bob does not believe that Alice actually knows the private key
a corresponding to A, so she asks her to solve the following challenge to prove it. Bob will
give Alice a positive integer d of his choosing. Alice must return (in a reasonable amount of
time) two integers b, c such that
g b · bc ≡ Ad
(mod p).
If she succeeds, Bob will be convinved that Alice really does know her private key.
(a) Describe a procedure that Alice can use to solve Bob’s challenge efficiently.
Hint. Choose an integer e at random, and choose b to be g e (mod p). Then find a choice
of c.
(b) Explain briefly why Bob should be convinced that Eve (or anyone else who doesn’t know
the private key) would not be able to carry out the procedure you describe in part (a).
Note. This exercise prefigures the basic idea behind Elgamal “digital signatures,” which we
will discuss soon. You can solve this problem without knowing anything about signatures,
however.
Solution.
(a) Following the hint, suppose that Alice begins by choosing an integer k at random and
setting b ≡ g k (mod p) (we’ll see shortly that she shouldn’t choose it completely at
random, but impose a mild condition; this will come out in the analysis). We still
need to choose c. At this stage, she is attempting to solve the congruence g b g kc ≡ g ad
(mod p), i.e. g b+kc−ad ≡ 1 (mod p). Since g is a primitive root, it’s order is p − 1, and
this congruence is equivalent to (p − 1) | (b + kc − ad), i.e. b + kc − ad ≡ 0 (mod p − 1).
But all of the variables in this congruence have been fixed except one: Alice needs merely
solve for c to obtain
c ≡ k −1 (ad − b) (mod p − 1).
Note that there is one tricky point here: she can only do this if she previously chose k
relatively prime to p − 1, so that it has an inverse modulo p − 1. But that’s no problem:
she got to chose k herself, so she can just make sure to choose k relatively prime to p − 1.
(b) In order to carry out this process, Alice needed to use her knowledge of the secret key
a in a crucial way. Without it, she would not have been able to write the congruence
entirely in terms of powers of g, which was the crucial step to move from modulo p to
modulo p − 1.
(It is conceivable that there is a completely different route to solving the congruence
that doesn’t require knowing a, of course; but the method outlined in part (a) cannot
be adapted.)
Due the night of Thursday 10/27 (hard deadline 4am on 10/28).
page 8 of 13
Problem Set 6 Solutions
Math 158, Fall 2016
Programming problems
Full formulation and submission: https://www.hackerrank.com/m158-2016-pset-6
9. Determine whether a given integer N (up to 1024 bits long) is prime or not.
Here is one implementation of the Miller-Rabin primality test. We first write a method to
test if a specific integer a is a witness for n, then write a “wrapper” function that simply calls
this function on 100 randomly chosen values of a, seeing if any one of them turn out to be
witnesses. To save time, it is sensible not to finish all 100 trials once we have found even one
witness; the function returns right away in this case.
import random
# Determines whether a is a Miller-Rabin witness of n
def is_witness(a,n):
q = n-1
k = 0
while q%2 == 0:
q /= 2
k += 1
b = pow(a,q,n)
if b == 1: return False
for i in xrange(k):
if b == n-1: return False
b = b*b % n
return True
# Miller-Rabin test, with 100 trials by default
def is_prime(n, trials=100):
for i in xrange(trials):
a = random.randrange(1,n)
if is_witness(a,n): return False
return True
n = int(raw_input())
if is_prime(n):
print ’prime’
else:
print ’composite’
10. Generate two prime numbers p, q, of specified length (number of bits), such that p ≡ 1
(mod q), along with a number g ∈ {1, 2, · · · , p − 1} such that ord[g]p = q.
Hint. See exercise 1.33 in the textbook for a useful fact that can help you create g (Problem
Set 2, number 6).
Note. Generating p, q, g of this form is important for choosing parameters for the DSA system,
which we will discuss soon.
Solution.
Due the night of Thursday 10/27 (hard deadline 4am on 10/28).
page 9 of 13
Problem Set 6 Solutions
Math 158, Fall 2016
First, we must decide how to create p and q. If we choose p first, then finding q will be
difficult: we would need to factor p − 1. It is easier to go in the opposite order: we can first
choose q, and then choose p of the form p = 1 + kq. As discussed in class, if k is chosen at
random, then the odds of 1 + kq being prime are about the same as the odds that any other
integer of that size is prime, so we can expect to find a prime fairly quickly.
One issue that must be sorted out is what range to choose k in when hunting for p. We can
work this out by solving the inequalities 2pbits−1 ≤ 1 + kq ≤ 2pbits − 1, obtaining
2pbits−1 − 1
2pbits − 2
≤k≤
.
q
q
As long as we choose k in that range, we’ll get values of p of the right size.
The next matter to consider is how to find the integer g of order q. Here we follow the hint
and invoke the result of exercise 1.33 in the textbook (Problem set 2, problem 6): if we just
choose a number a ∈ {1, 2, · · · , p − 1} at random, and set g ≡ a(p−1)/q (mod p), then
(a) As long as g 6≡ 1 (mod p), it will be an element of order q, as desired, and
(b) With probability 1 − 1q , g won’t be 1 (mod p).
Probability 1 − 1q is high enough that we’ll find such a g in very short order (if q is over 64
bits or so in length, as it would be in applications, 1 − 1q is so close to 1 that you would not
expect to ever need to try more that one random value of a in this process).
Here’s a sample implementation (if you haven’t seen the syntax 1 << n before: it just does
the same thing as 2n , but it’s more universal across other programming languages).
### Omitted: is_prime function from previous problem.
# Make a random prime of given size
def makePrime(bits):
while True:
p = random.randrange(1<<(bits-1), 1<<bits)
if is_prime(p): return p
# Make a prime of a given size, in a given congruence class
def makePrimeInCongClass(bits, rem, modulus):
# Find p as rem + k*modulus for some k. Must find min and max possible k.
# mink is (2^(bits-1) - rem)/modulus, rounded up.
# Rounding up is achieved by adding modulus-1 before division.
mink = ((1<<(bits-1))-rem + modulus - 1)/modulus
# maxk is (2^bits - 1 - rem)/modulus, rounded down.
maxk = ((1<<bits) - 1 - rem)/modulus
while True:
k = random.randrange(mink,maxk+1)
p = rem+modulus*k
if is_prime(p): return p
def dsaparams(qbits,pbits):
q = makeprime(qbits)
Due the night of Thursday 10/27 (hard deadline 4am on 10/28).
page 10 of 13
Problem Set 6 Solutions
Math 158, Fall 2016
p = makeprime(pbits,1,q)
g = 1 # Just to make the while loop run the first time
while g == 1:
a = random.randrange(1,p)
g = pow(a,(p-1)/q,p)
return p,q,g
qbits,pbits = map(int,raw_input().split())
p,q,g = dsaparams(qbits,pbits)
print p,q,g
11. Alice decides that she wants to receive messages using a non-standard variant of RSA. Like
in the usual RSA, she will choose a public key N, e, where N is a number whose factorization
she knows, and gcd(e, φ(N )) = 1. In this case, she will take N = pqr, where p, q, r are distinct
primes. To encrypt a message m for Alice (0 ≤ m < N ), Bob computes c ≡ me (mod N ).
Given the three primes p, q, r, the number e, and the ciphertext c sent by Bob, recover the
original plaintext m.
Note. While this setup is perfectly functional, in practice it is more efficient to use products
of two primes, hence that is the standard. I encourage you to think about why it is more
efficient to use only two primes.
Solution. We proceed as in problem 3 of this problem set, using φ(pqr) = (p−1)(q−1)(r−1).
We can simply invert e modulo φ(pqr) and use it as a decryption exponent.
### Omitted: impementation of extended Euclidean algorithm
p,q,r,e,c = map(int,raw_input().split())
N = p*q*r
phi = (p-1)*(q-1)*(r-1)
d = ext_euclid(e,phi)[0] % phi
print pow(c,d,N)
12. You will be given an RSA modulus (a product of two distinct primes), 112 bits in length.
Factor it if possible. The primes will have been chosen completely at random, i.e. no effort
has been made to avoid “weak” primes. As a result, some of the moduli will be susceptible to
Pollard’s algorithm (which we discuss in class on Monday), but not all. You are not expected
to solve all, or even most, of the test cases; a score of 10/50 (as listed on hackerrank; this
corresponds to 20 out of 100 test cases correct) will be regarded as full points in the actual
grading spreadsheet. If you devise a problem to solve more testcases than these, you will
receive extra credit.
Solution.
Here’s one implementation of Pollard’s p − 1 algorithm that solves 20/100 test cases (10/50
points). It differs from the presentation of the book in two main ways:
(a) Rather than choosing one value of a (like a = 2), it runs a series of “trials” (until one
succeeds), choosing a different a at random each time.
Due the night of Thursday 10/27 (hard deadline 4am on 10/28).
page 11 of 13
Problem Set 6 Solutions
Math 158, Fall 2016
(b) Instead of stopping at some chosen value for the exponent, it only stops once either a
factor is found or a becomes 1 (mod N ). Both are detected in the same way: the gcd
of a − 1 and N becomes greater than 1.
import random,fractions
# Use Pollard’s algorithm to find SOME factor of N
# It’s possible that it will return N itself
def pollard_findfact(N):
a = random.randrange(1,N)
# Check first whether a is a unit. If not, you have a factor.
if fractions.gcd(a,N) != 1:
return fractions.gcd(a,N)
j = 2
while fractions.gcd(a-1,N) == 1:
a = pow(a,j,N)
j += 1
return fractions.gcd(a-1,N)
def pollard(N):
p = N
while p == N:
p = pollard_findfact(N)
q = N/p
if p<q: return p,q
else: return q,p
# I/O
N = int(raw_input())
p,q = pollard(N)
print p,q
There are a number of ways to try to improve this algorithm to squeeze the runtime down
by a constant factor and get a few more test cases in the time limit. One way is suggested
in the book: only compute gcd(a − 1, N ) periodically (not after every step). This saves more
time than you would expect; the divide operations needed in the Euclidean algorithm are
somewhat more expensive than the multiplications in the fast-powering algorithm.
In case you are curious, the following code is the most effective program I found after a
fair bit of experimentation. I would not be surprised if it is possible to squeeze still better
performance out with some different ideas. This program is able to solve 38/100 test cases
within the time limit (19/50 points).
I encourage you to puzzle out the ideas underlying this code, and am happy to explain it
during office hours. The basic idea is to reach up to larger primes in the exponent more
rapidly by skipping any numbers that have already-present factors in them (in this case,
we include large powers of 2, 3, 5, 7, 11, and 13 at the beginning, and then skip multiples
of any of these primes for the rest of the program). Do note, however, that it is not any
better asymptotically than the much easier-to-implement solution above; it just squeezes the
Due the night of Thursday 10/27 (hard deadline 4am on 10/28).
page 12 of 13
Problem Set 6 Solutions
Math 158, Fall 2016
constant factor in the runtime down somewhat. In real applications you probably wouldn’t
want to do this sort of optimization unless you’re planning to run a program for a period of
weeks or months.
import random,fractions
smallPrimes = 2*3*5*7*11*13
remainders = [i for i in xrange(smallPrimes) if fractions.gcd(i,smallPrimes)==1]
# Use Pollard’s algorithm to find SOME factor of N
# It’s possible that it will return N itself
def pollard_findfact(N):
a = random.randrange(1,N)
for p in [2,3,5,7,11,13]:
a = pow(a,p**50,N)
if fractions.gcd(a-1,N) > 1:
return fractions.gcd(a-1,N)
j = 0
while fractions.gcd(a-1,N) == 1:
for r in remainders:
a = pow(a,smallPrimes*j+r,N)
j += 1
return fractions.gcd(a-1,N)
def pollard(N):
while True:
p = pollard_findfact(N)
assert(p > 1)
if p < N:
q = N/p
if p<q: return p,q
else: return q,p
Due the night of Thursday 10/27 (hard deadline 4am on 10/28).
page 13 of 13