Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
commit patch 3262555
  • Loading branch information
turly221 committed Dec 1, 2024
commit c7897fbae364dd3fad4fa7319bec0d393570b5a9
58 changes: 20 additions & 38 deletions rsa/pkcs1.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@
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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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')

Expand Down