From 6213bad3353ade35bc680dcc73fd6e33506b6e32 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Mon, 27 Jul 2020 15:41:28 -0400 Subject: [PATCH] Port a tiny tiny bit of the ASN.1 parsing to Rust --- .../hazmat/backends/openssl/decode_asn1.py | 29 +++----- src/rust/Cargo.lock | 44 ++++++++++++ src/rust/Cargo.toml | 1 + src/rust/src/lib.rs | 71 ++++++++++++++++++- 4 files changed, 125 insertions(+), 20 deletions(-) diff --git a/src/cryptography/hazmat/backends/openssl/decode_asn1.py b/src/cryptography/hazmat/backends/openssl/decode_asn1.py index 96ba4cdbc42c4..bdc7012c432f8 100644 --- a/src/cryptography/hazmat/backends/openssl/decode_asn1.py +++ b/src/cryptography/hazmat/backends/openssl/decode_asn1.py @@ -7,8 +7,7 @@ import ipaddress from cryptography import x509 -from cryptography.hazmat._der import DERReader, INTEGER, NULL, SEQUENCE -from cryptography.x509.extensions import _TLS_FEATURE_TYPE_TO_ENUM +from cryptography.hazmat.bindings import _rust from cryptography.x509.name import _ASN1_TYPE_TO_ENUM from cryptography.x509.oid import ( CRLEntryExtensionOID, @@ -208,27 +207,19 @@ def parse(self, x509_obj): # ourselves. if oid == ExtensionOID.TLS_FEATURE: # The extension contents are a SEQUENCE OF INTEGERs. - data = self._backend._lib.X509_EXTENSION_get_data(ext) - data_bytes = _asn1_string_to_bytes(self._backend, data) - features = DERReader(data_bytes).read_single_element(SEQUENCE) - parsed = [] - while not features.is_empty(): - parsed.append(features.read_element(INTEGER).as_integer()) - # Map the features to their enum value. - value = x509.TLSFeature( - [_TLS_FEATURE_TYPE_TO_ENUM[x] for x in parsed] - ) + data = backend._lib.X509_EXTENSION_get_data(ext) + data_bytes = _asn1_string_to_bytes(backend, data) + value = _rust.parse_tls_feature(data_bytes) + extensions.append(x509.Extension(oid, critical, value)) seen_oids.add(oid) continue elif oid == ExtensionOID.PRECERT_POISON: - data = self._backend._lib.X509_EXTENSION_get_data(ext) - # The contents of the extension must be an ASN.1 NULL. - reader = DERReader(_asn1_string_to_bytes(self._backend, data)) - reader.read_single_element(NULL).check_empty() - extensions.append( - x509.Extension(oid, critical, x509.PrecertPoison()) - ) + data = backend._lib.X509_EXTENSION_get_data(ext) + data_bytes = _asn1_string_to_bytes(backend, data) + value = _rust.parse_precert_poison(data_bytes) + + extensions.append(x509.Extension(oid, critical, value)) seen_oids.add(oid) continue diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock index 8ceebf4264c5a..39a3b1af95c08 100644 --- a/src/rust/Cargo.lock +++ b/src/rust/Cargo.lock @@ -1,15 +1,40 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "asn1" +version = "0.3.0" +source = "git+https://github.com/alex/rust-asn1#fba6a567272e032e4776f477495d5d024b341235" +dependencies = [ + "chrono", +] + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "num-integer", + "num-traits", +] + [[package]] name = "cryptography-rust" version = "0.1.0" dependencies = [ + "asn1", "pyo3", ] @@ -89,6 +114,25 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + [[package]] name = "parking_lot" version = "0.11.1" diff --git a/src/rust/Cargo.toml b/src/rust/Cargo.toml index 1f80ca8a9e1c7..0516eb67d6805 100644 --- a/src/rust/Cargo.toml +++ b/src/rust/Cargo.toml @@ -7,6 +7,7 @@ publish = false [dependencies] pyo3 = { version = "0.13.0", features = ["extension-module"] } +asn1 = { git = "https://github.com/alex/rust-asn1" } [lib] name = "cryptography_rust" diff --git a/src/rust/src/lib.rs b/src/rust/src/lib.rs index f06ac5f021256..22b020a2d3331 100644 --- a/src/rust/src/lib.rs +++ b/src/rust/src/lib.rs @@ -2,7 +2,76 @@ // 2.0, and the BSD License. See the LICENSE file in the root of this repository // for complete details. +use pyo3::conversion::ToPyObject; + +enum PyAsn1Error { + Asn1(asn1::ParseError), + Py(pyo3::PyErr), +} + +impl From for PyAsn1Error { + fn from(e: asn1::ParseError) -> PyAsn1Error { + PyAsn1Error::Asn1(e) + } +} + +impl From for PyAsn1Error { + fn from(e: pyo3::PyErr) -> PyAsn1Error { + PyAsn1Error::Py(e) + } +} + +impl From for pyo3::PyErr { + fn from(e: PyAsn1Error) -> pyo3::PyErr { + match e { + PyAsn1Error::Asn1(asn1_error) => pyo3::exceptions::ValueError::py_err(format!( + "error parsing asn1 value: {:?}", + asn1_error + )), + PyAsn1Error::Py(py_error) => py_error, + } + } +} + +#[pyo3::prelude::pyfunction] +fn parse_tls_feature(py: pyo3::Python<'_>, data: &[u8]) -> pyo3::PyResult { + let tls_feature_type_to_enum = py + .import("cryptography.x509.extensions")? + .getattr("_TLS_FEATURE_TYPE_TO_ENUM")?; + + let features = asn1::parse::<_, PyAsn1Error, _>(data, |p| { + p.read_element::()?.parse(|p| { + let features = pyo3::types::PyList::empty(py); + while !p.is_empty() { + let feature = p.read_element::()?; + let py_feature = tls_feature_type_to_enum.get_item(feature.to_object(py))?; + features.append(py_feature)? + } + Ok(features) + }) + })?; + + let x509_module = py.import("cryptography.x509")?; + x509_module + .call1("TLSFeature", (features,)) + .map(|o| o.to_object(py)) +} + +#[pyo3::prelude::pyfunction] +fn parse_precert_poison(py: pyo3::Python<'_>, data: &[u8]) -> pyo3::PyResult { + asn1::parse::<_, PyAsn1Error, _>(data, |p| { + p.read_element::<()>()?; + Ok(()) + })?; + + let x509_module = py.import("cryptography.x509")?; + x509_module.call0("PrecertPoison").map(|o| o.to_object(py)) +} + #[pyo3::prelude::pymodule] -fn _rust(_py: pyo3::Python<'_>, _m: &pyo3::types::PyModule) -> pyo3::PyResult<()> { +fn _rust(_py: pyo3::Python<'_>, m: &pyo3::types::PyModule) -> pyo3::PyResult<()> { + m.add_wrapped(pyo3::wrap_pyfunction!(parse_tls_feature))?; + m.add_wrapped(pyo3::wrap_pyfunction!(parse_precert_poison))?; + Ok(()) }