$OpenBSD: patch-rsa_pkcs1_py,v 1.1 2016/01/08 09:23:00 ajacoutot Exp $

https://bitbucket.org/sybren/python-rsa/commits/0cbcc529926afd61c6df4f50cfc29971beafd2c2/raw/

--- rsa/pkcs1.py.orig	Thu Nov  5 21:23:16 2015
+++ rsa/pkcs1.py	Fri Jan  8 10:20:09 2016
@@ -22,10 +22,10 @@ very clear example, read http://www.di-mgt.com.au/rsa_
 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')
 
