Skip to content

Commit 1811500

Browse files
fix RSA PSS salt lengths
- use maximum salt length by default when creating sigs - use automatic salt length inferred from sigs when verifying this should be the simplest, backwards-compatible, and secure way to verify cross-platform RSA PSS signatures, especially with Golang crypto/rsa
1 parent 2bec929 commit 1811500

File tree

4 files changed

+35
-8
lines changed

4 files changed

+35
-8
lines changed

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,6 @@
3131
# 3. Use this command to remove per-version files
3232
# `rm requirements-?.?.txt`
3333
#
34-
cryptography >= 3.3.2; python_version >= '3'
34+
cryptography >= 37.0.0; python_version >= '3'
3535
pynacl
3636
colorama

securesystemslib/rsa_keys.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -326,10 +326,11 @@ def create_rsa_signature(private_key, data, scheme='rsassa-pss-sha256'):
326326
if scheme.startswith('rsassa-pss'):
327327
# Generate an RSSA-PSS signature. Raise
328328
# 'securesystemslib.exceptions.CryptoError' for any of the expected
329-
# exceptions raised by pyca/cryptography.
329+
# exceptions raised by pyca/cryptography. 'salt_length' is set to
330+
# the maximum length available.
330331
signature = private_key_object.sign(
331332
data, padding.PSS(mgf=padding.MGF1(digest_obj.algorithm),
332-
salt_length=digest_obj.algorithm.digest_size), digest_obj.algorithm)
333+
salt_length=padding.PSS.MAX_LENGTH), digest_obj.algorithm)
333334

334335
elif scheme.startswith('rsa-pkcs1v15'):
335336
# Generate an RSA-PKCS1v15 signature. Raise
@@ -453,13 +454,13 @@ def verify_rsa_signature(signature, signature_scheme, public_key, data):
453454
digest_obj = digest_from_rsa_scheme(signature_scheme, 'pyca_crypto')
454455

455456
# verify() raises 'cryptography.exceptions.InvalidSignature' if the
456-
# signature is invalid. 'salt_length' is set to the digest size of the
457-
# hashing algorithm.
457+
# signature is invalid. 'salt_length' is automatically
458+
# determined when verifying the signature.
458459
try:
459460
if signature_scheme.startswith('rsassa-pss'):
460461
public_key_object.verify(signature, data,
461462
padding.PSS(mgf=padding.MGF1(digest_obj.algorithm),
462-
salt_length=digest_obj.algorithm.digest_size),
463+
salt_length=padding.PSS.AUTO),
463464
digest_obj.algorithm)
464465

465466
elif signature_scheme.startswith('rsa-pkcs1v15'):

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@
103103
python_requires = "~=3.7",
104104
extras_require = {
105105
'colors': ['colorama>=0.3.9'],
106-
'crypto': ['cryptography>=3.3.2'],
106+
'crypto': ['cryptography>=37.0.0'],
107107
'pynacl': ['pynacl>1.2.0']},
108108
packages = find_packages(exclude=['tests', 'debian']),
109109
scripts = []

tests/test_rsa_keys.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,11 @@
2323
import securesystemslib.formats
2424
import securesystemslib.keys
2525
import securesystemslib.rsa_keys
26+
import securesystemslib.hash
2627

27-
from cryptography.hazmat.primitives import hashes
28+
from cryptography.hazmat.backends import default_backend
29+
from cryptography.hazmat.primitives.asymmetric import padding
30+
from cryptography.hazmat.primitives.serialization import load_pem_private_key
2831

2932
public_rsa, private_rsa = securesystemslib.rsa_keys.generate_rsa_public_and_private()
3033
FORMAT_ERROR_MSG = 'securesystemslib.exceptions.FormatError raised. Check object\'s format.'
@@ -154,6 +157,29 @@ def test_verify_rsa_signature(self):
154157
scheme, public_rsa, data))
155158

156159

160+
def test_verify_rsa_pss_sha256(self):
161+
rsa_scheme = 'rsassa-pss-sha256'
162+
data = 'The ancients say the longer the salt, the more provable the security'.encode('utf-8')
163+
164+
private_key = load_pem_private_key(private_rsa.encode('utf-8'),
165+
password=None, backend=default_backend())
166+
digest = securesystemslib.hash.digest_from_rsa_scheme(rsa_scheme, 'pyca_crypto')
167+
168+
# Old-style signature: use the hash length as the salt length.
169+
old_signature = private_key.sign(data,
170+
padding.PSS(mgf=padding.MGF1(digest.algorithm), salt_length=padding.PSS.DIGEST_LENGTH),
171+
digest.algorithm)
172+
173+
# New-style signature: use the automatic salt length.
174+
new_signature, _ = securesystemslib.rsa_keys.create_rsa_signature(private_rsa, data)
175+
176+
# Verify both old-style and new-style signatures.
177+
for signature in (old_signature, new_signature):
178+
verified = securesystemslib.rsa_keys.verify_rsa_signature(signature, rsa_scheme,
179+
public_rsa, data)
180+
self.assertTrue(verified)
181+
182+
157183
def test_create_rsa_encrypted_pem(self):
158184
global public_rsa
159185
global private_rsa

0 commit comments

Comments
 (0)