Skip to content

Commit 652867b

Browse files
authored
fixes #12307 -- added unsafe_skip_rsa_key_validation kwarg to ssh private key loading (#12342)
1 parent 8844ca3 commit 652867b

File tree

4 files changed

+54
-21
lines changed

4 files changed

+54
-21
lines changed

CHANGELOG.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ Changelog
1919
provided (previously no exception was raised), and raises a ``TypeError`` if
2020
the key is encrypted but no password is provided (previously a ``ValueError``
2121
was raised).
22+
* Added ``unsafe_skip_rsa_key_validation`` keyword-argument to
23+
:func:`~cryptography.hazmat.primitives.serialization.load_ssh_private_key`.
2224

2325
.. _v44-0-0:
2426

docs/hazmat/primitives/asymmetric/serialization.rst

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,7 @@ An example ECDSA key in OpenSSH format::
456456
:class:`~cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey`.
457457

458458

459-
.. function:: load_ssh_private_key(data, password)
459+
.. function:: load_ssh_private_key(data, password, *, unsafe_skip_rsa_key_validation=False)
460460

461461
.. versionadded:: 3.0
462462

@@ -474,6 +474,19 @@ An example ECDSA key in OpenSSH format::
474474
:param bytes password: Password bytes to use to decrypt
475475
password-protected key. Or ``None`` if not needed.
476476

477+
:param unsafe_skip_rsa_key_validation:
478+
479+
.. versionadded:: 45.0.0
480+
481+
A keyword-only argument that defaults to ``False``. If ``True``
482+
RSA private keys will not be validated. This significantly speeds up
483+
loading the keys, but is :term:`unsafe` unless you are certain the
484+
key is valid. User supplied keys should never be loaded with this
485+
parameter set to ``True``. If you do load an invalid key this way and
486+
attempt to use it OpenSSL may hang, crash, or otherwise misbehave.
487+
488+
:type unsafe_skip_rsa_key_validation: bool
489+
477490
:returns: One of :data:`SSHPrivateKeyTypes` depending on the contents of
478491
``data``.
479492

@@ -1289,11 +1302,11 @@ contain certificates, CRLs, and much more. PKCS7 files commonly have a ``p7b``,
12891302

12901303
.. method:: set_content_encryption_algorithm(content_encryption_algorithm)
12911304

1292-
:param content_encryption_algorithm: the content encryption algorithm to use.
1305+
:param content_encryption_algorithm: the content encryption algorithm to use.
12931306
Only AES is supported, with a key size of 128 or 256 bits.
1294-
:type content_encryption_algorithm:
1295-
:class:`~cryptography.hazmat.primitives.ciphers.algorithms.AES128`
1296-
or :class:`~cryptography.hazmat.primitives.ciphers.algorithms.AES256`
1307+
:type content_encryption_algorithm:
1308+
:class:`~cryptography.hazmat.primitives.ciphers.algorithms.AES128`
1309+
or :class:`~cryptography.hazmat.primitives.ciphers.algorithms.AES256`
12971310

12981311
.. method:: add_recipient(certificate)
12991312

@@ -1361,10 +1374,10 @@ contain certificates, CRLs, and much more. PKCS7 files commonly have a ``p7b``,
13611374
associated with the certificate provided. Only private RSA keys are supported.
13621375

13631376
:param options: A list of
1364-
:class:`~cryptography.hazmat.primitives.serialization.pkcs7.PKCS7Options`. For
1377+
:class:`~cryptography.hazmat.primitives.serialization.pkcs7.PKCS7Options`. For
13651378
this operation only
13661379
:attr:`~cryptography.hazmat.primitives.serialization.pkcs7.PKCS7Options.Text` is supported.
1367-
1380+
13681381
:returns bytes: The decrypted message.
13691382

13701383
:raises ValueError: If the recipient certificate does not match any of the encrypted keys in the
@@ -1377,7 +1390,7 @@ contain certificates, CRLs, and much more. PKCS7 files commonly have a ``p7b``,
13771390
another algorithm than AES (with key sizes 128 and 256), with CBC mode.
13781391

13791392
:raises ValueError: If the PKCS7 data does not contain encrypted content.
1380-
1393+
13811394
:raises ValueError: If the PKCS7 data is not of the enveloped data type.
13821395

13831396
.. function:: pkcs7_decrypt_pem(data, certificate, private_key, options)
@@ -1416,10 +1429,10 @@ contain certificates, CRLs, and much more. PKCS7 files commonly have a ``p7b``,
14161429
associated with the certificate provided. Only private RSA keys are supported.
14171430

14181431
:param options: A list of
1419-
:class:`~cryptography.hazmat.primitives.serialization.pkcs7.PKCS7Options`. For
1432+
:class:`~cryptography.hazmat.primitives.serialization.pkcs7.PKCS7Options`. For
14201433
this operation only
14211434
:attr:`~cryptography.hazmat.primitives.serialization.pkcs7.PKCS7Options.Text` is supported.
1422-
1435+
14231436
:returns bytes: The decrypted message.
14241437

14251438
:raises ValueError: If the PEM data does not have the PKCS7 tag.
@@ -1434,7 +1447,7 @@ contain certificates, CRLs, and much more. PKCS7 files commonly have a ``p7b``,
14341447
another algorithm than AES (with key sizes 128 and 256), with CBC mode.
14351448

14361449
:raises ValueError: If the PKCS7 data does not contain encrypted content.
1437-
1450+
14381451
:raises ValueError: If the PKCS7 data is not of the enveloped data type.
14391452

14401453
.. function:: pkcs7_decrypt_smime(data, certificate, private_key, options)
@@ -1474,10 +1487,10 @@ contain certificates, CRLs, and much more. PKCS7 files commonly have a ``p7b``,
14741487
associated with the certificate provided. Only private RSA keys are supported.
14751488

14761489
:param options: A list of
1477-
:class:`~cryptography.hazmat.primitives.serialization.pkcs7.PKCS7Options`. For
1490+
:class:`~cryptography.hazmat.primitives.serialization.pkcs7.PKCS7Options`. For
14781491
this operation only
14791492
:attr:`~cryptography.hazmat.primitives.serialization.pkcs7.PKCS7Options.Text` is supported.
1480-
1493+
14811494
:returns bytes: The decrypted message.
14821495

14831496
:raises ValueError: If the S/MIME data is not one of the correct content types.
@@ -1492,7 +1505,7 @@ contain certificates, CRLs, and much more. PKCS7 files commonly have a ``p7b``,
14921505
another algorithm than AES (with key sizes 128 and 256), with CBC mode.
14931506

14941507
:raises ValueError: If the PKCS7 data does not contain encrypted content.
1495-
1508+
14961509
:raises ValueError: If the PKCS7 data is not of the enveloped data type.
14971510

14981511

@@ -1505,7 +1518,7 @@ contain certificates, CRLs, and much more. PKCS7 files commonly have a ``p7b``,
15051518
.. attribute:: Text
15061519

15071520
For signing, the text option adds ``text/plain`` headers to an S/MIME message when
1508-
serializing to
1521+
serializing to
15091522
:attr:`~cryptography.hazmat.primitives.serialization.Encoding.SMIME`.
15101523
This option is disallowed with ``DER`` serialization.
15111524
For envelope creation, it adds ``text/plain`` headers to the encrypted content, regardless

src/cryptography/hazmat/primitives/serialization/ssh.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ def load_public(
331331
return public_key, data
332332

333333
def load_private(
334-
self, data: memoryview, pubfields
334+
self, data: memoryview, pubfields, unsafe_skip_rsa_key_validation: bool
335335
) -> tuple[rsa.RSAPrivateKey, memoryview]:
336336
"""Make RSA private key from data."""
337337
n, data = _get_mpint(data)
@@ -349,7 +349,9 @@ def load_private(
349349
private_numbers = rsa.RSAPrivateNumbers(
350350
p, q, d, dmp1, dmq1, iqmp, public_numbers
351351
)
352-
private_key = private_numbers.private_key()
352+
private_key = private_numbers.private_key(
353+
unsafe_skip_rsa_key_validation=unsafe_skip_rsa_key_validation
354+
)
353355
return private_key, data
354356

355357
def encode_public(
@@ -405,7 +407,7 @@ def load_public(
405407
return public_key, data
406408

407409
def load_private(
408-
self, data: memoryview, pubfields
410+
self, data: memoryview, pubfields, unsafe_skip_rsa_key_validation: bool
409411
) -> tuple[dsa.DSAPrivateKey, memoryview]:
410412
"""Make DSA private key from data."""
411413
(p, q, g, y), data = self.get_public(data)
@@ -485,7 +487,7 @@ def load_public(
485487
return public_key, data
486488

487489
def load_private(
488-
self, data: memoryview, pubfields
490+
self, data: memoryview, pubfields, unsafe_skip_rsa_key_validation: bool
489491
) -> tuple[ec.EllipticCurvePrivateKey, memoryview]:
490492
"""Make ECDSA private key from data."""
491493
(curve_name, point), data = self.get_public(data)
@@ -545,7 +547,7 @@ def load_public(
545547
return public_key, data
546548

547549
def load_private(
548-
self, data: memoryview, pubfields
550+
self, data: memoryview, pubfields, unsafe_skip_rsa_key_validation: bool
549551
) -> tuple[ed25519.Ed25519PrivateKey, memoryview]:
550552
"""Make Ed25519 private key from data."""
551553
(point,), data = self.get_public(data)
@@ -681,6 +683,8 @@ def load_ssh_private_key(
681683
data: bytes,
682684
password: bytes | None,
683685
backend: typing.Any = None,
686+
*,
687+
unsafe_skip_rsa_key_validation: bool = False,
684688
) -> SSHPrivateKeyTypes:
685689
"""Load private key from OpenSSH custom encoding."""
686690
utils._check_byteslike("data", data)
@@ -765,7 +769,11 @@ def load_ssh_private_key(
765769
key_type, edata = _get_sshstr(edata)
766770
if key_type != pub_key_type:
767771
raise ValueError("Corrupt data: key type mismatch")
768-
private_key, edata = kformat.load_private(edata, pubfields)
772+
private_key, edata = kformat.load_private(
773+
edata,
774+
pubfields,
775+
unsafe_skip_rsa_key_validation=unsafe_skip_rsa_key_validation,
776+
)
769777
# We don't use the comment
770778
_, edata = _get_sshstr(edata)
771779

tests/hazmat/primitives/test_ssh.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -868,6 +868,16 @@ def test_load_ssh_public_key_rsa(self, backend):
868868

869869
assert numbers == expected
870870

871+
def test_unsafe_skip_rsa_key_validation(self):
872+
key = load_vectors_from_file(
873+
os.path.join("asymmetric", "OpenSSH", "rsa-nopsw.key"),
874+
lambda f: load_ssh_private_key(
875+
f.read(), password=None, unsafe_skip_rsa_key_validation=True
876+
),
877+
mode="rb",
878+
)
879+
assert isinstance(key, rsa.RSAPrivateKey)
880+
871881

872882
class TestDSSSSHSerialization:
873883
def test_load_ssh_public_key_dss_too_short(self, backend):

0 commit comments

Comments
 (0)