In [1]:
# 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 [2]:
# Digital signatures using RSA

# Setup stage for RSA. Alice generats public, private keys
p = next_prime(2^100 + randint(1,10000)); p
Out[2]:
1267650600228229401496703209771
In [3]:
q = next_prime(2^100 + randint(1,10000)); q
Out[3]:
1267650600228229401496703209433
In [4]:
n = p*q
In [5]:
e = 17; gcd(e,(p-1)*(q-1)) # check that e, (p-1)(q-1) are relatively prime
Out[5]:
1
In [6]:
# Alice's public key
public_key = (e,n)
In [7]:
# Now Alice computes her private key
# want d to be inverse of e mod (p-1)(q-1)
In [8]:
xgcd(e, (p-1)*(q-1))
Out[8]:
(1, 661680371165466584046690277727627718891481767941737915873793, -7)
In [9]:
d = xgcd(e, (p-1)*(q-1))[1]% ((p-1)*(q-1))
In [10]:
d
Out[10]:
661680371165466584046690277727627718891481767941737915873793
In [11]:
private_key = d
In [12]:
# Alice has message M, wants to sign and send to Bob; make sure to pad with random digits 
M = 12340083712397197237
In [13]:
def RSA_encrypt(m, (e,n)):
    """Computes RSA encryption of m with public-key (e,n)"""
    return exp_mod(m,e,n)
In [14]:
def RSA_decrypt(c, d, (e,n)):
    """Computes RSA decryption of c with private-key d, public-key (e,n)
    and public-key (e,n)"""
    return exp_mod(c,d,n)
In [15]:
# checking encryption and decryption
RSA_decrypt(RSA_encrypt(M, (e,n)), d, (e,n))
Out[15]:
12340083712397197237
In [16]:
# Now to digitally sign M, Alice decrypts with RSA, using her private key
signature = RSA_decrypt(M, d, (e,n)); signature
Out[16]:
1377274213360066249975782906152382787528761226795274476015245
In [17]:
# to verify the signature, Bob encrypts with RSA, using Alice's
# public key
# checks whether this equals the original message
RSA_encrypt(signature, (e,n))
Out[17]:
12340083712397197237
In [28]:
# Hash functions
In [18]:
import hashlib # python library that contains hash functions
In [31]:
# Hash function SHA
hexhash = hashlib.sha224("12dsf lsdfjge").hexdigest(); hexhash
# outputs a hexadecimal (base 16) string
Out[31]:
'49b84d307e275a9c69d92ed7fb0944f595f4c7703279ae004cb699f2'
In [32]:
hexhash = hashlib.sha224("03dsf lsdfjge").hexdigest(); hexhash
# changing the input string a little dramatically changes hash
Out[32]:
'c655fd4a7a977a33d07e2d6a75a0231f555f767656f9220247b15638'
In [33]:
int(hexhash,16) #convert hexadecimal string to an integer
Out[33]:
20887207745054064481520792647511631849574556548999446886336632542776L
In [22]:
# Hashing a longer message; hash value will still have the same length
m = "WASHINGTON — Democratic leaders are planning to put a resolution condemning anti-Semitism and bigotry on the House floor for a vote on Thursday, hoping to put to rest the internal uproar that erupted after Representative Ilhan Omar indinuated that backers of Israel exhibit dual loyalty."
In [23]:
m
Out[23]:
'WASHINGTON \xe2\x80\x94 Democratic leaders are planning to put a resolution condemning anti-Semitism and bigotry on the House floor for a vote on Thursday, hoping to put to rest the internal uproar that erupted after Representative Ilhan Omar indinuated that backers of Israel exhibit dual loyalty.'
In [35]:
len(bin(int(hashlib.sha224(m).hexdigest(),16))) #length in bits
Out[35]:
224
In [26]:
# key property of hash function: hard to find two different messages with same hash