In [54]:
# copied from last time
def exp_mod(a,e,b):
    """Return a^e % b, computed using repeated squaring."""
    
    bin_rep=bin(e) # first find binary representation of e 
    prod = 1    
    cur_pow_two = a # this will run through a^(2^0), a^(2^1), a^(2^2),...
    for i in range(len(bin_rep)-2): 
        if bin_rep[-1-i]=='1':
            prod = prod * cur_pow_two % b # multiply by cur_pow_two if the corresponding bit is 1
        cur_pow_two = (cur_pow_two)^2 % b  
    return prod

# runtime (for fixed a,b) is approx number of bits of e
In [4]:
# Alice does Setup stage
# Choose large primes 
p = next_prime(2^100 + randint(1,10000)); p
Out[4]:
1267650600228229401496703211467
In [6]:
q = next_prime(2^100 + randint(1,2^99)); q
Out[6]:
1602712780496125242398003925899
In [8]:
n=p*q; n
Out[8]:
2031679818189367639864722847493329578452509534683507195083833
In [9]:
e = 17; gcd(e, (p-1)*(q-1)) # if not 1, choose new e
Out[9]:
1
In [12]:
(a,b,c) = xgcd(e, (p-1)*(q-1)); (a,b,c)
Out[12]:
(1, -358531732621653112917304031910081037953844443536402203755259, 3)
In [15]:
b*e + c*(p-1)*(q-1) # so b is the inverse of e mod (p-1)(q-1)
Out[15]:
1
In [56]:
d = b % ((p-1)*(q-1)); d # d is b reduce mod (p-1)(q-1)
Out[56]:
1673148085567714526947418815580378177117940736503210284191209
In [55]:
# public key = (e,n)
# private key = d
# Setup stage is complete
In [58]:
# Bob encrypts message m
m = 1234567891212
In [60]:
c = exp_mod(m,e,n); c # The ciphertext that Bob sends to Alice
Out[60]:
1616940411438374401072271444326419245332195942513738861212983
In [61]:
# Alice decrypts:
exp_mod(c,d,n) # should equal original message
Out[61]:
1234567891212
In [63]:
factor(n)  
# this takes a couple seconds, 
# so for secure use, primes should be bigger
Out[63]:
1267650600228229401496703211467 * 1602712780496125242398003925899
In [65]:
%%time
next_prime(2^1024 + randint(1,10000))
# 1024-bit primes are often used
CPU times: user 2.18 s, sys: 12 µs, total: 2.18 s
Wall time: 2.18 s
Out[65]:
179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137859
In [66]:
%%time
# test how long factoring takes
factor(next_prime(2^50 + randint(1,10000))\
*next_prime(2^50 + randint(1,2^10)))
CPU times: user 11.1 ms, sys: 0 ns, total: 11.1 ms
Wall time: 11.8 ms
Out[66]:
1125899906843339 * 1125899906848751
In [67]:
# Suppose Eve knows message is either m=10, 11
# She intercepts a ciphertext c.
# Wants to know which m it corresponds to.
# Eve knows public key, so she can try encrypting
# 10 and 11, and see which one messages the intercepted c.  
In [35]:
m1 = 10
In [37]:
c1 = exp_mod(m1,e,n); c1
Out[37]:
100000000000000000
In [39]:
m2 = 11
c2 = exp_mod(m2,e,n); c2
Out[39]:
505447028499293771
In [ ]:
# Always use random padding at end of message
# This also makes sure that m^e is big (much bigger than n)