From 6c24848797ea99d478aa0d3c865d540f3d22fb89 Mon Sep 17 00:00:00 2001 From: Brian Warner Date: Wed, 15 Aug 2007 17:36:39 -0700 Subject: [PATCH] copy RSA from PyCrypto into the allmydata/ tree, we'll use it eventually --- setup.py | 5 +- src/allmydata/Crypto/PublicKey/RSA.py | 257 +++++++ src/allmydata/Crypto/PublicKey/__init__.py | 17 + src/allmydata/Crypto/PublicKey/pubkey.py | 173 +++++ src/allmydata/Crypto/_fastmath.c | 804 +++++++++++++++++++++ 5 files changed, 1255 insertions(+), 1 deletion(-) create mode 100644 src/allmydata/Crypto/PublicKey/RSA.py create mode 100644 src/allmydata/Crypto/PublicKey/__init__.py create mode 100644 src/allmydata/Crypto/PublicKey/pubkey.py create mode 100644 src/allmydata/Crypto/_fastmath.c diff --git a/setup.py b/setup.py index 2fc54ea9c..f2da9a303 100644 --- a/setup.py +++ b/setup.py @@ -85,7 +85,8 @@ setup(name='allmydata-tahoe', packages=["allmydata", "allmydata.test", "allmydata.util", "allmydata.scripts", "allmydata.Crypto", "allmydata.Crypto.Cipher", - "allmydata.Crypto.Hash", "allmydata.Crypto.Util"], + "allmydata.Crypto.Hash", "allmydata.Crypto.Util", + "allmydata.Crypto.PublicKey"], package_dir={ "allmydata": "src/allmydata",}, scripts = ["bin/allmydata-tahoe"], package_data={ 'allmydata': ['web/*.xhtml', 'web/*.html', 'web/*.css'] }, @@ -98,5 +99,7 @@ setup(name='allmydata-tahoe', Extension("allmydata.Crypto.Hash.SHA256", include_dirs=["src/allmydata/Crypto"], sources=["src/allmydata/Crypto/SHA256.c"]), + Extension("allmydata.Crypto.PublicKey._fastmath", + sources=["src/allmydata/Crypto/_fastmath.c"]), ], ) diff --git a/src/allmydata/Crypto/PublicKey/RSA.py b/src/allmydata/Crypto/PublicKey/RSA.py new file mode 100644 index 000000000..6453373f5 --- /dev/null +++ b/src/allmydata/Crypto/PublicKey/RSA.py @@ -0,0 +1,257 @@ +# +# RSA.py : RSA encryption/decryption +# +# Part of the Python Cryptography Toolkit +# +# Distribute and use freely; there are no restrictions on further +# dissemination and usage except those imposed by the laws of your +# country of residence. This software is provided "as is" without +# warranty of fitness for use or suitability for any purpose, express +# or implied. Use at your own risk or not at all. +# + +__revision__ = "$Id: RSA.py,v 1.20 2004/05/06 12:52:54 akuchling Exp $" + +from allmydata.Crypto.PublicKey import pubkey +from allmydata.Crypto.Util import number + +_fastmath = None +try: + from allmydata.Crypto.PublicKey import _fastmath +except ImportError: + pass + +class error (Exception): + pass + +def generate(bits, randfunc, progress_func=None): + """generate(bits:int, randfunc:callable, progress_func:callable) + + Generate an RSA key of length 'bits', using 'randfunc' to get + random data and 'progress_func', if present, to display + the progress of the key generation. + """ + obj=RSAobj() + + # Generate the prime factors of n + if progress_func: + progress_func('p,q\n') + p = q = 1L + while number.size(p*q) < bits: + p = pubkey.getPrime(bits/2, randfunc) + q = pubkey.getPrime(bits/2, randfunc) + + # p shall be smaller than q (for calc of u) + if p > q: + (p, q)=(q, p) + obj.p = p + obj.q = q + + if progress_func: + progress_func('u\n') + obj.u = pubkey.inverse(obj.p, obj.q) + obj.n = obj.p*obj.q + + obj.e = 65537L + if progress_func: + progress_func('d\n') + obj.d=pubkey.inverse(obj.e, (obj.p-1)*(obj.q-1)) + + assert bits <= 1+obj.size(), "Generated key is too small" + + return obj + +def construct(tuple): + """construct(tuple:(long,) : RSAobj + Construct an RSA object from a 2-, 3-, 5-, or 6-tuple of numbers. + """ + + obj=RSAobj() + if len(tuple) not in [2,3,5,6]: + raise error, 'argument for construct() wrong length' + for i in range(len(tuple)): + field = obj.keydata[i] + setattr(obj, field, tuple[i]) + if len(tuple) >= 5: + # Ensure p is smaller than q + if obj.p>obj.q: + (obj.p, obj.q)=(obj.q, obj.p) + + if len(tuple) == 5: + # u not supplied, so we're going to have to compute it. + obj.u=pubkey.inverse(obj.p, obj.q) + + return obj + +class RSAobj(pubkey.pubkey): + keydata = ['n', 'e', 'd', 'p', 'q', 'u'] + def _encrypt(self, plaintext, K=''): + if self.n<=plaintext: + raise error, 'Plaintext too large' + return (pow(plaintext, self.e, self.n),) + + def _decrypt(self, ciphertext): + if (not hasattr(self, 'd')): + raise error, 'Private key not available in this object' + if self.n<=ciphertext[0]: + raise error, 'Ciphertext too large' + return pow(ciphertext[0], self.d, self.n) + + def _sign(self, M, K=''): + return (self._decrypt((M,)),) + + def _verify(self, M, sig): + m2=self._encrypt(sig[0]) + if m2[0]==M: + return 1 + else: return 0 + + def _blind(self, M, B): + tmp = pow(B, self.e, self.n) + return (M * tmp) % self.n + + def _unblind(self, M, B): + tmp = pubkey.inverse(B, self.n) + return (M * tmp) % self.n + + def can_blind (self): + """can_blind() : bool + Return a Boolean value recording whether this algorithm can + blind data. (This does not imply that this + particular key object has the private information required to + to blind a message.) + """ + return 1 + + def size(self): + """size() : int + Return the maximum number of bits that can be handled by this key. + """ + return number.size(self.n) - 1 + + def has_private(self): + """has_private() : bool + Return a Boolean denoting whether the object contains + private components. + """ + if hasattr(self, 'd'): + return 1 + else: return 0 + + def publickey(self): + """publickey(): RSAobj + Return a new key object containing only the public key information. + """ + return construct((self.n, self.e)) + +class RSAobj_c(pubkey.pubkey): + keydata = ['n', 'e', 'd', 'p', 'q', 'u'] + + def __init__(self, key): + self.key = key + + def __getattr__(self, attr): + if attr in self.keydata: + return getattr(self.key, attr) + else: + if self.__dict__.has_key(attr): + self.__dict__[attr] + else: + raise AttributeError, '%s instance has no attribute %s' % (self.__class__, attr) + + def __getstate__(self): + d = {} + for k in self.keydata: + if hasattr(self.key, k): + d[k]=getattr(self.key, k) + return d + + def __setstate__(self, state): + n,e = state['n'], state['e'] + if not state.has_key('d'): + self.key = _fastmath.rsa_construct(n,e) + else: + d = state['d'] + if not state.has_key('q'): + self.key = _fastmath.rsa_construct(n,e,d) + else: + p, q, u = state['p'], state['q'], state['u'] + self.key = _fastmath.rsa_construct(n,e,d,p,q,u) + + def _encrypt(self, plain, K): + return (self.key._encrypt(plain),) + + def _decrypt(self, cipher): + return self.key._decrypt(cipher[0]) + + def _sign(self, M, K): + return (self.key._sign(M),) + + def _verify(self, M, sig): + return self.key._verify(M, sig[0]) + + def _blind(self, M, B): + return self.key._blind(M, B) + + def _unblind(self, M, B): + return self.key._unblind(M, B) + + def can_blind (self): + return 1 + + def size(self): + return self.key.size() + + def has_private(self): + return self.key.has_private() + + def publickey(self): + return construct_c((self.key.n, self.key.e)) + +def generate_c(bits, randfunc, progress_func = None): + # Generate the prime factors of n + if progress_func: + progress_func('p,q\n') + + p = q = 1L + while number.size(p*q) < bits: + p = pubkey.getPrime(bits/2, randfunc) + q = pubkey.getPrime(bits/2, randfunc) + + # p shall be smaller than q (for calc of u) + if p > q: + (p, q)=(q, p) + if progress_func: + progress_func('u\n') + u=pubkey.inverse(p, q) + n=p*q + + e = 65537L + if progress_func: + progress_func('d\n') + d=pubkey.inverse(e, (p-1)*(q-1)) + key = _fastmath.rsa_construct(n,e,d,p,q,u) + obj = RSAobj_c(key) + +## print p +## print q +## print number.size(p), number.size(q), number.size(q*p), +## print obj.size(), bits + assert bits <= 1+obj.size(), "Generated key is too small" + return obj + + +def construct_c(tuple): + key = apply(_fastmath.rsa_construct, tuple) + return RSAobj_c(key) + +object = RSAobj + +generate_py = generate +construct_py = construct + +if _fastmath: + #print "using C version of RSA" + generate = generate_c + construct = construct_c + error = _fastmath.error diff --git a/src/allmydata/Crypto/PublicKey/__init__.py b/src/allmydata/Crypto/PublicKey/__init__.py new file mode 100644 index 000000000..0d4dfa39c --- /dev/null +++ b/src/allmydata/Crypto/PublicKey/__init__.py @@ -0,0 +1,17 @@ +"""Public-key encryption and signature algorithms. + +Public-key encryption uses two different keys, one for encryption and +one for decryption. The encryption key can be made public, and the +decryption key is kept private. Many public-key algorithms can also +be used to sign messages, and some can *only* be used for signatures. + +Crypto.PublicKey.DSA Digital Signature Algorithm. (Signature only) +Crypto.PublicKey.ElGamal (Signing and encryption) +Crypto.PublicKey.RSA (Signing, encryption, and blinding) +Crypto.PublicKey.qNEW (Signature only) + +""" + +__all__ = ['RSA'] +__revision__ = "$Id: __init__.py,v 1.4 2003/04/03 20:27:13 akuchling Exp $" + diff --git a/src/allmydata/Crypto/PublicKey/pubkey.py b/src/allmydata/Crypto/PublicKey/pubkey.py new file mode 100644 index 000000000..091a9c91d --- /dev/null +++ b/src/allmydata/Crypto/PublicKey/pubkey.py @@ -0,0 +1,173 @@ +# +# pubkey.py : Internal functions for public key operations +# +# Part of the Python Cryptography Toolkit +# +# Distribute and use freely; there are no restrictions on further +# dissemination and usage except those imposed by the laws of your +# country of residence. This software is provided "as is" without +# warranty of fitness for use or suitability for any purpose, express +# or implied. Use at your own risk or not at all. +# + +__revision__ = "$Id: pubkey.py,v 1.11 2003/04/03 20:36:14 akuchling Exp $" + +import types, warnings +from allmydata.Crypto.Util.number import bignum, bytes_to_long, \ + long_to_bytes, error + +# Basic public key class +class pubkey: + def __init__(self): + pass + + def __getstate__(self): + """To keep key objects platform-independent, the key data is + converted to standard Python long integers before being + written out. It will then be reconverted as necessary on + restoration.""" + d=self.__dict__ + for key in self.keydata: + if d.has_key(key): d[key]=long(d[key]) + return d + + def __setstate__(self, d): + """On unpickling a key object, the key data is converted to the big +number representation being used, whether that is Python long +integers, MPZ objects, or whatever.""" + for key in self.keydata: + if d.has_key(key): self.__dict__[key]=bignum(d[key]) + + def encrypt(self, plaintext, K): + """encrypt(plaintext:string|long, K:string|long) : tuple + Encrypt the string or integer plaintext. K is a random + parameter required by some algorithms. + """ + wasString=0 + if isinstance(plaintext, types.StringType): + plaintext=bytes_to_long(plaintext) ; wasString=1 + if isinstance(K, types.StringType): + K=bytes_to_long(K) + ciphertext=self._encrypt(plaintext, K) + if wasString: return tuple(map(long_to_bytes, ciphertext)) + else: return ciphertext + + def decrypt(self, ciphertext): + """decrypt(ciphertext:tuple|string|long): string + Decrypt 'ciphertext' using this key. + """ + wasString=0 + if not isinstance(ciphertext, types.TupleType): + ciphertext=(ciphertext,) + if isinstance(ciphertext[0], types.StringType): + ciphertext=tuple(map(bytes_to_long, ciphertext)) ; wasString=1 + plaintext=self._decrypt(ciphertext) + if wasString: return long_to_bytes(plaintext) + else: return plaintext + + def sign(self, M, K): + """sign(M : string|long, K:string|long) : tuple + Return a tuple containing the signature for the message M. + K is a random parameter required by some algorithms. + """ + if (not self.has_private()): + raise error, 'Private key not available in this object' + if isinstance(M, types.StringType): M=bytes_to_long(M) + if isinstance(K, types.StringType): K=bytes_to_long(K) + return self._sign(M, K) + + def verify (self, M, signature): + """verify(M:string|long, signature:tuple) : bool + Verify that the signature is valid for the message M; + returns true if the signature checks out. + """ + if isinstance(M, types.StringType): M=bytes_to_long(M) + return self._verify(M, signature) + + # alias to compensate for the old validate() name + def validate (self, M, signature): + warnings.warn("validate() method name is obsolete; use verify()", + DeprecationWarning) + + def blind(self, M, B): + """blind(M : string|long, B : string|long) : string|long + Blind message M using blinding factor B. + """ + wasString=0 + if isinstance(M, types.StringType): + M=bytes_to_long(M) ; wasString=1 + if isinstance(B, types.StringType): B=bytes_to_long(B) + blindedmessage=self._blind(M, B) + if wasString: return long_to_bytes(blindedmessage) + else: return blindedmessage + + def unblind(self, M, B): + """unblind(M : string|long, B : string|long) : string|long + Unblind message M using blinding factor B. + """ + wasString=0 + if isinstance(M, types.StringType): + M=bytes_to_long(M) ; wasString=1 + if isinstance(B, types.StringType): B=bytes_to_long(B) + unblindedmessage=self._unblind(M, B) + if wasString: return long_to_bytes(unblindedmessage) + else: return unblindedmessage + + + # The following methods will usually be left alone, except for + # signature-only algorithms. They both return Boolean values + # recording whether this key's algorithm can sign and encrypt. + def can_sign (self): + """can_sign() : bool + Return a Boolean value recording whether this algorithm can + generate signatures. (This does not imply that this + particular key object has the private information required to + to generate a signature.) + """ + return 1 + + def can_encrypt (self): + """can_encrypt() : bool + Return a Boolean value recording whether this algorithm can + encrypt data. (This does not imply that this + particular key object has the private information required to + to decrypt a message.) + """ + return 1 + + def can_blind (self): + """can_blind() : bool + Return a Boolean value recording whether this algorithm can + blind data. (This does not imply that this + particular key object has the private information required to + to blind a message.) + """ + return 0 + + # The following methods will certainly be overridden by + # subclasses. + + def size (self): + """size() : int + Return the maximum number of bits that can be handled by this key. + """ + return 0 + + def has_private (self): + """has_private() : bool + Return a Boolean denoting whether the object contains + private components. + """ + return 0 + + def publickey (self): + """publickey(): object + Return a new key object containing only the public information. + """ + return self + + def __eq__ (self, other): + """__eq__(other): 0, 1 + Compare us to other for equality. + """ + return self.__getstate__() == other.__getstate__() diff --git a/src/allmydata/Crypto/_fastmath.c b/src/allmydata/Crypto/_fastmath.c new file mode 100644 index 000000000..7fe7abecf --- /dev/null +++ b/src/allmydata/Crypto/_fastmath.c @@ -0,0 +1,804 @@ + +/* + * _fastmath.c: Accelerator module that uses GMP for faster numerics. + * + * Part of the Python Cryptography Toolkit + * + * Distribute and use freely; there are no restrictions on further + * dissemination and usage except those imposed by the laws of your + * country of residence. + * + * $Id: _fastmath.c,v 1.13 2003/04/04 19:20:29 jbontje Exp $ + */ + +#include +#include +#include +#include /* for conversions */ +#include + +void +longObjToMPZ (mpz_t m, PyLongObject * p) +{ + int size, i; + mpz_t temp, temp2; + mpz_init (temp); + mpz_init (temp2); + if (p->ob_size > 0) + size = p->ob_size; + else + size = -p->ob_size; + for (i = 0; i < size; i++) + { + mpz_set_ui (temp, p->ob_digit[i]); + mpz_mul_2exp (temp2, temp, SHIFT * i); + mpz_add (m, m, temp2); + } + mpz_clear (temp); + mpz_clear (temp2); +} + +PyObject * +mpzToLongObj (mpz_t m) +{ + /* borrowed from gmpy */ + int size = (mpz_sizeinbase (m, 2) + SHIFT - 1) / SHIFT; + int i; + mpz_t temp; + PyLongObject *l = _PyLong_New (size); + if (!l) + return NULL; + mpz_init_set (temp, m); + for (i = 0; i < size; i++) + { + l->ob_digit[i] = (digit) (mpz_get_ui (temp) & MASK); + mpz_fdiv_q_2exp (temp, temp, SHIFT); + } + i = size; + while ((i > 0) && (l->ob_digit[i - 1] == 0)) + i--; + l->ob_size = i; + mpz_clear (temp); + return (PyObject *) l; +} + +typedef struct +{ + PyObject_HEAD mpz_t y; + mpz_t g; + mpz_t p; + mpz_t q; + mpz_t x; +} +dsaKey; + +typedef struct +{ + PyObject_HEAD mpz_t n; + mpz_t e; + mpz_t d; + mpz_t p; + mpz_t q; + mpz_t u; +} +rsaKey; + +PyObject *rsaKey_new (PyObject *, PyObject *); +PyObject *dsaKey_new (PyObject *, PyObject *); + +static void dsaKey_dealloc (dsaKey *); +static PyObject *dsaKey_getattr (dsaKey *, char *); +static PyObject *dsaKey__sign (dsaKey *, PyObject *); +static PyObject *dsaKey__verify (dsaKey *, PyObject *); +static PyObject *dsaKey_size (dsaKey *, PyObject *); +static PyObject *dsaKey_has_private (dsaKey *, PyObject *); + +static void rsaKey_dealloc (rsaKey *); +static PyObject *rsaKey_getattr (rsaKey *, char *); +static PyObject *rsaKey__encrypt (rsaKey *, PyObject *); +static PyObject *rsaKey__decrypt (rsaKey *, PyObject *); +static PyObject *rsaKey__verify (rsaKey *, PyObject *); +static PyObject *rsaKey__blind (rsaKey *, PyObject *); +static PyObject *rsaKey__unblind (rsaKey *, PyObject *); +static PyObject *rsaKey_size (rsaKey *, PyObject *); +static PyObject *rsaKey_has_private (rsaKey *, PyObject *); + +static int +dsaSign (dsaKey * key, mpz_t m, mpz_t k, mpz_t r, mpz_t s) +{ + mpz_t temp; + if (mpz_cmp_ui (k, 2) < 0 || mpz_cmp (k, key->q) >= 0) + { + return 1; + } + mpz_init (temp); + mpz_powm (r, key->g, k, key->p); + mpz_mod (r, r, key->q); + mpz_invert (s, k, key->q); + mpz_mul (temp, key->x, r); + mpz_add (temp, m, temp); + mpz_mul (s, s, temp); + mpz_mod (s, s, key->q); + mpz_clear (temp); + return 0; +} + +static int +dsaVerify (dsaKey * key, mpz_t m, mpz_t r, mpz_t s) +{ + int result; + mpz_t u1, u2, v1, v2, w; + if (mpz_cmp_ui (r, 0) <= 0 || mpz_cmp (r, key->q) >= 0 || + mpz_cmp_ui (s, 0) <= 0 || mpz_cmp (s, key->q) >= 0) + return 0; + mpz_init (u1); + mpz_init (u2); + mpz_init (v1); + mpz_init (v2); + mpz_init (w); + mpz_invert (w, s, key->q); + mpz_mul (u1, m, w); + mpz_mod (u1, u1, key->q); + mpz_mul (u2, r, w); + mpz_mod (u2, u2, key->q); + mpz_powm (v1, key->g, u1, key->p); + mpz_powm (v2, key->y, u2, key->p); + mpz_mul (w, v1, v2); + mpz_mod (w, w, key->p); + mpz_mod (w, w, key->q); + if (mpz_cmp (r, w) == 0) + result = 1; + else + result = 0; + mpz_clear (u1); + mpz_clear (u2); + mpz_clear (v1); + mpz_clear (v2); + mpz_clear (w); + return result; +} + + +static int +rsaEncrypt (rsaKey * key, mpz_t v) +{ + if (mpz_cmp (v, key->n) >= 0) + { + return 1; + } + mpz_powm (v, v, key->e, key->n); + return 0; +} + +static int +rsaDecrypt (rsaKey * key, mpz_t v) +{ + mpz_t m1, m2, h; + if (mpz_cmp (v, key->n) >= 0) + { + return 1; + } + if (mpz_size (key->d) == 0) + { + return 2; + } + + if ((mpz_size (key->p) != 0) && (mpz_size (key->q) != 0) && + (mpz_size (key->u) != 0)) + { + /* fast path */ + mpz_init(m1); + mpz_init(m2); + mpz_init(h); + + /* m1 = c ^ (d mod (p-1)) mod p */ + mpz_sub_ui(h, key->p, 1); + mpz_fdiv_r(h, key->d, h); + mpz_powm(m1, v, h, key->p); + /* m2 = c ^ (d mod (q-1)) mod q */ + mpz_sub_ui(h, key->q, 1); + mpz_fdiv_r(h, key->d, h); + mpz_powm(m2, v, h, key->q); + /* h = u * ( m2 - m1 ) mod q */ + mpz_sub(h, m2, m1); + if (mpz_sgn(h)==-1) + mpz_add(h, h, key->q); + mpz_mul(h, key->u, h); + mpz_mod(h, h, key->q); + /* m = m2 + h * p */ + mpz_mul(h, h, key->p); + mpz_add(v, m1, h); + /* ready */ + + mpz_clear(m1); + mpz_clear(m2); + mpz_clear(h); + return 0; + } + + /* slow */ + mpz_powm (v, v, key->d, key->n); + return 0; +} + +static int +rsaBlind (rsaKey * key, mpz_t v, mpz_t b) +{ + if (mpz_cmp (v, key->n) >= 0) + { + return 1; + } + if (mpz_cmp (b, key->n) >= 0) + { + return 2; + } + mpz_powm (b, b, key->e, key->n); + mpz_mul (v, v, b); + mpz_mod (v, v, key->n); + return 0; +} + +static int +rsaUnBlind (rsaKey * key, mpz_t v, mpz_t b) +{ + if (mpz_cmp (v, key->n) >= 0) + { + return 1; + } + if (mpz_cmp (b, key->n) >= 0) + { + return 2; + } + if (!mpz_invert (b, b, key->n)) + { + return 3; + } + mpz_mul (v, v, b); + mpz_mod (v, v, key->n); + return 0; +} + + +static PyTypeObject dsaKeyType = { + PyObject_HEAD_INIT (NULL) 0, + "dsaKey", + sizeof (dsaKey), + 0, + (destructor) dsaKey_dealloc, /* dealloc */ + 0, /* print */ + (getattrfunc) dsaKey_getattr, /* getattr */ + 0, /* setattr */ + 0, /* compare */ + 0, /* repr */ + 0, /* as_number */ + 0, /* as_sequence */ + 0, /* as_mapping */ + 0, /* hash */ + 0, /* call */ +}; + +static PyMethodDef dsaKey__methods__[] = { + {"_sign", (PyCFunction) dsaKey__sign, METH_VARARGS, + "Sign the given long."}, + {"_verify", (PyCFunction) dsaKey__verify, METH_VARARGS, + "Verify that the signature is valid."}, + {"size", (PyCFunction) dsaKey_size, METH_VARARGS, + "Return the number of bits that this key can handle."}, + {"has_private", (PyCFunction) dsaKey_has_private, METH_VARARGS, + "Return 1 or 0 if this key does/doesn't have a private key."}, + {NULL, NULL, 0, NULL} +}; + +PyObject *fastmathError; /* raised on errors */ + +static PyTypeObject rsaKeyType = { + PyObject_HEAD_INIT (NULL) 0, + "rsaKey", + sizeof (rsaKey), + 0, + (destructor) rsaKey_dealloc, /* dealloc */ + 0, /* print */ + (getattrfunc) rsaKey_getattr, /* getattr */ + 0, /* setattr */ + 0, /* compare */ + 0, /* repr */ + 0, /* as_number */ + 0, /* as_sequence */ + 0, /* as_mapping */ + 0, /* hash */ + 0, /* call */ +}; + +static PyMethodDef rsaKey__methods__[] = { + {"_encrypt", (PyCFunction) rsaKey__encrypt, METH_VARARGS, + "Encrypt the given long."}, + {"_decrypt", (PyCFunction) rsaKey__decrypt, METH_VARARGS, + "Decrypt the given long."}, + {"_sign", (PyCFunction) rsaKey__decrypt, METH_VARARGS, + "Sign the given long."}, + {"_verify", (PyCFunction) rsaKey__verify, METH_VARARGS, + "Verify that the signature is valid."}, + {"_blind", (PyCFunction) rsaKey__blind, METH_VARARGS, + "Blind the given long."}, + {"_unblind", (PyCFunction) rsaKey__unblind, METH_VARARGS, + "Unblind the given long."}, + {"size", (PyCFunction) rsaKey_size, METH_VARARGS, + "Return the number of bits that this key can handle."}, + {"has_private", (PyCFunction) rsaKey_has_private, METH_VARARGS, + "Return 1 or 0 if this key does/doesn't have a private key."}, + {NULL, NULL, 0, NULL} +}; + +PyObject * +dsaKey_new (PyObject * self, PyObject * args) +{ + PyLongObject *y = NULL, *g = NULL, *p = NULL, *q = NULL, *x = NULL; + dsaKey *key; + if (!PyArg_ParseTuple(args, "O!O!O!O!|O!", &PyLong_Type, &y, + &PyLong_Type, &g, &PyLong_Type, &p, + &PyLong_Type, &q, &PyLong_Type, &x)) + return NULL; + + key = PyObject_New (dsaKey, &dsaKeyType); + mpz_init (key->y); + mpz_init (key->g); + mpz_init (key->p); + mpz_init (key->q); + mpz_init (key->x); + longObjToMPZ (key->y, y); + longObjToMPZ (key->g, g); + longObjToMPZ (key->p, p); + longObjToMPZ (key->q, q); + if (x) + { + longObjToMPZ (key->x, x); + } + return (PyObject *) key; +} + +static void +dsaKey_dealloc (dsaKey * key) +{ + mpz_clear (key->y); + mpz_clear (key->g); + mpz_clear (key->p); + mpz_clear (key->q); + mpz_clear (key->x); + PyObject_Del (key); +} + +static PyObject * +dsaKey_getattr (dsaKey * key, char *attr) +{ + if (strcmp (attr, "y") == 0) + return mpzToLongObj (key->y); + else if (strcmp (attr, "g") == 0) + return mpzToLongObj (key->g); + else if (strcmp (attr, "p") == 0) + return mpzToLongObj (key->p); + else if (strcmp (attr, "q") == 0) + return mpzToLongObj (key->q); + else if (strcmp (attr, "x") == 0) + { + if (mpz_size (key->x) == 0) + { + PyErr_SetString (PyExc_AttributeError, + "dsaKey instance has no attribute 'x'"); + return NULL; + } + return mpzToLongObj (key->x); + } + else + { + return Py_FindMethod (dsaKey__methods__, (PyObject *) key, attr); + } +} + +PyObject * +dsaKey__sign (dsaKey * key, PyObject * args) +{ + PyObject *lm, *lk, *lr, *ls; + mpz_t m, k, r, s; + int result; + if (!PyArg_ParseTuple (args, "O!O!", &PyLong_Type, &lm, + &PyLong_Type, &lk)) + { + return NULL; + } + mpz_init (m); + mpz_init (k); + mpz_init (r); + mpz_init (s); + longObjToMPZ (m, (PyLongObject *) lm); + longObjToMPZ (k, (PyLongObject *) lk); + result = dsaSign (key, m, k, r, s); + if (result == 1) + { + PyErr_SetString (fastmathError, "K not between 2 and q"); + return NULL; + } + lr = mpzToLongObj (r); + ls = mpzToLongObj (s); + mpz_clear (m); + mpz_clear (k); + mpz_clear (r); + mpz_clear (s); + return Py_BuildValue ("(NN)", lr, ls); +} + +PyObject * +dsaKey__verify (dsaKey * key, PyObject * args) +{ + PyObject *lm, *lr, *ls; + mpz_t m, r, s; + int result; + if (!PyArg_ParseTuple (args, "O!O!O!", &PyLong_Type, &lm, + &PyLong_Type, &lr, &PyLong_Type, &ls)) + { + return NULL; + } + mpz_init (m); + mpz_init (r); + mpz_init (s); + longObjToMPZ (m, (PyLongObject *) lm); + longObjToMPZ (r, (PyLongObject *) lr); + longObjToMPZ (s, (PyLongObject *) ls); + result = dsaVerify (key, m, r, s); + mpz_clear (m); + mpz_clear (r); + mpz_clear (s); + if (result) { + Py_INCREF(Py_True); + return Py_True; + } else { + Py_INCREF(Py_False); + return Py_False; + } +} + +PyObject * +dsaKey_size (dsaKey * key, PyObject * args) +{ + if (!PyArg_ParseTuple (args, "")) + return NULL; + return Py_BuildValue ("i", mpz_sizeinbase (key->p, 2) - 1); +} + +PyObject * +dsaKey_has_private (dsaKey * key, PyObject * args) +{ + if (!PyArg_ParseTuple (args, "")) + return NULL; + if (mpz_size (key->x) == 0) { + Py_INCREF(Py_False); + return Py_False; + } else { + Py_INCREF(Py_True); + return Py_True; + } +} + +PyObject * +rsaKey_new (PyObject * self, PyObject * args) +{ + PyLongObject *n = NULL, *e = NULL, *d = NULL, *p = NULL, *q = NULL, + *u = NULL; + rsaKey *key; + + if (!PyArg_ParseTuple(args, "O!O!|O!O!O!O!", &PyLong_Type, &n, + &PyLong_Type, &e, &PyLong_Type, &d, + &PyLong_Type, &p, &PyLong_Type, &q, + &PyLong_Type, &u)) + return NULL; + + key = PyObject_New (rsaKey, &rsaKeyType); + mpz_init (key->n); + mpz_init (key->e); + mpz_init (key->d); + mpz_init (key->p); + mpz_init (key->q); + mpz_init (key->u); + longObjToMPZ (key->n, n); + longObjToMPZ (key->e, e); + if (!d) + { + return (PyObject *) key; + } + longObjToMPZ (key->d, d); + if (p && q) + { + longObjToMPZ (key->p, p); + longObjToMPZ (key->q, q); + if (u) { + longObjToMPZ (key->u, u); + } else { + mpz_invert (key->u, key->p, key->q); + } + } + return (PyObject *) key; +} + +static void +rsaKey_dealloc (rsaKey * key) +{ + mpz_clear (key->n); + mpz_clear (key->e); + mpz_clear (key->d); + mpz_clear (key->p); + mpz_clear (key->q); + mpz_clear (key->u); + PyObject_Del (key); +} + +static PyObject * +rsaKey_getattr (rsaKey * key, char *attr) +{ + if (strcmp (attr, "n") == 0) + return mpzToLongObj (key->n); + else if (strcmp (attr, "e") == 0) + return mpzToLongObj (key->e); + else if (strcmp (attr, "d") == 0) + { + if (mpz_size (key->d) == 0) + { + PyErr_SetString(PyExc_AttributeError, + "rsaKey instance has no attribute 'd'"); + return NULL; + } + return mpzToLongObj (key->d); + } + else if (strcmp (attr, "p") == 0) + { + if (mpz_size (key->p) == 0) + { + PyErr_SetString(PyExc_AttributeError, + "rsaKey instance has no attribute 'p'"); + return NULL; + } + return mpzToLongObj (key->p); + } + else if (strcmp (attr, "q") == 0) + { + if (mpz_size (key->q) == 0) + { + PyErr_SetString(PyExc_AttributeError, + "rsaKey instance has no attribute 'q'"); + return NULL; + } + return mpzToLongObj (key->q); + } + else if (strcmp (attr, "u") == 0) + { + if (mpz_size (key->u) == 0) + { + PyErr_SetString(PyExc_AttributeError, + "rsaKey instance has no attribute 'u'"); + return NULL; + } + return mpzToLongObj (key->u); + } + else + { + return Py_FindMethod (rsaKey__methods__, + (PyObject *) key, attr); + } +} + +PyObject * +rsaKey__encrypt (rsaKey * key, PyObject * args) +{ + PyObject *l, *r; + mpz_t v; + int result; + if (!PyArg_ParseTuple (args, "O!", &PyLong_Type, &l)) + { + return NULL; + } + mpz_init (v); + longObjToMPZ (v, (PyLongObject *) l); + result = rsaEncrypt (key, v); + if (result == 1) + { + PyErr_SetString (fastmathError, "Plaintext too large"); + return NULL; + } + r = (PyObject *) mpzToLongObj (v); + mpz_clear (v); + return Py_BuildValue ("N", r); +} + +PyObject * +rsaKey__decrypt (rsaKey * key, PyObject * args) +{ + PyObject *l, *r; + mpz_t v; + int result; + if (!PyArg_ParseTuple (args, "O!", &PyLong_Type, &l)) + { + return NULL; + } + mpz_init (v); + longObjToMPZ (v, (PyLongObject *) l); + result = rsaDecrypt (key, v); + if (result == 1) + { + PyErr_SetString (fastmathError, + "Ciphertext too large"); + return NULL; + } + else if (result == 2) + { + PyErr_SetString (fastmathError, + "Private key not available in this object"); + return NULL; + } + r = mpzToLongObj (v); + mpz_clear (v); + return Py_BuildValue ("N", r); +} + +PyObject * +rsaKey__verify (rsaKey * key, PyObject * args) +{ + PyObject *l, *lsig; + mpz_t v, vsig; + if (!PyArg_ParseTuple(args, "O!O!", + &PyLong_Type, &l, &PyLong_Type, &lsig)) + { + return NULL; + } + mpz_init (v); + mpz_init (vsig); + longObjToMPZ (v, (PyLongObject *) l); + longObjToMPZ (vsig, (PyLongObject *) lsig); + rsaEncrypt (key, vsig); + if (mpz_cmp (v, vsig) == 0) { + Py_INCREF(Py_True); + return Py_True; + } + else { + Py_INCREF(Py_False); + return Py_False; + } +} + +PyObject * +rsaKey__blind (rsaKey * key, PyObject * args) +{ + PyObject *l, *lblind, *r; + mpz_t v, vblind; + int result; + if (!PyArg_ParseTuple (args, "O!O!", &PyLong_Type, &l, + &PyLong_Type, &lblind)) + { + return NULL; + } + mpz_init (v); + mpz_init (vblind); + longObjToMPZ (v, (PyLongObject *) l); + longObjToMPZ (vblind, (PyLongObject *) lblind); + result = rsaBlind (key, v, vblind); + if (result == 1) + { + PyErr_SetString (fastmathError, "Message too large"); + return NULL; + } + else if (result == 2) + { + PyErr_SetString (fastmathError, "Blinding factor too large"); + return NULL; + } + r = (PyObject *) mpzToLongObj (v); + mpz_clear (v); + mpz_clear (vblind); + return Py_BuildValue ("N", r); +} + +PyObject * +rsaKey__unblind (rsaKey * key, PyObject * args) +{ + PyObject *l, *lblind, *r; + mpz_t v, vblind; + int result; + if (!PyArg_ParseTuple (args, "O!O!", &PyLong_Type, &l, + &PyLong_Type, &lblind)) + { + return NULL; + } + mpz_init (v); + mpz_init (vblind); + longObjToMPZ (v, (PyLongObject *) l); + longObjToMPZ (vblind, (PyLongObject *) lblind); + result = rsaUnBlind (key, v, vblind); + if (result == 1) + { + PyErr_SetString (fastmathError, "Message too large"); + return NULL; + } + else if (result == 2) + { + PyErr_SetString (fastmathError, "Blinding factor too large"); + return NULL; + } + else if (result == 3) + { + PyErr_SetString (fastmathError, "Inverse doesn't exist"); + return NULL; + } + r = (PyObject *) mpzToLongObj (v); + mpz_clear (v); + mpz_clear (vblind); + return Py_BuildValue ("N", r); +} + +PyObject * +rsaKey_size (rsaKey * key, PyObject * args) +{ + if (!PyArg_ParseTuple (args, "")) + return NULL; + return Py_BuildValue ("i", mpz_sizeinbase (key->n, 2) - 1); +} + +PyObject * +rsaKey_has_private (rsaKey * key, PyObject * args) +{ + if (!PyArg_ParseTuple (args, "")) + return NULL; + if (mpz_size (key->d) == 0) { + Py_INCREF(Py_False); + return Py_False; + } else { + Py_INCREF(Py_True); + return Py_True; + } +} + + +PyObject * +isPrime (PyObject * self, PyObject * args) +{ + PyObject *l; + mpz_t n; + int result; + + if (!PyArg_ParseTuple (args, "O!", &PyLong_Type, &l)) + { + return NULL; + } + mpz_init (n); + longObjToMPZ (n, (PyLongObject *) l); + + result = mpz_probab_prime_p(n, 5); + + mpz_clear (n); + + if (result == 0) { + Py_INCREF(Py_False); + return Py_False; + } else { + Py_INCREF(Py_True); + return Py_True; + } +} + + +static PyMethodDef _fastmath__methods__[] = { + {"dsa_construct", dsaKey_new, METH_VARARGS}, + {"rsa_construct", rsaKey_new, METH_VARARGS}, + {"isPrime", isPrime, METH_VARARGS}, + {NULL, NULL} +}; + +void +init_fastmath (void) +{ + PyObject *_fastmath_module; + PyObject *_fastmath_dict; + + rsaKeyType.ob_type = &PyType_Type; + dsaKeyType.ob_type = &PyType_Type; + _fastmath_module = Py_InitModule ("_fastmath", _fastmath__methods__); + _fastmath_dict = PyModule_GetDict (_fastmath_module); + fastmathError = PyErr_NewException ("_fastmath.error", NULL, NULL); + PyDict_SetItemString (_fastmath_dict, "error", fastmathError); +}