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 7, 2020
1 parent dabc36d commit 4495275
Show file tree
Hide file tree
Showing 10 changed files with 141 additions and 52 deletions.
32 changes: 21 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
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,13 @@ jobs:
with:
python-version: ${{ matrix.PYTHON.VERSION }}

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

- run: python -m pip install tox requests coverage

- run: git clone https://github.com/google/wycheproof
Expand Down Expand Up @@ -57,14 +65,15 @@ jobs:
strategy:
matrix:
WINDOWS:
- {ARCH: 'x86', WINDOWS: 'win32'}
- {ARCH: 'x64', WINDOWS: 'win64'}
- {ARCH: 'x86', WINDOWS: 'win32', RUST_TRIPLE: 'i686-pc-windows-msvc'}
- {ARCH: 'x64', WINDOWS: 'win64', RUST_TRIPLE: 'x86_64-pc-windows-msvc'}
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 +83,14 @@ 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
default: true
target: ${{ matrix.WINDOWS.RUST_TRIPLE }}

- run: python -m pip install tox requests coverage
- name: Download OpenSSL
run: |
Expand Down
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
30 changes: 6 additions & 24 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,18 @@ branches:
matrix:
include:
- python: 3.8
env: TOXENV=pep8
env: TOXENV=pep8,rust
# Setting 'python' is just to make travis's UI a bit prettier
- python: 3.6
env: TOXENV=py36
# Travis lists available Pythons (including PyPy) by arch and distro here:
# https://docs.travis-ci.com/user/languages/python/#python-versions
- python: pypy2.7-7.3.1
env: TOXENV=pypy-nocoverage
- python: pypy3.6-7.3.1
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 +46,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 +70,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 Down Expand Up @@ -120,11 +100,11 @@ matrix:
env: DOWNSTREAM=pyopenssl
- 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
Expand All @@ -138,9 +118,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
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ include pyproject.toml

recursive-include docs *
recursive-include src/_cffi_src *.py *.c *.h
recursive-include src/rust Cargo.toml *.rs
prune docs/_build
recursive-include tests *.py
exclude vectors
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>=0.11.1",
]
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>=0.11.1"]

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("_rust", "src/rust/Cargo.toml"),
],
}


Expand Down
23 changes: 7 additions & 16 deletions src/cryptography/hazmat/backends/openssl/decode_asn1.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@
import six

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,
Expand Down Expand Up @@ -209,25 +208,17 @@ 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()
extensions.append(
x509.Extension(oid, critical, x509.PrecertPoison())
)
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

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 <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
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 _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(())
}
9 changes: 9 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,15 @@ commands =
flake8 .
black --check .

[testenv:rust]
basepython = python3
changedir = src/rust/
allowlist_externals =
cargo
commands =
cargo fmt --check --all
cargo clippy

[testenv:packaging]
deps =
check-manifest
Expand Down

0 comments on commit 4495275

Please sign in to comment.