diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c2ba827f7..3057be632 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -25,6 +25,10 @@ Changelog details. * Added two new OpenSSL functions to the bindings to support an upcoming ``pyOpenSSL`` release. +* When parsing :class:`~cryptography.x509.CertificateRevocationList` and + :class:`~cryptography.x509.CertificateSigningRequest` values, it is now + enforced that the ``version`` value in the input must be valid according to + the rules of :rfc:`2986` and :rfc:`5280`. .. _v37-0-2: diff --git a/docs/x509/reference.rst b/docs/x509/reference.rst index d7cbabb2b..c954cd8e1 100644 --- a/docs/x509/reference.rst +++ b/docs/x509/reference.rst @@ -7,7 +7,7 @@ X.509 Reference pem_crl_data = b""" -----BEGIN X509 CRL----- - MIIBtDCBnQIBAjANBgkqhkiG9w0BAQsFADAnMQswCQYDVQQGEwJVUzEYMBYGA1UE + MIIBtDCBnQIBATANBgkqhkiG9w0BAQsFADAnMQswCQYDVQQGEwJVUzEYMBYGA1UE AwwPY3J5cHRvZ3JhcGh5LmlvGA8yMDE1MDEwMTAwMDAwMFoYDzIwMTYwMTAxMDAw MDAwWjA+MDwCAQAYDzIwMTUwMTAxMDAwMDAwWjAmMBgGA1UdGAQRGA8yMDE1MDEw MTAwMDAwMFowCgYDVR0VBAMKAQEwDQYJKoZIhvcNAQELBQADggEBABRA4ww50Lz5 @@ -508,7 +508,7 @@ X.509 CRL (Certificate Revocation List) Object >>> from cryptography.hazmat.primitives import hashes >>> crl.fingerprint(hashes.SHA256()) - b'e\xcf.\xc4:\x83?1\xdc\xf3\xfc\x95\xd7\xb3\x87\xb3\x8e\xf8\xb93!\x87\x07\x9d\x1b\xb4!\xb9\xe4W\xf4\x1f' + b'\xe3\x1d\xb5P\x18\x9ed\x9f\x16O\x9dm\xc1>\x8c\xca\xb1\xc6x?T\x9f\xe9t_\x1d\x8dF8V\xf78' .. method:: get_revoked_certificate_by_serial_number(serial_number) diff --git a/src/rust/src/x509/crl.rs b/src/rust/src/x509/crl.rs index 8fa982678..1388d2527 100644 --- a/src/rust/src/x509/crl.rs +++ b/src/rust/src/x509/crl.rs @@ -13,7 +13,7 @@ use std::sync::Arc; #[pyo3::prelude::pyfunction] fn load_der_x509_crl( - _py: pyo3::Python<'_>, + py: pyo3::Python<'_>, data: &[u8], ) -> Result { let raw = OwnedRawCertificateRevocationList::try_new( @@ -22,6 +22,16 @@ fn load_der_x509_crl( |_| Ok(pyo3::once_cell::GILOnceCell::new()), )?; + let version = raw.borrow_value().tbs_cert_list.version.unwrap_or(1); + if version != 1 { + let x509_module = py.import("cryptography.x509")?; + return Err(PyAsn1Error::from(pyo3::PyErr::from_instance( + x509_module + .getattr("InvalidVersion")? + .call1((format!("{} is not a valid CRL version", version), version))?, + ))); + } + Ok(CertificateRevocationList { raw: Arc::new(raw), cached_extensions: None, diff --git a/src/rust/src/x509/csr.rs b/src/rust/src/x509/csr.rs index 44be04fc6..004055717 100644 --- a/src/rust/src/x509/csr.rs +++ b/src/rust/src/x509/csr.rs @@ -328,11 +328,19 @@ fn load_pem_x509_csr(py: pyo3::Python<'_>, data: &[u8]) -> PyAsn1Result, - data: &[u8], -) -> PyAsn1Result { +fn load_der_x509_csr(py: pyo3::Python<'_>, data: &[u8]) -> PyAsn1Result { let raw = OwnedRawCsr::try_new(data.to_vec(), |data| asn1::parse_single(data))?; + + let version = raw.borrow_value().csr_info.version; + if version != 0 { + let x509_module = py.import("cryptography.x509")?; + return Err(PyAsn1Error::from(pyo3::PyErr::from_instance( + x509_module + .getattr("InvalidVersion")? + .call1((format!("{} is not a valid CSR version", version), version))?, + ))); + } + Ok(CertificateSigningRequest { raw, cached_extensions: None, diff --git a/tests/x509/test_x509.py b/tests/x509/test_x509.py index ca82a05a4..cff5583e8 100644 --- a/tests/x509/test_x509.py +++ b/tests/x509/test_x509.py @@ -156,6 +156,14 @@ class TestCertificateRevocationList: with pytest.raises(UnsupportedAlgorithm): crl.signature_hash_algorithm + def test_invalid_version(self, backend): + with pytest.raises(x509.InvalidVersion): + _load_cert( + os.path.join("x509", "custom", "crl_bad_version.pem"), + x509.load_pem_x509_crl, + backend, + ) + def test_issuer(self, backend): crl = _load_cert( os.path.join("x509", "PKITS_data", "crls", "GoodCACRL.crl"), @@ -1477,6 +1485,14 @@ class TestRSACertificateRequest: with pytest.raises(UnsupportedAlgorithm): request.signature_hash_algorithm + def test_invalid_version(self, backend): + with pytest.raises(x509.InvalidVersion): + _load_cert( + os.path.join("x509", "requests", "bad-version.pem"), + x509.load_pem_x509_csr, + backend, + ) + def test_duplicate_extension(self, backend): request = _load_cert( os.path.join("x509", "requests", "two_basic_constraints.pem"),