2008-09-18 21:42:28 -04:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
#
|
|
|
|
|
# PublicKey/RSA.py : RSA public key primitive
|
|
|
|
|
#
|
|
|
|
|
# Copyright (C) 2008 Dwayne C. Litzenberger <dlitz@dlitz.net>
|
|
|
|
|
#
|
|
|
|
|
# =======================================================================
|
|
|
|
|
# Permission is hereby granted, free of charge, to any person obtaining
|
|
|
|
|
# a copy of this software and associated documentation files (the
|
|
|
|
|
# "Software"), to deal in the Software without restriction, including
|
|
|
|
|
# without limitation the rights to use, copy, modify, merge, publish,
|
|
|
|
|
# distribute, sublicense, and/or sell copies of the Software, and to
|
|
|
|
|
# permit persons to whom the Software is furnished to do so.
|
|
|
|
|
#
|
|
|
|
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
|
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
|
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
|
|
|
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
|
|
|
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
|
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
|
|
|
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
|
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
|
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
# =======================================================================
|
|
|
|
|
|
|
|
|
|
"""RSA public-key cryptography algorithm."""
|
|
|
|
|
|
|
|
|
|
__revision__ = "$Id$"
|
|
|
|
|
|
|
|
|
|
__all__ = ['generate', 'construct', 'error']
|
|
|
|
|
|
|
|
|
|
from Crypto.Util.python_compat import *
|
|
|
|
|
|
|
|
|
|
from Crypto.PublicKey import _RSA, _slowmath, pubkey
|
|
|
|
|
from Crypto import Random
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
from Crypto.PublicKey import _fastmath
|
|
|
|
|
except ImportError:
|
|
|
|
|
_fastmath = None
|
|
|
|
|
|
|
|
|
|
class _RSAobj(pubkey.pubkey):
|
|
|
|
|
keydata = ['n', 'e', 'd', 'p', 'q', 'u']
|
|
|
|
|
|
|
|
|
|
def __init__(self, implementation, key):
|
|
|
|
|
self.implementation = implementation
|
|
|
|
|
self.key = key
|
|
|
|
|
|
|
|
|
|
def __getattr__(self, attrname):
|
|
|
|
|
if attrname in self.keydata:
|
|
|
|
|
# For backward compatibility, allow the user to get (not set) the
|
|
|
|
|
# RSA key parameters directly from this object.
|
|
|
|
|
return getattr(self.key, attrname)
|
|
|
|
|
else:
|
|
|
|
|
raise AttributeError("%s object has no %r attribute" % (self.__class__.__name__, attrname,))
|
|
|
|
|
|
2008-10-18 20:26:01 -04:00
|
|
|
def _encrypt(self, c, K):
|
2008-09-18 21:42:28 -04:00
|
|
|
return (self.key._encrypt(c),)
|
|
|
|
|
|
|
|
|
|
def _decrypt(self, c):
|
RSA: Make .verify() accept extra garbage after the signature.
The developers of Twisted Conch (an SSH protocol implementation) apparently
think they need to add extra junk to the end of the signature tuple when
calling RSAobj.verify. In other words, they do something like this:
RSAobj.verify("foo", (sig, ''))
instead of something like this:
RSAobj.verify("foo", (sig,))
This isn't necessary, but it worked in PyCrypto 2.0.1.
The people behind Twisted Conch probably got confused by a similar requirement
in RSAobj.sign(). I could call it "user error" and rebuke them for misusing
the API, but that would be dumb. The unified Crypto.PublicKey API is
confusing, encouraging exactly this kind of mistake.
The Crypto.PublicKey API needs to be replaced with something less error-prone,
but in the meantime, I am applying this change to make the .verify() method
behave how Twisted Conch expects.
2008-11-02 10:33:52 -05:00
|
|
|
#(ciphertext,) = c
|
|
|
|
|
(ciphertext,) = c[:1] # HACK - We should use the previous line
|
|
|
|
|
# instead, but this is more compatible and we're
|
|
|
|
|
# going to replace the Crypto.PublicKey API soon
|
|
|
|
|
# anyway.
|
2008-09-18 21:42:28 -04:00
|
|
|
return self.key._decrypt(ciphertext)
|
|
|
|
|
|
|
|
|
|
def _blind(self, m, r):
|
|
|
|
|
return self.key._blind(m, r)
|
|
|
|
|
|
|
|
|
|
def _unblind(self, m, r):
|
|
|
|
|
return self.key._unblind(m, r)
|
|
|
|
|
|
2008-10-18 20:20:49 -04:00
|
|
|
def _sign(self, m, K=None):
|
|
|
|
|
return (self.key._sign(m),)
|
2008-09-18 21:42:28 -04:00
|
|
|
|
|
|
|
|
def _verify(self, m, sig):
|
RSA: Make .verify() accept extra garbage after the signature.
The developers of Twisted Conch (an SSH protocol implementation) apparently
think they need to add extra junk to the end of the signature tuple when
calling RSAobj.verify. In other words, they do something like this:
RSAobj.verify("foo", (sig, ''))
instead of something like this:
RSAobj.verify("foo", (sig,))
This isn't necessary, but it worked in PyCrypto 2.0.1.
The people behind Twisted Conch probably got confused by a similar requirement
in RSAobj.sign(). I could call it "user error" and rebuke them for misusing
the API, but that would be dumb. The unified Crypto.PublicKey API is
confusing, encouraging exactly this kind of mistake.
The Crypto.PublicKey API needs to be replaced with something less error-prone,
but in the meantime, I am applying this change to make the .verify() method
behave how Twisted Conch expects.
2008-11-02 10:33:52 -05:00
|
|
|
#(s,) = sig
|
|
|
|
|
(s,) = sig[:1] # HACK - We should use the previous line instead, but
|
|
|
|
|
# this is more compatible and we're going to replace
|
|
|
|
|
# the Crypto.PublicKey API soon anyway.
|
2008-10-18 20:20:49 -04:00
|
|
|
return self.key._verify(m, s)
|
2008-09-18 21:42:28 -04:00
|
|
|
|
|
|
|
|
def has_private(self):
|
|
|
|
|
return self.key.has_private()
|
|
|
|
|
|
|
|
|
|
def size(self):
|
|
|
|
|
return self.key.size()
|
|
|
|
|
|
|
|
|
|
def can_blind(self):
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
def can_encrypt(self):
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
def can_sign(self):
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
def publickey(self):
|
|
|
|
|
return self.implementation.construct((self.key.n, self.key.e))
|
|
|
|
|
|
|
|
|
|
def __getstate__(self):
|
|
|
|
|
d = {}
|
|
|
|
|
for k in self.keydata:
|
|
|
|
|
try:
|
|
|
|
|
d[k] = getattr(self.key, k)
|
|
|
|
|
except AttributeError:
|
|
|
|
|
pass
|
|
|
|
|
return d
|
|
|
|
|
|
|
|
|
|
def __setstate__(self, d):
|
|
|
|
|
if not hasattr(self, 'implementation'):
|
|
|
|
|
self.implementation = RSAImplementation()
|
|
|
|
|
t = []
|
|
|
|
|
for k in self.keydata:
|
|
|
|
|
if not d.has_key(k):
|
|
|
|
|
break
|
|
|
|
|
t.append(d[k])
|
|
|
|
|
self.key = self.implementation._math.rsa_construct(*tuple(t))
|
|
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
|
attrs = []
|
|
|
|
|
for k in self.keydata:
|
|
|
|
|
if k == 'n':
|
|
|
|
|
attrs.append("n(%d)" % (self.size()+1,))
|
|
|
|
|
elif hasattr(self.key, k):
|
|
|
|
|
attrs.append(k)
|
|
|
|
|
if self.has_private():
|
|
|
|
|
attrs.append("private")
|
|
|
|
|
return "<%s @0x%x %s>" % (self.__class__.__name__, id(self), ",".join(attrs))
|
|
|
|
|
|
|
|
|
|
class RSAImplementation(object):
|
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
|
# 'use_fast_math' parameter:
|
|
|
|
|
# None (default) - Use fast math if available; Use slow math if not.
|
|
|
|
|
# True - Use fast math, and raise RuntimeError if it's not available.
|
|
|
|
|
# False - Use slow math.
|
|
|
|
|
use_fast_math = kwargs.get('use_fast_math', None)
|
|
|
|
|
if use_fast_math is None: # Automatic
|
|
|
|
|
if _fastmath is not None:
|
|
|
|
|
self._math = _fastmath
|
|
|
|
|
else:
|
|
|
|
|
self._math = _slowmath
|
|
|
|
|
|
|
|
|
|
elif use_fast_math: # Explicitly select fast math
|
|
|
|
|
if _fastmath is not None:
|
|
|
|
|
self._math = _fastmath
|
|
|
|
|
else:
|
|
|
|
|
raise RuntimeError("fast math module not available")
|
|
|
|
|
|
|
|
|
|
else: # Explicitly select slow math
|
|
|
|
|
self._math = _slowmath
|
|
|
|
|
|
|
|
|
|
self.error = self._math.error
|
|
|
|
|
|
|
|
|
|
# 'default_randfunc' parameter:
|
|
|
|
|
# None (default) - use Random.new().read
|
|
|
|
|
# not None - use the specified function
|
|
|
|
|
self._default_randfunc = kwargs.get('default_randfunc', None)
|
|
|
|
|
self._current_randfunc = None
|
|
|
|
|
|
|
|
|
|
def _get_randfunc(self, randfunc):
|
|
|
|
|
if randfunc is not None:
|
|
|
|
|
return randfunc
|
|
|
|
|
elif self._current_randfunc is None:
|
|
|
|
|
self._current_randfunc = Random.new().read
|
|
|
|
|
return self._current_randfunc
|
|
|
|
|
|
|
|
|
|
def generate(self, bits, randfunc=None, progress_func=None):
|
|
|
|
|
rf = self._get_randfunc(randfunc)
|
|
|
|
|
obj = _RSA.generate_py(bits, rf, progress_func) # TODO: Don't use legacy _RSA module
|
|
|
|
|
key = self._math.rsa_construct(obj.n, obj.e, obj.d, obj.p, obj.q, obj.u)
|
|
|
|
|
return _RSAobj(self, key)
|
|
|
|
|
|
|
|
|
|
def construct(self, tup):
|
|
|
|
|
key = self._math.rsa_construct(*tup)
|
|
|
|
|
return _RSAobj(self, key)
|
|
|
|
|
|
|
|
|
|
_impl = RSAImplementation()
|
|
|
|
|
generate = _impl.generate
|
|
|
|
|
construct = _impl.construct
|
|
|
|
|
error = _impl.error
|
|
|
|
|
|
|
|
|
|
# vim:set ts=4 sw=4 sts=4 expandtab:
|
|
|
|
|
|