summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSybren A. Stüvel <sybren@stuvel.eu>2020-11-15 15:18:38 +0100
committerSybren A. Stüvel <sybren@stuvel.eu>2020-11-15 15:21:31 +0100
commitdae8ce0d85478e16f2368b2341632775313d41ed (patch)
treeb93513f2f24d50246410ab8379eb60a9fc2cdff0
parent6f59ff07a317409fe68696935daf8549b1555c74 (diff)
downloadrsa-git-dae8ce0d85478e16f2368b2341632775313d41ed.tar.gz
Fix #165: CVE-2020-25658 - Bleichenbacher-style timing oracle
Use as many constant-time comparisons as practical in the `rsa.pkcs1.decrypt` function. `cleartext.index(b'\x00', 2)` will still be non-constant-time. The alternative would be to iterate over all the data byte by byte in Python, which is several orders of magnitude slower. Given that a perfect constant-time implementation is very hard or even impossible to do in Python [1], I chose the more performant option here. [1]: https://securitypitfalls.wordpress.com/2018/08/03/constant-time-compare-in-python/
-rw-r--r--CHANGELOG.md5
-rw-r--r--rsa/pkcs1.py12
2 files changed, 13 insertions, 4 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bc41f70..1838377 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
# Python-RSA changelog
+## Version 4.7 - in development
+
+- Fix #165: CVE-2020-25658 - Bleichenbacher-style timing oracle in PKCS#1 v1.5
+ decryption code
+
## Version 4.4 & 4.6 - released 2020-06-12
diff --git a/rsa/pkcs1.py b/rsa/pkcs1.py
index 57b0276..19e24c7 100644
--- a/rsa/pkcs1.py
+++ b/rsa/pkcs1.py
@@ -30,6 +30,7 @@ import hashlib
import os
import sys
import typing
+from hmac import compare_digest
from . import common, transform, core, key
@@ -251,17 +252,20 @@ def decrypt(crypto: bytes, priv_key: key.PrivateKey) -> bytes:
# 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')
+ crypto_len_bad = len(crypto) > blocksize
# If we can't find the cleartext marker, decryption failed.
- if cleartext[0:2] != b'\x00\x02':
- raise DecryptionError('Decryption failed')
+ cleartext_marker_bad = not compare_digest(cleartext[:2], b'\x00\x02')
# Find the 00 separator between the padding and the message
try:
sep_idx = cleartext.index(b'\x00', 2)
except ValueError:
+ sep_idx = -1
+ sep_idx_bad = sep_idx < 0
+
+ anything_bad = crypto_len_bad | cleartext_marker_bad | sep_idx_bad
+ if anything_bad:
raise DecryptionError('Decryption failed')
return cleartext[sep_idx + 1:]