diff options
author | Sybren Stüvel <sybren@stuvel.eu> | 2008-04-23 13:22:44 +0200 |
---|---|---|
committer | Sybren Stüvel <sybren@stuvel.eu> | 2008-04-23 13:22:44 +0200 |
commit | 454261b199c6c633f40afec2b198d6c330b8ac8a (patch) | |
tree | fe2c987747df861bdf4b62ae1aa47271a9fed267 | |
download | rsa-git-454261b199c6c633f40afec2b198d6c330b8ac8a.tar.gz |
Initial checkin of version 1.1
-rw-r--r-- | .hgignore | 5 | ||||
-rw-r--r-- | rsa-test.py | 38 | ||||
-rw-r--r-- | rsa/__init__.py | 448 | ||||
-rwxr-xr-x | runme.py | 122 | ||||
-rwxr-xr-x | setup.py | 26 |
5 files changed, 639 insertions, 0 deletions
diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000..42b6988 --- /dev/null +++ b/.hgignore @@ -0,0 +1,5 @@ +\.pyc$ +\..*\.swp$ +^build/ +^dist/ +^rsa.egg-info/ diff --git a/rsa-test.py b/rsa-test.py new file mode 100644 index 0000000..319c138 --- /dev/null +++ b/rsa-test.py @@ -0,0 +1,38 @@ +import rsa + +(pub, priv) = rsa.gen_pubpriv_keys(64) + +print "Testing integer operations:" + +message = 42 +print "\tMessage: %d" % message + +encrypted = rsa.encrypt_int(message, pub['e'], pub['n']) +print "\tEncrypted: %d" % encrypted + +decrypted = rsa.decrypt_int(encrypted, priv['d'], pub['n']) +print "\tDecrypted: %d" % decrypted + +signed = rsa.sign_int(message,priv['d'], pub['n']) +print "\tSigned: %d" % signed + +verified = rsa.verify_int(signed, pub['e'],pub['n']) +print "\tVerified: %d" % verified + + +print "Testing string operations:" + +message = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +print "\tMessage: %s" % message + +encrypted = rsa.encrypt(message, pub) +print "\tEncrypted: %s" % encrypted + +decrypted = rsa.decrypt(encrypted, priv) +print "\tDecrypted: %s" % decrypted + +signed = rsa.sign(message,priv) +print "\tSigned: %s" % signed + +verified = rsa.verify(signed, pub) +print "\tVerified: %s" % verified diff --git a/rsa/__init__.py b/rsa/__init__.py new file mode 100644 index 0000000..996a7e2 --- /dev/null +++ b/rsa/__init__.py @@ -0,0 +1,448 @@ +"""RSA module + +Module for calculating large primes, and RSA encryption, decryption, +signing and verification. Includes generating public and private keys. +""" + +__author__ = "Sybren Stuvel, Marloes de Boer and Ivo Tamboer" +__date__ = "2008-04-23" + +# NOTE: Python's modulo can return negative numbers. We compensate for +# this behaviour using the abs() function + +import math +import sys +import random # For picking semi-random numbers +import types +from cPickle import dumps, loads +import base64 +import zlib + +RANDOM_DEV="/dev/urandom" +has_broken_randint = False + +try: + random.randint(1, 10000000000000000000000000L) +except: + has_broken_randint = True + print "This system's random.randint() can't handle large numbers" + print "Random integers will all be read from %s" % RANDOM_DEV + + +def log(x, base = 10): + return math.log(x) / math.log(base) + +def gcd(p, q): + """Returns the greatest common divisor of p and q + + + >>> gcd(42, 6) + 6 + """ + if p<q: return gcd(q, p) + if q == 0: return p + return gcd(q, abs(p%q)) + +def bytes2int(bytes): + """Converts a list of bytes or a string to an integer + + >>> (128*256 + 64)*256 + + 15 + 8405007 + >>> l = [128, 64, 15] + >>> bytes2int(l) + 8405007 + """ + + if not (type(bytes) is types.ListType or type(bytes) is types.StringType): + raise TypeError("You must pass a string or a list") + + # Convert byte stream to integer + integer = 0 + for byte in bytes: + integer *= 256 + if type(byte) is types.StringType: byte = ord(byte) + integer += byte + + return integer + +def int2bytes(number): + """Converts a number to a string of bytes + + >>> bytes2int(int2bytes(123456789)) + 123456789 + """ + + if not (type(number) is types.LongType or type(number) is types.IntType): + raise TypeError("You must pass a long or an int") + + string = "" + + while number > 0: + string = "%s%s" % (chr(number & 0xFF), string) + number /= 256 + + return string + +def fast_exponentiation(a, p, n): + """Calculates r = a^p mod n + """ + result = a % n + remainders = [] + while p != 1: + remainders.append(p & 1) + p = p >> 1 + while remainders: + rem = remainders.pop() + result = ((a ** rem) * result ** 2) % n + return result + +def read_random_int(nbits): + """Reads a random integer from RANDOM_DEV of approximately nbits + bits rounded up to whole bytes""" + + nbytes = ceil(nbits/8) + + # Read 'nbits' bits of random data + fd = open(RANDOM_DEV) + randomdata = fd.read(nbytes) + fd.close() + + if len(randomdata) != nbytes: + raise Exception("Unable to read enough random bytes") + + return bytes2int(randomdata) + +def ceil(x): + """Returns int(math.ceil(x))""" + + return int(math.ceil(x)) + +def randint(minvalue, maxvalue): + """Returns a random integer x with minvalue <= x <= maxvalue""" + + if not has_broken_randint: + return random.randint(minvalue, maxvalue) + + # Safety - get a lot of random data even if the range is fairly + # small + min_nbits = 32 + + # The range of the random numbers we need to generate + range = maxvalue - minvalue + + # Which is this number of bytes + rangebytes = ceil(log(range, 2) / 8) + + # Convert to bits, but make sure it's always at least min_nbits*2 + rangebits = max(rangebytes * 8, min_nbits * 2) + + # Take a random number of bits between min_nbits and rangebits + nbits = random.randint(min_nbits, rangebits) + + return (read_random_int(nbits) % range) + minvalue + +def fermat_little_theorem(p): + """Returns 1 if p may be prime, and something else if p definitely + is not prime""" + + a = randint(1, p-1) + return fast_exponentiation(a, p-1, p) + +def jacobi(a, b): + """Calculates the value of the Jacobi symbol (a/b) + """ + + if a % b == 0: + return 0 + result = 1 + while a > 1: + if a & 1: + if ((a-1)*(b-1) >> 2) & 1: + result = -result + b, a = a, b % a + else: + if ((b ** 2 - 1) >> 3) & 1: + result = -result + a = a >> 1 + return result + +def jacobi_witness(x, n): + """Returns False if n is an Euler pseudo-prime with base x, and + True otherwise. + """ + + j = jacobi(x, n) % n + f = fast_exponentiation(x, (n-1)/2, n) + + if j == f: return False + return True + +def randomized_primality_testing(n, k): + """Calculates whether n is composite (which is always correct) or + prime (which is incorrect with error probability 2**-k) + + Returns False if the number if composite, and True if it's + probably prime. + """ + + q = 0.5 # Property of the jacobi_witness function + + # t = int(math.ceil(k / log(1/q, 2))) + t = ceil(k / log(1/q, 2)) + for i in range(t+1): + x = randint(1, n-1) + if jacobi_witness(x, n): return False + + return True + +def is_prime(number): + """Returns True if the number is prime, and False otherwise. + + >>> is_prime(42) + 0 + >>> is_prime(41) + 1 + """ + + """ + if not fermat_little_theorem(number) == 1: + # Not prime, according to Fermat's little theorem + return False + """ + + if randomized_primality_testing(number, 5): + # Prime, according to Jacobi + return True + + # Not prime + return False + + +def getprime(nbits): + """Returns a prime number of max. 'math.ceil(nbits/8)*8' bits. In + other words: nbits is rounded up to whole bytes. + + >>> p = getprime(8) + >>> is_prime(p-1) + 0 + >>> is_prime(p) + 1 + >>> is_prime(p+1) + 0 + """ + + nbytes = int(math.ceil(nbits/8)) + + while True: + integer = read_random_int(nbits) + + # Make sure it's odd + integer |= 1 + + # Test for primeness + if is_prime(integer): break + + # Retry if not prime + + return integer + +def are_relatively_prime(a, b): + """Returns True if a and b are relatively prime, and False if they + are not. + + >>> are_relatively_prime(2, 3) + 1 + >>> are_relatively_prime(2, 4) + 0 + """ + + d = gcd(a, b) + return (d == 1) + +def find_p_q(nbits): + """Returns a tuple of two different primes of nbits bits""" + + p = getprime(nbits) + while True: + q = getprime(nbits) + if not q == p: break + + return (p, q) + +def extended_euclid_gcd(a, b): + """Returns a tuple (d, i, j) such that d = gcd(a, b) = ia + jb + """ + + if b == 0: + return (a, 1, 0) + + q = abs(a % b) + r = long(a / b) + (d, k, l) = extended_euclid_gcd(b, q) + + return (d, l, k - l*r) + +# Main function: calculate encryption and decryption keys +def calculate_keys(p, q, nbits): + """Calculates an encryption and a decryption key for p and q, and + returns them as a tuple (e, d)""" + + n = p * q + phi_n = (p-1) * (q-1) + + while True: + # Make sure e has enough bits so we ensure "wrapping" through + # modulo n + e = getprime(max(8, nbits/2)) + if are_relatively_prime(e, n) and are_relatively_prime(e, phi_n): break + + (d, i, j) = extended_euclid_gcd(e, phi_n) + + if not d == 1: + raise Exception("e (%d) and phi_n (%d) are not relatively prime" % (e, phi_n)) + + if not (e * i) % phi_n == 1: + raise Exception("e (%d) and i (%d) are not mult. inv. modulo phi_n (%d)" % (e, i, phi_n)) + + return (e, i) + + +def gen_keys(nbits): + """Generate RSA keys of nbits bits. Returns (p, q, e, d). + """ + + while True: + (p, q) = find_p_q(nbits) + (e, d) = calculate_keys(p, q, nbits) + + # For some reason, d is sometimes negative. We don't know how + # to fix it (yet), so we keep trying until everything is shiny + if d > 0: break + + return (p, q, e, d) + +def gen_pubpriv_keys(nbits): + """Generates public and private keys, and returns them as (pub, + priv). + + The public key consists of a dict {e: ..., , n: ....). The private + key consists of a dict {d: ...., p: ...., q: ....). + """ + + (p, q, e, d) = gen_keys(nbits) + + return ( {'e': e, 'n': p*q}, {'d': d, 'p': p, 'q': q} ) + +def encrypt_int(message, ekey, n): + """Encrypts a message using encryption key 'ekey', working modulo + n""" + + if type(message) is types.IntType: + return encrypt_int(long(message), ekey, n) + + if not type(message) is types.LongType: + raise TypeError("You must pass a long or an int") + + if math.floor(log(message, 2)) > math.floor(log(n, 2)): + raise OverflowError("The message is too long") + + return fast_exponentiation(message, ekey, n) + +def decrypt_int(cyphertext, dkey, n): + """Decrypts a cypher text using the decryption key 'dkey', working + modulo n""" + + return encrypt_int(cyphertext, dkey, n) + +def sign_int(message, dkey, n): + """Signs 'message' using key 'dkey', working modulo n""" + + return decrypt_int(message, dkey, n) + +def verify_int(signed, ekey, n): + """verifies 'signed' using key 'ekey', working modulo n""" + + return encrypt_int(signed, ekey, n) + +def picklechops(chops): + """Pickles and base64encodes it's argument chops""" + + value = zlib.compress(dumps(chops)) + encoded = base64.encodestring(value) + return encoded.strip() + +def unpicklechops(string): + """base64decodes and unpickes it's argument string into chops""" + + return loads(zlib.decompress(base64.decodestring(string))) + +def chopstring(message, key, n, funcref): + """Splits 'message' into chops that are at most as long as n, + converts these into integers, and calls funcref(integer, key, n) + for each chop. + + Used by 'encrypt' and 'sign'. + """ + + msglen = len(message) + mbits = msglen * 8 + nbits = int(math.floor(log(n, 2))) + nbytes = nbits / 8 + blocks = msglen / nbytes + + if msglen % nbytes > 0: + blocks += 1 + + cypher = [] + + for bindex in range(blocks): + offset = bindex * nbytes + block = message[offset:offset+nbytes] + value = bytes2int(block) + cypher.append(funcref(value, key, n)) + + return picklechops(cypher) + +def gluechops(chops, key, n, funcref): + """Glues chops back together into a string. calls + funcref(integer, key, n) for each chop. + + Used by 'decrypt' and 'verify'. + """ + message = "" + + chops = unpicklechops(chops) + + for cpart in chops: + mpart = funcref(cpart, key, n) + message += int2bytes(mpart) + + return message + +def encrypt(message, key): + """Encrypts a string 'message' with the public key 'key'""" + + return chopstring(message, key['e'], key['n'], encrypt_int) + +def sign(message, key): + """Signs a string 'message' with the private key 'key'""" + + return chopstring(message, key['d'], key['p']*key['q'], decrypt_int) + +def decrypt(cypher, key): + """Decrypts a cypher with the private key 'key'""" + + return gluechops(cypher, key['d'], key['p']*key['q'], decrypt_int) + +def verify(cypher, key): + """Verifies a cypher with the public key 'key'""" + + return gluechops(cypher, key['e'], key['n'], encrypt_int) + +# Do doctest if we're not imported +if __name__ == "__main__": + import doctest + doctest.testmod() + +__all__ = ["gen_pubpriv_keys", "encrypt", "decrypt", "sign", "verify"] + diff --git a/runme.py b/runme.py new file mode 100755 index 0000000..9218666 --- /dev/null +++ b/runme.py @@ -0,0 +1,122 @@ +#!/usr/bin/python + +import sys +import rsa + +pub = {'e': 248780186015280096941266517855109524861L, 'n': 289829846799217715870297548823528254304373554012264300246542488949110436220426067982155063515456468780177806574004719751854439098845391023946149242816599L} + +print "Running rsa.verify(verslag, pub)..." + +verslag = """ +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 +""" + +verslag = rsa.verify(verslag, pub) + +print "Decryption done, press enter to read" +sys.stdin.readline() +print verslag + +print "Generating public & private keypair for demonstrational purposes..." +(pub, priv) = rsa.gen_pubpriv_keys(256) + +print +print "Public:" +print "\te: %d" % pub['e'] +print "\tn: %d" % pub['n'] +print + +print "Private:" +print "\td: %d" % priv['d'] +print "\tp: %d" % priv['p'] +print "\tq: %d" % priv['q'] + diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..f704dd1 --- /dev/null +++ b/setup.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from setuptools import setup + +setup(name='rsa', + version='1.1', + description='Pure-Python RSA implementation', + author=u'Sybren Stüvel'.encode('utf-8'), + author_email='sybren@stuvel.eu', + maintainer=u'Sybren Stüvel'.encode('utf-8'), + maintainer_email='sybren@stuvel.eu', + url='http://www.stuvel.eu/rsa', + packages=['rsa'], + license='GPL', + classifiers=[ + 'Development Status :: 5 - Beta', + 'Intended Audience :: End Users/Desktop', + 'License :: OSI Approved :: GNU General Public License (GPL)', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Topic :: Multimedia :: Graphics :: Capture :: Digital Camera', + 'Topic :: Artistic Software', + 'Natural Language :: English', + ], +) |