Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resurrect the PoC of OpenSSL from Rust #7164

Merged
merged 1 commit into from Mar 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
16 changes: 8 additions & 8 deletions .github/workflows/ci.yml
Expand Up @@ -98,8 +98,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 @@ -318,7 +319,7 @@ jobs:
- name: Clone wycheproof
timeout-minutes: 2
uses: ./.github/actions/wycheproof
- 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 @@ -419,9 +420,9 @@ jobs:
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: Build toxenv
run: |
CRYPTOGRAPHY_SUPPRESS_LINK_FLAGS=1 \
LDFLAGS="$(readlink -f ../openssl-macos-universal2/lib/libcrypto.a) $(readlink -f ../openssl-macos-universal2/lib/libssl.a)" \
CFLAGS="-I$(readlink -f ../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=$(readlink -f ../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 @@ -481,8 +482,7 @@ jobs:
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: Configure
run: |
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
shell: bash

Expand Down
13 changes: 6 additions & 7 deletions .github/workflows/wheel-builder.yml
Expand Up @@ -113,8 +113,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="../../openssl-macos-universal2/lib/libcrypto.a ../../openssl-macos-universal2/lib/libssl.a" \
CFLAGS="-I../../openssl-macos-universal2/include" \
OPENSSL_DIR="$(readlink -f ../../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 @@ -286,8 +285,8 @@ jobs:
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: Configure OpenSSL
run: |
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
reaperhulk marked this conversation as resolved.
Show resolved Hide resolved
shell: bash

- run: python -m pip install -U pip wheel
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:** As announced in the 39.0.0 changelog, 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 valid. 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 @@ -25,6 +32,7 @@ Changelog
:class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPublicKey`
and
:class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPrivateKey`.
* The minimum supported version of PyPy3 is now 7.3.10.
* Added support for parsing SSH certificates in addition to public keys with
:func:`~cryptography.hazmat.primitives.serialization.load_ssh_public_identity`.
:func:`~cryptography.hazmat.primitives.serialization.load_ssh_public_key`
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-cache-dir --no-binary cryptography cryptography
OPENSSL_DIR="${CWD}/openssl" pip wheel --no-cache-dir --no-binary cryptography 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")
reaperhulk marked this conversation as resolved.
Show resolved Hide resolved
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 @@ -12,7 +12,7 @@
import cryptography
from cryptography import utils
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 @@ -65,9 +65,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 @@ -137,7 +137,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 @@ -179,7 +179,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 @@ -217,7 +219,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 @@ -229,6 +233,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