summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSybren A. Stüvel <sybren@stuvel.eu>2016-01-22 14:29:41 +0100
committerSybren A. Stüvel <sybren@stuvel.eu>2016-01-22 14:29:41 +0100
commit10bf544b641bba6370b7c503e17ae2442958e53b (patch)
treeb06566429e71150f60b110c41125fe088f2825a2
parentca0e4e576450b91f50b2580dd8903071cad973be (diff)
downloadrsa-git-10bf544b641bba6370b7c503e17ae2442958e53b.tar.gz
Use random number when blinding, and also blind when verifying signatures.
-rw-r--r--rsa/key.py108
-rw-r--r--rsa/pkcs1.py12
2 files changed, 75 insertions, 45 deletions
diff --git a/rsa/key.py b/rsa/key.py
index 62ec34b..3d51675 100644
--- a/rsa/key.py
+++ b/rsa/key.py
@@ -31,6 +31,8 @@ from rsa._compat import b, bytes_type
import rsa.prime
import rsa.pem
import rsa.common
+import rsa.randnum
+import rsa.core
log = logging.getLogger(__name__)
@@ -38,6 +40,12 @@ log = logging.getLogger(__name__)
class AbstractKey(object):
"""Abstract superclass for private and public keys."""
+ __slots__ = ('n', 'e')
+
+ def __init__(self, n, e):
+ self.n = n
+ self.e = e
+
@classmethod
def load_pkcs1(cls, keyfile, format='PEM'):
"""Loads a key in PKCS#1 DER or PEM format.
@@ -82,6 +90,42 @@ class AbstractKey(object):
method = methods[format]
return method()
+ def blind(self, message, r):
+ """Performs blinding on the message using random number 'r'.
+
+ :param message: the message, as integer, to blind.
+ :param r: the random number to blind with.
+ :return: the blinded message.
+
+ The blinding is such that message = unblind(decrypt(blind(encrypt(message))).
+
+ See https://en.wikipedia.org/wiki/Blinding_%28cryptography%29
+
+ >>> pk = PrivateKey(3727264081, 65537, 3349121513, 65063, 57287)
+ >>> message = 12345
+ >>> encrypted = rsa.core.encrypt_int(message, pk.e, pk.n)
+ >>> blinded = pk.blind(encrypted, 4134431) # blind before decrypting
+ >>> decrypted = rsa.core.decrypt_int(blinded, pk.d, pk.n)
+ >>> pk.unblind(decrypted, 4134431)
+ 12345
+ """
+
+ return (message * pow(r, self.e, self.n)) % self.n
+
+ def unblind(self, blinded, r):
+ """Performs blinding on the message using random number 'r'.
+
+ :param blinded: the blinded message, as integer, to unblind.
+ :param r: the random number to unblind with.
+ :return: the original message.
+
+ The blinding is such that message = unblind(decrypt(blind(encrypt(message))).
+
+ See https://en.wikipedia.org/wiki/Blinding_%28cryptography%29
+ """
+
+ return (rsa.common.inverse(r, self.n) * blinded) % self.n
+
class PublicKey(AbstractKey):
"""Represents a public RSA key.
@@ -109,10 +153,6 @@ class PublicKey(AbstractKey):
__slots__ = ('n', 'e')
- def __init__(self, n, e):
- self.n = n
- self.e = e
-
def __getitem__(self, key):
return getattr(self, key)
@@ -131,6 +171,23 @@ class PublicKey(AbstractKey):
def __ne__(self, other):
return not (self == other)
+ def blinded_decrypt(self, encrypted):
+ """Decrypts the message using blinding to prevent side-channel attacks.
+
+ :param encrypted: the encrypted message
+ :type encrypted: int
+
+ :returns: the decrypted message
+ :rtype: int
+ """
+
+ # return self._blinded_decrypt(encrypted, self.e)
+ blind_r = rsa.randnum.randint(self.n - 1)
+ blinded = self.unblind(encrypted, blind_r) # blind before decrypting
+ decrypted = rsa.core.decrypt_int(blinded, self.e, self.n)
+
+ return self.blind(decrypted, blind_r)
+
@classmethod
def _load_pkcs1_der(cls, keyfile):
"""Loads a key in PKCS#1 DER format.
@@ -275,8 +332,7 @@ class PrivateKey(AbstractKey):
__slots__ = ('n', 'e', 'd', 'p', 'q', 'exp1', 'exp2', 'coef')
def __init__(self, n, e, d, p, q, exp1=None, exp2=None, coef=None):
- self.n = n
- self.e = e
+ AbstractKey.__init__(self, n, e)
self.d = d
self.p = p
self.q = q
@@ -322,41 +378,21 @@ class PrivateKey(AbstractKey):
def __ne__(self, other):
return not (self == other)
- def blind(self, message, r):
- """Performs blinding on the message using random number 'r'.
+ def blinded_decrypt(self, encrypted):
+ """Decrypts the message using blinding to prevent side-channel attacks.
- @param message: the message, as integer, to blind.
- @param r: the random number to blind with.
- @return: the blinded message.
-
- The blinding is such that message = unblind(decrypt(blind(encrypt(message))).
-
- See https://en.wikipedia.org/wiki/Blinding_%28cryptography%29
+ :param encrypted: the encrypted message
+ :type encrypted: int
- >>> pk = PrivateKey(3727264081, 65537, 3349121513, 65063, 57287)
- >>> message = 12345
- >>> encrypted = rsa.core.encrypt_int(message, pk.e, pk.n)
- >>> blinded = pk.blind(encrypted, 4134431) # blind before decrypting
- >>> decrypted = rsa.core.decrypt_int(blinded, pk.d, pk.n)
- >>> pk.unblind(decrypted, 4134431)
- 12345
+ :returns: the decrypted message
+ :rtype: int
"""
- return (message * pow(r, self.e, self.n)) % self.n
-
- def unblind(self, blinded, r):
- """Performs blinding on the message using random number 'r'.
-
- @param blinded: the blinded message, as integer, to unblind.
- @param r: the random number to unblind with.
- @return: the original message.
-
- The blinding is such that message = unblind(decrypt(blind(encrypt(message))).
+ blind_r = rsa.randnum.randint(self.n - 1)
+ blinded = self.blind(encrypted, blind_r) # blind before decrypting
+ decrypted = rsa.core.decrypt_int(blinded, self.d, self.n)
- See https://en.wikipedia.org/wiki/Blinding_%28cryptography%29
- """
-
- return (rsa.common.inverse(r, self.n) * blinded) % self.n
+ return self.unblind(decrypted, blind_r)
@classmethod
def _load_pkcs1_der(cls, keyfile):
diff --git a/rsa/pkcs1.py b/rsa/pkcs1.py
index 0b7982c..0660a50 100644
--- a/rsa/pkcs1.py
+++ b/rsa/pkcs1.py
@@ -229,14 +229,8 @@ def decrypt(crypto, priv_key):
blocksize = common.byte_size(priv_key.n)
encrypted = transform.bytes2int(crypto)
-
- # Perform blinded decryption to prevent side-channel attacks.
- # See https://en.wikipedia.org/wiki/Blinding_%28cryptography%29
- blinded = priv_key.blind(encrypted, 4134431) # blind before decrypting
- decrypted = core.decrypt_int(blinded, priv_key.d, priv_key.n)
- unblinded = priv_key.unblind(decrypted, 4134431)
-
- cleartext = transform.int2bytes(unblinded, blocksize)
+ decrypted = priv_key.blinded_decrypt(encrypted)
+ cleartext = transform.int2bytes(decrypted, blocksize)
# If we can't find the cleartext marker, decryption failed.
if cleartext[0:2] != b('\x00\x02'):
@@ -305,7 +299,7 @@ def verify(message, signature, pub_key):
keylength = common.byte_size(pub_key.n)
encrypted = transform.bytes2int(signature)
- decrypted = core.decrypt_int(encrypted, pub_key.e, pub_key.n)
+ decrypted = pub_key.blinded_decrypt(encrypted)
clearsig = transform.int2bytes(decrypted, keylength)
# Get the hash method