diff options
author | Sybren A. Stüvel <sybren@stuvel.eu> | 2016-01-22 14:29:41 +0100 |
---|---|---|
committer | Sybren A. Stüvel <sybren@stuvel.eu> | 2016-01-22 14:29:41 +0100 |
commit | 10bf544b641bba6370b7c503e17ae2442958e53b (patch) | |
tree | b06566429e71150f60b110c41125fe088f2825a2 | |
parent | ca0e4e576450b91f50b2580dd8903071cad973be (diff) | |
download | rsa-git-10bf544b641bba6370b7c503e17ae2442958e53b.tar.gz |
Use random number when blinding, and also blind when verifying signatures.
-rw-r--r-- | rsa/key.py | 108 | ||||
-rw-r--r-- | rsa/pkcs1.py | 12 |
2 files changed, 75 insertions, 45 deletions
@@ -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 |