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 Jul 30, 2020
1 parent 84a15eb commit 745c650
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 14 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ htmlcov/
.eggs/
*.py[cdo]
.hypothesis/
target/
Cargo.lock
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,11 @@ matrix:
dist: xenial

install:
- curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain nightly
- ./.travis/install.sh

script:
- source $HOME/.cargo/env
- ./.travis/run.sh

after_success:
Expand Down
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ requires = [
"wheel",
# Must be kept in sync with the `setup_requirements` in `setup.py`
"cffi>=1.8,!=1.11.3; platform_python_implementation != 'PyPy'",

"setuptools-rust",
]
build-backend = "setuptools.build_meta"

Expand Down
7 changes: 6 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
from setuptools import find_packages, setup
from setuptools.command.install import install

from setuptools_rust import RustExtension


if pkg_resources.parse_version(
setuptools.__version__
Expand All @@ -39,7 +41,7 @@


# `setup_requirements` must be kept in sync with `pyproject.toml`
setup_requirements = ["cffi>=1.8,!=1.11.3"]
setup_requirements = ["cffi>=1.8,!=1.11.3", "setuptools-rust"]

if platform.python_implementation() == "PyPy":
if sys.pypy_version_info < (5, 4):
Expand Down Expand Up @@ -156,6 +158,9 @@ def argument_without_setup_requirements(argv, i):
return {
"setup_requires": setup_requirements,
"cffi_modules": cffi_modules,
"rust_extensions": [
RustExtension("cryptography._rust", "src/rust/Cargo.toml"),
],
}


Expand Down
20 changes: 7 additions & 13 deletions src/cryptography/hazmat/backends/openssl/decode_asn1.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

import six

from cryptography import x509
from cryptography import x509, _rust
from cryptography.hazmat._der import DERReader, INTEGER, NULL, SEQUENCE
from cryptography.x509.extensions import _TLS_FEATURE_TYPE_TO_ENUM
from cryptography.x509.name import _ASN1_TYPE_TO_ENUM
Expand Down Expand Up @@ -209,24 +209,18 @@ def parse(self, backend, x509_obj):
# The extension contents are a SEQUENCE OF INTEGERs.
data = backend._lib.X509_EXTENSION_get_data(ext)
data_bytes = _asn1_string_to_bytes(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 = _rust.parse_tls_feature(data_bytes)

extensions.append(x509.Extension(oid, critical, value))
seen_oids.add(oid)
continue
elif oid == ExtensionOID.PRECERT_POISON:
data = backend._lib.X509_EXTENSION_get_data(ext)
# The contents of the extension must be an ASN.1 NULL.
reader = DERReader(_asn1_string_to_bytes(backend, data))
reader.read_single_element(NULL).check_empty()
data_bytes = _asn1_string_to_bytes(backend, data)
value = _rust.parse_precert_poison(data_bytes)

extensions.append(
x509.Extension(oid, critical, x509.PrecertPoison())
x509.Extension(oid, critical, value)
)
seen_oids.add(oid)
continue
Expand Down
14 changes: 14 additions & 0 deletions src/rust/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "cryptography-rust"
version = "0.1.0"
authors = ["The cryptography developers <[email protected]>"]
edition = "2018"
publish = false

[dependencies]
asn1 = { git = "https://github.com/alex/rust-asn1" }
pyo3 = { version = "0.11", features = ["extension-module"] }

[lib]
name = "cryptography_rust"
crate-type = ["cdylib"]
73 changes: 73 additions & 0 deletions src/rust/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
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::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<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| {
p.read_element::<asn1::Sequence>()?.parse(|p| {
let features = pyo3::types::PyList::empty(py);
while !p.is_empty() {
let feature = p.read_element::<u64>()?;
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))
}

#[pyo3::prelude::pymodule]
fn cryptography_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(())
}

0 comments on commit 745c650

Please sign in to comment.