CVE-2020-25658 - Bleichenbacher-style timing oracle in PKCS#1 v1.5 decryption code · Issue #165 · sybrenstuvel/python-rsa (original) (raw)
This repository was archived by the owner on Apr 20, 2025. It is now read-only.
This repository was archived by the owner on Apr 20, 2025. It is now read-only.
Description
Current PKCS#1 v1.5 decryption code:
blocksize = common.byte_size(priv_key.n) |
---|
encrypted = transform.bytes2int(crypto) |
decrypted = priv_key.blinded_decrypt(encrypted) |
cleartext = transform.int2bytes(decrypted, blocksize) |
# Detect leading zeroes in the crypto. These are not reflected in the |
# encrypted value (as leading zeroes do not influence the value of an |
# integer). This fixes CVE-2020-13757. |
if len(crypto) > blocksize: |
raise DecryptionError('Decryption failed') |
# If we can't find the cleartext marker, decryption failed. |
if cleartext[0:2] != b'\x00\x02': |
raise DecryptionError('Decryption failed') |
# Find the 00 separator between the padding and the message |
try: |
sep_idx = cleartext.index(b'\x00', 2) |
except ValueError: |
raise DecryptionError('Decryption failed') |
return cleartext[sep_idx + 1:] |
performs the checks on the decrypted value in turn, aborting as soon as first error is found, it also raises an exception in case of errors. This likely provides enough of a timing side channel to mount a Bleichenbacher style attack.
While it's unlikely that a completely side-channel free implementation is possible (see https://securitypitfalls.wordpress.com/2018/08/03/constant-time-compare-in-python/ ), it should be possible to minimise the side-channel by making at least the execution path the same irrespective of previous checks and by providing an API that returns a randomly generated secret in case of error (instead of leaking the timing side-channel by rising an exception) for uses that decrypted value directly as an input to a hash or use it as a symmetric key.