summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFilippo Valsorda <hi@filippo.io>2015-12-16 00:42:43 +0000
committerFilippo Valsorda <hi@filippo.io>2015-12-16 00:42:43 +0000
commit8cbff506fe43c42e55b764697cdf1f1d5412b98d (patch)
treea41b1163e667887a4983ad6bcbb8e7c011406b60
parent2dd9ac5fa966e53053857967d690a62392b63cdd (diff)
downloadrsa-8cbff506fe43c42e55b764697cdf1f1d5412b98d.tar.gz
Fix BB'06 attack in verify() by switching from parsing to comparison
-rw-r--r--rsa/pkcs1.py58
1 files changed, 20 insertions, 38 deletions
diff --git a/rsa/pkcs1.py b/rsa/pkcs1.py
index 15e4cf6..0e51928 100644
--- a/rsa/pkcs1.py
+++ b/rsa/pkcs1.py
@@ -22,10 +22,10 @@ very clear example, read http://www.di-mgt.com.au/rsa_alg.html#pkcs1schemes
At least 8 bytes of random padding is used when encrypting a message. This makes
these methods much more secure than the ones in the ``rsa`` module.
-WARNING: this module leaks information when decryption or verification fails.
-The exceptions that are raised contain the Python traceback information, which
-can be used to deduce where in the process the failure occurred. DO NOT PASS
-SUCH INFORMATION to your users.
+WARNING: this module leaks information when decryption fails. The exceptions
+that are raised contain the Python traceback information, which can be used to
+deduce where in the process the failure occurred. DO NOT PASS SUCH INFORMATION
+to your users.
'''
import hashlib
@@ -288,37 +288,23 @@ def verify(message, signature, pub_key):
:param pub_key: the :py:class:`rsa.PublicKey` of the person signing the message.
:raise VerificationError: when the signature doesn't match the message.
- .. warning::
-
- Never display the stack trace of a
- :py:class:`rsa.pkcs1.VerificationError` exception. It shows where in
- the code the exception occurred, and thus leaks information about the
- key. It's only a tiny bit of information, but every bit makes cracking
- the keys easier.
-
'''
- blocksize = common.byte_size(pub_key.n)
+ keylength = common.byte_size(pub_key.n)
encrypted = transform.bytes2int(signature)
decrypted = core.decrypt_int(encrypted, pub_key.e, pub_key.n)
- clearsig = transform.int2bytes(decrypted, blocksize)
-
- # If we can't find the signature marker, verification failed.
- if clearsig[0:2] != b('\x00\x01'):
- raise VerificationError('Verification failed')
+ clearsig = transform.int2bytes(decrypted, keylength)
- # Find the 00 separator between the padding and the payload
- try:
- sep_idx = clearsig.index(b('\x00'), 2)
- except ValueError:
- raise VerificationError('Verification failed')
-
- # Get the hash and the hash method
- (method_name, signature_hash) = _find_method_hash(clearsig[sep_idx+1:])
+ # Get the hash method
+ method_name = _find_method_hash(clearsig)
message_hash = _hash(message, method_name)
- # Compare the real hash to the hash in the signature
- if message_hash != signature_hash:
+ # Reconstruct the expected padded hash
+ cleartext = HASH_ASN1[method_name] + message_hash
+ expected = _pad_for_signing(cleartext, keylength)
+
+ # Compare with the signed one
+ if expected != clearsig:
raise VerificationError('Verification failed')
return True
@@ -351,24 +337,20 @@ def _hash(message, method_name):
return hasher.digest()
-def _find_method_hash(method_hash):
- '''Finds the hash method and the hash itself.
+def _find_method_hash(clearsig):
+ '''Finds the hash method.
- :param method_hash: ASN1 code for the hash method concatenated with the
- hash itself.
+ :param clearsig: full padded ASN1 and hash.
- :return: tuple (method, hash) where ``method`` is the used hash method, and
- ``hash`` is the hash itself.
+ :return: the used hash method.
:raise VerificationFailed: when the hash method cannot be found
'''
for (hashname, asn1code) in HASH_ASN1.items():
- if not method_hash.startswith(asn1code):
- continue
-
- return (hashname, method_hash[len(asn1code):])
+ if asn1code in clearsig:
+ return hashname
raise VerificationError('Verification failed')