Skip to content

Commit

Permalink
Ressurect the PoC of OpenSSL from Rust
Browse files Browse the repository at this point in the history
  • Loading branch information
alex committed Feb 7, 2023
1 parent 75e3d16 commit de715aa
Show file tree
Hide file tree
Showing 17 changed files with 370 additions and 60 deletions.
16 changes: 8 additions & 8 deletions .github/workflows/ci.yml
Expand Up @@ -106,8 +106,9 @@ jobs:
if: matrix.PYTHON.OPENSSL && steps.ossl-cache.outputs.cache-hit != 'true'
- name: Set CFLAGS/LDFLAGS
run: |
echo "CFLAGS=${CFLAGS} -Werror=implicit-function-declaration -I${OSSL_PATH}/include" >> $GITHUB_ENV
echo "LDFLAGS=${LDFLAGS} -L${OSSL_PATH}/lib -L${OSSL_PATH}/lib64 -Wl,-rpath=${OSSL_PATH}/lib -Wl,-rpath=${OSSL_PATH}/lib64" >> $GITHUB_ENV
echo "OPENSSL_DIR=${OSSL_PATH}" >> $GITHUB_ENV
echo "CFLAGS=${CFLAGS} -Werror=implicit-function-declaration" >> $GITHUB_ENV
echo "RUSTFLAGS=-Clink-arg=-Wl,-rpath=${OSSL_PATH}/lib -Clink-arg=-Wl,-rpath=${OSSL_PATH}/lib64" >> $GITHUB_ENV
if: matrix.PYTHON.OPENSSL
- name: Build toxenv
run: |
Expand Down Expand Up @@ -320,7 +321,7 @@ jobs:
repository: "google/wycheproof"
path: "wycheproof"
ref: "master"
- run: python -m pip install -c ci-constraints-requirements.txt 'tox>3' coverage[toml]
- run: python -m pip install -c ci-constraints-requirements.txt 'tox>3' coverage[toml] cffi
- name: Create toxenv
run: tox -vvv --notest
env:
Expand Down Expand Up @@ -423,9 +424,9 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build toxenv
run: |
CRYPTOGRAPHY_SUPPRESS_LINK_FLAGS=1 \
LDFLAGS="${HOME}/openssl-macos-universal2/lib/libcrypto.a ${HOME}/openssl-macos-universal2/lib/libssl.a" \
CFLAGS="-I${HOME}/openssl-macos-universal2/include -Werror -Wno-error=deprecated-declarations -Wno-error=incompatible-pointer-types-discards-qualifiers -Wno-error=unused-function -mmacosx-version-min=10.12 $EXTRA_CFLAGS" \
OPENSSL_DIR="${HOME}/openssl-macos-universal2" \
OPENSSL_STATIC=1 \
CFLAGS="-Werror -Wno-error=deprecated-declarations -Wno-error=incompatible-pointer-types-discards-qualifiers -Wno-error=unused-function -mmacosx-version-min=10.12 $EXTRA_CFLAGS" \
tox -vvv --notest
env:
TOXENV: ${{ matrix.PYTHON.TOXENV }}
Expand Down Expand Up @@ -480,8 +481,7 @@ jobs:
- name: Download OpenSSL
run: |
python .github/workflows/download_openssl.py windows openssl-${{ matrix.WINDOWS.WINDOWS }}
echo "INCLUDE=C:/openssl-${{ matrix.WINDOWS.WINDOWS }}/include;$INCLUDE" >> $GITHUB_ENV
echo "LIB=C:/openssl-${{ matrix.WINDOWS.WINDOWS }}/lib;$LIB" >> $GITHUB_ENV
echo "OPENSSL_DIR=C:/openssl-${{ matrix.WINDOWS.WINDOWS }}" >> $GITHUB_ENV
echo "CL=${{ matrix.PYTHON.CL_FLAGS }}" >> $GITHUB_ENV
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand Down
13 changes: 6 additions & 7 deletions .github/workflows/wheel-builder.yml
Expand Up @@ -110,8 +110,8 @@ jobs:
PY_LIMITED_API="--py-limited-api=${{ matrix.PYTHON.ABI_VERSION }}"
fi
cd cryptography*
LDFLAGS="-L/opt/pyca/cryptography/openssl/lib -L/opt/pyca/cryptography/openssl/lib64" \
CFLAGS="-I/opt/pyca/cryptography/openssl/include -Wl,--exclude-libs,ALL" \
OPENSSL_DIR="/opt/pyca/cryptography/openssl" \
OPENSSL_STATIC=1 \
../.venv/bin/python setup.py bdist_wheel $PY_LIMITED_API && mv dist/cryptography*.whl ../tmpwheelhouse
env:
RUSTUP_HOME: /root/.rustup
Expand Down Expand Up @@ -216,9 +216,8 @@ jobs:
- name: Build the wheel
run: |
cd cryptography*
CRYPTOGRAPHY_SUPPRESS_LINK_FLAGS="1" \
LDFLAGS="${HOME}/openssl-macos-universal2/lib/libcrypto.a ${HOME}/openssl-macos-universal2/lib/libssl.a" \
CFLAGS="-I${HOME}/openssl-macos-universal2/include" \
OPENSSL_DIR="${HOME}/openssl-macos-universal2" \
OPENSSL_STATIC=1 \
../venv/bin/python setup.py bdist_wheel --py-limited-api=${{ matrix.PYTHON.ABI_VERSION }} && mv dist/cryptography*.whl ../wheelhouse
env:
MACOSX_DEPLOYMENT_TARGET: ${{ matrix.PYTHON.DEPLOYMENT_TARGET }}
Expand Down Expand Up @@ -285,8 +284,8 @@ jobs:
- name: Download OpenSSL
run: |
python .github/workflows/download_openssl.py windows openssl-${{ matrix.WINDOWS.WINDOWS }}
echo "INCLUDE=C:/openssl-${{ matrix.WINDOWS.WINDOWS }}/include;$INCLUDE" >> $GITHUB_ENV
echo "LIB=C:/openssl-${{ matrix.WINDOWS.WINDOWS }}/lib;$LIB" >> $GITHUB_ENV
echo "OPENSSL_DIR=C:/openssl-${{ matrix.WINDOWS.WINDOWS }}" >> $GITHUB_ENV
echo "OPENSSL_STATIC=1" >> $GITHUB_ENV
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.rst
Expand Up @@ -8,6 +8,13 @@ Changelog

.. note:: This version is not yet released and is under active development.

* **BACKWARDS INCOMPATIBLE:** The way ``cryptography`` links OpenSSL has
changed. This only impacts users who build ``cryptography`` from source
(i.e., not from a ``wheel``), and specify their own version of OpenSSL. For
those users, the ``CFLAGS``, ``LDFLAGS``, ``INCLUDE``, ``LIB``, and
``CRYPTOGRAPHY_SUPPRESS_LINK_FLAGS`` environment variables are no longer be
respected. Instead, users need to configure their builds
`as documented here`_.
* Support for Python 3.6 is deprecated and will be removed in the next
release.
* Deprecated the current minimum supported Rust version (MSRV) of 1.48.0.
Expand All @@ -31,6 +38,7 @@ Changelog
:class:`~cryptography.hazmat.primitives.serialization.SSHCertificateBuilder`.
* Added :meth:`~cryptography.x509.Certificate.verify_directly_issued_by` to
:class:`~cryptography.x509.Certificate`.
* The minimum supported version of PyPy3 is now 7.3.10.

.. _v39-0-1:

Expand Down
2 changes: 1 addition & 1 deletion README.rst
Expand Up @@ -15,7 +15,7 @@ pyca/cryptography

``cryptography`` is a package which provides cryptographic recipes and
primitives to Python developers. Our goal is for it to be your "cryptographic
standard library". It supports Python 3.6+ and PyPy3 7.2+.
standard library". It supports Python 3.6+ and PyPy3 7.3.10+.

``cryptography`` includes both high level recipes and low level interfaces to
common cryptographic algorithms such as symmetric ciphers, message digests, and
Expand Down
23 changes: 12 additions & 11 deletions docs/installation.rst
Expand Up @@ -13,7 +13,7 @@ single most common cause of installation problems.
Supported platforms
-------------------

Currently we test ``cryptography`` on Python 3.6+ and PyPy3 on these
Currently we test ``cryptography`` on Python 3.6+ and PyPy3 7.3.10+ on these
operating systems.

* x86-64 RHEL 8.x
Expand Down Expand Up @@ -55,14 +55,13 @@ If you prefer to compile it yourself you'll need to have OpenSSL installed.
You can compile OpenSSL yourself as well or use `a binary distribution`_.
Be sure to download the proper version for your architecture and Python
(VC2015 is required for 3.6 and above). Wherever you place your copy of OpenSSL
you'll need to set the ``LIB`` and ``INCLUDE`` environment variables to include
the proper locations. For example:
you'll need to set the ``OPENSSL_DIR`` environment variable to include the
proper location. For example:

.. code-block:: console
C:\> \path\to\vcvarsall.bat x86_amd64
C:\> set LIB=C:\OpenSSL-win64\lib;%LIB%
C:\> set INCLUDE=C:\OpenSSL-win64\include;%INCLUDE%
C:\> set OPENSSL_DIR=C:\OpenSSL-win64
C:\> pip install cryptography
You will also need to have :ref:`Rust installed and
Expand Down Expand Up @@ -227,7 +226,7 @@ dependencies.
./config no-shared no-ssl2 no-ssl3 -fPIC --prefix=${CWD}/openssl
make && make install
cd ..
CFLAGS="-I${CWD}/openssl/include" LDFLAGS="-L${CWD}/openssl/lib" pip wheel --no-binary :all: cryptography
OPENSSL_DIR="${CWD}/openssl" pip wheel --no-binary :all: cryptography
Building cryptography on macOS
------------------------------
Expand Down Expand Up @@ -259,7 +258,9 @@ development headers.

You will also need to have :ref:`Rust installed and
available<installation:Rust>`, which can be obtained from `Homebrew`_,
`MacPorts`_, or directly from the Rust website.
`MacPorts`_, or directly from the Rust website. If you are linking against a
``universal2`` archive of OpenSSL, the minimum supported Rust version is
1.66.0.

Finally you need OpenSSL, which you can obtain from `Homebrew`_ or `MacPorts`_.
Cryptography does **not** support the OpenSSL/LibreSSL libraries Apple ships
Expand All @@ -272,14 +273,14 @@ To build cryptography and dynamically link it:
.. code-block:: console
$ brew install openssl@3 rust
$ env LDFLAGS="-L$(brew --prefix openssl@3)/lib" CFLAGS="-I$(brew --prefix openssl@3)/include" pip install cryptography
$ env OPENSSL_DIR="$(brew --prefix openssl@3)" pip install cryptography
`MacPorts`_:

.. code-block:: console
$ sudo port install openssl rust
$ env LDFLAGS="-L/opt/local/lib" CFLAGS="-I/opt/local/include" pip install cryptography
$ env OPENSSL_DIR="-L/opt/local" pip install cryptography
You can also build cryptography statically:

Expand All @@ -288,14 +289,14 @@ You can also build cryptography statically:
.. code-block:: console
$ brew install openssl@3 rust
$ env CRYPTOGRAPHY_SUPPRESS_LINK_FLAGS=1 LDFLAGS="$(brew --prefix openssl@3)/lib/libssl.a $(brew --prefix openssl@3)/lib/libcrypto.a" CFLAGS="-I$(brew --prefix openssl@3)/include" pip install cryptography
$ env OPENSSL_STATIC=1 OPENSSL_DIR="$(brew --prefix openssl@3)" pip install cryptography
`MacPorts`_:

.. code-block:: console
$ sudo port install openssl rust
$ env CRYPTOGRAPHY_SUPPRESS_LINK_FLAGS=1 LDFLAGS="/opt/local/lib/libssl.a /opt/local/lib/libcrypto.a" CFLAGS="-I/opt/local/include" pip install cryptography
$ env OPENSSL_STATIC=1 OPENSSL_DIR="/opt/local" pip install cryptography
If you need to rebuild ``cryptography`` for any reason be sure to clear the
local `wheel cache`_.
Expand Down
3 changes: 0 additions & 3 deletions setup.py
Expand Up @@ -46,9 +46,6 @@
try:
# See setup.cfg for most of the config metadata.
setup(
cffi_modules=[
"src/_cffi_src/build_openssl.py:ffi",
],
rust_extensions=[
RustExtension(
"cryptography.hazmat.bindings._rust",
Expand Down
17 changes: 16 additions & 1 deletion src/_cffi_src/build_openssl.py
Expand Up @@ -4,6 +4,7 @@


import os
import platform
import sys
from distutils import dist
from distutils.ccompiler import get_default_compiler
Expand Down Expand Up @@ -70,7 +71,7 @@ def _extra_compile_args(platform):


ffi = build_ffi_for_binding(
module_name="cryptography.hazmat.bindings._openssl",
module_name="_openssl",
module_prefix="_cffi_src.openssl.",
modules=[
# This goes first so we can define some cryptography-wide symbols.
Expand Down Expand Up @@ -110,3 +111,17 @@ def _extra_compile_args(platform):
libraries=_get_openssl_libraries(sys.platform),
extra_compile_args=_extra_compile_args(sys.platform),
)

if __name__ == "__main__":
out_dir = os.getenv("OUT_DIR")
module_name, source, source_extension, kwds = ffi._assigned_source
c_file = os.path.join(out_dir, module_name + source_extension)
if platform.python_implementation() == "PyPy":
# Necessary because CFFI will ignore this if there's no declarations.
ffi.embedding_api(
"""
extern "Python" void Cryptography_unused(void);
"""
)
ffi.embedding_init_code("")
ffi.emit_c_code(c_file)
13 changes: 13 additions & 0 deletions src/_cffi_src/utils.py
Expand Up @@ -4,6 +4,7 @@


import os
import platform
import sys
from distutils.ccompiler import new_compiler
from distutils.dist import Distribution
Expand Down Expand Up @@ -70,6 +71,18 @@ def build_ffi(
verify_source += '\n#define CRYPTOGRAPHY_PACKAGE_VERSION "{}"'.format(
about["__version__"]
)
if platform.python_implementation() == "PyPy":
verify_source += r"""
int Cryptography_make_openssl_module(void) {
int result;
Py_BEGIN_ALLOW_THREADS
result = cffi_start_python();
Py_END_ALLOW_THREADS
return result;
}
"""
ffi.cdef(cdef_source)
ffi.set_source(
module_name,
Expand Down
5 changes: 5 additions & 0 deletions src/cryptography/hazmat/bindings/_rust/openssl.pyi
@@ -0,0 +1,5 @@
# 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.

def openssl_version() -> int: ...
23 changes: 16 additions & 7 deletions src/cryptography/hazmat/bindings/openssl/binding.py
Expand Up @@ -11,7 +11,7 @@

import cryptography
from cryptography.exceptions import InternalError
from cryptography.hazmat.bindings._openssl import ffi, lib
from cryptography.hazmat.bindings._rust import _openssl, openssl
from cryptography.hazmat.bindings.openssl._conditional import CONDITIONAL_NAMES


Expand Down Expand Up @@ -64,9 +64,9 @@ def _errors_with_text(
) -> typing.List[_OpenSSLErrorWithText]:
errors_with_text = []
for err in errors:
buf = ffi.new("char[]", 256)
lib.ERR_error_string_n(err.code, buf, len(buf))
err_text_reason: bytes = ffi.string(buf)
buf = _openssl.ffi.new("char[]", 256)
_openssl.lib.ERR_error_string_n(err.code, buf, len(buf))
err_text_reason: bytes = _openssl.ffi.string(buf)

errors_with_text.append(
_OpenSSLErrorWithText(
Expand Down Expand Up @@ -136,7 +136,7 @@ class Binding:
"""

lib: typing.ClassVar = None
ffi = ffi
ffi = _openssl.ffi
_lib_loaded = False
_init_lock = threading.Lock()
_legacy_provider: typing.Any = ffi.NULL
Expand Down Expand Up @@ -178,7 +178,9 @@ def _register_osrandom_engine(cls) -> None:
def _ensure_ffi_initialized(cls) -> None:
with cls._init_lock:
if not cls._lib_loaded:
cls.lib = build_conditional_library(lib, CONDITIONAL_NAMES)
cls.lib = build_conditional_library(
_openssl.lib, CONDITIONAL_NAMES
)
cls._lib_loaded = True
cls._register_osrandom_engine()
# As of OpenSSL 3.0.0 we must register a legacy cipher provider
Expand Down Expand Up @@ -216,7 +218,9 @@ def _verify_package_version(version: str) -> None:
# up later this code checks that the currently imported package and the
# shared object that were loaded have the same version and raise an
# ImportError if they do not
so_package_version = ffi.string(lib.CRYPTOGRAPHY_PACKAGE_VERSION)
so_package_version = _openssl.ffi.string(
_openssl.lib.CRYPTOGRAPHY_PACKAGE_VERSION
)
if version.encode("ascii") != so_package_version:
raise ImportError(
"The version of cryptography does not match the loaded "
Expand All @@ -228,6 +232,11 @@ def _verify_package_version(version: str) -> None:
)
)

_openssl_assert(
_openssl.lib,
_openssl.lib.OpenSSL_version_num() == openssl.openssl_version(),
)


_verify_package_version(cryptography.__version__)

Expand Down

0 comments on commit de715aa

Please sign in to comment.