Skip to content

Commit 3283b12

Browse files
committed
Fix CVE-2020-13757: detect cyphertext modifications by prepending zero bytes
Reject cyphertexts that have been modified by prepending zero bytes, by checking the cyphertext length against the expected size (given the decryption key). This resolves CVE-2020-13757. The same approach is used when verifying a signature. Thanks Carnil for pointing this out on sybrenstuvel#146
1 parent 3bf7b2e commit 3283b12

File tree

3 files changed

+62
-0
lines changed

3 files changed

+62
-0
lines changed

CHANGELOG.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ in `setup.py`.
1111
Two security fixes have also been backported, so 4.3 = 4.0 + these two fixes.
1212

1313
- Choose blinding factor relatively prime to N. Thanks Christian Heimes for pointing this out.
14+
- Reject cyphertexts (when decrypting) and signatures (when verifying) that have
15+
been modified by prepending zero bytes. This resolves CVE-2020-13757. Thanks
16+
Carnil for pointing this out.
1417

1518

1619
Version 4.0 - released 2018-09-16

rsa/pkcs1.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,12 @@ def decrypt(crypto, priv_key):
234234
decrypted = priv_key.blinded_decrypt(encrypted)
235235
cleartext = transform.int2bytes(decrypted, blocksize)
236236

237+
# Detect leading zeroes in the crypto. These are not reflected in the
238+
# encrypted value (as leading zeroes do not influence the value of an
239+
# integer). This fixes CVE-2020-13757.
240+
if len(crypto) > blocksize:
241+
raise DecryptionError('Decryption failed')
242+
237243
# If we can't find the cleartext marker, decryption failed.
238244
if cleartext[0:2] != b'\x00\x02':
239245
raise DecryptionError('Decryption failed')
@@ -331,6 +337,9 @@ def verify(message, signature, pub_key):
331337
cleartext = HASH_ASN1[method_name] + message_hash
332338
expected = _pad_for_signing(cleartext, keylength)
333339

340+
if len(signature) != keylength:
341+
raise VerificationError('Verification failed')
342+
334343
# Compare with the signed one
335344
if expected != clearsig:
336345
raise VerificationError('Verification failed')

tests/test_pkcs1.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"""Tests string operations."""
1818

1919
import struct
20+
import sys
2021
import unittest
2122

2223
import rsa
@@ -66,6 +67,37 @@ def test_randomness(self):
6667
self.assertNotEqual(encrypted1, encrypted2)
6768

6869

70+
class ExtraZeroesTest(unittest.TestCase):
71+
def setUp(self):
72+
# Key, cyphertext, and plaintext taken from https://github.com/sybrenstuvel/python-rsa/issues/146
73+
self.private_key = rsa.PrivateKey.load_pkcs1(
74+
"-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAs1EKK81M5kTFtZSuUFnhKy8FS2WNXaWVmi/fGHG4CLw98+Yo\n0nkuUarVwSS0O9pFPcpc3kvPKOe9Tv+6DLS3Qru21aATy2PRqjqJ4CYn71OYtSwM\n/ZfSCKvrjXybzgu+sBmobdtYm+sppbdL+GEHXGd8gdQw8DDCZSR6+dPJFAzLZTCd\nB+Ctwe/RXPF+ewVdfaOGjkZIzDoYDw7n+OHnsYCYozkbTOcWHpjVevipR+IBpGPi\n1rvKgFnlcG6d/tj0hWRl/6cS7RqhjoiNEtxqoJzpXs/Kg8xbCxXbCchkf11STA8u\ndiCjQWuWI8rcDwl69XMmHJjIQAqhKvOOQ8rYTQIDAQABAoIBABpQLQ7qbHtp4h1Y\nORAfcFRW7Q74UvtH/iEHH1TF8zyM6wZsYtcn4y0mxYE3Mp+J0xlTJbeVJkwZXYVH\nL3UH29CWHSlR+TWiazTwrCTRVJDhEoqbcTiRW8fb+o/jljVxMcVDrpyYUHNo2c6w\njBxhmKPtp66hhaDpds1Cwi0A8APZ8Z2W6kya/L/hRBzMgCz7Bon1nYBMak5PQEwV\nF0dF7Wy4vIjvCzO6DSqA415DvJDzUAUucgFudbANNXo4HJwNRnBpymYIh8mHdmNJ\n/MQ0YLSqUWvOB57dh7oWQwe3UsJ37ZUorTugvxh3NJ7Tt5ZqbCQBEECb9ND63gxo\n/a3YR/0CgYEA7BJc834xCi/0YmO5suBinWOQAF7IiRPU+3G9TdhWEkSYquupg9e6\nK9lC5k0iP+t6I69NYF7+6mvXDTmv6Z01o6oV50oXaHeAk74O3UqNCbLe9tybZ/+F\ndkYlwuGSNttMQBzjCiVy0+y0+Wm3rRnFIsAtd0RlZ24aN3bFTWJINIsCgYEAwnQq\nvNmJe9SwtnH5c/yCqPhKv1cF/4jdQZSGI6/p3KYNxlQzkHZ/6uvrU5V27ov6YbX8\nvKlKfO91oJFQxUD6lpTdgAStI3GMiJBJIZNpyZ9EWNSvwUj28H34cySpbZz3s4Xd\nhiJBShgy+fKURvBQwtWmQHZJ3EGrcOI7PcwiyYcCgYEAlql5jSUCY0ALtidzQogW\nJ+B87N+RGHsBuJ/0cxQYinwg+ySAAVbSyF1WZujfbO/5+YBN362A/1dn3lbswCnH\nK/bHF9+fZNqvwprPnceQj5oK1n4g6JSZNsy6GNAhosT+uwQ0misgR8SQE4W25dDG\nkdEYsz+BgCsyrCcu8J5C+tUCgYAFVPQbC4f2ikVyKzvgz0qx4WUDTBqRACq48p6e\n+eLatv7nskVbr7QgN+nS9+Uz80ihR0Ev1yCAvnwmM/XYAskcOea87OPmdeWZlQM8\nVXNwINrZ6LMNBLgorfuTBK1UoRo1pPUHCYdqxbEYI2unak18mikd2WB7Fp3h0YI4\nVpGZnwKBgBxkAYnZv+jGI4MyEKdsQgxvROXXYOJZkWzsKuKxVkVpYP2V4nR2YMOJ\nViJQ8FUEnPq35cMDlUk4SnoqrrHIJNOvcJSCqM+bWHAioAsfByLbUPM8sm3CDdIk\nXVJl32HuKYPJOMIWfc7hIfxLRHnCN+coz2M6tgqMDs0E/OfjuqVZ\n-----END RSA PRIVATE KEY-----",
75+
format='PEM')
76+
cyphertext = "4501b4d669e01b9ef2dc800aa1b06d49196f5a09fe8fbcd037323c60eaf027bfb98432be4e4a26c567ffec718bcbea977dd26812fa071c33808b4d5ebb742d9879806094b6fbeea63d25ea3141733b60e31c6912106e1b758a7fe0014f075193faa8b4622bfd5d3013f0a32190a95de61a3604711bc62945f95a6522bd4dfed0a994ef185b28c281f7b5e4c8ed41176d12d9fc1b837e6a0111d0132d08a6d6f0580de0c9eed8ed105531799482d1e466c68c23b0c222af7fc12ac279bc4ff57e7b4586d209371b38c4c1035edd418dc5f960441cb21ea2bedbfea86de0d7861e81021b650a1de51002c315f1e7c12debe4dcebf790caaa54a2f26b149cf9e77d"
77+
plaintext = "54657374"
78+
79+
if sys.version_info < (3, 0):
80+
self.cyphertext = cyphertext.decode("hex")
81+
self.plaintext = plaintext.decode('hex')
82+
else:
83+
self.cyphertext = bytes.fromhex(cyphertext)
84+
self.plaintext = bytes.fromhex(plaintext)
85+
86+
def test_unmodified(self):
87+
message = rsa.decrypt(self.cyphertext, self.private_key)
88+
self.assertEqual(message, self.plaintext)
89+
90+
def test_prepend_zeroes(self):
91+
cyphertext = b'\00\00' + self.cyphertext
92+
with self.assertRaises(rsa.DecryptionError):
93+
rsa.decrypt(cyphertext, self.private_key)
94+
95+
def test_append_zeroes(self):
96+
cyphertext = self.cyphertext + b'\00\00'
97+
with self.assertRaises(rsa.DecryptionError):
98+
rsa.decrypt(cyphertext, self.private_key)
99+
100+
69101
class SignatureTest(unittest.TestCase):
70102
def setUp(self):
71103
(self.pub, self.priv) = rsa.newkeys(512)
@@ -132,3 +164,21 @@ def test_hash_sign_verify(self):
132164
signature = pkcs1.sign_hash(msg_hash, self.priv, 'SHA-224')
133165

134166
self.assertTrue(pkcs1.verify(message, signature, self.pub))
167+
168+
def test_prepend_zeroes(self):
169+
"""Prepending the signature with zeroes should be detected."""
170+
171+
message = b'je moeder'
172+
signature = pkcs1.sign(message, self.priv, 'SHA-256')
173+
signature = b'\00\00' + signature
174+
with self.assertRaises(rsa.VerificationError):
175+
pkcs1.verify(message, signature, self.pub)
176+
177+
def test_apppend_zeroes(self):
178+
"""Apppending the signature with zeroes should be detected."""
179+
180+
message = b'je moeder'
181+
signature = pkcs1.sign(message, self.priv, 'SHA-256')
182+
signature = signature + b'\00\00'
183+
with self.assertRaises(rsa.VerificationError):
184+
pkcs1.verify(message, signature, self.pub)

0 commit comments

Comments
 (0)