Skip to content

Commit

Permalink
Port a tiny tiny bit of the ASN.1 parsing to Rust
Browse files Browse the repository at this point in the history
  • Loading branch information
alex committed Apr 11, 2021
1 parent 851877e commit 01b549d
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 17 deletions.
23 changes: 7 additions & 16 deletions src/cryptography/hazmat/backends/openssl/decode_asn1.py
Expand Up @@ -8,8 +8,7 @@
import typing

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._rust import asn1
from cryptography.x509.name import _ASN1_TYPE_TO_ENUM
from cryptography.x509.oid import (
CRLEntryExtensionOID,
Expand Down Expand Up @@ -211,25 +210,17 @@ def parse(self, x509_obj):
# 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]
)
value = asn1.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_bytes = _asn1_string_to_bytes(self._backend, data)
value = asn1.parse_precert_poison(data_bytes)

extensions.append(x509.Extension(oid, critical, value))
seen_oids.add(oid)
continue

Expand Down
47 changes: 47 additions & 0 deletions src/rust/Cargo.lock

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

1 change: 1 addition & 0 deletions src/rust/Cargo.toml
Expand Up @@ -7,6 +7,7 @@ publish = false

[dependencies]
pyo3 = { version = "0.13.1", features = ["extension-module"] }
asn1 = { version = "0.3", default-features = false }

[lib]
name = "cryptography_rust"
Expand Down
75 changes: 75 additions & 0 deletions src/rust/src/asn1.rs
@@ -0,0 +1,75 @@
// This file is dual licensed under the terms of the Apache License, Version
// 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<asn1::ParseError> for PyAsn1Error {
fn from(e: asn1::ParseError) -> PyAsn1Error {
PyAsn1Error::Asn1(e)
}
}

impl From<pyo3::PyErr> for PyAsn1Error {
fn from(e: pyo3::PyErr) -> PyAsn1Error {
PyAsn1Error::Py(e)
}
}

impl From<PyAsn1Error> for pyo3::PyErr {
fn from(e: PyAsn1Error) -> pyo3::PyErr {
match e {
PyAsn1Error::Asn1(asn1_error) => pyo3::exceptions::PyValueError::new_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<pyo3::PyObject> {
let tls_feature_type_to_enum = py
.import("cryptography.x509.extensions")?
.getattr("_TLS_FEATURE_TYPE_TO_ENUM")?;

let features = asn1::parse::<_, PyAsn1Error, _>(data, |p| {
let features = pyo3::types::PyList::empty(py);
for el in p.read_element::<asn1::SequenceOf<u64>>()? {
let feature = el?;
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<pyo3::PyObject> {
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))
}

pub(crate) fn create_submodule(py: pyo3::Python) -> pyo3::PyResult<&pyo3::prelude::PyModule> {
let submod = pyo3::prelude::PyModule::new(py, "asn1")?;
submod.add_wrapped(pyo3::wrap_pyfunction!(parse_tls_feature))?;
submod.add_wrapped(pyo3::wrap_pyfunction!(parse_precert_poison))?;

Ok(submod)
}
6 changes: 5 additions & 1 deletion src/rust/src/lib.rs
Expand Up @@ -2,6 +2,8 @@
// 2.0, and the BSD License. See the LICENSE file in the root of this repository
// for complete details.

mod asn1;

use std::convert::TryInto;

/// Returns the value of the input with the most-significant-bit copied to all
Expand Down Expand Up @@ -66,10 +68,12 @@ fn check_ansix923_padding(data: &[u8]) -> bool {
}

#[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_function(pyo3::wrap_pyfunction!(check_pkcs7_padding, m)?)?;
m.add_function(pyo3::wrap_pyfunction!(check_ansix923_padding, m)?)?;

m.add_submodule(asn1::create_submodule(py)?)?;

Ok(())
}

Expand Down

0 comments on commit 01b549d

Please sign in to comment.