root / trunk / pyopencoin / oc / rsa.py

Revision 84, 13.1 kB (checked in by ocjhb, 4 years ago)

added json to pyopencoin

  • Property svn:mime-type set to text/plain
  • Property svn:eol-style set to native
  • Property svn:executable set to *
Line 
1"""RSA module
2
3!!! This is just a playground, for understanding some bits and pieces,
4this is not at all the production code!!!
5
6
7
8Module for calculating large primes, and RSA encryption, decryption,
9signing and verification. Includes generating public and private keys.
10"""
11
12__author__ = "Sybren Stuvel, Marloes de Boer and Ivo Tamboer"
13__date__ = "2004-11-17"
14
15# NOTE: Python's modulo can return negative numbers. We compensate for
16# this behaviour using the abs() function
17
18import math
19import sys
20import random    # For picking semi-random numbers
21import types
22from pickle import dumps, loads
23import base64
24import zlib
25
26RANDOM_DEV="/dev/urandom"
27testing = False
28has_broken_randint = False
29
30try:
31    #random.randint(1, 10000000000000000000000000L)
32    1+1
33    pass
34except:
35    has_broken_randint = True
36    print "This system's random.randint() can't handle large numbers"
37    print "Random integers will all be read from %s" % RANDOM_DEV
38
39
40def log(x, base = 10):
41    return math.log(x) / math.log(base)
42
43def gcd(p, q):
44    """Returns the greatest common divisor of p and q
45
46
47    >>> gcd(42, 6)
48    6
49    """
50    if p<q: return gcd(q, p)
51    if q == 0: return p
52    return gcd(q, abs(p%q))
53
54def bytes2int(bytes):
55    """Converts a list of bytes or a string to an integer
56
57    >>> (128*256 + 64)*256 + + 15
58    8405007
59    >>> l = [128, 64, 15]
60    >>> bytes2int(l)
61    8405007
62    """
63
64    #if not (type(bytes) is types.ListType or type(bytes) is types.StringType):
65    #    raise TypeError("You must pass a string or a list")
66
67    # Convert byte stream to integer
68    integer = 0
69    for byte in bytes:
70        integer *= 256
71        if type(byte) is types.StringType: byte = ord(byte)
72        integer += byte
73
74    return integer
75
76def int2bytes(number):
77    """Converts a number to a string of bytes
78   
79    >>> bytes2int(int2bytes(123456789))
80    123456789
81    """
82
83    if not (type(number) is types.LongType or type(number) is types.IntType):
84        raise TypeError("You must pass a long or an int")
85
86    string = ""
87
88    while number > 0:
89        string = "%s%s" % (chr(number & 0xFF), string)
90        number /= 256
91   
92    return string
93
94
95def read_random_int(nbits):
96    """Reads a random integer from RANDOM_DEV of approximately nbits
97    bits rounded up to whole bytes"""
98    #print nbits   
99    nbytes  = ceil(nbits/8)
100   
101    bytes = []
102    for i in range(nbytes):
103        bytes.append(random.randint(0,255))       
104    return bytes2int(bytes)
105
106
107    #jhb
108    # Read 'nbits' bits of random data
109    #fd = open(RANDOM_DEV)
110    #randomdata = fd.read(nbytes)
111    #fd.close()
112
113    if len(randomdata) != nbytes:
114        raise Exception("Unable to read enough random bytes")
115
116    return bytes2int(randomdata)
117
118def ceil(x):
119    """Returns int(math.ceil(x))"""
120
121    return int(math.ceil(x))
122   
123def randint(minvalue, maxvalue):
124    """Returns a random integer x with minvalue <= x <= maxvalue"""
125    #jhb
126    #if not has_broken_randint:
127    #    return random.randint(minvalue, maxvalue)
128
129    # Safety - get a lot of random data even if the range is fairly
130    # small
131    min_nbits  = 32
132
133    # The range of the random numbers we need to generate
134    range      = maxvalue - minvalue
135
136    # Which is this number of bytes
137    rangebytes = ceil(log(range, 2) / 8)
138
139    # Convert to bits, but make sure it's always at least min_nbits*2
140    rangebits  = max(rangebytes * 8, min_nbits * 2)
141   
142    # Take a random number of bits between min_nbits and rangebits
143    nbits      = random.randint(min_nbits, rangebits)
144   
145    return (read_random_int(nbits) % range) + minvalue
146
147def fermat_little_theorem(p):
148    """Returns 1 if p may be prime, and something else if p definitely
149    is not prime"""
150
151    a = randint(1, p-1)
152    return fast_exponentiation(a, p-1, p)
153
154def jacobi(a, b):
155    """Calculates the value of the Jacobi symbol (a/b)
156    """
157
158    if a == 0: return 0
159    if a == 1: return 1
160
161    if a % 2 == 0:
162        if (b**2-1)/8 % 2 == 0:
163            return jacobi(a/2, b)
164        return -jacobi(a/2, b)
165   
166    if (a-1) * (b-1) / 4 % 2 == 0:
167        return jacobi(b % a, a)
168
169    return -jacobi(b % a, a)
170
171def jacobi_witness(x, n):
172    """Returns False if n is an Euler pseudo-prime with base x, and
173    True otherwise.
174    """
175
176    j = jacobi(x, n) % n
177    f = fast_exponentiation(x, (n-1)/2, n)
178
179    if j == f: return False
180    return True
181
182def randomized_primality_testing(n, k):
183    """Calculates whether n is composite (which is always correct) or
184    prime (which is incorrect with error probability 2**-k)
185
186    Returns False if the number if composite, and True if it's
187    probably prime.
188    """
189
190    q = 0.5        # Property of the jacobi_witness function
191
192    # t = int(math.ceil(k / log(1/q, 2)))
193    t = ceil(k / log(1/q, 2))
194    for i in range(t+1):
195        x = randint(1, n-1)
196        if jacobi_witness(x, n): return False
197   
198    return True
199
200def is_prime(number):
201    """Returns True if the number is prime, and False otherwise.
202
203    >>> is_prime(42)
204    0
205    >>> is_prime(41)
206    1
207    """
208
209    """
210    if not fermat_little_theorem(number) == 1:
211        # Not prime, according to Fermat's little theorem
212        return False
213    """
214
215    if randomized_primality_testing(number, 5):
216        # Prime, according to Jacobi
217        return True
218   
219    # Not prime
220    return False
221
222   
223def getprime(nbits):
224    """Returns a prime number of max. 'math.ceil(nbits/8)*8' bits. In
225    other words: nbits is rounded up to whole bytes.
226
227    >>> p = getprime(8)
228    >>> is_prime(p-1)
229    0
230    >>> is_prime(p)
231    1
232    >>> is_prime(p+1)
233    0
234    """
235
236    nbytes  = int(math.ceil(nbits/8))
237
238    while True:
239        integer = read_random_int(nbits)
240
241        # Make sure it's odd
242        integer |= 1
243
244        # Test for primeness
245        if is_prime(integer): break
246
247        # Retry if not prime
248
249    return integer
250
251def are_relatively_prime(a, b):
252    """Returns True if a and b are relatively prime, and False if they
253    are not.
254
255    >>> are_relatively_prime(2, 3)
256    1
257    >>> are_relatively_prime(2, 4)
258    0
259    """
260
261    d = gcd(a, b)
262    return (d == 1)
263
264def find_p_q(nbits):
265    """Returns a tuple of two different primes of nbits bits"""
266
267    print 'finding p'
268    p = getprime(nbits)
269    while True:
270        print 'finding q'
271        q = getprime(nbits)
272        if not q == p: break
273   
274    return (p, q)
275
276def extended_euclid_gcd(a, b):
277    """Returns a tuple (d, i, j) such that d = gcd(a, b) = ia + jb
278    """
279
280    if b == 0:
281        return (a, 1, 0)
282
283    q = abs(a % b)
284    r = long(a / b)
285    (d, k, l) = extended_euclid_gcd(b, q)
286
287    return (d, l, k - l*r)
288
289# Main function: calculate encryption and decryption keys
290def calculate_keys(p, q, nbits):
291    """Calculates an encryption and a decryption key for p and q, and
292    returns them as a tuple (e, d)"""
293
294    n     = p * q
295    phi_n = (p-1) * (q-1)
296
297    while True:
298        print 'c'
299        # Make sure e has enough bits so we ensure "wrapping" through
300        # modulo n
301        e = getprime(max(8, nbits/2))
302        if are_relatively_prime(e, n) and are_relatively_prime(e, phi_n): break
303
304    (d, i, j) = extended_euclid_gcd(e, phi_n)
305
306    if not d == 1:
307        raise Exception("e (%d) and phi_n (%d) are not relatively prime" % (e, phi_n))
308
309    if not (e * i) % phi_n == 1:
310        raise Exception("e (%d) and i (%d) are not mult. inv. modulo phi_n (%d)" % (e, i, phi_n))
311
312    return (e, i)
313
314
315def gen_keys(nbits):
316    """Generate RSA keys of nbits bits. Returns (p, q, e, d).
317    """
318
319    while True:
320        print 'find'
321        (p, q) = find_p_q(nbits)
322        print 'calculate'
323        (e, d) = calculate_keys(p, q, nbits)
324
325        # For some reason, d is sometimes negative. We don't know how
326        # to fix it (yet), so we keep trying until everything is shiny
327        if d > 0: break
328
329    return (p, q, e, d)
330
331def gen_pubpriv_keys(nbits):
332    """Generates public and private keys, and returns them as (pub,
333    priv).
334
335    The public key consists of a dict {e: ..., , n: ....). The private
336    key consists of a dict {d: ...., p: ...., q: ....).
337    """
338   
339    (p, q, e, d) = gen_keys(nbits)
340
341    return ( {'e': e, 'n': p*q}, {'d': d, 'p': p, 'q': q} )
342
343def encrypt_int(message, ekey, n):
344    """Encrypts a message using encryption key 'ekey', working modulo
345    n"""
346
347    if type(message) is types.IntType:
348        return encrypt_int(long(message), ekey, n)
349
350    if not type(message) is types.LongType:
351        raise TypeError("You must pass a long or an int")
352
353    #if math.floor(log(message, 2)) > math.floor(log(n, 2)):
354    #    raise OverflowError("The message is too long")
355
356    return fast_exponentiation(message, ekey, n)
357
358def decrypt_int(cyphertext, dkey, n):
359    """Decrypts a cypher text using the decryption key 'dkey', working
360    modulo n"""
361
362    return encrypt_int(cyphertext, dkey, n)
363
364def sign_int(message, dkey, n):
365    """Signs 'message' using key 'dkey', working modulo n"""
366
367    return decrypt_int(message, dkey, n)
368
369def verify_int(signed, ekey, n):
370    """verifies 'signed' using key 'ekey', working modulo n"""
371
372    return encrypt_int(signed, ekey, n)
373
374def picklechops(chops):
375    """Pickles and base64encodes it's argument chops"""
376
377    value   = zlib.compress(dumps(chops))
378    encoded = base64.encodestring(value)
379    return encoded.strip()
380
381def unpicklechops(string):
382    """base64decodes and unpickes it's argument string into chops"""
383
384    return loads(zlib.decompress(base64.decodestring(string)))
385
386def chopstring(message, key, n, funcref):
387    """Splits 'message' into chops that are at most as long as n,
388    converts these into integers, and calls funcref(integer, key, n)
389    for each chop.
390
391    Used by 'encrypt' and 'sign'.
392    """
393
394    msglen = len(message)
395    mbits  = msglen * 8
396    #nbits  = int(math.floor(log(n, 2)))
397    nbits = 1024
398    nbytes = nbits / 8
399    blocks = msglen / nbytes
400
401    if msglen % nbytes > 0:
402        blocks += 1
403
404    cypher = []
405   
406    for bindex in range(blocks):
407        offset = bindex * nbytes
408        block  = message[offset:offset+nbytes]
409        value  = bytes2int(block)
410        cypher.append(funcref(value, key, n))
411
412    return picklechops(cypher)
413
414def gluechops(chops, key, n, funcref):
415    """Glues chops back together into a string.  calls
416    funcref(integer, key, n) for each chop.
417
418    Used by 'decrypt' and 'verify'.
419    """
420    message = ""
421
422    chops = unpicklechops(chops)
423   
424    for cpart in chops:
425        mpart = funcref(cpart, key, n)
426        message += int2bytes(mpart)
427   
428    return message
429
430def encrypt(message, key):
431    """Encrypts a string 'message' with the public key 'key'"""
432   
433    return chopstring(message, key['e'], key['n'], encrypt_int)
434
435def sign(message, key):
436    """Signs a string 'message' with the private key 'key'"""
437   
438    return chopstring(message, key['d'], key['p']*key['q'], decrypt_int)
439
440def decrypt(cypher, key):
441    """Decrypts a cypher with the private key 'key'"""
442
443    return gluechops(cypher, key['d'], key['p']*key['q'], decrypt_int)
444
445def verify(cypher, key):
446    """Verifies a cypher with the public key 'key'"""
447
448    return gluechops(cypher, key['e'], key['n'], encrypt_int)
449#import math
450
451def bits(integer): #Gets number of bits in integer
452   result = 0
453   while integer:
454      integer >>= 1
455      result += 1
456   return result
457
458
459fast_exponentiation = pow
460def fast_exponentiation2(base, exponent, modulo=None): #Allows fast exponentation with and without moduli
461   print 'in fast_expon'
462   result = 1L
463   if modulo == None:
464      iteration = bits(exponent)
465      while iteration >= 0:
466         result *= result
467         if (exponent >> iteration) & 1:
468            result *= base
469         iteration -= 1
470   else:
471         firstModulus = base % modulo
472         iteration = bits(exponent)
473         while iteration >= 0:
474            result = (result * result) % modulo
475            if (exponent >> iteration) & 1:
476               result = (result * firstModulus) % modulo
477            iteration -= 1
478   return result
479
480def fast_exponentiation1(a, p, n):
481    """Calculates r = a^p mod n
482    """
483   
484   
485    out = a
486    while p > 1 :
487        print a,p,out           
488        if p & 1 == 0:
489            out = (a ** 2) % n
490            p = p/2
491        else:
492            out = (a * out) % n
493            p = p-1
494    return out
495
496def fast_exponentiation2(a, p, n):
497    """Calculates r = a^p mod n
498    """
499    if p == 0:
500        return 1
501    if p & 1 == 0: # even
502        t = fast_exponentiation2(a, p/2, n)
503        up = t**2 % n
504        #print up
505        #return up
506    else:
507        t = fast_exponentiation2(a, (p-1)/2, n)
508        up =  (a * (t**2 % n)) % n
509        #print up
510        #return up
511   
512    print "fast_exponentiation2", a, p, n,up
513    return up
514
515
516
517# Do doctest if we're not imported
518if __name__ == "__main__":
519    if 1:
520        import time
521        (pub,priv) =  gen_pubpriv_keys(512)
522        t = time.time()
523        print 'keys ',pub, priv
524        message = 'serial '*5
525        print 'cleartext ', message
526        for i in range(10):
527            cypher = encrypt(message,pub)
528            print 'cyphertext: ',cypher
529            print 'decrypted', decrypt(cypher,priv)
530        print time.time() - t
531        print `fast_exponentiation`
532    #(a,p,n) = (62,65,133)
533    #print fast_exponentiation(a, p,n)
534    #print fast_exponentiation2(a, p,n)
535    #print fast_exponentiation3(a, p,n)
536__all__ = ["gen_pubpriv_keys", "encrypt", "decrypt", "sign", "verify"]
Note: See TracBrowser for help on using the browser.