Skip to content

Commit f878c37

Browse files
committed
Fix #164: Add padding length check as described by PKCS#1 v1.5
According to PKCS#1 v1.5, the padding should be at least 8 bytes long. See https://tools.ietf.org/html/rfc8017#section-7.2.2 step 3 for more info.
1 parent dae8ce0 commit f878c37

File tree

3 files changed

+41
-1
lines changed

3 files changed

+41
-1
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
- Fix #165: CVE-2020-25658 - Bleichenbacher-style timing oracle in PKCS#1 v1.5
66
decryption code
7+
- Add padding length check as described by PKCS#1 v1.5 (Fixes
8+
[#164](https://github.com/sybrenstuvel/python-rsa/issues/164))
79

810

911
## Version 4.4 & 4.6 - released 2020-06-12

rsa/pkcs1.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,12 @@ def decrypt(crypto: bytes, priv_key: key.PrivateKey) -> bytes:
262262
sep_idx = cleartext.index(b'\x00', 2)
263263
except ValueError:
264264
sep_idx = -1
265-
sep_idx_bad = sep_idx < 0
265+
# sep_idx indicates the position of the `\x00` separator that separates the
266+
# padding from the actual message. The padding should be at least 8 bytes
267+
# long (see https://tools.ietf.org/html/rfc8017#section-7.2.2 step 3), which
268+
# means the separator should be at least at index 10 (because of the
269+
# `\x00\x02` marker that preceeds it).
270+
sep_idx_bad = sep_idx < 10
266271

267272
anything_bad = crypto_len_bad | cleartext_marker_bad | sep_idx_bad
268273
if anything_bad:

tests/test_pkcs1.py

+33
Original file line numberDiff line numberDiff line change
@@ -183,3 +183,36 @@ def test_apppend_zeroes(self):
183183
signature = signature + bytes.fromhex('0000')
184184
with self.assertRaises(rsa.VerificationError):
185185
pkcs1.verify(message, signature, self.pub)
186+
187+
188+
class PaddingSizeTest(unittest.TestCase):
189+
def test_too_little_padding(self):
190+
"""Padding less than 8 bytes should be rejected."""
191+
192+
# Construct key that will be small enough to need only 7 bytes of padding.
193+
# This key is 168 bit long, and was generated with rsa.newkeys(nbits=168).
194+
self.private_key = rsa.PrivateKey.load_pkcs1(b'''
195+
-----BEGIN RSA PRIVATE KEY-----
196+
MHkCAQACFgCIGbbNSkIRLtprxka9NgOf5UxgxCMCAwEAAQIVQqymO0gHubdEVS68
197+
CdCiWmOJxVfRAgwBQM+e1JJwMKmxSF0CCmya6CFxO8Evdn8CDACMM3AlVC4FhlN8
198+
3QIKC9cjoam/swMirwIMAR7Br9tdouoH7jAE
199+
-----END RSA PRIVATE KEY-----
200+
''')
201+
self.public_key = rsa.PublicKey(n=self.private_key.n, e=self.private_key.e)
202+
203+
cyphertext = self.encrypt_with_short_padding(b'op je hoofd')
204+
with self.assertRaises(rsa.DecryptionError):
205+
rsa.decrypt(cyphertext, self.private_key)
206+
207+
def encrypt_with_short_padding(self, message: bytes) -> bytes:
208+
# This is a copy of rsa.pkcs1.encrypt() adjusted to use the wrong padding length.
209+
keylength = rsa.common.byte_size(self.public_key.n)
210+
211+
# The word 'padding' has 7 letters, so is one byte short of a valid padding length.
212+
padded = b'\x00\x02padding\x00' + message
213+
214+
payload = rsa.transform.bytes2int(padded)
215+
encrypted_value = rsa.core.encrypt_int(payload, self.public_key.e, self.public_key.n)
216+
cyphertext = rsa.transform.int2bytes(encrypted_value, keylength)
217+
218+
return cyphertext

0 commit comments

Comments
 (0)