Skip to content
Permalink

Comparing changes

This is a direct comparison between two commits made in this repository or its related repositories. View the default comparison for this range or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: pyca/cryptography
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: b749188118f27f524aa1295c0fb619e1e6260a9d
Choose a base ref
..
head repository: pyca/cryptography
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 629fe36cade78e09a909f17d81ceb98b3e679982
Choose a head ref
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -44,8 +44,8 @@ jobs:
- {VERSION: "3.12", NOXSESSION: "tests-randomorder"}
# Latest commit on the BoringSSL main branch, as of Jan 24, 2025.
- {VERSION: "3.12", NOXSESSION: "rust,tests", OPENSSL: {TYPE: "boringssl", VERSION: "5823b5e1b1e964ef0d1f5be2be907ded3feaf155"}}
# Latest commit on the OpenSSL master branch, as of Jan 24, 2025.
- {VERSION: "3.12", NOXSESSION: "tests", OPENSSL: {TYPE: "openssl", VERSION: "8900cdf2305c55aa3a1d4b0d6b4d33afdc72e756"}}
# Latest commit on the OpenSSL master branch, as of Jan 26, 2025.
- {VERSION: "3.12", NOXSESSION: "tests", OPENSSL: {TYPE: "openssl", VERSION: "95a3662626602c7298170849819e02002b7add42"}}
# Builds with various Rust versions. Includes MSRV and next
# potential future MSRV.
# - 1.70: crates.io sparse protocol by default
2 changes: 2 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -19,6 +19,8 @@ Changelog
provided (previously no exception was raised), and raises a ``TypeError`` if
the key is encrypted but no password is provided (previously a ``ValueError``
was raised).
* Added ``unsafe_skip_rsa_key_validation`` keyword-argument to
:func:`~cryptography.hazmat.primitives.serialization.load_ssh_private_key`.

.. _v44-0-0:

4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@ rust-version = "1.65.0"
[workspace.dependencies]
asn1 = { version = "0.20.0", default-features = false }
pyo3 = { version = "0.23.4", features = ["abi3"] }
openssl = "0.10.68"
openssl = "0.10.69"

[profile.release]
overflow-checks = true
43 changes: 28 additions & 15 deletions docs/hazmat/primitives/asymmetric/serialization.rst
Original file line number Diff line number Diff line change
@@ -456,7 +456,7 @@ An example ECDSA key in OpenSSH format::
:class:`~cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey`.


.. function:: load_ssh_private_key(data, password)
.. function:: load_ssh_private_key(data, password, *, unsafe_skip_rsa_key_validation=False)

.. versionadded:: 3.0

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

:param unsafe_skip_rsa_key_validation:

.. versionadded:: 45.0.0

A keyword-only argument that defaults to ``False``. If ``True``
RSA private keys will not be validated. This significantly speeds up
loading the keys, but is :term:`unsafe` unless you are certain the
key is valid. User supplied keys should never be loaded with this
parameter set to ``True``. If you do load an invalid key this way and
attempt to use it OpenSSL may hang, crash, or otherwise misbehave.

:type unsafe_skip_rsa_key_validation: bool

:returns: One of :data:`SSHPrivateKeyTypes` depending on the contents of
``data``.

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

.. method:: set_content_encryption_algorithm(content_encryption_algorithm)

:param content_encryption_algorithm: the content encryption algorithm to use.
:param content_encryption_algorithm: the content encryption algorithm to use.
Only AES is supported, with a key size of 128 or 256 bits.
:type content_encryption_algorithm:
:class:`~cryptography.hazmat.primitives.ciphers.algorithms.AES128`
or :class:`~cryptography.hazmat.primitives.ciphers.algorithms.AES256`
:type content_encryption_algorithm:
:class:`~cryptography.hazmat.primitives.ciphers.algorithms.AES128`
or :class:`~cryptography.hazmat.primitives.ciphers.algorithms.AES256`

.. method:: add_recipient(certificate)

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

:param options: A list of
:class:`~cryptography.hazmat.primitives.serialization.pkcs7.PKCS7Options`. For
:class:`~cryptography.hazmat.primitives.serialization.pkcs7.PKCS7Options`. For
this operation only
:attr:`~cryptography.hazmat.primitives.serialization.pkcs7.PKCS7Options.Text` is supported.

:returns bytes: The decrypted message.

: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``,
another algorithm than AES (with key sizes 128 and 256), with CBC mode.

:raises ValueError: If the PKCS7 data does not contain encrypted content.

:raises ValueError: If the PKCS7 data is not of the enveloped data type.

.. 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``,
associated with the certificate provided. Only private RSA keys are supported.

:param options: A list of
:class:`~cryptography.hazmat.primitives.serialization.pkcs7.PKCS7Options`. For
:class:`~cryptography.hazmat.primitives.serialization.pkcs7.PKCS7Options`. For
this operation only
:attr:`~cryptography.hazmat.primitives.serialization.pkcs7.PKCS7Options.Text` is supported.

:returns bytes: The decrypted message.

: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``,
another algorithm than AES (with key sizes 128 and 256), with CBC mode.

:raises ValueError: If the PKCS7 data does not contain encrypted content.

:raises ValueError: If the PKCS7 data is not of the enveloped data type.

.. 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``,
associated with the certificate provided. Only private RSA keys are supported.

:param options: A list of
:class:`~cryptography.hazmat.primitives.serialization.pkcs7.PKCS7Options`. For
:class:`~cryptography.hazmat.primitives.serialization.pkcs7.PKCS7Options`. For
this operation only
:attr:`~cryptography.hazmat.primitives.serialization.pkcs7.PKCS7Options.Text` is supported.

:returns bytes: The decrypted message.

: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``,
another algorithm than AES (with key sizes 128 and 256), with CBC mode.

:raises ValueError: If the PKCS7 data does not contain encrypted content.

:raises ValueError: If the PKCS7 data is not of the enveloped data type.


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

For signing, the text option adds ``text/plain`` headers to an S/MIME message when
serializing to
serializing to
:attr:`~cryptography.hazmat.primitives.serialization.Encoding.SMIME`.
This option is disallowed with ``DER`` serialization.
For envelope creation, it adds ``text/plain`` headers to the encrypted content, regardless
20 changes: 14 additions & 6 deletions src/cryptography/hazmat/primitives/serialization/ssh.py
Original file line number Diff line number Diff line change
@@ -331,7 +331,7 @@ def load_public(
return public_key, data

def load_private(
self, data: memoryview, pubfields
self, data: memoryview, pubfields, unsafe_skip_rsa_key_validation: bool
) -> tuple[rsa.RSAPrivateKey, memoryview]:
"""Make RSA private key from data."""
n, data = _get_mpint(data)
@@ -349,7 +349,9 @@ def load_private(
private_numbers = rsa.RSAPrivateNumbers(
p, q, d, dmp1, dmq1, iqmp, public_numbers
)
private_key = private_numbers.private_key()
private_key = private_numbers.private_key(
unsafe_skip_rsa_key_validation=unsafe_skip_rsa_key_validation
)
return private_key, data

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

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

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

def load_private(
self, data: memoryview, pubfields
self, data: memoryview, pubfields, unsafe_skip_rsa_key_validation: bool
) -> tuple[ed25519.Ed25519PrivateKey, memoryview]:
"""Make Ed25519 private key from data."""
(point,), data = self.get_public(data)
@@ -681,6 +683,8 @@ def load_ssh_private_key(
data: bytes,
password: bytes | None,
backend: typing.Any = None,
*,
unsafe_skip_rsa_key_validation: bool = False,
) -> SSHPrivateKeyTypes:
"""Load private key from OpenSSH custom encoding."""
utils._check_byteslike("data", data)
@@ -765,7 +769,11 @@ def load_ssh_private_key(
key_type, edata = _get_sshstr(edata)
if key_type != pub_key_type:
raise ValueError("Corrupt data: key type mismatch")
private_key, edata = kformat.load_private(edata, pubfields)
private_key, edata = kformat.load_private(
edata,
pubfields,
unsafe_skip_rsa_key_validation=unsafe_skip_rsa_key_validation,
)
# We don't use the comment
_, edata = _get_sshstr(edata)

2 changes: 1 addition & 1 deletion src/rust/Cargo.toml
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@ cryptography-x509 = { path = "cryptography-x509" }
cryptography-x509-verification = { path = "cryptography-x509-verification" }
cryptography-openssl = { path = "cryptography-openssl" }
pem = { version = "3", default-features = false }
openssl = "0.10.68"
openssl.workspace = true
openssl-sys = "0.9.104"
foreign-types-shared = "0.1"
self_cell = "1"
10 changes: 10 additions & 0 deletions tests/hazmat/primitives/test_ssh.py
Original file line number Diff line number Diff line change
@@ -868,6 +868,16 @@ def test_load_ssh_public_key_rsa(self, backend):

assert numbers == expected

def test_unsafe_skip_rsa_key_validation(self):
key = load_vectors_from_file(
os.path.join("asymmetric", "OpenSSH", "rsa-nopsw.key"),
lambda f: load_ssh_private_key(
f.read(), password=None, unsafe_skip_rsa_key_validation=True
),
mode="rb",
)
assert isinstance(key, rsa.RSAPrivateKey)


class TestDSSSSHSerialization:
def test_load_ssh_public_key_dss_too_short(self, backend):