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 Aug 4, 2020
1 parent 143f56f commit 96262c7
Show file tree
Hide file tree
Showing 9 changed files with 130 additions and 49 deletions.
25 changes: 16 additions & 9 deletions .github/workflows/ci.yml
Expand Up @@ -15,11 +15,12 @@ jobs:
strategy:
matrix:
PYTHON:
- {VERSION: "2.7", TOXENV: "py27", EXTRA_CFLAGS: ""}
- {VERSION: "3.5", TOXENV: "py35", EXTRA_CFLAGS: ""}
- {VERSION: "3.6", TOXENV: "py36", EXTRA_CFLAGS: ""}
- {VERSION: "3.7", TOXENV: "py37", EXTRA_CFLAGS: ""}
- {VERSION: "3.8", TOXENV: "py38", EXTRA_CFLAGS: "-DUSE_OSRANDOM_RNG_FOR_TESTING"}
RUST:
- nightly
name: "Python ${{ matrix.PYTHON.VERSION }} on macOS"
steps:
- uses: actions/checkout@master
Expand All @@ -28,6 +29,12 @@ jobs:
with:
python-version: ${{ matrix.PYTHON.VERSION }}

- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ matrix.RUST }}
override: true

- run: python -m pip install tox requests coverage

- run: git clone https://github.com/google/wycheproof
Expand Down Expand Up @@ -60,11 +67,12 @@ jobs:
- {ARCH: 'x86', WINDOWS: 'win32'}
- {ARCH: 'x64', WINDOWS: 'win64'}
PYTHON:
- {VERSION: "2.7", TOXENV: "py27", MSVC_VERSION: "2010", CL_FLAGS: ""}
- {VERSION: "3.5", TOXENV: "py35", MSVC_VERSION: "2019", CL_FLAGS: ""}
- {VERSION: "3.6", TOXENV: "py36", MSVC_VERSION: "2019", CL_FLAGS: ""}
- {VERSION: "3.7", TOXENV: "py37", MSVC_VERSION: "2019", CL_FLAGS: ""}
- {VERSION: "3.8", TOXENV: "py38", MSVC_VERSION: "2019", CL_FLAGS: "/D USE_OSRANDOM_RNG_FOR_TESTING"}
RUST:
- nightly
name: "Python ${{ matrix.PYTHON.VERSION }} on ${{ matrix.WINDOWS.WINDOWS }}"
steps:
- uses: actions/checkout@master
Expand All @@ -74,13 +82,12 @@ jobs:
python-version: ${{ matrix.PYTHON.VERSION }}
architecture: ${{ matrix.WINDOWS.ARCH }}

- name: Install MSVC for Python 2.7
run: |
Invoke-WebRequest -Uri https://download.microsoft.com/download/7/9/6/796EF2E4-801B-4FC4-AB28-B59FBF6D907B/VCForPython27.msi -OutFile VCForPython27.msi
Start-Process msiexec -Wait -ArgumentList @('/i', 'VCForPython27.msi', '/qn', 'ALLUSERS=1')
Remove-Item VCForPython27.msi -Force
shell: powershell
if: matrix.PYTHON.VERSION == '2.7'
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ matrix.RUST }}
override: true

- run: python -m pip install tox requests coverage
- name: Download OpenSSL
run: |
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Expand Up @@ -12,3 +12,5 @@ htmlcov/
.eggs/
*.py[cdo]
.hypothesis/
target/
Cargo.lock
34 changes: 9 additions & 25 deletions .travis.yml
Expand Up @@ -31,14 +31,8 @@ matrix:
env: TOXENV=pypy3-nocoverage
- python: 3.8
env: TOXENV=py38 OPENSSL=1.0.2u
- python: 2.7
env: TOXENV=py27 OPENSSL=1.1.0l
- python: 2.7
env: TOXENV=py27-ssh OPENSSL=1.1.0l
- python: 3.8
env: TOXENV=py38 OPENSSL=1.1.0l
- python: 2.7
env: TOXENV=py27 OPENSSL=1.1.1g
- python: 3.8
env: TOXENV=py38 OPENSSL=1.1.1g
- python: 3.8
Expand All @@ -54,21 +48,12 @@ matrix:
- python: 3.8
env: TOXENV=py38 LIBRESSL=3.2.0

- python: 2.7
services: docker
env: TOXENV=py27 DOCKER=pyca/cryptography-runner-centos7
- python: 2.7
services: docker
env: TOXENV=py27 DOCKER=pyca/cryptography-runner-centos8
- python: 3.6
services: docker
env: TOXENV=py36 DOCKER=pyca/cryptography-runner-centos8
- python: 3.6
services: docker
env: TOXENV=py36 OPENSSL_FORCE_FIPS_MODE=1 DOCKER=pyca/cryptography-runner-centos8-fips
- python: 2.7
services: docker
env: TOXENV=py27 DOCKER=pyca/cryptography-runner-stretch
- python: 3.5
services: docker
env: TOXENV=py35 DOCKER=pyca/cryptography-runner-stretch
Expand All @@ -87,9 +72,6 @@ matrix:
- python: 3.8
services: docker
env: TOXENV=py38 DOCKER=pyca/cryptography-runner-ubuntu-focal
- python: 2.7
services: docker
env: TOXENV=py27 DOCKER=pyca/cryptography-runner-ubuntu-rolling
- python: 3.8
services: docker
env: TOXENV=py38 DOCKER=pyca/cryptography-runner-ubuntu-rolling
Expand All @@ -109,7 +91,7 @@ matrix:
apt:
packages:
- libenchant-dev
- python: 2.7
- python: 3.8
services: docker
env: TOXENV=docs-linkcheck DOCKER=pyca/cryptography-runner-buster
if: (branch = master AND type != pull_request) OR commit_message =~ /linkcheck/
Expand All @@ -122,27 +104,29 @@ matrix:
dist: bionic
- python: 3.7
env: DOWNSTREAM=twisted OPENSSL=1.1.1g
- python: 2.7
- python: 3.7
env: DOWNSTREAM=paramiko
- python: 2.7
- python: 3.7
env: DOWNSTREAM=aws-encryption-sdk
- python: 2.7
- python: 3.7
# BOTO_CONFIG works around this boto issue on travis:
# https://github.com/boto/boto/issues/3717
env: DOWNSTREAM=dynamodb-encryption-sdk BOTO_CONFIG=/dev/null
- python: 2.7
- python: 3.7
env: DOWNSTREAM=certbot
- python: 2.7
- python: 3.7
env: DOWNSTREAM=certbot-josepy
- python: 2.7
- python: 3.7
env: DOWNSTREAM=urllib3
# Tests hang when run under bionic/focal
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
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
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
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
@@ -0,0 +1,14 @@
[package]
name = "cryptography-rust"
version = "0.1.0"
authors = ["The cryptography developers <cryptography-dev@python.org>"]
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
@@ -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(())
}
2 changes: 1 addition & 1 deletion tox.ini
Expand Up @@ -55,7 +55,7 @@ commands =
[testenv:docs-linkcheck]
extras =
docs
basepython = python2.7
basepython = python3
commands =
sphinx-build -W -b linkcheck docs docs/_build/html

Expand Down

0 comments on commit 96262c7

Please sign in to comment.