diff --git a/.github/actions/upload-coverage/action.yml b/.github/actions/upload-coverage/action.yml new file mode 100644 index 000000000000..11d97a7a614d --- /dev/null +++ b/.github/actions/upload-coverage/action.yml @@ -0,0 +1,19 @@ +name: Upload Coverage +description: Upload coverage to codecov + +inputs: + name: + description: "Job name" + required: true + +runs: + using: "composite" + + steps: + - run: | + curl -o codecov.sh -f https://codecov.io/bash || \ + curl -o codecov.sh -f https://codecov.io/bash || \ + curl -o codecov.sh -f https://codecov.io/bash + + bash codecov.sh -n "${{ inputs.name }}" + shell: bash diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000000..123014908beb --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" diff --git a/.travis/downstream.d/aws-encryption-sdk.sh b/.github/downstream.d/aws-encryption-sdk.sh similarity index 100% rename from .travis/downstream.d/aws-encryption-sdk.sh rename to .github/downstream.d/aws-encryption-sdk.sh diff --git a/.travis/downstream.d/certbot-josepy.sh b/.github/downstream.d/certbot-josepy.sh similarity index 100% rename from .travis/downstream.d/certbot-josepy.sh rename to .github/downstream.d/certbot-josepy.sh diff --git a/.travis/downstream.d/certbot.sh b/.github/downstream.d/certbot.sh similarity index 100% rename from .travis/downstream.d/certbot.sh rename to .github/downstream.d/certbot.sh diff --git a/.travis/downstream.d/dynamodb-encryption-sdk.sh b/.github/downstream.d/dynamodb-encryption-sdk.sh similarity index 100% rename from .travis/downstream.d/dynamodb-encryption-sdk.sh rename to .github/downstream.d/dynamodb-encryption-sdk.sh diff --git a/.travis/downstream.d/paramiko.sh b/.github/downstream.d/paramiko.sh similarity index 100% rename from .travis/downstream.d/paramiko.sh rename to .github/downstream.d/paramiko.sh diff --git a/.travis/downstream.d/pyopenssl.sh b/.github/downstream.d/pyopenssl.sh similarity index 100% rename from .travis/downstream.d/pyopenssl.sh rename to .github/downstream.d/pyopenssl.sh diff --git a/.travis/downstream.d/twisted.sh b/.github/downstream.d/twisted.sh similarity index 87% rename from .travis/downstream.d/twisted.sh rename to .github/downstream.d/twisted.sh index 3d45413bbe21..522e763ec3b7 100755 --- a/.travis/downstream.d/twisted.sh +++ b/.github/downstream.d/twisted.sh @@ -5,7 +5,7 @@ case "${1}" in git clone --depth=1 https://github.com/twisted/twisted cd twisted git rev-parse HEAD - pip install ".[tls,conch,http2]" + pip install ".[all_non_platform]" ;; run) cd twisted diff --git a/.github/workflows/build_openssl.sh b/.github/workflows/build_openssl.sh new file mode 100755 index 000000000000..99c3f4d33805 --- /dev/null +++ b/.github/workflows/build_openssl.sh @@ -0,0 +1,34 @@ +#!/bin/bash +set -e +set -x + +shlib_sed() { + # modify the shlib version to a unique one to make sure the dynamic + # linker doesn't load the system one. + sed -i "s/^SHLIB_MAJOR=.*/SHLIB_MAJOR=100/" Makefile + sed -i "s/^SHLIB_MINOR=.*/SHLIB_MINOR=0.0/" Makefile + sed -i "s/^SHLIB_VERSION_NUMBER=.*/SHLIB_VERSION_NUMBER=100.0.0/" Makefile +} + +if [[ "${TYPE}" == "openssl" ]]; then + curl -O "https://www.openssl.org/source/openssl-${VERSION}.tar.gz" + tar zxf "openssl-${VERSION}.tar.gz" + pushd "openssl-${VERSION}" + # CONFIG_FLAGS is a global coming from a previous step + ./config ${CONFIG_FLAGS} -fPIC --prefix="${OSSL_PATH}" + shlib_sed + make depend + make -j"$(nproc)" + # avoid installing the docs (for performance) + # https://github.com/openssl/openssl/issues/6685#issuecomment-403838728 + make install_sw install_ssldirs + popd +elif [[ "${TYPE}" == "libressl" ]]; then + curl -O "https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-${VERSION}.tar.gz" + tar zxf "libressl-${VERSION}.tar.gz" + pushd "libressl-${VERSION}" + ./config -Wl -Wl,-Bsymbolic-functions -fPIC shared --prefix="${OSSL_PATH}" + shlib_sed + make -j"$(nproc)" install + popd +fi diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f3f514c1c5ba..3cc8433610f9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,19 +10,124 @@ on: - '*.*.*' jobs: + linux: + runs-on: ubuntu-latest + strategy: + matrix: + PYTHON: + - {VERSION: "3.9", TOXENV: "pep8,packaging,docs", COVERAGE: "false"} + - {VERSION: "pypy2", TOXENV: "pypy-nocoverage", COVERAGE: "false"} + - {VERSION: "pypy3", TOXENV: "pypy3-nocoverage", COVERAGE: "false"} + - {VERSION: "2.7", TOXENV: "py27", OPENSSL: {TYPE: "openssl", VERSION: "1.1.0l"}} + - {VERSION: "2.7", TOXENV: "py27-ssh", OPENSSL: {TYPE: "openssl", VERSION: "1.1.0l"}} + - {VERSION: "3.9", TOXENV: "py39", OPENSSL: {TYPE: "openssl", VERSION: "1.1.0l"}} + - {VERSION: "2.7", TOXENV: "py27", OPENSSL: {TYPE: "openssl", VERSION: "1.1.1h"}} + - {VERSION: "3.9", TOXENV: "py39", OPENSSL: {TYPE: "openssl", VERSION: "1.1.1h"}} + - {VERSION: "3.9", TOXENV: "py39-ssh", OPENSSL: {TYPE: "openssl", VERSION: "1.1.1h"}} + - {VERSION: "3.9", TOXENV: "py39", OPENSSL: {TYPE: "openssl", VERSION: "1.1.1h", CONFIG_FLAGS: "no-engine no-rc2 no-srtp no-ct"}} + - {VERSION: "3.9", TOXENV: "py39", OPENSSL: {TYPE: "libressl", VERSION: "2.9.2"}} + - {VERSION: "3.9", TOXENV: "py39", OPENSSL: {TYPE: "libressl", VERSION: "3.0.2"}} + - {VERSION: "3.9", TOXENV: "py39", OPENSSL: {TYPE: "libressl", VERSION: "3.1.4"}} + - {VERSION: "3.9", TOXENV: "py39", OPENSSL: {TYPE: "libressl", VERSION: "3.2.2"}} + name: "${{ matrix.PYTHON.TOXENV }} ${{ matrix.PYTHON.OPENSSL.TYPE }} ${{ matrix.PYTHON.OPENSSL.VERSION }} ${{ matrix.PYTHON.OPENSSL.CONFIG_FLAGS }}" + steps: + - uses: actions/checkout@v2 + - name: Setup python + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.PYTHON.VERSION }} + - run: git clone --depth=1 https://github.com/google/wycheproof + - run: python -m pip install tox requests coverage + - name: Compute config hash and set config vars + run: | + DEFAULT_CONFIG_FLAGS="shared no-ssl2 no-ssl3" + CONFIG_FLAGS="$DEFAULT_CONFIG_FLAGS $CONFIG_FLAGS" + CONFIG_HASH=$(echo "$CONFIG_FLAGS" | sha1sum | sed 's/ .*$//') + echo "CONFIG_FLAGS=${CONFIG_FLAGS}" >> $GITHUB_ENV + echo "CONFIG_HASH=${CONFIG_HASH}" >> $GITHUB_ENV + echo "OSSL_INFO=${{ matrix.PYTHON.OPENSSL.TYPE }}-${{ matrix.PYTHON.OPENSSL.VERSION }}-${CONFIG_FLAGS}" >> $GITHUB_ENV + echo "OSSL_PATH=${{ github.workspace }}/osslcache/${{ matrix.PYTHON.OPENSSL.TYPE }}-${{ matrix.PYTHON.OPENSSL.VERSION }}-${CONFIG_HASH}" >> $GITHUB_ENV + env: + CONFIG_FLAGS: ${{ matrix.PYTHON.OPENSSL.CONFIG_FLAGS }} + if: matrix.PYTHON.OPENSSL + - name: Load cache + uses: actions/cache@v2 + id: ossl-cache + with: + path: ${{ github.workspace }}/osslcache + # When altering the openssl build process you may need to increment the value on the end of this cache key + # so that you can prevent it from fetching the cache and skipping the build step. + key: ${{ matrix.PYTHON.OPENSSL.TYPE }}-${{ matrix.PYTHON.OPENSSL.VERSION }}-${{ env.CONFIG_HASH }}-1 + if: matrix.PYTHON.OPENSSL + - name: Build custom OpenSSL/LibreSSL + run: .github/workflows/build_openssl.sh + env: + TYPE: ${{ matrix.PYTHON.OPENSSL.TYPE }} + VERSION: ${{ matrix.PYTHON.OPENSSL.VERSION }} + if: matrix.PYTHON.OPENSSL && steps.ossl-cache.outputs.cache-hit != 'true' + - name: Set CFLAGS/LDFLAGS + run: | + echo "CFLAGS=${CFLAGS} -I${OSSL_PATH}/include" >> $GITHUB_ENV + echo "LDFLAGS=${LDFLAGS} -L${OSSL_PATH}/lib -Wl,-rpath=${OSSL_PATH}/lib" >> $GITHUB_ENV + if: matrix.PYTHON.OPENSSL + - name: Tests + run: | + tox -r -- --color=yes --wycheproof-root=wycheproof + env: + TOXENV: ${{ matrix.PYTHON.TOXENV }} + - uses: ./.github/actions/upload-coverage + with: + name: "tox -e ${{ matrix.PYTHON.TOXENV }} ${{ env.OSSL_INFO }}" + if: matrix.PYTHON.COVERAGE != 'false' + + linux-distros: + runs-on: ubuntu-latest + container: ghcr.io/${{ matrix.IMAGE.IMAGE }} + strategy: + matrix: + IMAGE: + - {IMAGE: "pyca/cryptography-runner-centos8", TOXENV: "py27"} + - {IMAGE: "pyca/cryptography-runner-centos8", TOXENV: "py36"} + - {IMAGE: "pyca/cryptography-runner-centos8-fips", TOXENV: "py36", FIPS: true} + - {IMAGE: "pyca/cryptography-runner-stretch", TOXENV: "py27"} + - {IMAGE: "pyca/cryptography-runner-buster", TOXENV: "py37"} + - {IMAGE: "pyca/cryptography-runner-bullseye", TOXENV: "py39"} + - {IMAGE: "pyca/cryptography-runner-sid", TOXENV: "py39"} + - {IMAGE: "pyca/cryptography-runner-ubuntu-bionic", TOXENV: "py36"} + - {IMAGE: "pyca/cryptography-runner-ubuntu-focal", TOXENV: "py38"} + - {IMAGE: "pyca/cryptography-runner-ubuntu-rolling", TOXENV: "py27"} + - {IMAGE: "pyca/cryptography-runner-ubuntu-rolling", TOXENV: "py38"} + - {IMAGE: "pyca/cryptography-runner-ubuntu-rolling", TOXENV: "py38-randomorder"} + - {IMAGE: "pyca/cryptography-runner-fedora", TOXENV: "py39"} + - {IMAGE: "pyca/cryptography-runner-alpine", TOXENV: "py38"} + name: "tox -e ${{ matrix.IMAGE.TOXENV }} on ${{ matrix.IMAGE.IMAGE }}" + steps: + - uses: actions/checkout@v2 + - run: 'git clone --depth=1 https://github.com/google/wycheproof "$HOME/wycheproof"' + - run: | + echo "OPENSSL_FORCE_FIPS_MODE=1" >> $GITHUB_ENV + echo "CFLAGS=-DUSE_OSRANDOM_RNG_FOR_TESTING" >> $GITHUB_ENV + if: matrix.IMAGE.FIPS + - run: 'tox -- --wycheproof-root="$HOME/wycheproof"' + env: + TOXENV: ${{ matrix.IMAGE.TOXENV }} + - uses: ./.github/actions/upload-coverage + with: + name: "tox -e ${{ matrix.IMAGE.TOXENV }} on ${{ matrix.IMAGE.IMAGE }}" + macos: runs-on: macos-latest strategy: matrix: PYTHON: - {VERSION: "2.7", TOXENV: "py27", EXTRA_CFLAGS: ""} - - {VERSION: "3.5", TOXENV: "py35", EXTRA_CFLAGS: ""} - - {VERSION: "3.8", TOXENV: "py38", EXTRA_CFLAGS: "-DUSE_OSRANDOM_RNG_FOR_TESTING"} + - {VERSION: "3.6", TOXENV: "py36", EXTRA_CFLAGS: ""} + - {VERSION: "3.9", TOXENV: "py39", EXTRA_CFLAGS: "-DUSE_OSRANDOM_RNG_FOR_TESTING"} name: "Python ${{ matrix.PYTHON.VERSION }} on macOS" steps: - - uses: actions/checkout@master + - uses: actions/checkout@v2 - name: Setup python - uses: actions/setup-python@v1 + uses: actions/setup-python@v2 with: python-version: ${{ matrix.PYTHON.VERSION }} @@ -32,23 +137,22 @@ jobs: - name: Download OpenSSL run: | - python .github/workflows/download_openssl.py macos openssl-macos + python .github/workflows/download_openssl.py macos openssl-macos-x86-64 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Tests run: | CRYPTOGRAPHY_SUPPRESS_LINK_FLAGS=1 \ - LDFLAGS="${HOME}/openssl-macos/lib/libcrypto.a ${HOME}/openssl-macos/lib/libssl.a" \ - CFLAGS="-I${HOME}/openssl-macos/include -Werror -Wno-error=deprecated-declarations -Wno-error=incompatible-pointer-types-discards-qualifiers -Wno-error=unused-function -Wno-error=unused-command-line-argument -mmacosx-version-min=10.10 -march=core2 $EXTRA_CFLAGS" \ + LDFLAGS="${HOME}/openssl-macos-x86-64/lib/libcrypto.a ${HOME}/openssl-macos-x86-64/lib/libssl.a" \ + CFLAGS="-I${HOME}/openssl-macos-x86-64/include -Werror -Wno-error=deprecated-declarations -Wno-error=incompatible-pointer-types-discards-qualifiers -Wno-error=unused-function -Wno-error=unused-command-line-argument -mmacosx-version-min=10.10 -march=core2 $EXTRA_CFLAGS" \ tox -r -- --color=yes --wycheproof-root=wycheproof env: TOXENV: ${{ matrix.PYTHON.TOXENV }} EXTRA_CFLAGS: ${{ matrix.PYTHON.EXTRA_CFLAGS }} - - name: Upload coverage - run: | - curl -o codecov.sh -f https://codecov.io/bash || curl -o codecov.sh -f https://codecov.io/bash || curl -o codecov.sh -f https://codecov.io/bash - bash codecov.sh -n "Python ${{ matrix.PYTHON.VERSION }} on macOS" + - uses: ./.github/actions/upload-coverage + with: + name: "Python ${{ matrix.PYTHON.VERSION }} on macOS" windows: runs-on: windows-latest @@ -59,15 +163,15 @@ jobs: - {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"} + - {VERSION: "3.8", TOXENV: "py38", MSVC_VERSION: "2019", CL_FLAGS: ""} + - {VERSION: "3.9", TOXENV: "py39", MSVC_VERSION: "2019", CL_FLAGS: "/D USE_OSRANDOM_RNG_FOR_TESTING"} name: "Python ${{ matrix.PYTHON.VERSION }} on ${{ matrix.WINDOWS.WINDOWS }}" steps: - - uses: actions/checkout@master + - uses: actions/checkout@v2 - name: Setup python - uses: actions/setup-python@v1 + uses: actions/setup-python@v2 with: python-version: ${{ matrix.PYTHON.VERSION }} architecture: ${{ matrix.WINDOWS.ARCH }} @@ -83,18 +187,58 @@ jobs: - name: Download OpenSSL run: | python .github/workflows/download_openssl.py windows openssl-${{ matrix.WINDOWS.WINDOWS }}-${{ matrix.PYTHON.MSVC_VERSION }} - echo "::set-env name=INCLUDE::C:/openssl-${{ matrix.WINDOWS.WINDOWS }}-${{ matrix.PYTHON.MSVC_VERSION }}/include;%INCLUDE%" - echo "::set-env name=LIB::C:/openssl-${{ matrix.WINDOWS.WINDOWS }}-${{ matrix.PYTHON.MSVC_VERSION }}/lib;%LIB%" - echo "::set-env name=CL::${{ matrix.PYTHON.CL_FLAGS }}" + echo "INCLUDE=C:/openssl-${{ matrix.WINDOWS.WINDOWS }}-${{ matrix.PYTHON.MSVC_VERSION }}/include;$INCLUDE" >> $GITHUB_ENV + echo "LIB=C:/openssl-${{ matrix.WINDOWS.WINDOWS }}-${{ matrix.PYTHON.MSVC_VERSION }}/lib;$LIB" >> $GITHUB_ENV + echo "CL=${{ matrix.PYTHON.CL_FLAGS }}" >> $GITHUB_ENV env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + shell: bash - run: git clone https://github.com/google/wycheproof - run: tox -r -- --color=yes --wycheproof-root=wycheproof env: TOXENV: ${{ matrix.PYTHON.TOXENV }} - - name: Upload coverage - run: | - curl -o codecov.sh -f https://codecov.io/bash || curl -o codecov.sh -f https://codecov.io/bash || curl -o codecov.sh -f https://codecov.io/bash - bash codecov.sh -n "Python ${{ matrix.PYTHON.VERSION }} on ${{ matrix.WINDOWS.WINDOWS }}" + - uses: ./.github/actions/upload-coverage + with: + name: "Python ${{ matrix.PYTHON.VERSION }} on ${{ matrix.WINDOWS.WINDOWS }}" + + linux-downstream: + runs-on: ubuntu-latest + strategy: + matrix: + DOWNSTREAM: + - paramiko + - pyopenssl + - twisted + - aws-encryption-sdk + - dynamodb-encryption-sdk + - certbot + - certbot-josepy + name: "Downstream tests for ${{ matrix.DOWNSTREAM }}" + steps: + - uses: actions/checkout@v2 + - name: Setup python + uses: actions/setup-python@v2 + with: + python-version: 3.7 + - run: python -m pip install -U pip wheel + - run: ./.github/downstream.d/${{ matrix.DOWNSTREAM }}.sh install + - run: pip uninstall -y enum34 + - run: pip install . + - run: ./.github/downstream.d/${{ matrix.DOWNSTREAM }}.sh run + + docs-linkcheck: + if: github.event_name == 'push' && github.ref == 'refs/heads/master' + runs-on: ubuntu-latest + name: "linkcheck" + steps: + - uses: actions/checkout@v2 + - name: Setup python + uses: actions/setup-python@v2 + with: + python-version: 3.9 + - run: python -m pip install -U tox + - run: tox -r -- --color=yes + env: + TOXENV: docs-linkcheck diff --git a/.github/workflows/download_openssl.py b/.github/workflows/download_openssl.py index 1d5e3bfb471a..46b3b7f0ee02 100644 --- a/.github/workflows/download_openssl.py +++ b/.github/workflows/download_openssl.py @@ -67,6 +67,7 @@ def main(platform, target): os.path.join(path, artifact["name"]) ) return + raise ValueError("Didn't find {} in {}".format(target, response)) if __name__ == "__main__": diff --git a/.github/workflows/wheel-builder.yml b/.github/workflows/wheel-builder.yml index d8ad49b595b9..94d24c5e3810 100644 --- a/.github/workflows/wheel-builder.yml +++ b/.github/workflows/wheel-builder.yml @@ -8,10 +8,10 @@ on: jobs: manylinux: runs-on: ubuntu-latest - container: ${{ matrix.MANYLINUX.CONTAINER }} + container: ghcr.io/${{ matrix.MANYLINUX.CONTAINER }} strategy: matrix: - PYTHON: ["cp27-cp27m", "cp27-cp27mu", "cp35-cp35m"] + PYTHON: ["cp27-cp27m", "cp27-cp27mu", "cp36-cp36m"] MANYLINUX: - NAME: manylinux1_x86_64 CONTAINER: "pyca/cryptography-manylinux1:x86_64" @@ -62,12 +62,12 @@ jobs: DOWNLOAD_URL: 'https://www.python.org/ftp/python/2.7.17/python-2.7.17-macosx10.9.pkg' BIN_PATH: '/Library/Frameworks/Python.framework/Versions/2.7/bin/python' - VERSION: '3.8' - ABI_VERSION: '3.5' + ABI_VERSION: '3.6' DOWNLOAD_URL: 'https://www.python.org/ftp/python/3.8.2/python-3.8.2-macosx10.9.pkg' BIN_PATH: '/Library/Frameworks/Python.framework/Versions/3.8/bin/python3' name: "${{ matrix.PYTHON.VERSION }} ABI ${{ matrix.PYTHON.ABI_VERSION }} macOS" steps: - - uses: actions/checkout@master + - uses: actions/checkout@v2 - run: | curl "$PYTHON_DOWNLOAD_URL" -o python.pkg sudo installer -pkg python.pkg -target / @@ -76,7 +76,7 @@ jobs: - run: ${{ matrix.PYTHON.BIN_PATH }} -m pip install -U virtualenv requests - name: Download OpenSSL run: | - ${{ matrix.PYTHON.BIN_PATH }} .github/workflows/download_openssl.py macos openssl-macos + ${{ matrix.PYTHON.BIN_PATH }} .github/workflows/download_openssl.py macos openssl-macos-x86-64 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -92,8 +92,8 @@ jobs: cd cryptography* CRYPTOGRAPHY_SUPPRESS_LINK_FLAGS="1" \ - LDFLAGS="${HOME}/openssl-macos/lib/libcrypto.a ${HOME}/openssl-macos/lib/libssl.a" \ - CFLAGS="-I${HOME}/openssl-macos/include -mmacosx-version-min=10.10 -march=core2" \ + LDFLAGS="${HOME}/openssl-macos-x86-64/lib/libcrypto.a ${HOME}/openssl-macos-x86-64/lib/libssl.a" \ + CFLAGS="-I${HOME}/openssl-macos-x86-64/include -mmacosx-version-min=10.10 -march=core2" \ ../venv/bin/python setup.py bdist_wheel $PY_LIMITED_API && mv dist/cryptography*.whl ../wheelhouse - run: venv/bin/pip install -f wheelhouse --no-index cryptography - run: | @@ -101,7 +101,7 @@ jobs: - run: mkdir cryptography-wheelhouse - run: mv wheelhouse/cryptography*.whl cryptography-wheelhouse/ - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v2.2.0 with: name: "cryptography-${{ github.event.inputs.version }}-macOS-${{ matrix.PYTHON.ABI_VERSION }}" path: cryptography-wheelhouse/ @@ -115,14 +115,10 @@ jobs: - {ARCH: 'x64', WINDOWS: 'win64'} PYTHON: - {VERSION: "2.7", MSVC_VERSION: "2010"} - - {VERSION: "3.5", MSVC_VERSION: "2019"} - - {VERSION: "3.6", MSVC_VERSION: "2019"} - - {VERSION: "3.7", MSVC_VERSION: "2019"} - - {VERSION: "3.8", MSVC_VERSION: "2019"} - {VERSION: "3.8", MSVC_VERSION: "2019", "USE_ABI3": "true", "ABI_VERSION": "cp36"} name: "${{ matrix.PYTHON.VERSION }} ${{ matrix.WINDOWS.WINDOWS }} ${{ matrix.PYTHON.ABI_VERSION }}" steps: - - uses: actions/checkout@master + - uses: actions/checkout@v2 - name: Setup python uses: actions/setup-python@v2 with: @@ -139,10 +135,11 @@ jobs: - name: Download OpenSSL run: | python .github/workflows/download_openssl.py windows openssl-${{ matrix.WINDOWS.WINDOWS }}-${{ matrix.PYTHON.MSVC_VERSION }} - echo "::set-env name=INCLUDE::C:/openssl-${{ matrix.WINDOWS.WINDOWS }}-${{ matrix.PYTHON.MSVC_VERSION }}/include;%INCLUDE%" - echo "::set-env name=LIB::C:/openssl-${{ matrix.WINDOWS.WINDOWS }}-${{ matrix.PYTHON.MSVC_VERSION }}/lib;%LIB%" + echo "INCLUDE=C:/openssl-${{ matrix.WINDOWS.WINDOWS }}-${{ matrix.PYTHON.MSVC_VERSION }}/include;$INCLUDE" >> $GITHUB_ENV + echo "LIB=C:/openssl-${{ matrix.WINDOWS.WINDOWS }}-${{ matrix.PYTHON.MSVC_VERSION }}/lib;$LIB" >> $GITHUB_ENV env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + shell: bash - run: python -m pip install -U pip wheel cffi six ipaddress "enum34; python_version < '3'" - run: pip download cryptography==${{ github.event.inputs.version }} --no-deps --no-binary cryptography && tar zxvf cryptography*.tar.gz && mkdir wheelhouse @@ -158,7 +155,7 @@ jobs: - run: mkdir cryptography-wheelhouse - run: move wheelhouse\cryptography*.whl cryptography-wheelhouse\ - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v2.2.0 with: name: "cryptography-${{ github.event.inputs.version }}-${{ matrix.WINDOWS.WINDOWS }}-${{ matrix.PYTHON.VERSION }}-${{ matrix.PYTHON.ABI_VERSION}}" path: cryptography-wheelhouse\ diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 000000000000..728bb390c30c --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,5 @@ +version: 2 + +build: + image: "7.0" + diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 103c2f835409..000000000000 --- a/.travis.yml +++ /dev/null @@ -1,159 +0,0 @@ -sudo: true -dist: focal - -language: python - -cache: - directories: - - $HOME/.cache/pip - - $HOME/ossl-2/ - -# Only build master, the version branches (e.g. 1.7.x), and -# version tags (which are apparently considered branches by travis) -branches: - only: - - master - - /\d+\.\d+\.x/ - - /\d+\.\d+(\.\d+)?/ - -matrix: - include: - - python: 3.8 - env: TOXENV=pep8,packaging - # 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 - env: TOXENV=py38 OPENSSL=1.1.1g OPENSSL_CONFIG_FLAGS="no-engine no-rc2 no-srtp no-ct" - - python: 3.8 - env: TOXENV=py38-ssh OPENSSL=1.1.1g - - python: 3.8 - env: TOXENV=py38 LIBRESSL=2.9.2 - - python: 3.8 - env: TOXENV=py38 LIBRESSL=3.0.2 - - python: 3.8 - env: TOXENV=py38 LIBRESSL=3.1.4 - - 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 - - python: 3.7 - services: docker - env: TOXENV=py37 DOCKER=pyca/cryptography-runner-buster - - python: 3.8 - services: docker - env: TOXENV=py38 DOCKER=pyca/cryptography-runner-bullseye - - python: 3.8 - services: docker - env: TOXENV=py38 DOCKER=pyca/cryptography-runner-sid - - python: 3.6 - services: docker - env: TOXENV=py36 DOCKER=pyca/cryptography-runner-ubuntu-bionic - - 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 - - python: 3.8 - services: docker - env: TOXENV=py38-randomorder DOCKER=pyca/cryptography-runner-ubuntu-rolling - - python: 3.8 - services: docker - env: TOXENV=py38 DOCKER=pyca/cryptography-runner-fedora - - python: 3.8 - services: docker - env: TOXENV=py38 DOCKER=pyca/cryptography-runner-alpine:latest - - - python: 3.8 - env: TOXENV=docs OPENSSL=1.1.1g - addons: - apt: - packages: - - libenchant-dev - - 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/ - - - python: 3.8 - env: DOWNSTREAM=pyopenssl - - python: 3.7 - env: DOWNSTREAM=twisted OPENSSL=1.1.1g - # Temporary disabled until - # https://github.com/paramiko/paramiko/pull/1723 is merged - # - python: 2.7 - # env: DOWNSTREAM=paramiko - - python: 3.7 - env: DOWNSTREAM=aws-encryption-sdk - - 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: 3.8 - env: DOWNSTREAM=certbot - - python: 3.8 - env: DOWNSTREAM=certbot-josepy - - python: 3.8 - env: DOWNSTREAM=urllib3 - # Tests hang when run under bionic/focal - dist: xenial - -install: - - ./.travis/install.sh - -script: - - ./.travis/run.sh - -after_success: - - ./.travis/upload_coverage.sh - -notifications: - irc: - channels: - # This is set to a secure variable to prevent forks from notifying the - # IRC channel whenever they fail a build. This can be removed when travis - # implements https://github.com/travis-ci/travis-ci/issues/1094. - # The value encrypted here was created via - # travis encrypt "irc.freenode.org#cryptography-dev" - - secure: "A93qvTOlwlMK5WoEvZQ5jQ8Z4Hd0JpeO53WYt8iIJ3s/L6AubkfiN7gwhThRtPnPx7DVMenoKRMlcRg76/ICvXEViVnGgXFjsypF0CzVcIay9pPdjpZjZHP735yLfX512RtxYEdEGwi5r25Z2CEFaydhhxNwfuMxGBtLUjusix4=" - use_notice: true - skip_join: true diff --git a/.travis/downstream.d/README.rst b/.travis/downstream.d/README.rst deleted file mode 100644 index 1553448c327f..000000000000 --- a/.travis/downstream.d/README.rst +++ /dev/null @@ -1,13 +0,0 @@ -To add downstream tests to be run in CI: - -1. Create a test handler for the downstream consumer that you want to test. - - * The test handler should be a single file in the ``.travis/downstream.d/`` directory. - * The file name should be ``{downstream name}.sh`` where ``{downstream name}`` - is the name that you wish to use to identify the consumer. - * The test handler should accept a single argument that can be either ``install`` or ``run``. - These should be used to separate installation of the downstream consumer and - any dependencies from the actual running of the tests. - -2. Add an entry to the test matrix in ``.travis.yml`` that sets the ``DOWNSTREAM`` - environment variable to the downstream name that you selected. diff --git a/.travis/downstream.d/urllib3.sh b/.travis/downstream.d/urllib3.sh deleted file mode 100755 index dad06159846f..000000000000 --- a/.travis/downstream.d/urllib3.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -ex - -case "${1}" in - install) - git clone --depth 1 https://github.com/shazow/urllib3 - cd urllib3 - git rev-parse HEAD - pip install -r ./dev-requirements.txt - pip install -e ".[socks]" - ;; - run) - cd urllib3 - pytest test - ;; - *) - exit 1 - ;; -esac diff --git a/.travis/install.sh b/.travis/install.sh deleted file mode 100755 index 91df42285cdb..000000000000 --- a/.travis/install.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/bin/bash - -set -e -set -x - -SCRIPT_DIR=$(dirname "${BASH_SOURCE[0]}") - -shlib_sed() { - # modify the shlib version to a unique one to make sure the dynamic - # linker doesn't load the system one. - sed -i "s/^SHLIB_MAJOR=.*/SHLIB_MAJOR=100/" Makefile - sed -i "s/^SHLIB_MINOR=.*/SHLIB_MINOR=0.0/" Makefile - sed -i "s/^SHLIB_VERSION_NUMBER=.*/SHLIB_VERSION_NUMBER=100.0.0/" Makefile -} - -# download, compile, and install if it's not already present via travis -# cache -if [ -n "${OPENSSL}" ]; then - . "$SCRIPT_DIR/openssl_config.sh" - if [[ ! -f "$HOME/$OPENSSL_DIR/bin/openssl" ]]; then - curl -O "https://www.openssl.org/source/openssl-${OPENSSL}.tar.gz" - tar zxf "openssl-${OPENSSL}.tar.gz" - pushd "openssl-${OPENSSL}" - ./config $OPENSSL_CONFIG_FLAGS -fPIC --prefix="$HOME/$OPENSSL_DIR" - shlib_sed - make depend - make -j"$(nproc)" - # CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 - if [ "${OPENSSL}" == "1.0.2u" ]; then - make install - else - # avoid installing the docs on versions of OpenSSL that aren't ancient. - # https://github.com/openssl/openssl/issues/6685#issuecomment-403838728 - make install_sw install_ssldirs - fi - popd - fi -elif [ -n "${LIBRESSL}" ]; then - LIBRESSL_DIR="ossl-2/${LIBRESSL}" - if [[ ! -f "$HOME/$LIBRESSL_DIR/bin/openssl" ]]; then - curl -O "https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-${LIBRESSL}.tar.gz" - tar zxf "libressl-${LIBRESSL}.tar.gz" - pushd "libressl-${LIBRESSL}" - ./config -Wl -Wl,-Bsymbolic-functions -fPIC shared --prefix="$HOME/$LIBRESSL_DIR" - shlib_sed - make -j"$(nproc)" install - popd - fi -fi - -if [ -n "${DOCKER}" ]; then - if [ -n "${OPENSSL}" ] || [ -n "${LIBRESSL}" ]; then - echo "OPENSSL and LIBRESSL are not allowed when DOCKER is set." - exit 1 - fi - docker pull "$DOCKER" || docker pull "$DOCKER" || docker pull "$DOCKER" -fi - -if [ -z "${DOWNSTREAM}" ]; then - git clone --depth=1 https://github.com/google/wycheproof "$HOME/wycheproof" -fi - -pip install -U pip -pip install virtualenv - -python -m virtualenv ~/.venv -source ~/.venv/bin/activate -# If we pin coverage it must be kept in sync with tox.ini and .github/workflows/ci.yml -pip install tox coverage diff --git a/.travis/openssl_config.sh b/.travis/openssl_config.sh deleted file mode 100755 index 83f16d2bfea8..000000000000 --- a/.travis/openssl_config.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -set -e -set -x - -DEFAULT_CONFIG_FLAGS="shared no-ssl2 no-ssl3" -if [ -n "${OPENSSL_CONFIG_FLAGS}" ]; then - OPENSSL_CONFIG_FLAGS="$DEFAULT_CONFIG_FLAGS $OPENSSL_CONFIG_FLAGS" -else - OPENSSL_CONFIG_FLAGS=$DEFAULT_CONFIG_FLAGS -fi -CONFIG_HASH=$(echo "$OPENSSL_CONFIG_FLAGS" | sha1sum | sed 's/ .*$//') -OPENSSL_DIR="ossl-2/${OPENSSL}${CONFIG_HASH}" diff --git a/.travis/run.sh b/.travis/run.sh deleted file mode 100755 index 01605c2717d4..000000000000 --- a/.travis/run.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/bash -ex - -SCRIPT_DIR=$(dirname "${BASH_SOURCE[0]}") - -if [ -n "${LIBRESSL}" ]; then - LIBRESSL_DIR="ossl-2/${LIBRESSL}" - export CFLAGS="-Werror -Wno-error=deprecated-declarations -Wno-error=discarded-qualifiers -Wno-error=unused-function -I$HOME/$LIBRESSL_DIR/include" - export PATH="$HOME/$LIBRESSL_DIR/bin:$PATH" - export LDFLAGS="-L$HOME/$LIBRESSL_DIR/lib -Wl,-rpath=$HOME/$LIBRESSL_DIR/lib" -fi - -if [ -n "${OPENSSL}" ]; then - . "$SCRIPT_DIR/openssl_config.sh" - export PATH="$HOME/$OPENSSL_DIR/bin:$PATH" - export CFLAGS="${CFLAGS} -I$HOME/$OPENSSL_DIR/include" - # rpath on linux will cause it to use an absolute path so we don't need to - # do LD_LIBRARY_PATH - export LDFLAGS="-L$HOME/$OPENSSL_DIR/lib -Wl,-rpath=$HOME/$OPENSSL_DIR/lib" -fi - -source ~/.venv/bin/activate - -if [ -n "${DOCKER}" ]; then - docker run --rm \ - -v "${TRAVIS_BUILD_DIR}":"${TRAVIS_BUILD_DIR}" \ - -v "${HOME}/wycheproof":/wycheproof \ - -w "${TRAVIS_BUILD_DIR}" \ - -e OPENSSL_FORCE_FIPS_MODE \ - -e TOXENV "${DOCKER}" \ - /bin/sh -c "tox -- --wycheproof-root='/wycheproof'" -elif [ -n "${TOXENV}" ]; then - tox -- --wycheproof-root="$HOME/wycheproof" -else - downstream_script="${TRAVIS_BUILD_DIR}/.travis/downstream.d/${DOWNSTREAM}.sh" - if [ ! -x "$downstream_script" ]; then - exit 1 - fi - $downstream_script install - pip install . - $downstream_script run -fi diff --git a/.travis/upload_coverage.sh b/.travis/upload_coverage.sh deleted file mode 100755 index 2999bb7e6b25..000000000000 --- a/.travis/upload_coverage.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -set -e -set -x - -if [ -n "${TOXENV}" ]; then - case "${TOXENV}" in - pypy-nocoverage);; - pypy3-nocoverage);; - pep8);; - py3pep8);; - docs);; - *) - source ~/.venv/bin/activate - curl -o codecov.sh -f https://codecov.io/bash || curl -o codecov.sh -f https://codecov.io/bash || curl -o codecov.sh -f https://codecov.io/bash - - bash codecov.sh -Z -e TRAVIS_OS_NAME,TOXENV,OPENSSL,DOCKER,OPENSSL_FORCE_FIPS_MODE || \ - bash codecov.sh -Z -e TRAVIS_OS_NAME,TOXENV,OPENSSL,DOCKER,OPENSSL_FORCE_FIPS_MODE - ;; - esac -fi diff --git a/.zuul.d/jobs.yaml b/.zuul.d/jobs.yaml index 1430b0c31510..83f2c6597038 100644 --- a/.zuul.d/jobs.yaml +++ b/.zuul.d/jobs.yaml @@ -44,9 +44,9 @@ vars: wheel_builds: - platform: manylinux2014_aarch64 - image: pyca/cryptography-manylinux2014_aarch64 + image: ghcr.io/pyca/cryptography-manylinux2014_aarch64 pythons: - - cp35-cp35m + - cp36-cp36m - job: name: pyca-cryptography-build-wheel-x86_64 @@ -55,14 +55,14 @@ vars: wheel_builds: - platform: manylinux1_x86_64 - image: pyca/cryptography-manylinux1:x86_64 + image: ghcr.io/pyca/cryptography-manylinux1:x86_64 pythons: - cp27-cp27m - cp27-cp27mu - - cp35-cp35m + - cp36-cp36m - platform: manylinux2010_x86_64 - image: pyca/cryptography-manylinux2010:x86_64 + image: ghcr.io/pyca/cryptography-manylinux2010:x86_64 pythons: - cp27-cp27m - cp27-cp27mu - - cp35-cp35m + - cp36-cp36m diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 3facb2ac1642..4dd71460069f 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,86 @@ Changelog ========= +.. _v3-3-2: + +3.3.2 - 2021-02-07 +~~~~~~~~~~~~~~~~~~ + +* **SECURITY ISSUE:** Fixed a bug where certain sequences of ``update()`` calls + when symmetrically encrypting very large payloads (>2GB) could result in an + integer overflow, leading to buffer overflows. *CVE-2020-36242* + +.. _v3-3-1: + +3.3.1 - 2020-12-09 +~~~~~~~~~~~~~~~~~~ + +* Re-added a legacy symbol causing problems for older ``pyOpenSSL`` users. + +.. _v3-3: + +3.3 - 2020-12-08 +~~~~~~~~~~~~~~~~ + +* **BACKWARDS INCOMPATIBLE:** Support for Python 3.5 has been removed due to + low usage and maintenance burden. +* **BACKWARDS INCOMPATIBLE:** The + :class:`~cryptography.hazmat.primitives.ciphers.modes.GCM` and + :class:`~cryptography.hazmat.primitives.ciphers.aead.AESGCM` now require + 64-bit to 1024-bit (8 byte to 128 byte) initialization vectors. This change + is to conform with an upcoming OpenSSL release that will no longer support + sizes outside this window. +* **BACKWARDS INCOMPATIBLE:** When deserializing asymmetric keys we now + raise ``ValueError`` rather than ``UnsupportedAlgorithm`` when an + unsupported cipher is used. This change is to conform with an upcoming + OpenSSL release that will no longer distinguish between error types. +* **BACKWARDS INCOMPATIBLE:** We no longer allow loading of finite field + Diffie-Hellman parameters of less than 512 bits in length. This change is to + conform with an upcoming OpenSSL release that no longer supports smaller + sizes. These keys were already wildly insecure and should not have been used + in any application outside of testing. +* Updated Windows, macOS, and ``manylinux`` wheels to be compiled with + OpenSSL 1.1.1i. +* Python 2 support is deprecated in ``cryptography``. This is the last release + that will support Python 2. +* Added the + :meth:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey.recover_data_from_signature` + function to + :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey` + for recovering the signed data from an RSA signature. + +.. _v3-2-1: + +3.2.1 - 2020-10-27 +~~~~~~~~~~~~~~~~~~ + +* Disable blinding on RSA public keys to address an error with some versions + of OpenSSL. + +.. _v3-2: + +3.2 - 2020-10-25 +~~~~~~~~~~~~~~~~ + +* **SECURITY ISSUE:** Attempted to make RSA PKCS#1v1.5 decryption more constant + time, to protect against Bleichenbacher vulnerabilities. Due to limitations + imposed by our API, we cannot completely mitigate this vulnerability and a + future release will contain a new API which is designed to be resilient to + these for contexts where it is required. Credit to **Hubert Kario** for + reporting the issue. *CVE-2020-25659* +* Support for OpenSSL 1.0.2 has been removed. Users on older version of OpenSSL + will need to upgrade. +* Added basic support for PKCS7 signing (including SMIME) via + :class:`~cryptography.hazmat.primitives.serialization.pkcs7.PKCS7SignatureBuilder`. + +.. _v3-1-1: + +3.1.1 - 2020-09-22 +~~~~~~~~~~~~~~~~~~ + +* Updated Windows, macOS, and ``manylinux`` wheels to be compiled with + OpenSSL 1.1.1h. + .. _v3-1: 3.1 - 2020-08-26 diff --git a/LICENSE b/LICENSE index fe5af51408c1..07074259b61a 100644 --- a/LICENSE +++ b/LICENSE @@ -2,5 +2,5 @@ This software is made available under the terms of *either* of the licenses found in LICENSE.APACHE or LICENSE.BSD. Contributions to cryptography are made under the terms of *both* these licenses. -The code used in the OpenSSL locking callback and OS random engine is derived -from CPython, and is licensed under the terms of the PSF License Agreement. +The code used in the OS random engine is derived from CPython, and is licensed +under the terms of the PSF License Agreement. diff --git a/MANIFEST.in b/MANIFEST.in index 7e97167a1b63..5c82725a4148 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -16,11 +16,9 @@ recursive-include tests *.py exclude vectors recursive-exclude vectors * -exclude .travis.yml .travis -recursive-exclude .travis * recursive-exclude .github * -exclude release.py .coveragerc codecov.yml dev-requirements.txt rtd-requirements.txt tox.ini +exclude release.py .coveragerc codecov.yml .readthedocs.yml dev-requirements.txt rtd-requirements.txt tox.ini recursive-exclude .zuul.d * recursive-exclude .zuul.playbooks * diff --git a/README.rst b/README.rst index fddde9878581..10de198b8af4 100644 --- a/README.rst +++ b/README.rst @@ -9,9 +9,6 @@ pyca/cryptography :target: https://cryptography.io :alt: Latest Docs -.. image:: https://travis-ci.org/pyca/cryptography.svg?branch=master - :target: https://travis-ci.org/pyca/cryptography - .. image:: https://github.com/pyca/cryptography/workflows/CI/badge.svg?branch=master :target: https://github.com/pyca/cryptography/actions?query=workflow%3ACI+branch%3Amaster @@ -21,7 +18,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 2.7, Python 3.5+, and PyPy 5.4+. +standard library". It supports Python 2.7, Python 3.6+, and PyPy 5.4+. ``cryptography`` includes both high level recipes and low level interfaces to common cryptographic algorithms such as symmetric ciphers, message digests, and @@ -68,7 +65,7 @@ documentation. .. _`documentation`: https://cryptography.io/ -.. _`the installation documentation`: https://cryptography.io/en/latest/installation/ +.. _`the installation documentation`: https://cryptography.io/en/latest/installation.html .. _`issue tracker`: https://github.com/pyca/cryptography/issues .. _`cryptography-dev`: https://mail.python.org/mailman/listinfo/cryptography-dev -.. _`security reporting`: https://cryptography.io/en/latest/security/ +.. _`security reporting`: https://cryptography.io/en/latest/security.html diff --git a/docs/api-stability.rst b/docs/api-stability.rst index 205b18447b05..fd34ced0aba1 100644 --- a/docs/api-stability.rst +++ b/docs/api-stability.rst @@ -1,7 +1,7 @@ API stability ============= -From its first release, ``cryptography`` will have a strong API stability +From its first release, ``cryptography`` has had a strong API stability policy. What does this policy cover? diff --git a/docs/conf.py b/docs/conf.py index 87e2b5869c6c..fb67adabc905 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -71,7 +71,7 @@ # General information about the project. project = "Cryptography" -copyright = "2013-2020, Individual Contributors" +copyright = "2013-2021, Individual Contributors" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -199,8 +199,4 @@ r"https://info.isl.ntt.co.jp/crypt/eng/camellia/", # Inconsistent small DH params they seem incapable of fixing r"https://www.secg.org/sec1-v2.pdf", - # 403ing from Travis - r"https://devblogs.microsoft.com/oldnewthing/\?p=4223", - # Incomplete cert chain - r"https://cveform.mitre.org/", ] diff --git a/docs/development/c-bindings.rst b/docs/development/c-bindings.rst index 1b58dab6290f..e53e0bae7f65 100644 --- a/docs/development/c-bindings.rst +++ b/docs/development/c-bindings.rst @@ -5,7 +5,7 @@ C bindings are bindings to C libraries, using cffi_ whenever possible. .. _cffi: https://cffi.readthedocs.io -Bindings live in :py:mod:`cryptography.hazmat.bindings`. +Bindings live in ``cryptography.hazmat.bindings``. When modifying the bindings you will need to recompile the C extensions to test the changes. This can be accomplished with ``pip install -e .`` in the @@ -189,9 +189,9 @@ Caveats Sometimes, a set of loosely related features are added in the same version, and it's impractical to create ``#ifdef`` statements for each one. In that case, it may make sense to either check for a particular -version. For example, to check for OpenSSL 1.1.0 or newer:: +version. For example, to check for OpenSSL 1.1.1 or newer:: - #if CRYPTOGRAPHY_OPENSSL_110_OR_GREATER + #if CRYPTOGRAPHY_OPENSSL_111_OR_GREATER Sometimes, the version of a library on a particular platform will have features that you thought it wouldn't, based on its version. diff --git a/docs/development/custom-vectors/secp256k1/verify_secp256k1.py b/docs/development/custom-vectors/secp256k1/verify_secp256k1.py index 485f0718bc02..f721b0001213 100644 --- a/docs/development/custom-vectors/secp256k1/verify_secp256k1.py +++ b/docs/development/custom-vectors/secp256k1/verify_secp256k1.py @@ -35,12 +35,12 @@ def verify_one_vector(vector): signature, ec.ECDSA(CRYPTOGRAPHY_HASH_TYPES[digest_algorithm]()) ) verifier.update(message) - return verifier.verify() + verifier.verify() def verify_vectors(vectors): for vector in vectors: - assert verify_one_vector(vector) + verify_one_vector(vector) vector_path = os.path.join("asymmetric", "ECDSA", "SECP256K1", "SigGen.txt") diff --git a/docs/development/test-vectors.rst b/docs/development/test-vectors.rst index 720bbbfb1cb7..f952337e2347 100644 --- a/docs/development/test-vectors.rst +++ b/docs/development/test-vectors.rst @@ -109,6 +109,8 @@ Custom asymmetric vectors * ``x509/custom/ca/ca_key.pem`` - An unencrypted PCKS8 ``secp256r1`` key. It is the private key for the certificate ``x509/custom/ca/ca.pem``. This key is encoded in several of the PKCS12 custom vectors. +* ``x509/custom/ca/rsa_key.pem`` - An unencrypted PCKS8 4096 bit RSA key. It is + the private key for the certificate ``x509/custom/ca/rsa_ca.pem``. * ``asymmetric/EC/compressed_points.txt`` - Contains compressed public points generated using OpenSSL. * ``asymmetric/X448/x448-pkcs8-enc.pem`` and @@ -182,6 +184,8 @@ Key exchange ``vectors/cryptography_vectors/asymmetric/DH/dhkey_rfc5114_2.der`` and ``vectors/cryptography_vectors/asymmetric/DH/dhpub_rfc5114_2.der`` contains are the above parameters and keys in DER format. +* ``vectors/cryptography_vectors/asymmetric/DH/dh_key_256.pem`` contains + a PEM PKCS8 encoded DH key with a 256-bit key size. * ``vectors/cryptoraphy_vectors/asymmetric/ECDH/brainpool.txt`` contains Brainpool vectors from :rfc:`7027`. @@ -414,6 +418,8 @@ Custom X.509 Vectors * ``rsa_pss.pem`` - A certificate with an RSA PSS signature. * ``root-ed448.pem`` - An ``ed448`` self-signed CA certificate using ``ed448-pkcs8.pem`` as key. +* ``ca/rsa_ca.pem`` - A self-signed RSA certificate with ``basicConstraints`` + set to true. Its private key is ``ca/rsa_key.pem``. Custom X.509 Request Vectors ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -731,7 +737,7 @@ header format (substituting the correct information): .. _`IETF`: https://www.ietf.org/ .. _`Project Wycheproof`: https://github.com/google/wycheproof .. _`NIST CAVP`: https://csrc.nist.gov/projects/cryptographic-algorithm-validation-program -.. _`Bruce Schneier's vectors`: https://www.schneier.com/code/vectors.txt +.. _`Bruce Schneier's vectors`: https://www.schneier.com/wp-content/uploads/2015/12/vectors-2.txt .. _`Camellia page`: https://info.isl.ntt.co.jp/crypt/eng/camellia/ .. _`CRYPTREC`: https://www.cryptrec.go.jp .. _`OpenSSL's test vectors`: https://github.com/openssl/openssl/blob/97cf1f6c2854a3a955fd7dd3a1f113deba00c9ef/crypto/evp/evptests.txt#L232 diff --git a/docs/faq.rst b/docs/faq.rst index dba7b05ed9ac..d6f4ad336ac7 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -16,7 +16,7 @@ If your pytest setup follows the best practices of failing on emitted warnings (``filterwarnings = error``), you may ignore it by adding the following line at the end of the list:: - ignore:Python 2 is no longer supported by the Python core team. Support for it is now deprecated in cryptography, and will be removed in a future release.:UserWarning:cryptography + ignore:Python 2 is no longer supported by the Python core team. Support for it is now deprecated in cryptography, and will be removed in a future release.:UserWarning **Note:** Using ``cryptography.utils.CryptographyDeprecationWarning`` is not possible here because specifying it triggers @@ -70,18 +70,17 @@ legacy libraries: :class:`AES-GCM ` and :class:`~cryptography.hazmat.primitives.kdf.hkdf.HKDF`. -Compiling ``cryptography`` on macOS produces a ``fatal error: 'openssl/aes.h' file not found`` error ----------------------------------------------------------------------------------------------------- +Installing ``cryptography`` produces a ``fatal error: 'openssl/opensslv.h' file not found`` error +------------------------------------------------------------------------------------------------- -This happens because macOS 10.11 no longer includes a copy of OpenSSL. -``cryptography`` now provides wheels which include a statically linked copy of -OpenSSL. You're seeing this error because your copy of pip is too old to find -our wheel files. Upgrade your copy of pip with ``pip install -U pip`` and then -try install ``cryptography`` again. +``cryptography`` provides wheels which include a statically linked copy of +OpenSSL. If you see this error it is likely because your copy of ``pip`` is too +old to find our wheel files. Upgrade your ``pip`` with ``pip install -U pip`` +and then try to install ``cryptography`` again. -If you are using PyPy, we do not currently ship ``cryptography`` wheels for -PyPy. You will need to install your own copy of OpenSSL -- we recommend using -Homebrew. +Users on PyPy, unusual CPU architectures, or distributions of Linux using +``musl`` (like Alpine) will need to compile ``cryptography`` themselves. Please +view our :doc:`/installation` documentation. ``cryptography`` raised an ``InternalError`` and I'm not sure what to do? ------------------------------------------------------------------------- @@ -109,22 +108,22 @@ Your ``pip`` and/or ``setuptools`` are outdated. Please upgrade to the latest versions with ``pip install -U pip setuptools`` (or on Windows ``python -m pip install -U pip setuptools``). -Installing cryptography with OpenSSL 0.9.8, 1.0.0, 1.0.1 fails --------------------------------------------------------------- +Installing cryptography with OpenSSL 0.9.8, 1.0.0, 1.0.1, 1.0.2 fails +--------------------------------------------------------------------- -The OpenSSL project has dropped support for the 0.9.8, 1.0.0, and 1.0.1 release -series. Since they are no longer receiving security patches from upstream, -``cryptography`` is also dropping support for them. To fix this issue you -should upgrade to a newer version of OpenSSL (1.0.2 or later). This may require -you to upgrade to a newer operating system. +The OpenSSL project has dropped support for the 0.9.8, 1.0.0, 1.0.1, and 1.0.2 +release series. Since they are no longer receiving security patches from +upstream, ``cryptography`` is also dropping support for them. To fix this issue +you should upgrade to a newer version of OpenSSL (1.1.0 or later). This may +require you to upgrade to a newer operating system. -Why are there no wheels for Python 3.6+ on Linux or macOS? ----------------------------------------------------------- +Why are there no wheels for my Python3.x version? +------------------------------------------------- -Our Python3 wheels, for macOS and Linux, are ``abi3`` wheels. This means they -support multiple versions of Python. The Python 3.5 ``abi3`` wheel can be used -with any version of Python greater than or equal to 3.5. Recent versions of -``pip`` will automatically install ``abi3`` wheels. +Our Python3 wheels are ``abi3`` wheels. This means they support multiple +versions of Python. The ``abi3`` wheel can be used with any version of Python +greater than or equal to the version it specifies. Recent versions of ``pip`` +will automatically install ``abi3`` wheels. Why can't I import my PEM file? ------------------------------- diff --git a/docs/fernet.rst b/docs/fernet.rst index dd9d75bd1dc6..5e2655d3a72b 100644 --- a/docs/fernet.rst +++ b/docs/fernet.rst @@ -229,7 +229,6 @@ password through a key derivation function such as >>> import base64 >>> import os >>> from cryptography.fernet import Fernet - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC >>> password = b"password" @@ -239,7 +238,6 @@ password through a key derivation function such as ... length=32, ... salt=salt, ... iterations=100000, - ... backend=default_backend() ... ) >>> key = base64.urlsafe_b64encode(kdf.derive(password)) >>> f = Fernet(key) @@ -276,8 +274,9 @@ Limitations ----------- Fernet is ideal for encrypting data that easily fits in memory. As a design -feature it does not expose unauthenticated bytes. Unfortunately, this makes it -generally unsuitable for very large files at this time. +feature it does not expose unauthenticated bytes. This means that the complete +message contents must be available in memory, making Fernet generally +unsuitable for very large files at this time. .. _`Fernet`: https://github.com/fernet/spec/ diff --git a/docs/hazmat/backends/openssl.rst b/docs/hazmat/backends/openssl.rst index 0e695279dbe4..dd85d869a635 100644 --- a/docs/hazmat/backends/openssl.rst +++ b/docs/hazmat/backends/openssl.rst @@ -3,7 +3,7 @@ OpenSSL backend =============== -The `OpenSSL`_ C library. Cryptography supports OpenSSL version 1.0.2 and +The `OpenSSL`_ C library. Cryptography supports OpenSSL version 1.1.0 and greater. .. data:: cryptography.hazmat.backends.openssl.backend diff --git a/docs/hazmat/bindings/index.rst b/docs/hazmat/bindings/index.rst deleted file mode 100644 index 655f4620d492..000000000000 --- a/docs/hazmat/bindings/index.rst +++ /dev/null @@ -1,22 +0,0 @@ -.. hazmat:: - -Bindings -======== - -.. module:: cryptography.hazmat.bindings - -``cryptography`` aims to provide low-level CFFI based bindings to multiple -native C libraries. These provide no automatic initialization of the library -and may not provide complete wrappers for its API. - -Using these functions directly is likely to require you to be careful in -managing memory allocation, locking and other resources. - - -Individual bindings -------------------- - -.. toctree:: - :maxdepth: 1 - - openssl diff --git a/docs/hazmat/bindings/openssl.rst b/docs/hazmat/bindings/openssl.rst deleted file mode 100644 index bc7ec2d916b6..000000000000 --- a/docs/hazmat/bindings/openssl.rst +++ /dev/null @@ -1,47 +0,0 @@ -.. hazmat:: - -OpenSSL binding -=============== - -.. currentmodule:: cryptography.hazmat.bindings.openssl.binding - -These are `CFFI`_ bindings to the `OpenSSL`_ C library. Cryptography supports -OpenSSL version 1.0.2 and greater. - -.. class:: cryptography.hazmat.bindings.openssl.binding.Binding() - - This is the exposed API for the OpenSSL bindings. It has two public - attributes: - - .. attribute:: ffi - - This is a ``cffi.FFI`` instance. It can be used to allocate and - otherwise manipulate OpenSSL structures. - - .. attribute:: lib - - This is a ``cffi`` library. It can be used to call OpenSSL functions, - and access constants. - - .. classmethod:: init_static_locks - - Enables the best available locking callback for OpenSSL. - See :ref:`openssl-threading`. - -.. _openssl-threading: - -Threading ---------- - -``cryptography`` enables OpenSSLs `thread safety facilities`_ in several -different ways depending on the configuration of your system. For users on -OpenSSL 1.1.0 or newer (including anyone who uses a binary wheel) the OpenSSL -internal locking callbacks are automatically used. Otherwise, we first attempt -to use the callbacks provided by your Python implementation specifically for -OpenSSL. This will work in every case except where ``cryptography`` is linked -against a different version of OpenSSL than the one used by your Python -implementation. For this final case we have a C-based locking callback. - -.. _`CFFI`: https://cffi.readthedocs.io -.. _`OpenSSL`: https://www.openssl.org/ -.. _`thread safety facilities`: https://www.openssl.org/docs/man1.0.2/man3/CRYPTO_THREADID_set_callback.html diff --git a/docs/hazmat/primitives/asymmetric/dh.rst b/docs/hazmat/primitives/asymmetric/dh.rst index 145196adc581..6b47da089378 100644 --- a/docs/hazmat/primitives/asymmetric/dh.rst +++ b/docs/hazmat/primitives/asymmetric/dh.rst @@ -31,13 +31,11 @@ present. .. code-block:: pycon - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.asymmetric import dh >>> from cryptography.hazmat.primitives.kdf.hkdf import HKDF >>> # Generate some parameters. These can be reused. - >>> parameters = dh.generate_parameters(generator=2, key_size=2048, - ... backend=default_backend()) + >>> parameters = dh.generate_parameters(generator=2, key_size=2048) >>> # Generate a private key for use in the exchange. >>> server_private_key = parameters.generate_private_key() >>> # In a real handshake the peer is a remote client. For this @@ -51,7 +49,6 @@ present. ... length=32, ... salt=None, ... info=b'handshake data', - ... backend=default_backend() ... ).derive(shared_key) >>> # And now we can demonstrate that the handshake performed in the >>> # opposite direction gives the same final value @@ -63,7 +60,6 @@ present. ... length=32, ... salt=None, ... info=b'handshake data', - ... backend=default_backend() ... ).derive(same_shared_key) >>> derived_key == same_derived_key @@ -75,13 +71,11 @@ example of the ephemeral form: .. code-block:: pycon - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.asymmetric import dh >>> from cryptography.hazmat.primitives.kdf.hkdf import HKDF >>> # Generate some parameters. These can be reused. - >>> parameters = dh.generate_parameters(generator=2, key_size=2048, - ... backend=default_backend()) + >>> parameters = dh.generate_parameters(generator=2, key_size=2048) >>> # Generate a private key for use in the exchange. >>> private_key = parameters.generate_private_key() >>> # In a real handshake the peer_public_key will be received from the @@ -96,7 +90,6 @@ example of the ephemeral form: ... length=32, ... salt=None, ... info=b'handshake data', - ... backend=default_backend() ... ).derive(shared_key) >>> # For the next handshake we MUST generate another private key, but >>> # we can reuse the parameters. @@ -108,7 +101,6 @@ example of the ephemeral form: ... length=32, ... salt=None, ... info=b'handshake data', - ... backend=default_backend() ... ).derive(shared_key_2) To assemble a :class:`~DHParameters` and a :class:`~DHPublicKey` from @@ -118,9 +110,9 @@ example, if **p**, **g**, and **y** are :class:`int` objects received from a peer:: pn = dh.DHParameterNumbers(p, g) - parameters = pn.parameters(default_backend()) + parameters = pn.parameters() peer_public_numbers = dh.DHPublicNumbers(y, pn) - peer_public_key = peer_public_numbers.public_key(default_backend()) + peer_public_key = peer_public_numbers.public_key() See also the :class:`~cryptography.hazmat.backends.interfaces.DHBackend` diff --git a/docs/hazmat/primitives/asymmetric/dsa.rst b/docs/hazmat/primitives/asymmetric/dsa.rst index 2eae56df1c36..788e4270b886 100644 --- a/docs/hazmat/primitives/asymmetric/dsa.rst +++ b/docs/hazmat/primitives/asymmetric/dsa.rst @@ -78,12 +78,10 @@ instance. .. doctest:: - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.asymmetric import dsa >>> private_key = dsa.generate_private_key( ... key_size=1024, - ... backend=default_backend() ... ) >>> data = b"this is some data I'd like to sign" >>> signature = private_key.sign( @@ -103,7 +101,7 @@ separately and pass that value using >>> from cryptography.hazmat.primitives.asymmetric import utils >>> chosen_hash = hashes.SHA256() - >>> hasher = hashes.Hash(chosen_hash, default_backend()) + >>> hasher = hashes.Hash(chosen_hash) >>> hasher.update(b"data & ") >>> hasher.update(b"more data") >>> digest = hasher.finalize() @@ -146,7 +144,7 @@ separately and pass that value using .. doctest:: >>> chosen_hash = hashes.SHA256() - >>> hasher = hashes.Hash(chosen_hash, default_backend()) + >>> hasher = hashes.Hash(chosen_hash) >>> hasher.update(b"data & ") >>> hasher.update(b"more data") >>> digest = hasher.finalize() diff --git a/docs/hazmat/primitives/asymmetric/ec.rst b/docs/hazmat/primitives/asymmetric/ec.rst index 72768f8332cd..5691560f3c76 100644 --- a/docs/hazmat/primitives/asymmetric/ec.rst +++ b/docs/hazmat/primitives/asymmetric/ec.rst @@ -56,11 +56,10 @@ Elliptic Curve Signature Algorithms .. doctest:: - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.asymmetric import ec >>> private_key = ec.generate_private_key( - ... ec.SECP384R1(), default_backend() + ... ec.SECP384R1() ... ) >>> data = b"this is some data I'd like to sign" >>> signature = private_key.sign( @@ -80,7 +79,7 @@ Elliptic Curve Signature Algorithms >>> from cryptography.hazmat.primitives.asymmetric import utils >>> chosen_hash = hashes.SHA256() - >>> hasher = hashes.Hash(chosen_hash, default_backend()) + >>> hasher = hashes.Hash(chosen_hash) >>> hasher.update(b"data & ") >>> hasher.update(b"more data") >>> digest = hasher.finalize() @@ -112,7 +111,7 @@ Elliptic Curve Signature Algorithms .. doctest:: >>> chosen_hash = hashes.SHA256() - >>> hasher = hashes.Hash(chosen_hash, default_backend()) + >>> hasher = hashes.Hash(chosen_hash) >>> hasher.update(b"data & ") >>> hasher.update(b"more data") >>> digest = hasher.finalize() @@ -270,18 +269,17 @@ Elliptic Curve Key Exchange algorithm .. doctest:: - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.asymmetric import ec >>> from cryptography.hazmat.primitives.kdf.hkdf import HKDF >>> # Generate a private key for use in the exchange. >>> server_private_key = ec.generate_private_key( - ... ec.SECP384R1(), default_backend() + ... ec.SECP384R1() ... ) >>> # In a real handshake the peer is a remote client. For this >>> # example we'll generate another local private key though. >>> peer_private_key = ec.generate_private_key( - ... ec.SECP384R1(), default_backend() + ... ec.SECP384R1() ... ) >>> shared_key = server_private_key.exchange( ... ec.ECDH(), peer_private_key.public_key()) @@ -291,7 +289,6 @@ Elliptic Curve Key Exchange algorithm ... length=32, ... salt=None, ... info=b'handshake data', - ... backend=default_backend() ... ).derive(shared_key) >>> # And now we can demonstrate that the handshake performed in the >>> # opposite direction gives the same final value @@ -303,7 +300,6 @@ Elliptic Curve Key Exchange algorithm ... length=32, ... salt=None, ... info=b'handshake data', - ... backend=default_backend() ... ).derive(same_shared_key) >>> derived_key == same_derived_key True @@ -316,19 +312,18 @@ Elliptic Curve Key Exchange algorithm .. doctest:: - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.asymmetric import ec >>> from cryptography.hazmat.primitives.kdf.hkdf import HKDF >>> # Generate a private key for use in the exchange. >>> private_key = ec.generate_private_key( - ... ec.SECP384R1(), default_backend() + ... ec.SECP384R1() ... ) >>> # In a real handshake the peer_public_key will be received from the >>> # other party. For this example we'll generate another private key >>> # and get a public key from that. >>> peer_public_key = ec.generate_private_key( - ... ec.SECP384R1(), default_backend() + ... ec.SECP384R1() ... ).public_key() >>> shared_key = private_key.exchange(ec.ECDH(), peer_public_key) >>> # Perform key derivation. @@ -337,14 +332,13 @@ Elliptic Curve Key Exchange algorithm ... length=32, ... salt=None, ... info=b'handshake data', - ... backend=default_backend() ... ).derive(shared_key) >>> # For the next handshake we MUST generate another private key. >>> private_key_2 = ec.generate_private_key( - ... ec.SECP384R1(), default_backend() + ... ec.SECP384R1() ... ) >>> peer_public_key_2 = ec.generate_private_key( - ... ec.SECP384R1(), default_backend() + ... ec.SECP384R1() ... ).public_key() >>> shared_key_2 = private_key_2.exchange(ec.ECDH(), peer_public_key_2) >>> derived_key_2 = HKDF( @@ -352,7 +346,6 @@ Elliptic Curve Key Exchange algorithm ... length=32, ... salt=None, ... info=b'handshake data', - ... backend=default_backend() ... ).derive(shared_key_2) Elliptic Curves @@ -787,11 +780,10 @@ This sample demonstrates how to generate a private key and serialize it. .. doctest:: - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import serialization >>> from cryptography.hazmat.primitives.asymmetric import ec - >>> private_key = ec.generate_private_key(ec.SECP384R1(), default_backend()) + >>> private_key = ec.generate_private_key(ec.SECP384R1()) >>> serialized_private = private_key.private_bytes( ... encoding=serialization.Encoding.PEM, @@ -831,14 +823,12 @@ in PEM format. >>> loaded_public_key = serialization.load_pem_public_key( ... serialized_public, - ... backend=default_backend() ... ) >>> loaded_private_key = serialization.load_pem_private_key( ... serialized_private, ... # or password=None, if in plain text ... password=b'testpassword', - ... backend=default_backend() ... ) diff --git a/docs/hazmat/primitives/asymmetric/rsa.rst b/docs/hazmat/primitives/asymmetric/rsa.rst index c3311c80c96c..33f402b65a43 100644 --- a/docs/hazmat/primitives/asymmetric/rsa.rst +++ b/docs/hazmat/primitives/asymmetric/rsa.rst @@ -32,12 +32,10 @@ mathematical properties`_. .. doctest:: - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives.asymmetric import rsa >>> private_key = rsa.generate_private_key( ... public_exponent=65537, ... key_size=2048, - ... backend=default_backend() ... ) :param int public_exponent: The public exponent of the new key. @@ -68,14 +66,12 @@ markers), you can load it: .. code-block:: pycon - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import serialization >>> with open("path/to/key.pem", "rb") as key_file: ... private_key = serialization.load_pem_private_key( ... key_file.read(), ... password=None, - ... backend=default_backend() ... ) Serialized keys may optionally be encrypted on disk using a password. In this @@ -171,7 +167,7 @@ separately and pass that value using >>> from cryptography.hazmat.primitives.asymmetric import utils >>> chosen_hash = hashes.SHA256() - >>> hasher = hashes.Hash(chosen_hash, default_backend()) + >>> hasher = hashes.Hash(chosen_hash) >>> hasher.update(b"data & ") >>> hasher.update(b"more data") >>> digest = hasher.finalize() @@ -221,7 +217,7 @@ separately and pass that value using .. doctest:: >>> chosen_hash = hashes.SHA256() - >>> hasher = hashes.Hash(chosen_hash, default_backend()) + >>> hasher = hashes.Hash(chosen_hash) >>> hasher.update(b"data & ") >>> hasher.update(b"more data") >>> digest = hasher.finalize() @@ -346,6 +342,11 @@ Padding :class:`OAEP` should be preferred for encryption and :class:`PSS` should be preferred for signatures. + .. warning:: + + Our implementation of PKCS1 v1.5 decryption is not constant time. See + :doc:`/limitations` for details. + .. function:: calculate_max_pss_salt_length(key, hash_algorithm) @@ -713,6 +714,55 @@ Key interfaces :raises cryptography.exceptions.InvalidSignature: If the signature does not validate. + .. method:: recover_data_from_signature(signature, padding, algorithm) + + .. versionadded:: 3.3 + + Recovers the signed data from the signature. The data contains the + digest of the original message string. The ``padding`` and + ``algorithm`` parameters must match the ones used when the signature + was created for the recovery to succeed. + + The ``algorithm`` parameter can also be set to ``None`` to recover all + the data present in the signature, without regard to its format or the + hash algorithm used for its creation. + + For + :class:`~cryptography.hazmat.primitives.asymmetric.padding.PKCS1v15` + padding, this returns the data after removing the padding layer. For + standard signatures the data contains the full ``DigestInfo`` structure. + For non-standard signatures, any data can be returned, including zero- + length data. + + Normally you should use the + :meth:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey.verify` + function to validate the signature. But for some non-standard signature + formats you may need to explicitly recover and validate the signed + data. Following are some examples: + + - Some old Thawte and Verisign timestamp certificates without ``DigestInfo``. + - Signed MD5/SHA1 hashes in TLS 1.1 or earlier (RFC 4346, section 4.7). + - IKE version 1 signatures without ``DigestInfo`` (RFC 2409, section 5.1). + + :param bytes signature: The signature. + + :param padding: An instance of + :class:`~cryptography.hazmat.primitives.asymmetric.padding.AsymmetricPadding`. + Recovery is only supported with some of the padding types. (Currently + only with + :class:`~cryptography.hazmat.primitives.asymmetric.padding.PKCS1v15`). + + :param algorithm: An instance of + :class:`~cryptography.hazmat.primitives.hashes.HashAlgorithm`. + Can be ``None`` to return the all the data present in the signature. + + :return bytes: The signed data. + + :raises cryptography.exceptions.InvalidSignature: If the signature is + invalid. + + :raises cryptography.exceptions.UnsupportedAlgorithm: If signature + data recovery is not supported with the provided ``padding`` type. .. class:: RSAPublicKeyWithSerialization diff --git a/docs/hazmat/primitives/asymmetric/serialization.rst b/docs/hazmat/primitives/asymmetric/serialization.rst index 914fe51df7ca..2324340e119c 100644 --- a/docs/hazmat/primitives/asymmetric/serialization.rst +++ b/docs/hazmat/primitives/asymmetric/serialization.rst @@ -88,10 +88,9 @@ methods. .. doctest:: - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives.asymmetric import dsa, rsa >>> from cryptography.hazmat.primitives.serialization import load_pem_private_key - >>> key = load_pem_private_key(pem_data, password=None, backend=default_backend()) + >>> key = load_pem_private_key(pem_data, password=None) >>> if isinstance(key, rsa.RSAPrivateKey): ... signature = sign_with_rsa_key(key, message) ... elif isinstance(key, dsa.DSAPrivateKey): @@ -159,8 +158,7 @@ all begin with ``-----BEGIN {format}-----`` and end with ``-----END password was supplied. :raises cryptography.exceptions.UnsupportedAlgorithm: If the serialized key - is of a type that is not supported by the backend or if the key is - encrypted with a symmetric cipher that is not supported by the backend. + is of a type that is not supported by the backend. .. function:: load_pem_public_key(data, backend=None) @@ -173,7 +171,7 @@ all begin with ``-----BEGIN {format}-----`` and end with ``-----END .. doctest:: >>> from cryptography.hazmat.primitives.serialization import load_pem_public_key - >>> key = load_pem_public_key(public_pem_data, backend=default_backend()) + >>> key = load_pem_public_key(public_pem_data) >>> isinstance(key, rsa.RSAPublicKey) True @@ -208,7 +206,7 @@ all begin with ``-----BEGIN {format}-----`` and end with ``-----END >>> from cryptography.hazmat.primitives.serialization import load_pem_parameters >>> from cryptography.hazmat.primitives.asymmetric import dh - >>> parameters = load_pem_parameters(parameters_pem_data, backend=default_backend()) + >>> parameters = load_pem_parameters(parameters_pem_data) >>> isinstance(parameters, dh.DHParameters) True @@ -268,16 +266,14 @@ the rest. not encrypted. Or if the key was encrypted but no password was supplied. - :raises cryptography.exceptions.UnsupportedAlgorithm: If the serialized key is of a type that - is not supported by the backend or if the key is encrypted with a - symmetric cipher that is not supported by the backend. + :raises cryptography.exceptions.UnsupportedAlgorithm: If the serialized key + is of a type that is not supported by the backend. .. doctest:: - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives.asymmetric import rsa >>> from cryptography.hazmat.primitives.serialization import load_der_private_key - >>> key = load_der_private_key(der_data, password=None, backend=default_backend()) + >>> key = load_der_private_key(der_data, password=None) >>> isinstance(key, rsa.RSAPrivateKey) True @@ -310,10 +306,9 @@ the rest. .. doctest:: - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives.asymmetric import rsa >>> from cryptography.hazmat.primitives.serialization import load_der_public_key - >>> key = load_der_public_key(public_der_data, backend=default_backend()) + >>> key = load_der_public_key(public_der_data) >>> isinstance(key, rsa.RSAPublicKey) True @@ -341,10 +336,9 @@ the rest. .. doctest:: - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives.asymmetric import dh >>> from cryptography.hazmat.primitives.serialization import load_der_parameters - >>> parameters = load_der_parameters(parameters_der_data, backend=default_backend()) + >>> parameters = load_der_parameters(parameters_der_data) >>> isinstance(parameters, dh.DHParameters) True @@ -502,6 +496,11 @@ file suffix. Serialize a PKCS12 blob. + .. note:: + + Due to `a bug in Firefox`_ it's not possible to load unencrypted PKCS12 + blobs in Firefox. + :param name: The friendly name to use for the supplied certificate and key. :type name: bytes @@ -578,6 +577,146 @@ contain certificates, CRLs, and much more. PKCS7 files commonly have a ``p7b``, :raises cryptography.exceptions.UnsupportedAlgorithm: If the PKCS7 data is of a type that is not supported. +.. testsetup:: + + ca_key = b""" + -----BEGIN PRIVATE KEY----- + MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgA8Zqz5vLeR0ePZUe + jBfdyMmnnI4U5uAJApWTsMn/RuWhRANCAAQY/8+7+Tm49d3D7sBAiwZ1BqtPzdgs + UiROH+AQRme1XxW5Yr07zwxvvhr3tKEPtLnLboazUPlsUb/Bgte+xfkF + -----END PRIVATE KEY----- + """.strip() + + ca_cert = b""" + -----BEGIN CERTIFICATE----- + MIIBUTCB96ADAgECAgIDCTAKBggqhkjOPQQDAjAnMQswCQYDVQQGEwJVUzEYMBYG + A1UEAwwPY3J5cHRvZ3JhcGh5IENBMB4XDTE3MDEwMTEyMDEwMFoXDTM4MTIzMTA4 + MzAwMFowJzELMAkGA1UEBhMCVVMxGDAWBgNVBAMMD2NyeXB0b2dyYXBoeSBDQTBZ + MBMGByqGSM49AgEGCCqGSM49AwEHA0IABBj/z7v5Obj13cPuwECLBnUGq0/N2CxS + JE4f4BBGZ7VfFblivTvPDG++Gve0oQ+0uctuhrNQ+WxRv8GC177F+QWjEzARMA8G + A1UdEwEB/wQFMAMBAf8wCgYIKoZIzj0EAwIDSQAwRgIhANES742XWm64tkGnz8Dn + pG6u2lHkZFQr3oaVvPcemvlbAiEA0WGGzmYx5C9UvfXIK7NEziT4pQtyESE0uRVK + Xw4nMqk= + -----END CERTIFICATE----- + """.strip() + + +.. class:: PKCS7SignatureBuilder + + The PKCS7 signature builder can create both basic PKCS7 signed messages as + well as S/MIME messages, which are commonly used in email. S/MIME has + multiple versions, but this implements a subset of :rfc:`2632`, also known + as S/MIME Version 3. + + .. versionadded:: 3.2 + + .. doctest:: + + >>> from cryptography import x509 + >>> from cryptography.hazmat.primitives import hashes, serialization + >>> from cryptography.hazmat.primitives.serialization import pkcs7 + >>> cert = x509.load_pem_x509_certificate(ca_cert) + >>> key = serialization.load_pem_private_key(ca_key, None) + >>> options = [pkcs7.PKCS7Options.DetachedSignature] + >>> pkcs7.PKCS7SignatureBuilder().set_data( + ... b"data to sign" + ... ).add_signer( + ... cert, key, hashes.SHA256() + ... ).sign( + ... serialization.Encoding.SMIME, options + ... ) + b'...' + + .. method:: set_data(data) + + :param data: The data to be hashed and signed. + :type data: :term:`bytes-like` + + .. method:: add_signer(certificate, private_key, hash_algorithm) + + :param certificate: The :class:`~cryptography.x509.Certificate`. + + :param private_key: The + :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey` or + :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKey` + associated with the certificate provided. + + :param hash_algorithm: The + :class:`~cryptography.hazmat.primitives.hashes.HashAlgorithm` that + will be used to generate the signature. This must be an instance of + :class:`~cryptography.hazmat.primitives.hashes.SHA1`, + :class:`~cryptography.hazmat.primitives.hashes.SHA224`, + :class:`~cryptography.hazmat.primitives.hashes.SHA256`, + :class:`~cryptography.hazmat.primitives.hashes.SHA384`, or + :class:`~cryptography.hazmat.primitives.hashes.SHA512`. + + .. method:: add_certificate(certificate) + + Add an additional certificate (typically used to help build a + verification chain) to the PKCS7 structure. This method may + be called multiple times to add as many certificates as desired. + + :param certificate: The :class:`~cryptography.x509.Certificate` to add. + + .. method:: sign(encoding, options, backend=None) + + :param encoding: :attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM`, + :attr:`~cryptography.hazmat.primitives.serialization.Encoding.DER`, + or :attr:`~cryptography.hazmat.primitives.serialization.Encoding.SMIME`. + + :param options: A list of + :class:`~cryptography.hazmat.primitives.serialization.pkcs7.PKCS7Options`. + + :return bytes: The signed PKCS7 message. + + :param backend: An optional backend. + + +.. class:: PKCS7Options + + .. versionadded:: 3.2 + + An enumeration of options for PKCS7 signature creation. + + .. attribute:: Text + + The text option adds ``text/plain`` headers to an S/MIME message when + serializing to + :attr:`~cryptography.hazmat.primitives.serialization.Encoding.SMIME`. + This option is disallowed with ``DER`` serialization. + + .. attribute:: Binary + + Signing normally converts line endings (LF to CRLF). When + passing this option the data will not be converted. + + .. attribute:: DetachedSignature + + Don't embed the signed data within the ASN.1. When signing with + :attr:`~cryptography.hazmat.primitives.serialization.Encoding.SMIME` + this also results in the data being added as clear text before the + PEM encoded structure. + + .. attribute:: NoCapabilities + + PKCS7 structures contain a ``MIMECapabilities`` section inside the + ``authenticatedAttributes``. Passing this as an option removes + ``MIMECapabilities``. + + .. attribute:: NoAttributes + + PKCS7 structures contain an ``authenticatedAttributes`` section. + Passing this as an option removes that section. Note that if you + pass ``NoAttributes`` you can't pass ``NoCapabilities`` since + ``NoAttributes`` removes ``MIMECapabilities`` and more. + + .. attribute:: NoCerts + + Don't include the signer's certificate in the PKCS7 structure. This can + reduce the size of the signature but requires that the recipient can + obtain the signer's certificate by other means (for example from a + previously signed message). + Serialization Formats ~~~~~~~~~~~~~~~~~~~~~ @@ -771,6 +910,12 @@ Serialization Encodings The format used by elliptic curve point encodings. This is a binary format. + .. attribute:: SMIME + + .. versionadded:: 3.2 + + An output format used for PKCS7. This is a text format. + Serialization Encryption Types ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -802,6 +947,7 @@ Serialization Encryption Types Do not encrypt. +.. _`a bug in Firefox`: https://bugzilla.mozilla.org/show_bug.cgi?id=773111 .. _`PKCS3`: https://www.teletrust.de/fileadmin/files/oid/oid_pkcs-3v1-4.pdf .. _`SEC 1 v2.0`: https://www.secg.org/sec1-v2.pdf .. _`PROTOCOL.key`: https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key diff --git a/docs/hazmat/primitives/asymmetric/utils.rst b/docs/hazmat/primitives/asymmetric/utils.rst index f46acb2ec081..487926e91256 100644 --- a/docs/hazmat/primitives/asymmetric/utils.rst +++ b/docs/hazmat/primitives/asymmetric/utils.rst @@ -57,7 +57,6 @@ Asymmetric Utilities .. doctest:: >>> import hashlib - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.asymmetric import ( ... padding, rsa, utils @@ -65,7 +64,6 @@ Asymmetric Utilities >>> private_key = rsa.generate_private_key( ... public_exponent=65537, ... key_size=2048, - ... backend=default_backend() ... ) >>> prehashed_msg = hashlib.sha256(b"A message I want to sign").digest() >>> signature = private_key.sign( diff --git a/docs/hazmat/primitives/asymmetric/x25519.rst b/docs/hazmat/primitives/asymmetric/x25519.rst index ea01fbaa08e7..014f3d01d5d3 100644 --- a/docs/hazmat/primitives/asymmetric/x25519.rst +++ b/docs/hazmat/primitives/asymmetric/x25519.rst @@ -21,7 +21,6 @@ present. .. doctest:: - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey >>> from cryptography.hazmat.primitives.kdf.hkdf import HKDF @@ -39,7 +38,6 @@ present. ... length=32, ... salt=None, ... info=b'handshake data', - ... backend=default_backend() ... ).derive(shared_key) >>> # For the next handshake we MUST generate another private key. >>> private_key_2 = X25519PrivateKey.generate() @@ -50,7 +48,6 @@ present. ... length=32, ... salt=None, ... info=b'handshake data', - ... backend=default_backend() ... ).derive(shared_key_2) Key interfaces diff --git a/docs/hazmat/primitives/asymmetric/x448.rst b/docs/hazmat/primitives/asymmetric/x448.rst index 4e1f0421f542..f166355b83fa 100644 --- a/docs/hazmat/primitives/asymmetric/x448.rst +++ b/docs/hazmat/primitives/asymmetric/x448.rst @@ -21,7 +21,6 @@ present. .. doctest:: - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.asymmetric.x448 import X448PrivateKey >>> from cryptography.hazmat.primitives.kdf.hkdf import HKDF @@ -39,7 +38,6 @@ present. ... length=32, ... salt=None, ... info=b'handshake data', - ... backend=default_backend() ... ).derive(shared_key) >>> # For the next handshake we MUST generate another private key. >>> private_key_2 = X448PrivateKey.generate() @@ -50,7 +48,6 @@ present. ... length=32, ... salt=None, ... info=b'handshake data', - ... backend=default_backend() ... ).derive(shared_key_2) Key interfaces diff --git a/docs/hazmat/primitives/cryptographic-hashes.rst b/docs/hazmat/primitives/cryptographic-hashes.rst index 6615ba6fa5ba..4cdc034a6b84 100644 --- a/docs/hazmat/primitives/cryptographic-hashes.rst +++ b/docs/hazmat/primitives/cryptographic-hashes.rst @@ -20,9 +20,8 @@ Message digests (Hashing) .. doctest:: - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes - >>> digest = hashes.Hash(hashes.SHA256(), backend=default_backend()) + >>> digest = hashes.Hash(hashes.SHA256()) >>> digest.update(b"abc") >>> digest.update(b"123") >>> digest.finalize() diff --git a/docs/hazmat/primitives/key-derivation-functions.rst b/docs/hazmat/primitives/key-derivation-functions.rst index fc74a98f039d..62457b28490c 100644 --- a/docs/hazmat/primitives/key-derivation-functions.rst +++ b/docs/hazmat/primitives/key-derivation-functions.rst @@ -55,8 +55,6 @@ PBKDF2 >>> import os >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC - >>> from cryptography.hazmat.backends import default_backend - >>> backend = default_backend() >>> # Salts should be randomly generated >>> salt = os.urandom(16) >>> # derive @@ -65,7 +63,6 @@ PBKDF2 ... length=32, ... salt=salt, ... iterations=100000, - ... backend=backend ... ) >>> key = kdf.derive(b"my great password") >>> # verify @@ -74,7 +71,6 @@ PBKDF2 ... length=32, ... salt=salt, ... iterations=100000, - ... backend=backend ... ) >>> kdf.verify(b"my great password", key) @@ -159,8 +155,6 @@ Scrypt >>> import os >>> from cryptography.hazmat.primitives.kdf.scrypt import Scrypt - >>> from cryptography.hazmat.backends import default_backend - >>> backend = default_backend() >>> salt = os.urandom(16) >>> # derive >>> kdf = Scrypt( @@ -169,7 +163,6 @@ Scrypt ... n=2**14, ... r=8, ... p=1, - ... backend=backend ... ) >>> key = kdf.derive(b"my great password") >>> # verify @@ -179,7 +172,6 @@ Scrypt ... n=2**14, ... r=8, ... p=1, - ... backend=backend ... ) >>> kdf.verify(b"my great password", key) @@ -276,21 +268,17 @@ ConcatKDF >>> import os >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.kdf.concatkdf import ConcatKDFHash - >>> from cryptography.hazmat.backends import default_backend - >>> backend = default_backend() >>> otherinfo = b"concatkdf-example" >>> ckdf = ConcatKDFHash( ... algorithm=hashes.SHA256(), ... length=32, ... otherinfo=otherinfo, - ... backend=backend ... ) >>> key = ckdf.derive(b"input key") >>> ckdf = ConcatKDFHash( ... algorithm=hashes.SHA256(), ... length=32, ... otherinfo=otherinfo, - ... backend=backend ... ) >>> ckdf.verify(b"input key", key) @@ -364,8 +352,6 @@ ConcatKDF >>> import os >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.kdf.concatkdf import ConcatKDFHMAC - >>> from cryptography.hazmat.backends import default_backend - >>> backend = default_backend() >>> salt = os.urandom(16) >>> otherinfo = b"concatkdf-example" >>> ckdf = ConcatKDFHMAC( @@ -373,7 +359,6 @@ ConcatKDF ... length=32, ... salt=salt, ... otherinfo=otherinfo, - ... backend=backend ... ) >>> key = ckdf.derive(b"input key") >>> ckdf = ConcatKDFHMAC( @@ -381,7 +366,6 @@ ConcatKDF ... length=32, ... salt=salt, ... otherinfo=otherinfo, - ... backend=backend ... ) >>> ckdf.verify(b"input key", key) @@ -468,8 +452,6 @@ HKDF >>> import os >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.kdf.hkdf import HKDF - >>> from cryptography.hazmat.backends import default_backend - >>> backend = default_backend() >>> salt = os.urandom(16) >>> info = b"hkdf-example" >>> hkdf = HKDF( @@ -477,7 +459,6 @@ HKDF ... length=32, ... salt=salt, ... info=info, - ... backend=backend ... ) >>> key = hkdf.derive(b"input key") >>> hkdf = HKDF( @@ -485,7 +466,6 @@ HKDF ... length=32, ... salt=salt, ... info=info, - ... backend=backend ... ) >>> hkdf.verify(b"input key", key) @@ -575,22 +555,18 @@ HKDF >>> import os >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.kdf.hkdf import HKDFExpand - >>> from cryptography.hazmat.backends import default_backend - >>> backend = default_backend() >>> info = b"hkdf-example" >>> key_material = os.urandom(16) >>> hkdf = HKDFExpand( ... algorithm=hashes.SHA256(), ... length=32, ... info=info, - ... backend=backend ... ) >>> key = hkdf.derive(key_material) >>> hkdf = HKDFExpand( ... algorithm=hashes.SHA256(), ... length=32, ... info=info, - ... backend=backend ... ) >>> hkdf.verify(key_material, key) @@ -676,8 +652,6 @@ KBKDF >>> from cryptography.hazmat.primitives.kdf.kbkdf import ( ... CounterLocation, KBKDFHMAC, Mode ... ) - >>> from cryptography.hazmat.backends import default_backend - >>> backend = default_backend() >>> label = b"KBKDF HMAC Label" >>> context = b"KBKDF HMAC Context" >>> kdf = KBKDFHMAC( @@ -690,7 +664,6 @@ KBKDF ... label=label, ... context=context, ... fixed=None, - ... backend=backend ... ) >>> key = kdf.derive(b"input key") >>> kdf = KBKDFHMAC( @@ -703,7 +676,6 @@ KBKDF ... label=label, ... context=context, ... fixed=None, - ... backend=backend ... ) >>> kdf.verify(b"input key", key) @@ -835,21 +807,17 @@ X963KDF >>> import os >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.kdf.x963kdf import X963KDF - >>> from cryptography.hazmat.backends import default_backend - >>> backend = default_backend() >>> sharedinfo = b"ANSI X9.63 Example" >>> xkdf = X963KDF( ... algorithm=hashes.SHA256(), ... length=32, ... sharedinfo=sharedinfo, - ... backend=backend ... ) >>> key = xkdf.derive(b"input key") >>> xkdf = X963KDF( ... algorithm=hashes.SHA256(), ... length=32, ... sharedinfo=sharedinfo, - ... backend=backend ... ) >>> xkdf.verify(b"input key", key) diff --git a/docs/hazmat/primitives/mac/cmac.rst b/docs/hazmat/primitives/mac/cmac.rst index 796af22007b1..b40a90a82aa9 100644 --- a/docs/hazmat/primitives/mac/cmac.rst +++ b/docs/hazmat/primitives/mac/cmac.rst @@ -26,10 +26,9 @@ A subset of CMAC with the AES-128 algorithm is described in :rfc:`4493`. .. doctest:: - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import cmac >>> from cryptography.hazmat.primitives.ciphers import algorithms - >>> c = cmac.CMAC(algorithms.AES(key), backend=default_backend()) + >>> c = cmac.CMAC(algorithms.AES(key)) >>> c.update(b"message to authenticate") >>> c.finalize() b'CT\x1d\xc8\x0e\x15\xbe4e\xdb\xb6\x84\xca\xd9Xk' @@ -47,7 +46,7 @@ A subset of CMAC with the AES-128 algorithm is described in :rfc:`4493`. .. doctest:: - >>> c = cmac.CMAC(algorithms.AES(key), backend=default_backend()) + >>> c = cmac.CMAC(algorithms.AES(key)) >>> c.update(b"message to authenticate") >>> c.verify(b"an incorrect signature") Traceback (most recent call last): diff --git a/docs/hazmat/primitives/mac/hmac.rst b/docs/hazmat/primitives/mac/hmac.rst index ba49da224236..3695270b7ab2 100644 --- a/docs/hazmat/primitives/mac/hmac.rst +++ b/docs/hazmat/primitives/mac/hmac.rst @@ -27,9 +27,8 @@ of a message. .. doctest:: - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes, hmac - >>> h = hmac.HMAC(key, hashes.SHA256(), backend=default_backend()) + >>> h = hmac.HMAC(key, hashes.SHA256()) >>> h.update(b"message to hash") >>> h.finalize() b'#F\xdaI\x8b"e\xc4\xf1\xbb\x9a\x8fc\xff\xf5\xdex.\xbc\xcd/+\x8a\x86\x1d\x84\'\xc3\xa6\x1d\xd8J' @@ -47,7 +46,7 @@ of a message. .. doctest:: - >>> h = hmac.HMAC(key, hashes.SHA256(), backend=default_backend()) + >>> h = hmac.HMAC(key, hashes.SHA256()) >>> h.update(b"message to hash") >>> h.verify(b"an incorrect signature") Traceback (most recent call last): diff --git a/docs/hazmat/primitives/padding.rst b/docs/hazmat/primitives/padding.rst index 9581df88bd70..99d500a05b68 100644 --- a/docs/hazmat/primitives/padding.rst +++ b/docs/hazmat/primitives/padding.rst @@ -107,7 +107,8 @@ multiple of the block size. .. method:: update(data) - :param bytes data: The data you wish to pass into the context. + :param data: The data you wish to pass into the context. + :type data: :term:`bytes-like` :return bytes: Returns the data that was padded or unpadded. :raises TypeError: Raised if data is not bytes. :raises cryptography.exceptions.AlreadyFinalized: See :meth:`finalize`. diff --git a/docs/hazmat/primitives/symmetric-encryption.rst b/docs/hazmat/primitives/symmetric-encryption.rst index 74db180cb2a9..8551acb2693f 100644 --- a/docs/hazmat/primitives/symmetric-encryption.rst +++ b/docs/hazmat/primitives/symmetric-encryption.rst @@ -11,7 +11,8 @@ where the sender and receiver both use the same secret key. Note that symmetric encryption is **not** sufficient for most applications because it only provides secrecy but not authenticity. That means an attacker can't see the message but an attacker can create bogus messages and force the application to -decrypt them. +decrypt them. In many contexts, a lack of authentication on encrypted messages +can result in a loss of secrecy as well. For this reason it is **strongly** recommended to combine encryption with a message authentication code, such as :doc:`HMAC `, @@ -33,11 +34,9 @@ it fits your needs before implementing anything using this module.** >>> import os >>> from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes - >>> from cryptography.hazmat.backends import default_backend - >>> backend = default_backend() >>> key = os.urandom(32) >>> iv = os.urandom(16) - >>> cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=backend) + >>> cipher = Cipher(algorithms.AES(key), modes.CBC(iv)) >>> encryptor = cipher.encryptor() >>> ct = encryptor.update(b"a secret message") + encryptor.finalize() >>> decryptor = cipher.decryptor() @@ -147,10 +146,9 @@ Algorithms .. doctest:: >>> from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes - >>> from cryptography.hazmat.backends import default_backend >>> nonce = os.urandom(16) >>> algorithm = algorithms.ChaCha20(key, nonce) - >>> cipher = Cipher(algorithm, mode=None, backend=default_backend()) + >>> cipher = Cipher(algorithm, mode=None) >>> encryptor = cipher.encryptor() >>> ct = encryptor.update(b"a secret message") >>> decryptor = cipher.decryptor() @@ -231,9 +229,8 @@ Weak ciphers .. doctest:: >>> from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes - >>> from cryptography.hazmat.backends import default_backend >>> algorithm = algorithms.ARC4(key) - >>> cipher = Cipher(algorithm, mode=None, backend=default_backend()) + >>> cipher = Cipher(algorithm, mode=None) >>> encryptor = cipher.encryptor() >>> ct = encryptor.update(b"a secret message") >>> decryptor = cipher.decryptor() @@ -425,7 +422,6 @@ Modes import os - from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.ciphers import ( Cipher, algorithms, modes ) @@ -439,7 +435,6 @@ Modes encryptor = Cipher( algorithms.AES(key), modes.GCM(iv), - backend=default_backend() ).encryptor() # associated_data will be authenticated but not encrypted, @@ -458,7 +453,6 @@ Modes decryptor = Cipher( algorithms.AES(key), modes.GCM(iv, tag), - backend=default_backend() ).decryptor() # We put associated_data back in or the tag will fail to verify @@ -595,11 +589,9 @@ Interfaces >>> import os >>> from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes - >>> from cryptography.hazmat.backends import default_backend - >>> backend = default_backend() >>> key = os.urandom(32) >>> iv = os.urandom(16) - >>> cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=backend) + >>> cipher = Cipher(algorithms.AES(key), modes.CBC(iv)) >>> encryptor = cipher.encryptor() >>> # the buffer needs to be at least len(data) + n - 1 where n is cipher/mode block size in bytes >>> buf = bytearray(31) diff --git a/docs/hazmat/primitives/twofactor.rst b/docs/hazmat/primitives/twofactor.rst index 838e5e1055c3..1d2ab452ce0a 100644 --- a/docs/hazmat/primitives/twofactor.rst +++ b/docs/hazmat/primitives/twofactor.rst @@ -33,11 +33,10 @@ codes (HMAC). .. doctest:: >>> import os - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives.twofactor.hotp import HOTP >>> from cryptography.hazmat.primitives.hashes import SHA1 >>> key = os.urandom(20) - >>> hotp = HOTP(key, 6, SHA1(), backend=default_backend()) + >>> hotp = HOTP(key, 6, SHA1()) >>> hotp_value = hotp.generate(0) >>> hotp.verify(hotp_value, 0) @@ -129,7 +128,7 @@ similar to the following code. assert look_ahead >= 0 correct_counter = None - otp = HOTP(key, 6, default_backend()) + otp = HOTP(key, 6) for count in range(counter, counter + look_ahead): try: otp.verify(hotp, count) @@ -155,11 +154,10 @@ similar to the following code. >>> import os >>> import time - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives.twofactor.totp import TOTP >>> from cryptography.hazmat.primitives.hashes import SHA1 >>> key = os.urandom(20) - >>> totp = TOTP(key, 8, SHA1(), 30, backend=default_backend()) + >>> totp = TOTP(key, 8, SHA1(), 30) >>> time_value = time.time() >>> totp_value = totp.generate(time_value) >>> totp.verify(totp_value, time_value) diff --git a/docs/index.rst b/docs/index.rst index 396ed0b6e9d5..ec3913f41d8c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -68,7 +68,6 @@ hazmat layer only when necessary. exceptions random-numbers hazmat/backends/index - hazmat/bindings/index .. toctree:: :maxdepth: 2 diff --git a/docs/installation.rst b/docs/installation.rst index 62126fc76816..ea4625582a87 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -10,7 +10,7 @@ You can install ``cryptography`` with ``pip``: Supported platforms ------------------- -Currently we test ``cryptography`` on Python 2.7, 3.5+, +Currently we test ``cryptography`` on Python 2.7, 3.6+, PyPy 7.3.1, and PyPy3 7.3.1 on these operating systems. * x86-64 CentOS 7.x @@ -27,13 +27,9 @@ PyPy 7.3.1, and PyPy3 7.3.1 on these operating systems. We test compiling with ``clang`` as well as ``gcc`` and use the following OpenSSL releases: -* ``OpenSSL 1.0.2-latest`` * ``OpenSSL 1.1.0-latest`` * ``OpenSSL 1.1.1-latest`` -.. warning:: - - Cryptography 3.1 has deprecated support for OpenSSL 1.0.2. Building cryptography on Windows -------------------------------- @@ -49,7 +45,7 @@ just run 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 -(VC2010 works for Python 2.7 while VC2015 is required for 3.5 and above). +(VC2010 works for Python 2.7 while 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: diff --git a/docs/limitations.rst b/docs/limitations.rst index 092d8a7cff91..5763ecd40299 100644 --- a/docs/limitations.rst +++ b/docs/limitations.rst @@ -20,5 +20,25 @@ like almost all software in Python is potentially vulnerable to this attack. The Likelihood: unlikely, Remediation Cost: expensive to repair" and we do not consider this a high risk for most users. +RSA PKCS1 v1.5 constant time decryption +--------------------------------------- + +RSA decryption has several different modes, one of which is PKCS1 v1.5. When +used in online contexts, a secure protocol implementation requires that peers +not be able to tell whether RSA PKCS1 v1.5 decryption failed or succeeded, +even by timing variability. + +``cryptography`` does not provide an API that makes this possible, due to the +fact that RSA decryption raises an exception on failure, which takes a +different amount of time than returning a value in the success case. + +For this reason, at present, we recommend not implementing online protocols +that use RSA PKCS1 v1.5 decryption with ``cryptography`` -- independent of this +limitation, such protocols generally have poor security properties due to their +lack of forward security. + +If a constant time RSA PKCS1 v1.5 decryption API is truly required, you should +contribute one to ``cryptography``. + .. _`Memory wiping`: https://devblogs.microsoft.com/oldnewthing/?p=4223 .. _`CERT secure coding guidelines`: https://wiki.sei.cmu.edu/confluence/display/c/MEM03-C.+Clear+sensitive+information+stored+in+reusable+resources diff --git a/docs/security.rst b/docs/security.rst index 8cdd2d114d9a..d11f2700012a 100644 --- a/docs/security.rst +++ b/docs/security.rst @@ -53,10 +53,9 @@ We ask that you do not report security issues to our normal GitHub issue tracker. If you believe you've identified a security issue with ``cryptography``, please -report it to ``alex.gaynor@gmail.com``. Messages may be optionally encrypted -with PGP using key fingerprint -``F7FC 698F AAE2 D2EF BECD E98E D1B3 ADC0 E023 8CA6`` (this public key is -available from most commonly-used key servers). +report it to ``alex.gaynor@gmail.com`` and/or ``paul.l.kehrer@gmail.com``. You +should verify that your MTA uses TLS to ensure the confidentiality of your +message. Once you've submitted an issue via email, you should receive an acknowledgment within 48 hours, and depending on the action to be taken, you may receive diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt index 9ec971b36ad2..f0486e05f704 100644 --- a/docs/spelling_wordlist.txt +++ b/docs/spelling_wordlist.txt @@ -7,6 +7,7 @@ backend Backends backends bcrypt +Bleichenbacher Blowfish boolean Botan @@ -102,6 +103,7 @@ Solaris syscall Tanja testability +Thawte timestamp timestamps tunable diff --git a/docs/x509/ocsp.rst b/docs/x509/ocsp.rst index 80ff99087c78..0c2d07aef852 100644 --- a/docs/x509/ocsp.rst +++ b/docs/x509/ocsp.rst @@ -167,12 +167,11 @@ Creating Requests .. doctest:: - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import serialization >>> from cryptography.hazmat.primitives.hashes import SHA1 >>> from cryptography.x509 import load_pem_x509_certificate, ocsp - >>> cert = load_pem_x509_certificate(pem_cert, default_backend()) - >>> issuer = load_pem_x509_certificate(pem_issuer, default_backend()) + >>> cert = load_pem_x509_certificate(pem_cert) + >>> issuer = load_pem_x509_certificate(pem_issuer) >>> builder = ocsp.OCSPRequestBuilder() >>> # SHA1 is in this example because RFC 5019 mandates its use. >>> builder = builder.add_certificate(cert, issuer, SHA1()) @@ -315,13 +314,12 @@ Creating Responses .. doctest:: >>> import datetime - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes, serialization >>> from cryptography.x509 import load_pem_x509_certificate, ocsp - >>> cert = load_pem_x509_certificate(pem_cert, default_backend()) - >>> issuer = load_pem_x509_certificate(pem_issuer, default_backend()) - >>> responder_cert = load_pem_x509_certificate(pem_responder_cert, default_backend()) - >>> responder_key = serialization.load_pem_private_key(pem_responder_key, None, default_backend()) + >>> cert = load_pem_x509_certificate(pem_cert) + >>> issuer = load_pem_x509_certificate(pem_issuer) + >>> responder_cert = load_pem_x509_certificate(pem_responder_cert) + >>> responder_key = serialization.load_pem_private_key(pem_responder_key, None) >>> builder = ocsp.OCSPResponseBuilder() >>> # SHA1 is in this example because RFC 5019 mandates its use. >>> builder = builder.add_response( @@ -350,7 +348,6 @@ Creating Responses .. doctest:: - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes, serialization >>> from cryptography.x509 import load_pem_x509_certificate, ocsp >>> response = ocsp.OCSPResponseBuilder.build_unsuccessful( diff --git a/docs/x509/reference.rst b/docs/x509/reference.rst index 484339e61036..a46c5d623238 100644 --- a/docs/x509/reference.rst +++ b/docs/x509/reference.rst @@ -168,8 +168,7 @@ Loading Certificates .. doctest:: >>> from cryptography import x509 - >>> from cryptography.hazmat.backends import default_backend - >>> cert = x509.load_pem_x509_certificate(pem_data, default_backend()) + >>> cert = x509.load_pem_x509_certificate(pem_data) >>> cert.serial_number 2 @@ -212,9 +211,8 @@ Loading Certificate Revocation Lists .. doctest:: >>> from cryptography import x509 - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes - >>> crl = x509.load_pem_x509_crl(pem_crl_data, default_backend()) + >>> crl = x509.load_pem_x509_crl(pem_crl_data) >>> isinstance(crl.signature_hash_algorithm, hashes.SHA256) True @@ -258,9 +256,8 @@ Loading Certificate Signing Requests .. doctest:: >>> from cryptography import x509 - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes - >>> csr = x509.load_pem_x509_csr(pem_req_data, default_backend()) + >>> csr = x509.load_pem_x509_csr(pem_req_data) >>> isinstance(csr.signature_hash_algorithm, hashes.SHA256) True @@ -474,8 +471,8 @@ X.509 Certificate Object >>> from cryptography.hazmat.primitives.serialization import load_pem_public_key >>> from cryptography.hazmat.primitives.asymmetric import padding - >>> issuer_public_key = load_pem_public_key(pem_issuer_public_key, default_backend()) - >>> cert_to_check = x509.load_pem_x509_certificate(pem_data_to_check, default_backend()) + >>> issuer_public_key = load_pem_public_key(pem_issuer_public_key) + >>> cert_to_check = x509.load_pem_x509_certificate(pem_data_to_check) >>> issuer_public_key.verify( ... cert_to_check.signature, ... cert_to_check.tbs_certificate_bytes, @@ -671,7 +668,6 @@ X.509 Certificate Builder .. doctest:: >>> from cryptography import x509 - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.asymmetric import rsa >>> from cryptography.x509.oid import NameOID @@ -680,7 +676,6 @@ X.509 Certificate Builder >>> private_key = rsa.generate_private_key( ... public_exponent=65537, ... key_size=2048, - ... backend=default_backend() ... ) >>> public_key = private_key.public_key() >>> builder = x509.CertificateBuilder() @@ -705,7 +700,6 @@ X.509 Certificate Builder ... ) >>> certificate = builder.sign( ... private_key=private_key, algorithm=hashes.SHA256(), - ... backend=default_backend() ... ) >>> isinstance(certificate, x509.Certificate) True @@ -945,7 +939,6 @@ X.509 Certificate Revocation List Builder .. doctest:: >>> from cryptography import x509 - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.asymmetric import rsa >>> from cryptography.x509.oid import NameOID @@ -954,7 +947,6 @@ X.509 Certificate Revocation List Builder >>> private_key = rsa.generate_private_key( ... public_exponent=65537, ... key_size=2048, - ... backend=default_backend() ... ) >>> builder = x509.CertificateRevocationListBuilder() >>> builder = builder.issuer_name(x509.Name([ @@ -966,11 +958,10 @@ X.509 Certificate Revocation List Builder ... 333 ... ).revocation_date( ... datetime.datetime.today() - ... ).build(default_backend()) + ... ).build() >>> builder = builder.add_revoked_certificate(revoked_cert) >>> crl = builder.sign( ... private_key=private_key, algorithm=hashes.SHA256(), - ... backend=default_backend() ... ) >>> len(crl) 1 @@ -1107,12 +1098,11 @@ X.509 Revoked Certificate Builder .. doctest:: >>> from cryptography import x509 - >>> from cryptography.hazmat.backends import default_backend >>> import datetime >>> builder = x509.RevokedCertificateBuilder() >>> builder = builder.revocation_date(datetime.datetime.today()) >>> builder = builder.serial_number(3333) - >>> revoked_certificate = builder.build(default_backend()) + >>> revoked_certificate = builder.build() >>> isinstance(revoked_certificate, x509.RevokedCertificate) True @@ -1161,14 +1151,12 @@ X.509 CSR (Certificate Signing Request) Builder Object .. doctest:: >>> from cryptography import x509 - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes >>> from cryptography.hazmat.primitives.asymmetric import rsa >>> from cryptography.x509.oid import AttributeOID, NameOID >>> private_key = rsa.generate_private_key( ... public_exponent=65537, ... key_size=2048, - ... backend=default_backend() ... ) >>> builder = x509.CertificateSigningRequestBuilder() >>> builder = builder.subject_name(x509.Name([ @@ -1181,7 +1169,7 @@ X.509 CSR (Certificate Signing Request) Builder Object ... AttributeOID.CHALLENGE_PASSWORD, b"changeit" ... ) >>> request = builder.sign( - ... private_key, hashes.SHA256(), default_backend() + ... private_key, hashes.SHA256() ... ) >>> isinstance(request, x509.CertificateSigningRequest) True @@ -1907,8 +1895,7 @@ X.509 Extensions .. doctest:: >>> from cryptography import x509 - >>> from cryptography.hazmat.backends import default_backend - >>> issuer_cert = x509.load_pem_x509_certificate(pem_data, default_backend()) + >>> issuer_cert = x509.load_pem_x509_certificate(pem_data) >>> x509.AuthorityKeyIdentifier.from_issuer_public_key(issuer_cert.public_key()) @@ -1937,8 +1924,7 @@ X.509 Extensions .. doctest:: >>> from cryptography import x509 - >>> from cryptography.hazmat.backends import default_backend - >>> issuer_cert = x509.load_pem_x509_certificate(pem_data, default_backend()) + >>> issuer_cert = x509.load_pem_x509_certificate(pem_data) >>> ski_ext = issuer_cert.extensions.get_extension_for_class(x509.SubjectKeyIdentifier) >>> x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier(ski_ext.value) @@ -1985,8 +1971,7 @@ X.509 Extensions .. doctest:: >>> from cryptography import x509 - >>> from cryptography.hazmat.backends import default_backend - >>> csr = x509.load_pem_x509_csr(pem_req_data, default_backend()) + >>> csr = x509.load_pem_x509_csr(pem_req_data) >>> x509.SubjectKeyIdentifier.from_public_key(csr.public_key()) @@ -2021,9 +2006,8 @@ X.509 Extensions .. doctest:: >>> from cryptography import x509 - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import hashes - >>> cert = x509.load_pem_x509_certificate(cryptography_cert_pem, default_backend()) + >>> cert = x509.load_pem_x509_certificate(cryptography_cert_pem) >>> # Get the subjectAltName extension from the certificate >>> ext = cert.extensions.get_extension_for_oid(ExtensionOID.SUBJECT_ALTERNATIVE_NAME) >>> # Get the dNSName entries from the SAN extension diff --git a/docs/x509/tutorial.rst b/docs/x509/tutorial.rst index cc2ffb770683..f5ca416ceb9f 100644 --- a/docs/x509/tutorial.rst +++ b/docs/x509/tutorial.rst @@ -27,14 +27,12 @@ are the most common types of keys on the web right now): .. code-block:: pycon - >>> from cryptography.hazmat.backends import default_backend >>> from cryptography.hazmat.primitives import serialization >>> from cryptography.hazmat.primitives.asymmetric import rsa >>> # Generate our key >>> key = rsa.generate_private_key( ... public_exponent=65537, ... key_size=2048, - ... backend=default_backend() ... ) >>> # Write our key to disk for safe keeping >>> with open("path/to/store/key.pem", "wb") as f: @@ -76,7 +74,7 @@ a few details: ... ]), ... critical=False, ... # Sign the CSR with our private key. - ... ).sign(key, hashes.SHA256(), default_backend()) + ... ).sign(key, hashes.SHA256()) >>> # Write our CSR out to disk. >>> with open("path/to/csr.pem", "wb") as f: ... f.write(csr.public_bytes(serialization.Encoding.PEM)) @@ -105,7 +103,6 @@ Like generating a CSR, we start with creating a new private key: >>> key = rsa.generate_private_key( ... public_exponent=65537, ... key_size=2048, - ... backend=default_backend() ... ) >>> # Write our key to disk for safe keeping >>> with open("path/to/store/key.pem", "wb") as f: @@ -145,7 +142,7 @@ Then we generate the certificate itself: ... x509.SubjectAlternativeName([x509.DNSName(u"localhost")]), ... critical=False, ... # Sign our certificate with our private key - ... ).sign(key, hashes.SHA256(), default_backend()) + ... ).sign(key, hashes.SHA256()) >>> # Write our certificate out to disk. >>> with open("path/to/certificate.pem", "wb") as f: ... f.write(cert.public_bytes(serialization.Encoding.PEM)) diff --git a/pyproject.toml b/pyproject.toml index 957b1fd04bb0..667f29e23ff2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ requires = [ "setuptools>=40.6.0", "wheel", # Must be kept in sync with the `setup_requirements` in `setup.py` - "cffi>=1.8,!=1.11.3; platform_python_implementation != 'PyPy'", + "cffi>=1.12; platform_python_implementation != 'PyPy'", ] build-backend = "setuptools.build_meta" diff --git a/setup.py b/setup.py index 82800a96e59b..4ebbc1b50203 100644 --- a/setup.py +++ b/setup.py @@ -10,20 +10,9 @@ import platform import sys -import pkg_resources - -import setuptools from setuptools import find_packages, setup -if pkg_resources.parse_version( - setuptools.__version__ -) < pkg_resources.parse_version("18.5"): - raise RuntimeError( - "cryptography requires setuptools 18.5 or newer, please upgrade to a " - "newer version of setuptools" - ) - base_dir = os.path.dirname(__file__) src_dir = os.path.join(base_dir, "src") @@ -37,7 +26,7 @@ # `setup_requirements` must be kept in sync with `pyproject.toml` -setup_requirements = ["cffi>=1.8,!=1.11.3"] +setup_requirements = ["cffi>=1.12"] if platform.python_implementation() == "PyPy": if sys.pypy_version_info < (5, 4): @@ -51,74 +40,107 @@ long_description = f.read() -setup( - name=about["__title__"], - version=about["__version__"], - description=about["__summary__"], - long_description=long_description, - long_description_content_type="text/x-rst", - license=about["__license__"], - url=about["__uri__"], - author=about["__author__"], - author_email=about["__email__"], - classifiers=[ - "Development Status :: 5 - Production/Stable", - "Intended Audience :: Developers", - "License :: OSI Approved :: Apache Software License", - "License :: OSI Approved :: BSD License", - "Natural Language :: English", - "Operating System :: MacOS :: MacOS X", - "Operating System :: POSIX", - "Operating System :: POSIX :: BSD", - "Operating System :: POSIX :: Linux", - "Operating System :: Microsoft :: Windows", - "Programming Language :: Python", - "Programming Language :: Python :: 2", - "Programming Language :: Python :: 2.7", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.5", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: Implementation :: CPython", - "Programming Language :: Python :: Implementation :: PyPy", - "Topic :: Security :: Cryptography", - ], - package_dir={"": "src"}, - packages=find_packages(where="src", exclude=["_cffi_src", "_cffi_src.*"]), - include_package_data=True, - python_requires=">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*", - install_requires=["six >= 1.4.1"] + setup_requirements, - setup_requires=setup_requirements, - extras_require={ - ":python_version < '3'": ["enum34", "ipaddress"], - "test": [ - "pytest>=3.6.0,!=3.9.0,!=3.9.1,!=3.9.2", - "pretend", - "iso8601", - "pytz", - "hypothesis>=1.11.4,!=3.79.2", +try: + setup( + name=about["__title__"], + version=about["__version__"], + description=about["__summary__"], + long_description=long_description, + long_description_content_type="text/x-rst", + license=about["__license__"], + url=about["__uri__"], + author=about["__author__"], + author_email=about["__email__"], + classifiers=[ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "License :: OSI Approved :: Apache Software License", + "License :: OSI Approved :: BSD License", + "Natural Language :: English", + "Operating System :: MacOS :: MacOS X", + "Operating System :: POSIX", + "Operating System :: POSIX :: BSD", + "Operating System :: POSIX :: Linux", + "Operating System :: Microsoft :: Windows", + "Programming Language :: Python", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", + "Topic :: Security :: Cryptography", ], - "docs": [ - "sphinx >= 1.6.5,!=1.8.0,!=3.1.0,!=3.1.1", - "sphinx_rtd_theme", + package_dir={"": "src"}, + packages=find_packages( + where="src", exclude=["_cffi_src", "_cffi_src.*"] + ), + include_package_data=True, + python_requires=( + ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*" + ), + install_requires=["six >= 1.4.1"] + setup_requirements, + setup_requires=setup_requirements, + extras_require={ + ":python_version < '3'": ["enum34", "ipaddress"], + "test": [ + "pytest>=3.6.0,!=3.9.0,!=3.9.1,!=3.9.2", + "pretend", + "iso8601", + "pytz", + "hypothesis>=1.11.4,!=3.79.2", + ], + "docs": [ + "sphinx >= 1.6.5,!=1.8.0,!=3.1.0,!=3.1.1", + "sphinx_rtd_theme", + ], + "docstest": [ + "doc8", + "pyenchant >= 1.6.11", + "twine >= 1.12.0", + "sphinxcontrib-spelling >= 4.0.1", + ], + "pep8test": [ + "black", + "flake8", + "flake8-import-order", + "pep8-naming", + ], + # This extra is for OpenSSH private keys that use bcrypt KDF + # Versions: v3.1.3 - ignore_few_rounds, v3.1.5 - abi3 + "ssh": ["bcrypt >= 3.1.5"], + }, + # for cffi + zip_safe=False, + ext_package="cryptography.hazmat.bindings", + cffi_modules=[ + "src/_cffi_src/build_openssl.py:ffi", + "src/_cffi_src/build_padding.py:ffi", ], - "docstest": [ - "doc8", - "pyenchant >= 1.6.11", - "twine >= 1.12.0", - "sphinxcontrib-spelling >= 4.0.1", - ], - "pep8test": ["black", "flake8", "flake8-import-order", "pep8-naming"], - # This extra is for OpenSSH private keys that use bcrypt KDF - # Versions: v3.1.3 - ignore_few_rounds, v3.1.5 - abi3 - "ssh": ["bcrypt >= 3.1.5"], - }, - # for cffi - zip_safe=False, - ext_package="cryptography.hazmat.bindings", - cffi_modules=[ - "src/_cffi_src/build_openssl.py:ffi", - "src/_cffi_src/build_padding.py:ffi", - ], -) + ) +except: # noqa: E722 + # Note: This is a bare exception that re-raises so that we don't interfere + # with anything the installation machinery might want to do. Because we + # print this for any exception this msg can appear (e.g. in verbose logs) + # even if there's no failure. For example, SetupRequirementsError is raised + # during PEP517 building and prints this text. setuptools raises SystemExit + # when compilation fails right now, but it's possible this isn't stable + # or a public API commitment so we'll remain ultra conservative. + print( + """ + =============================DEBUG ASSISTANCE============================= + If you are seeing a compilation error please try the following steps to + successfully install cryptography: + 1) Upgrade to the latest pip and try again. This will fix errors for most + users. See: https://pip.pypa.io/en/stable/installing/#upgrading-pip + 2) Read https://cryptography.io/en/latest/installation.html for specific + instructions for your platform. + 3) Check our frequently asked questions for more information: + https://cryptography.io/en/latest/faq.html + =============================DEBUG ASSISTANCE============================= + """ + ) + raise diff --git a/src/_cffi_src/build_openssl.py b/src/_cffi_src/build_openssl.py index 35ccd6b9be0a..4380c3396909 100644 --- a/src/_cffi_src/build_openssl.py +++ b/src/_cffi_src/build_openssl.py @@ -22,17 +22,15 @@ def _get_openssl_libraries(platform): return [] # OpenSSL goes by a different library name on different operating systems. if platform == "win32" and compiler_type() == "msvc": - windows_link_legacy_openssl = os.environ.get( - "CRYPTOGRAPHY_WINDOWS_LINK_LEGACY_OPENSSL", None - ) - if windows_link_legacy_openssl is None: - # Link against the 1.1.0 names - # CRYPTOGRAPHY_OPENSSL_110_OR_GREATER - libs = ["libssl", "libcrypto"] - else: - # Link against the 1.0.2 and lower names - libs = ["libeay32", "ssleay32"] - return libs + ["advapi32", "crypt32", "gdi32", "user32", "ws2_32"] + return [ + "libssl", + "libcrypto", + "advapi32", + "crypt32", + "gdi32", + "user32", + "ws2_32", + ] else: # darwin, linux, mingw all use this path # In some circumstances, the order in which these libs are @@ -53,10 +51,9 @@ def _extra_compile_args(platform): We set -Wconversion args here so that we only do Wconversion checks on the code we're compiling and not on cffi itself (as passing -Wconversion in CFLAGS would do). We set no error on sign conversion because some - function signatures in OpenSSL have changed from long -> unsigned long - in the past. Since that isn't a precision issue we don't care. - When we drop support for CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 we can - revisit this. + function signatures in LibreSSL differ from OpenSSL have changed on long + vs. unsigned long in the past. Since that isn't a precision issue we don't + care. """ # make sure the compiler used supports the flags to be added is_gcc = False @@ -119,13 +116,6 @@ def _extra_compile_args(platform): "callbacks", ], libraries=_get_openssl_libraries(sys.platform), - # These args are passed here so that we only do Wconversion checks on the - # code we're compiling and not on cffi itself (as passing -Wconversion in - # CFLAGS would do). We set no error on sign convesrion because some - # function signatures in OpenSSL have changed from long -> unsigned long - # in the past. Since that isn't a precision issue we don't care. - # When we drop support for CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 we can - # revisit this. extra_compile_args=_extra_compile_args(sys.platform), extra_link_args=extra_link_args(compiler_type()), ) diff --git a/src/_cffi_src/openssl/bio.py b/src/_cffi_src/openssl/bio.py index 8f5a3e6a2b6f..52d57c6228d1 100644 --- a/src/_cffi_src/openssl/bio.py +++ b/src/_cffi_src/openssl/bio.py @@ -41,10 +41,4 @@ """ CUSTOMIZATIONS = """ -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 && !CRYPTOGRAPHY_IS_LIBRESSL -int BIO_up_ref(BIO *b) { - CRYPTO_add(&b->references, 1, CRYPTO_LOCK_BIO); - return 1; -} -#endif """ diff --git a/src/_cffi_src/openssl/callbacks.py b/src/_cffi_src/openssl/callbacks.py index 33ebf4df400f..19301b973a28 100644 --- a/src/_cffi_src/openssl/callbacks.py +++ b/src/_cffi_src/openssl/callbacks.py @@ -5,25 +5,7 @@ from __future__ import absolute_import, division, print_function INCLUDES = """ -#include -#include -#include -#include - -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#include -#include -#else -#include -#include -#include -#endif - -#ifdef __MVS__ -#include -#endif +#include """ TYPES = """ @@ -37,124 +19,10 @@ """ FUNCTIONS = """ -int Cryptography_setup_ssl_threads(void); int Cryptography_pem_password_cb(char *, int, int, void *); """ CUSTOMIZATIONS = """ -/* This code is derived from the locking code found in the Python _ssl module's - locking callback for OpenSSL. - - Copyright 2001-2016 Python Software Foundation; All Rights Reserved. - - It has been subsequently modified to use cross platform locking without - using CPython APIs by Armin Rigo of the PyPy project. -*/ - -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 -#ifdef _WIN32 -typedef CRITICAL_SECTION Cryptography_mutex; -static __inline void cryptography_mutex_init(Cryptography_mutex *mutex) { - InitializeCriticalSection(mutex); -} -static __inline void cryptography_mutex_lock(Cryptography_mutex *mutex) { - EnterCriticalSection(mutex); -} -static __inline void cryptography_mutex_unlock(Cryptography_mutex *mutex) { - LeaveCriticalSection(mutex); -} -#else -typedef pthread_mutex_t Cryptography_mutex; -#define ASSERT_STATUS(call) \ - if ((call) != 0) { \ - perror("Fatal error in callback initialization: " #call); \ - abort(); \ - } -#ifdef __MVS__ -/* When pthread_mutex_init is called more than once on the same mutex, - on z/OS this throws an EBUSY error. -*/ -#define ASSERT_STATUS_INIT(call) \ - if ((call) != 0 && errno != EBUSY) { \ - perror("Fatal error in callback initialization: " #call); \ - abort(); \ - } -#else -#define ASSERT_STATUS_INIT ASSERT_STATUS -#endif -static inline void cryptography_mutex_init(Cryptography_mutex *mutex) { -#if !defined(pthread_mutexattr_default) -# define pthread_mutexattr_default ((pthread_mutexattr_t *)NULL) -#endif - ASSERT_STATUS_INIT(pthread_mutex_init(mutex, pthread_mutexattr_default)); -} -static inline void cryptography_mutex_lock(Cryptography_mutex *mutex) { - ASSERT_STATUS(pthread_mutex_lock(mutex)); -} -static inline void cryptography_mutex_unlock(Cryptography_mutex *mutex) { - ASSERT_STATUS(pthread_mutex_unlock(mutex)); -} -#endif - - -static int _ssl_locks_count = 0; -static Cryptography_mutex *_ssl_locks = NULL; - -static void _ssl_thread_locking_function(int mode, int n, const char *file, - int line) { - /* this function is needed to perform locking on shared data - structures. (Note that OpenSSL uses a number of global data - structures that will be implicitly shared whenever multiple - threads use OpenSSL.) Multi-threaded applications will - crash at random if it is not set. - - locking_function() must be able to handle up to - CRYPTO_num_locks() different mutex locks. It sets the n-th - lock if mode & CRYPTO_LOCK, and releases it otherwise. - - file and line are the file number of the function setting the - lock. They can be useful for debugging. - */ - - if ((_ssl_locks == NULL) || - (n < 0) || (n >= _ssl_locks_count)) { - return; - } - - if (mode & CRYPTO_LOCK) { - cryptography_mutex_lock(_ssl_locks + n); - } else { - cryptography_mutex_unlock(_ssl_locks + n); - } -} - -static void init_mutexes(void) { - int i; - for (i = 0; i < _ssl_locks_count; i++) { - cryptography_mutex_init(_ssl_locks + i); - } -} - - -int Cryptography_setup_ssl_threads(void) { - if (_ssl_locks == NULL) { - _ssl_locks_count = CRYPTO_num_locks(); - _ssl_locks = calloc(_ssl_locks_count, sizeof(Cryptography_mutex)); - if (_ssl_locks == NULL) { - return 0; - } - init_mutexes(); - CRYPTO_set_locking_callback(_ssl_thread_locking_function); -#ifndef _WIN32 - pthread_atfork(NULL, NULL, &init_mutexes); -#endif - } - return 1; -} -#else -int (*Cryptography_setup_ssl_threads)(void) = NULL; -#endif - typedef struct { char *password; int length; diff --git a/src/_cffi_src/openssl/crypto.py b/src/_cffi_src/openssl/crypto.py index f3623b21f146..6064a4eeea99 100644 --- a/src/_cffi_src/openssl/crypto.py +++ b/src/_cffi_src/openssl/crypto.py @@ -9,7 +9,6 @@ """ TYPES = """ -static const long Cryptography_HAS_LOCKING_CALLBACKS; static const long Cryptography_HAS_MEM_FUNCTIONS; static const long Cryptography_HAS_OPENSSL_CLEANUP; @@ -28,10 +27,6 @@ FUNCTIONS = """ void OPENSSL_cleanup(void); -/* as of 1.1.0 OpenSSL does its own locking *angelic chorus*. This function - is now a noop macro. We can delete this once we drop 1.0.2 support. */ -void (*CRYPTO_get_locking_callback(void))(int, int, const char *, int); - /* SSLeay was removed in 1.1.0 */ unsigned long SSLeay(void); const char *SSLeay_version(int); @@ -79,13 +74,8 @@ # define OPENSSL_PLATFORM SSLEAY_PLATFORM # define OPENSSL_DIR SSLEAY_DIR #endif -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 -static const long Cryptography_HAS_LOCKING_CALLBACKS = 1; -#else -static const long Cryptography_HAS_LOCKING_CALLBACKS = 0; -#endif -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 +#if CRYPTOGRAPHY_IS_LIBRESSL static const long Cryptography_HAS_OPENSSL_CLEANUP = 0; void (*OPENSSL_cleanup)(void) = NULL; diff --git a/src/_cffi_src/openssl/cryptography.py b/src/_cffi_src/openssl/cryptography.py index d9d4a9ea0f41..f24bee5a4f82 100644 --- a/src/_cffi_src/openssl/cryptography.py +++ b/src/_cffi_src/openssl/cryptography.py @@ -33,19 +33,9 @@ #include #endif -#define CRYPTOGRAPHY_OPENSSL_102L_OR_GREATER \ - (OPENSSL_VERSION_NUMBER >= 0x100020cf && !CRYPTOGRAPHY_IS_LIBRESSL) -#define CRYPTOGRAPHY_OPENSSL_102U_OR_GREATER \ - (OPENSSL_VERSION_NUMBER >= 0x1000215fL && !CRYPTOGRAPHY_IS_LIBRESSL) -#define CRYPTOGRAPHY_OPENSSL_110_OR_GREATER \ - (OPENSSL_VERSION_NUMBER >= 0x10100000 && !CRYPTOGRAPHY_IS_LIBRESSL) #define CRYPTOGRAPHY_OPENSSL_110F_OR_GREATER \ (OPENSSL_VERSION_NUMBER >= 0x1010006f && !CRYPTOGRAPHY_IS_LIBRESSL) -#define CRYPTOGRAPHY_OPENSSL_LESS_THAN_102I \ - (OPENSSL_VERSION_NUMBER < 0x1000209f || CRYPTOGRAPHY_IS_LIBRESSL) -#define CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 \ - (OPENSSL_VERSION_NUMBER < 0x10100000 || CRYPTOGRAPHY_IS_LIBRESSL) #define CRYPTOGRAPHY_OPENSSL_LESS_THAN_110J \ (OPENSSL_VERSION_NUMBER < 0x101000af || CRYPTOGRAPHY_IS_LIBRESSL) #define CRYPTOGRAPHY_OPENSSL_LESS_THAN_111 \ @@ -54,8 +44,8 @@ (OPENSSL_VERSION_NUMBER < 0x10101020 || CRYPTOGRAPHY_IS_LIBRESSL) #define CRYPTOGRAPHY_OPENSSL_LESS_THAN_111D \ (OPENSSL_VERSION_NUMBER < 0x10101040 || CRYPTOGRAPHY_IS_LIBRESSL) -#if (CRYPTOGRAPHY_OPENSSL_LESS_THAN_111D && !defined(OPENSSL_NO_ENGINE)) || \ - defined(USE_OSRANDOM_RNG_FOR_TESTING) +#if (CRYPTOGRAPHY_OPENSSL_LESS_THAN_111D && !CRYPTOGRAPHY_IS_LIBRESSL && \ + !defined(OPENSSL_NO_ENGINE)) || defined(USE_OSRANDOM_RNG_FOR_TESTING) #define CRYPTOGRAPHY_NEEDS_OSRANDOM_ENGINE 1 #else #define CRYPTOGRAPHY_NEEDS_OSRANDOM_ENGINE 0 @@ -63,13 +53,8 @@ """ TYPES = """ -static const int CRYPTOGRAPHY_OPENSSL_102L_OR_GREATER; -static const int CRYPTOGRAPHY_OPENSSL_102U_OR_GREATER; -static const int CRYPTOGRAPHY_OPENSSL_110_OR_GREATER; static const int CRYPTOGRAPHY_OPENSSL_110F_OR_GREATER; -static const int CRYPTOGRAPHY_OPENSSL_LESS_THAN_102I; -static const int CRYPTOGRAPHY_OPENSSL_LESS_THAN_110; static const int CRYPTOGRAPHY_OPENSSL_LESS_THAN_111; static const int CRYPTOGRAPHY_OPENSSL_LESS_THAN_111B; static const int CRYPTOGRAPHY_NEEDS_OSRANDOM_ENGINE; diff --git a/src/_cffi_src/openssl/ct.py b/src/_cffi_src/openssl/ct.py index 162004a8da73..5f0670635fa2 100644 --- a/src/_cffi_src/openssl/ct.py +++ b/src/_cffi_src/openssl/ct.py @@ -50,13 +50,14 @@ int SCT_set_source(SCT *, sct_source_t); +Cryptography_STACK_OF_SCT *sk_SCT_new_null(void); +void sk_SCT_free(Cryptography_STACK_OF_SCT *); int sk_SCT_num(const Cryptography_STACK_OF_SCT *); SCT *sk_SCT_value(const Cryptography_STACK_OF_SCT *, int); +int sk_SCT_push(Cryptography_STACK_OF_SCT *, SCT *); void SCT_LIST_free(Cryptography_STACK_OF_SCT *); -int sk_SCT_push(Cryptography_STACK_OF_SCT *, SCT *); -Cryptography_STACK_OF_SCT *sk_SCT_new_null(void); SCT *SCT_new(void); int SCT_set1_log_id(SCT *, unsigned char *, size_t); void SCT_set_timestamp(SCT *, uint64_t); @@ -101,12 +102,13 @@ int (*SCT_set_source)(SCT *, sct_source_t) = NULL; +Cryptography_STACK_OF_SCT *(*sk_SCT_new_null)(void) = NULL; +void (*sk_SCT_free)(Cryptography_STACK_OF_SCT *) = NULL; int (*sk_SCT_num)(const Cryptography_STACK_OF_SCT *) = NULL; SCT *(*sk_SCT_value)(const Cryptography_STACK_OF_SCT *, int) = NULL; +int (*sk_SCT_push)(Cryptography_STACK_OF_SCT *, SCT *) = NULL; void (*SCT_LIST_free)(Cryptography_STACK_OF_SCT *) = NULL; -int (*sk_SCT_push)(Cryptography_STACK_OF_SCT *, SCT *) = NULL; -Cryptography_STACK_OF_SCT *(*sk_SCT_new_null)(void) = NULL; SCT *(*SCT_new)(void) = NULL; int (*SCT_set1_log_id)(SCT *, unsigned char *, size_t) = NULL; void (*SCT_set_timestamp)(SCT *, uint64_t) = NULL; diff --git a/src/_cffi_src/openssl/dh.py b/src/_cffi_src/openssl/dh.py index 0e1df23a6ac9..947a5a8ee02e 100644 --- a/src/_cffi_src/openssl/dh.py +++ b/src/_cffi_src/openssl/dh.py @@ -38,79 +38,7 @@ """ CUSTOMIZATIONS = """ -/* These functions were added in OpenSSL 1.1.0 */ -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 && !CRYPTOGRAPHY_IS_LIBRESSL -void DH_get0_pqg(const DH *dh, - const BIGNUM **p, const BIGNUM **q, const BIGNUM **g) -{ - if (p != NULL) - *p = dh->p; - if (q != NULL) - *q = dh->q; - if (g != NULL) - *g = dh->g; -} - -int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g) -{ - /* If the fields p and g in d are NULL, the corresponding input - * parameters MUST be non-NULL. q may remain NULL. - */ - if ((dh->p == NULL && p == NULL) - || (dh->g == NULL && g == NULL)) - return 0; - - if (p != NULL) { - BN_free(dh->p); - dh->p = p; - } - if (q != NULL) { - BN_free(dh->q); - dh->q = q; - } - if (g != NULL) { - BN_free(dh->g); - dh->g = g; - } - - if (q != NULL) { - dh->length = BN_num_bits(q); - } - - return 1; -} - -void DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key) -{ - if (pub_key != NULL) - *pub_key = dh->pub_key; - if (priv_key != NULL) - *priv_key = dh->priv_key; -} - -int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key) -{ - /* If the field pub_key in dh is NULL, the corresponding input - * parameters MUST be non-NULL. The priv_key field may - * be left NULL. - */ - if (dh->pub_key == NULL && pub_key == NULL) - return 0; - - if (pub_key != NULL) { - BN_free(dh->pub_key); - dh->pub_key = pub_key; - } - if (priv_key != NULL) { - BN_free(dh->priv_key); - dh->priv_key = priv_key; - } - - return 1; -} -#endif - -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 +#if CRYPTOGRAPHY_IS_LIBRESSL #ifndef DH_CHECK_Q_NOT_PRIME #define DH_CHECK_Q_NOT_PRIME 0x10 #endif diff --git a/src/_cffi_src/openssl/dsa.py b/src/_cffi_src/openssl/dsa.py index 938c18fcf1b1..3a290067bc5b 100644 --- a/src/_cffi_src/openssl/dsa.py +++ b/src/_cffi_src/openssl/dsa.py @@ -34,70 +34,4 @@ """ CUSTOMIZATIONS = """ -/* These functions were added in OpenSSL 1.1.0 */ -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 && !CRYPTOGRAPHY_IS_LIBRESSL -void DSA_get0_pqg(const DSA *d, - const BIGNUM **p, const BIGNUM **q, const BIGNUM **g) -{ - if (p != NULL) - *p = d->p; - if (q != NULL) - *q = d->q; - if (g != NULL) - *g = d->g; -} -int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g) -{ - /* If the fields p, q and g in d are NULL, the corresponding input - * parameters MUST be non-NULL. - */ - if ((d->p == NULL && p == NULL) - || (d->q == NULL && q == NULL) - || (d->g == NULL && g == NULL)) - return 0; - - if (p != NULL) { - BN_free(d->p); - d->p = p; - } - if (q != NULL) { - BN_free(d->q); - d->q = q; - } - if (g != NULL) { - BN_free(d->g); - d->g = g; - } - - return 1; -} -void DSA_get0_key(const DSA *d, - const BIGNUM **pub_key, const BIGNUM **priv_key) -{ - if (pub_key != NULL) - *pub_key = d->pub_key; - if (priv_key != NULL) - *priv_key = d->priv_key; -} -int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key) -{ - /* If the field pub_key in d is NULL, the corresponding input - * parameters MUST be non-NULL. The priv_key field may - * be left NULL. - */ - if (d->pub_key == NULL && pub_key == NULL) - return 0; - - if (pub_key != NULL) { - BN_free(d->pub_key); - d->pub_key = pub_key; - } - if (priv_key != NULL) { - BN_free(d->priv_key); - d->priv_key = priv_key; - } - - return 1; -} -#endif """ diff --git a/src/_cffi_src/openssl/engine.py b/src/_cffi_src/openssl/engine.py index fa503a2644b5..24cdd42a8393 100644 --- a/src/_cffi_src/openssl/engine.py +++ b/src/_cffi_src/openssl/engine.py @@ -10,6 +10,7 @@ TYPES = """ typedef ... ENGINE; +typedef ... UI_METHOD; static const long Cryptography_HAS_ENGINE; """ @@ -25,6 +26,12 @@ int ENGINE_free(ENGINE *); const char *ENGINE_get_name(const ENGINE *); +// These bindings are unused by cryptography or pyOpenSSL but are present +// for advanced users who need them. +int ENGINE_ctrl_cmd_string(ENGINE *, const char *, const char *, int); +void ENGINE_load_builtin_engines(void); +EVP_PKEY *ENGINE_load_private_key(ENGINE *, const char *, UI_METHOD *, void *); +EVP_PKEY *ENGINE_load_public_key(ENGINE *, const char *, UI_METHOD *, void *); """ CUSTOMIZATIONS = """ @@ -44,6 +51,14 @@ const char *(*ENGINE_get_id)(const ENGINE *) = NULL; const char *(*ENGINE_get_name)(const ENGINE *) = NULL; +int (*ENGINE_ctrl_cmd_string)(ENGINE *, const char *, const char *, + int) = NULL; +void (*ENGINE_load_builtin_engines)(void) = NULL; +EVP_PKEY *(*ENGINE_load_private_key)(ENGINE *, const char *, UI_METHOD *, + void *) = NULL; +EVP_PKEY *(*ENGINE_load_public_key)(ENGINE *, const char *, + UI_METHOD *, void *) = NULL; + #else static const long Cryptography_HAS_ENGINE = 1; #endif diff --git a/src/_cffi_src/openssl/evp.py b/src/_cffi_src/openssl/evp.py index d7ac93e603fc..ab7cfeb39511 100644 --- a/src/_cffi_src/openssl/evp.py +++ b/src/_cffi_src/openssl/evp.py @@ -101,6 +101,9 @@ int EVP_PKEY_verify_init(EVP_PKEY_CTX *); int EVP_PKEY_verify(EVP_PKEY_CTX *, const unsigned char *, size_t, const unsigned char *, size_t); +int EVP_PKEY_verify_recover_init(EVP_PKEY_CTX *); +int EVP_PKEY_verify_recover(EVP_PKEY_CTX *, unsigned char *, + size_t *, const unsigned char *, size_t); int EVP_PKEY_encrypt_init(EVP_PKEY_CTX *); int EVP_PKEY_decrypt_init(EVP_PKEY_CTX *); @@ -120,11 +123,12 @@ int EVP_PKEY_id(const EVP_PKEY *); int Cryptography_EVP_PKEY_id(const EVP_PKEY *); -/* in 1.1.0 _create and _destroy were renamed to _new and _free. The following - two functions wrap both the old and new functions so we can call them - without worrying about what OpenSSL we're running against. */ +EVP_MD_CTX *EVP_MD_CTX_new(void); +void EVP_MD_CTX_free(EVP_MD_CTX *); +/* Backwards compat aliases for pyOpenSSL */ EVP_MD_CTX *Cryptography_EVP_MD_CTX_new(void); void Cryptography_EVP_MD_CTX_free(EVP_MD_CTX *); + /* Added in 1.1.1 */ int EVP_DigestSign(EVP_MD_CTX *, unsigned char *, size_t *, const unsigned char *, size_t); @@ -174,22 +178,14 @@ int Cryptography_EVP_PKEY_id(const EVP_PKEY *key) { return EVP_PKEY_id(key); } - EVP_MD_CTX *Cryptography_EVP_MD_CTX_new(void) { -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 - return EVP_MD_CTX_create(); -#else return EVP_MD_CTX_new(); -#endif } -void Cryptography_EVP_MD_CTX_free(EVP_MD_CTX *ctx) { -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 - EVP_MD_CTX_destroy(ctx); -#else - EVP_MD_CTX_free(ctx); -#endif +void Cryptography_EVP_MD_CTX_free(EVP_MD_CTX *md) { + EVP_MD_CTX_free(md); } -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 || defined(OPENSSL_NO_SCRYPT) + +#if CRYPTOGRAPHY_IS_LIBRESSL || defined(OPENSSL_NO_SCRYPT) static const long Cryptography_HAS_SCRYPT = 0; int (*EVP_PBE_scrypt)(const char *, size_t, const unsigned char *, size_t, uint64_t, uint64_t, uint64_t, uint64_t, unsigned char *, @@ -198,7 +194,7 @@ static const long Cryptography_HAS_SCRYPT = 1; #endif -#if CRYPTOGRAPHY_OPENSSL_110_OR_GREATER +#if !CRYPTOGRAPHY_IS_LIBRESSL static const long Cryptography_HAS_EVP_PKEY_get_set_tls_encodedpoint = 1; #else static const long Cryptography_HAS_EVP_PKEY_get_set_tls_encodedpoint = 0; diff --git a/src/_cffi_src/openssl/hmac.py b/src/_cffi_src/openssl/hmac.py index 2bc70068ed6b..2e0e33ffe3b0 100644 --- a/src/_cffi_src/openssl/hmac.py +++ b/src/_cffi_src/openssl/hmac.py @@ -18,31 +18,9 @@ int HMAC_Final(HMAC_CTX *, unsigned char *, unsigned int *); int HMAC_CTX_copy(HMAC_CTX *, HMAC_CTX *); -HMAC_CTX *Cryptography_HMAC_CTX_new(void); -void Cryptography_HMAC_CTX_free(HMAC_CTX *ctx); +HMAC_CTX *HMAC_CTX_new(void); +void HMAC_CTX_free(HMAC_CTX *ctx); """ CUSTOMIZATIONS = """ -HMAC_CTX *Cryptography_HMAC_CTX_new(void) { -#if CRYPTOGRAPHY_OPENSSL_110_OR_GREATER - return HMAC_CTX_new(); -#else - /* This uses OPENSSL_zalloc in 1.1.0, which is malloc + memset */ - HMAC_CTX *ctx = (HMAC_CTX *)OPENSSL_malloc(sizeof(HMAC_CTX)); - memset(ctx, 0, sizeof(HMAC_CTX)); - return ctx; -#endif -} - - -void Cryptography_HMAC_CTX_free(HMAC_CTX *ctx) { -#if CRYPTOGRAPHY_OPENSSL_110_OR_GREATER - HMAC_CTX_free(ctx); -#else - if (ctx != NULL) { - HMAC_CTX_cleanup(ctx); - OPENSSL_free(ctx); - } -#endif -} """ diff --git a/src/_cffi_src/openssl/nid.py b/src/_cffi_src/openssl/nid.py index aecdc9c0f4ed..9ef88cdbbd41 100644 --- a/src/_cffi_src/openssl/nid.py +++ b/src/_cffi_src/openssl/nid.py @@ -9,8 +9,6 @@ """ TYPES = """ -static const int Cryptography_HAS_X25519; -static const int Cryptography_HAS_X448; static const int Cryptography_HAS_ED448; static const int Cryptography_HAS_ED25519; static const int Cryptography_HAS_POLY1305; @@ -33,24 +31,12 @@ """ CUSTOMIZATIONS = """ -#ifndef NID_X25519 -static const long Cryptography_HAS_X25519 = 0; -static const int NID_X25519 = 0; -#else -static const long Cryptography_HAS_X25519 = 1; -#endif #ifndef NID_ED25519 static const long Cryptography_HAS_ED25519 = 0; static const int NID_ED25519 = 0; #else static const long Cryptography_HAS_ED25519 = 1; #endif -#ifndef NID_X448 -static const long Cryptography_HAS_X448 = 0; -static const int NID_X448 = 0; -#else -static const long Cryptography_HAS_X448 = 1; -#endif #ifndef NID_ED448 static const long Cryptography_HAS_ED448 = 0; static const int NID_ED448 = 0; diff --git a/src/_cffi_src/openssl/ocsp.py b/src/_cffi_src/openssl/ocsp.py index f1a8bf617941..c3d034c2c48d 100644 --- a/src/_cffi_src/openssl/ocsp.py +++ b/src/_cffi_src/openssl/ocsp.py @@ -78,7 +78,7 @@ CUSTOMIZATIONS = """ #if ( \ - CRYPTOGRAPHY_OPENSSL_110_OR_GREATER && \ + !CRYPTOGRAPHY_IS_LIBRESSL && \ CRYPTOGRAPHY_OPENSSL_LESS_THAN_110J \ ) /* These structs come from ocsp_lcl.h and are needed to de-opaque the struct @@ -105,7 +105,7 @@ }; #endif -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 +#if CRYPTOGRAPHY_IS_LIBRESSL /* These functions are all taken from ocsp_cl.c in OpenSSL 1.1.0 */ const OCSP_CERTID *OCSP_SINGLERESP_get0_id(const OCSP_SINGLERESP *single) { @@ -147,7 +147,7 @@ #if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110J const X509_ALGOR *OCSP_resp_get0_tbs_sigalg(const OCSP_BASICRESP *bs) { -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 +#if CRYPTOGRAPHY_IS_LIBRESSL return bs->signatureAlgorithm; #else return &bs->signatureAlgorithm; @@ -156,7 +156,7 @@ const OCSP_RESPDATA *OCSP_resp_get0_respdata(const OCSP_BASICRESP *bs) { -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 +#if CRYPTOGRAPHY_IS_LIBRESSL return bs->tbsResponseData; #else return &bs->tbsResponseData; diff --git a/src/_cffi_src/openssl/pkcs7.py b/src/_cffi_src/openssl/pkcs7.py index 7f31b82b6296..c22263dfe6c1 100644 --- a/src/_cffi_src/openssl/pkcs7.py +++ b/src/_cffi_src/openssl/pkcs7.py @@ -24,6 +24,7 @@ typedef ... PKCS7_DIGEST; typedef ... PKCS7_ENCRYPT; typedef ... PKCS7_ENVELOPE; +typedef ... PKCS7_SIGNER_INFO; typedef struct { ASN1_OBJECT *type; @@ -51,10 +52,23 @@ static const int PKCS7_NOVERIFY; static const int PKCS7_STREAM; static const int PKCS7_TEXT; +static const int PKCS7_PARTIAL; """ FUNCTIONS = """ void PKCS7_free(PKCS7 *); +PKCS7 *PKCS7_sign(X509 *, EVP_PKEY *, Cryptography_STACK_OF_X509 *, + BIO *, int); +int SMIME_write_PKCS7(BIO *, PKCS7 *, BIO *, int); +int PEM_write_bio_PKCS7_stream(BIO *, PKCS7 *, BIO *, int); +PKCS7_SIGNER_INFO *PKCS7_sign_add_signer(PKCS7 *, X509 *, EVP_PKEY *, + const EVP_MD *, int); +int PKCS7_final(PKCS7 *, BIO *, int); +/* Included verify due to external consumer, see + https://github.com/pyca/cryptography/issues/5433 */ +int PKCS7_verify(PKCS7 *, Cryptography_STACK_OF_X509 *, X509_STORE *, BIO *, + BIO *, int); +PKCS7 *SMIME_read_PKCS7(BIO *, BIO **); int PKCS7_type_is_signed(PKCS7 *); int PKCS7_type_is_enveloped(PKCS7 *); diff --git a/src/_cffi_src/openssl/rand.py b/src/_cffi_src/openssl/rand.py index c0cd68365960..1bc2ec0bc386 100644 --- a/src/_cffi_src/openssl/rand.py +++ b/src/_cffi_src/openssl/rand.py @@ -10,8 +10,6 @@ TYPES = """ typedef ... RAND_METHOD; - -static const long Cryptography_HAS_EGD; """ FUNCTIONS = """ @@ -27,5 +25,4 @@ """ CUSTOMIZATIONS = """ -static const long Cryptography_HAS_EGD = 0; """ diff --git a/src/_cffi_src/openssl/rsa.py b/src/_cffi_src/openssl/rsa.py index 765498fbc9ef..92b8fa4600d8 100644 --- a/src/_cffi_src/openssl/rsa.py +++ b/src/_cffi_src/openssl/rsa.py @@ -17,7 +17,6 @@ static const int RSA_PKCS1_PSS_PADDING; static const int RSA_F4; -static const int Cryptography_HAS_PSS_PADDING; static const int Cryptography_HAS_RSA_OAEP_MD; static const int Cryptography_HAS_RSA_OAEP_LABEL; """ @@ -49,127 +48,14 @@ """ CUSTOMIZATIONS = """ -static const long Cryptography_HAS_PSS_PADDING = 1; - -#if defined(EVP_PKEY_CTX_set_rsa_oaep_md) +#if !CRYPTOGRAPHY_IS_LIBRESSL static const long Cryptography_HAS_RSA_OAEP_MD = 1; -#else -static const long Cryptography_HAS_RSA_OAEP_MD = 0; -int (*EVP_PKEY_CTX_set_rsa_oaep_md)(EVP_PKEY_CTX *, EVP_MD *) = NULL; -#endif - -#if defined(EVP_PKEY_CTX_set0_rsa_oaep_label) static const long Cryptography_HAS_RSA_OAEP_LABEL = 1; #else +static const long Cryptography_HAS_RSA_OAEP_MD = 0; static const long Cryptography_HAS_RSA_OAEP_LABEL = 0; +int (*EVP_PKEY_CTX_set_rsa_oaep_md)(EVP_PKEY_CTX *, EVP_MD *) = NULL; int (*EVP_PKEY_CTX_set0_rsa_oaep_label)(EVP_PKEY_CTX *, unsigned char *, int) = NULL; #endif - -/* These functions were added in OpenSSL 1.1.0 */ -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 && !CRYPTOGRAPHY_IS_LIBRESSL -int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) -{ - /* If the fields n and e in r are NULL, the corresponding input - * parameters MUST be non-NULL for n and e. d may be - * left NULL (in case only the public key is used). - */ - if ((r->n == NULL && n == NULL) - || (r->e == NULL && e == NULL)) - return 0; - - if (n != NULL) { - BN_free(r->n); - r->n = n; - } - if (e != NULL) { - BN_free(r->e); - r->e = e; - } - if (d != NULL) { - BN_free(r->d); - r->d = d; - } - - return 1; -} - -int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q) -{ - /* If the fields p and q in r are NULL, the corresponding input - * parameters MUST be non-NULL. - */ - if ((r->p == NULL && p == NULL) - || (r->q == NULL && q == NULL)) - return 0; - - if (p != NULL) { - BN_free(r->p); - r->p = p; - } - if (q != NULL) { - BN_free(r->q); - r->q = q; - } - - return 1; -} - -int RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp) -{ - /* If the fields dmp1, dmq1 and iqmp in r are NULL, the corresponding input - * parameters MUST be non-NULL. - */ - if ((r->dmp1 == NULL && dmp1 == NULL) - || (r->dmq1 == NULL && dmq1 == NULL) - || (r->iqmp == NULL && iqmp == NULL)) - return 0; - - if (dmp1 != NULL) { - BN_free(r->dmp1); - r->dmp1 = dmp1; - } - if (dmq1 != NULL) { - BN_free(r->dmq1); - r->dmq1 = dmq1; - } - if (iqmp != NULL) { - BN_free(r->iqmp); - r->iqmp = iqmp; - } - - return 1; -} - -void RSA_get0_key(const RSA *r, - const BIGNUM **n, const BIGNUM **e, const BIGNUM **d) -{ - if (n != NULL) - *n = r->n; - if (e != NULL) - *e = r->e; - if (d != NULL) - *d = r->d; -} - -void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q) -{ - if (p != NULL) - *p = r->p; - if (q != NULL) - *q = r->q; -} - -void RSA_get0_crt_params(const RSA *r, - const BIGNUM **dmp1, const BIGNUM **dmq1, - const BIGNUM **iqmp) -{ - if (dmp1 != NULL) - *dmp1 = r->dmp1; - if (dmq1 != NULL) - *dmq1 = r->dmq1; - if (iqmp != NULL) - *iqmp = r->iqmp; -} -#endif """ diff --git a/src/_cffi_src/openssl/ssl.py b/src/_cffi_src/openssl/ssl.py index c38e309a1835..9400f115fb44 100644 --- a/src/_cffi_src/openssl/ssl.py +++ b/src/_cffi_src/openssl/ssl.py @@ -13,24 +13,18 @@ TYPES = """ static const long Cryptography_HAS_SSL_ST; static const long Cryptography_HAS_TLS_ST; -static const long Cryptography_HAS_SSL2; static const long Cryptography_HAS_SSL3_METHOD; static const long Cryptography_HAS_TLSv1_1; static const long Cryptography_HAS_TLSv1_2; static const long Cryptography_HAS_TLSv1_3; static const long Cryptography_HAS_SECURE_RENEGOTIATION; -static const long Cryptography_HAS_TLSEXT_STATUS_REQ_CB; -static const long Cryptography_HAS_STATUS_REQ_OCSP_RESP; -static const long Cryptography_HAS_TLSEXT_STATUS_REQ_TYPE; static const long Cryptography_HAS_SSL_CTX_CLEAR_OPTIONS; static const long Cryptography_HAS_DTLS; static const long Cryptography_HAS_SIGALGS; static const long Cryptography_HAS_PSK; -static const long Cryptography_HAS_CIPHER_DETAILS; static const long Cryptography_HAS_VERIFIED_CHAIN; static const long Cryptography_HAS_KEYLOG; - -/* Internally invented symbol to tell us if SNI is supported */ +static const long Cryptography_HAS_GET_PROTO_VERSION; static const long Cryptography_HAS_TLSEXT_HOSTNAME; /* Internally invented symbol to tell us if SSL_MODE_RELEASE_BUFFERS is @@ -320,6 +314,16 @@ long SSL_total_renegotiations(SSL *); long SSL_get_secure_renegotiation_support(SSL *); +long SSL_CTX_set_min_proto_version(SSL_CTX *, int); +long SSL_CTX_set_max_proto_version(SSL_CTX *, int); +long SSL_set_min_proto_version(SSL *, int); +long SSL_set_max_proto_version(SSL *, int); + +long SSL_CTX_get_min_proto_version(SSL_CTX *); +long SSL_CTX_get_max_proto_version(SSL_CTX *); +long SSL_get_min_proto_version(SSL *); +long SSL_get_max_proto_version(SSL *); + /* Defined as unsigned long because SSL_OP_ALL is greater than signed 32-bit and Windows defines long as 32-bit. */ unsigned long SSL_CTX_set_options(SSL_CTX *, unsigned long); @@ -338,10 +342,6 @@ /* methods */ -/* - * TLSv1_1 and TLSv1_2 are recent additions. Only sufficiently new versions of - * OpenSSL support them. - */ const SSL_METHOD *TLSv1_1_method(void); const SSL_METHOD *TLSv1_1_server_method(void); const SSL_METHOD *TLSv1_1_client_method(void); @@ -371,6 +371,10 @@ const SSL_METHOD *SSLv23_server_method(void); const SSL_METHOD *SSLv23_client_method(void); +const SSL_METHOD *TLS_method(void); +const SSL_METHOD *TLS_server_method(void); +const SSL_METHOD *TLS_client_method(void); + /*- These aren't macros these arguments are all const X on openssl > 1.0.x -*/ SSL_CTX *SSL_CTX_new(SSL_METHOD *); long SSL_CTX_get_timeout(const SSL_CTX *); @@ -501,7 +505,12 @@ """ CUSTOMIZATIONS = """ -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 +// This symbol is being preserved because removing it will break users with +// pyOpenSSL < 19.1 and pip < 20.x. We need to leave this in place until those +// users have upgraded. PersistentlyDeprecated2020 +static const long Cryptography_HAS_TLSEXT_HOSTNAME = 1; + +#if CRYPTOGRAPHY_IS_LIBRESSL static const long Cryptography_HAS_VERIFIED_CHAIN = 0; Cryptography_STACK_OF_X509 *(*SSL_get0_verified_chain)(const SSL *) = NULL; #else @@ -521,66 +530,8 @@ static const long Cryptography_HAS_KEYLOG = 1; #endif -/* Added in 1.1.0 in the great opaquing, but we need to define it for older - OpenSSLs. Such is our burden. */ -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 && !CRYPTOGRAPHY_IS_LIBRESSL -/* from ssl/ssl_lib.c */ -size_t SSL_get_client_random(const SSL *ssl, unsigned char *out, size_t outlen) -{ - if (outlen == 0) - return sizeof(ssl->s3->client_random); - if (outlen > sizeof(ssl->s3->client_random)) - outlen = sizeof(ssl->s3->client_random); - memcpy(out, ssl->s3->client_random, outlen); - return outlen; -} -/* Added in 1.1.0 as well */ -/* from ssl/ssl_lib.c */ -size_t SSL_get_server_random(const SSL *ssl, unsigned char *out, size_t outlen) -{ - if (outlen == 0) - return sizeof(ssl->s3->server_random); - if (outlen > sizeof(ssl->s3->server_random)) - outlen = sizeof(ssl->s3->server_random); - memcpy(out, ssl->s3->server_random, outlen); - return outlen; -} -/* Added in 1.1.0 as well */ -/* from ssl/ssl_lib.c */ -size_t SSL_SESSION_get_master_key(const SSL_SESSION *session, - unsigned char *out, size_t outlen) -{ - if (session->master_key_length < 0) { - /* Should never happen */ - return 0; - } - if (outlen == 0) - return session->master_key_length; - if (outlen > (size_t)session->master_key_length) - outlen = session->master_key_length; - memcpy(out, session->master_key, outlen); - return outlen; -} -/* from ssl/ssl_sess.c */ -int SSL_SESSION_has_ticket(const SSL_SESSION *s) -{ - return (s->tlsext_ticklen > 0) ? 1 : 0; -} -/* from ssl/ssl_sess.c */ -unsigned long SSL_SESSION_get_ticket_lifetime_hint(const SSL_SESSION *s) -{ - return s->tlsext_tick_lifetime_hint; -} -#endif - static const long Cryptography_HAS_SECURE_RENEGOTIATION = 1; -/* Cryptography now compiles out all SSLv2 bindings. This exists to allow - * clients that use it to check for SSLv2 support to keep functioning as - * expected. - */ -static const long Cryptography_HAS_SSL2 = 0; - #ifdef OPENSSL_NO_SSL3_METHOD static const long Cryptography_HAS_SSL3_METHOD = 0; SSL_METHOD* (*SSLv3_method)(void) = NULL; @@ -590,10 +541,6 @@ static const long Cryptography_HAS_SSL3_METHOD = 1; #endif -static const long Cryptography_HAS_TLSEXT_HOSTNAME = 1; -static const long Cryptography_HAS_TLSEXT_STATUS_REQ_CB = 1; -static const long Cryptography_HAS_STATUS_REQ_OCSP_RESP = 1; -static const long Cryptography_HAS_TLSEXT_STATUS_REQ_TYPE = 1; static const long Cryptography_HAS_RELEASE_BUFFERS = 1; static const long Cryptography_HAS_OP_NO_COMPRESSION = 1; static const long Cryptography_HAS_TLSv1_1 = 1; @@ -616,7 +563,7 @@ /* in OpenSSL 1.1.0 the SSL_ST values were renamed to TLS_ST and several were removed */ -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 +#if CRYPTOGRAPHY_IS_LIBRESSL static const long Cryptography_HAS_SSL_ST = 1; #else static const long Cryptography_HAS_SSL_ST = 0; @@ -625,7 +572,7 @@ static const long SSL_ST_INIT = 0; static const long SSL_ST_RENEGOTIATE = 0; #endif -#if CRYPTOGRAPHY_OPENSSL_110_OR_GREATER +#if !CRYPTOGRAPHY_IS_LIBRESSL static const long Cryptography_HAS_TLS_ST = 1; #else static const long Cryptography_HAS_TLS_ST = 0; @@ -729,17 +676,6 @@ SRTP_PROTECTION_PROFILE * (*SSL_get_selected_srtp_profile)(SSL *) = NULL; #endif -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 && !CRYPTOGRAPHY_IS_LIBRESSL -int (*SSL_CIPHER_is_aead)(const SSL_CIPHER *) = NULL; -int (*SSL_CIPHER_get_cipher_nid)(const SSL_CIPHER *) = NULL; -int (*SSL_CIPHER_get_digest_nid)(const SSL_CIPHER *) = NULL; -int (*SSL_CIPHER_get_kx_nid)(const SSL_CIPHER *) = NULL; -int (*SSL_CIPHER_get_auth_nid)(const SSL_CIPHER *) = NULL; -static const long Cryptography_HAS_CIPHER_DETAILS = 0; -#else -static const long Cryptography_HAS_CIPHER_DETAILS = 1; -#endif - #if CRYPTOGRAPHY_OPENSSL_LESS_THAN_111 static const long Cryptography_HAS_TLSv1_3 = 0; static const long SSL_OP_NO_TLSv1_3 = 0; @@ -755,4 +691,15 @@ #else static const long Cryptography_HAS_TLSv1_3 = 1; #endif + +#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_111 && !CRYPTOGRAPHY_IS_LIBRESSL +static const long Cryptography_HAS_GET_PROTO_VERSION = 0; + +long (*SSL_CTX_get_min_proto_version)(SSL_CTX *) = NULL; +long (*SSL_CTX_get_max_proto_version)(SSL_CTX *) = NULL; +long (*SSL_get_min_proto_version)(SSL *) = NULL; +long (*SSL_get_max_proto_version)(SSL *) = NULL; +#else +static const long Cryptography_HAS_GET_PROTO_VERSION = 1; +#endif """ diff --git a/src/_cffi_src/openssl/x509.py b/src/_cffi_src/openssl/x509.py index b88daa1f213d..24946ea48d07 100644 --- a/src/_cffi_src/openssl/x509.py +++ b/src/_cffi_src/openssl/x509.py @@ -288,7 +288,7 @@ } /* Added in 1.1.0 but we need it in all versions now due to the great opaquing. */ -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 +#if CRYPTOGRAPHY_IS_LIBRESSL int i2d_re_X509_REQ_tbs(X509_REQ *req, unsigned char **pp) { req->req_info->enc.modified = 1; @@ -298,47 +298,5 @@ crl->crl->enc.modified = 1; return i2d_X509_CRL_INFO(crl->crl, pp); } - -#if !CRYPTOGRAPHY_IS_LIBRESSL -int X509_up_ref(X509 *x) { - return CRYPTO_add(&x->references, 1, CRYPTO_LOCK_X509); -} - -const X509_ALGOR *X509_get0_tbs_sigalg(const X509 *x) -{ - return x->cert_info->signature; -} - -/* from x509/x509_req.c */ -void X509_REQ_get0_signature(const X509_REQ *req, const ASN1_BIT_STRING **psig, - const X509_ALGOR **palg) -{ - if (psig != NULL) - *psig = req->signature; - if (palg != NULL) - *palg = req->sig_alg; -} -void X509_CRL_get0_signature(const X509_CRL *crl, const ASN1_BIT_STRING **psig, - const X509_ALGOR **palg) -{ - if (psig != NULL) - *psig = crl->signature; - if (palg != NULL) - *palg = crl->sig_alg; -} -const ASN1_TIME *X509_REVOKED_get0_revocationDate(const X509_REVOKED *x) -{ - return x->revocationDate; -} -const ASN1_INTEGER *X509_REVOKED_get0_serialNumber(const X509_REVOKED *x) -{ - return x->serialNumber; -} - -#define X509_set1_notBefore X509_set_notBefore -#define X509_set1_notAfter X509_set_notAfter -#define X509_getm_notAfter X509_get_notAfter -#define X509_getm_notBefore X509_get_notBefore -#endif #endif """ diff --git a/src/_cffi_src/openssl/x509_vfy.py b/src/_cffi_src/openssl/x509_vfy.py index d2bc5f4ec6e1..ba3d3dbb1421 100644 --- a/src/_cffi_src/openssl/x509_vfy.py +++ b/src/_cffi_src/openssl/x509_vfy.py @@ -234,7 +234,7 @@ static const long X509_V_FLAG_SUITEB_128_LOS = 0; #endif -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 || CRYPTOGRAPHY_IS_LIBRESSL +#if CRYPTOGRAPHY_IS_LIBRESSL static const long Cryptography_HAS_110_VERIFICATION_PARAMS = 0; #ifndef X509_CHECK_FLAG_NEVER_CHECK_SUBJECT static const long X509_CHECK_FLAG_NEVER_CHECK_SUBJECT = 0; @@ -243,29 +243,7 @@ static const long Cryptography_HAS_110_VERIFICATION_PARAMS = 1; #endif -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 && !CRYPTOGRAPHY_IS_LIBRESSL -Cryptography_STACK_OF_X509_OBJECT *X509_STORE_get0_objects(X509_STORE *ctx) { - return ctx->objs; -} -X509_VERIFY_PARAM *X509_STORE_get0_param(X509_STORE *store) { - return store->param; -} -int X509_OBJECT_get_type(const X509_OBJECT *x) { - return x->type; -} - -/* from x509/x509_vfy.c */ -X509 *X509_STORE_CTX_get0_cert(X509_STORE_CTX *ctx) -{ - return ctx->cert; -} - -X509 *X509_OBJECT_get0_X509(X509_OBJECT *x) { - return x->data.x509; -} -#endif - -#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 +#if CRYPTOGRAPHY_IS_LIBRESSL static const long Cryptography_HAS_X509_STORE_CTX_GET_ISSUER = 0; typedef void *X509_STORE_CTX_get_issuer_fn; X509_STORE_CTX_get_issuer_fn (*X509_STORE_get_get_issuer)(X509_STORE *) = NULL; diff --git a/src/_cffi_src/openssl/x509name.py b/src/_cffi_src/openssl/x509name.py index f88c8b063b33..1fbe26aa7432 100644 --- a/src/_cffi_src/openssl/x509name.py +++ b/src/_cffi_src/openssl/x509name.py @@ -35,7 +35,7 @@ int X509_NAME_get_index_by_NID(X509_NAME *, int, int); int X509_NAME_cmp(const X509_NAME *, const X509_NAME *); X509_NAME *X509_NAME_dup(X509_NAME *); -int Cryptography_X509_NAME_ENTRY_set(X509_NAME_ENTRY *); +int X509_NAME_ENTRY_set(X509_NAME_ENTRY *); /* These became const X509_NAME * in 1.1.0 */ int X509_NAME_entry_count(X509_NAME *); X509_NAME_ENTRY *X509_NAME_get_entry(X509_NAME *, int); @@ -75,13 +75,4 @@ """ CUSTOMIZATIONS = """ -#if CRYPTOGRAPHY_OPENSSL_110_OR_GREATER -int Cryptography_X509_NAME_ENTRY_set(X509_NAME_ENTRY *ne) { - return X509_NAME_ENTRY_set(ne); -} -#else -int Cryptography_X509_NAME_ENTRY_set(X509_NAME_ENTRY *ne) { - return ne->set; -} -#endif """ diff --git a/src/cryptography/__about__.py b/src/cryptography/__about__.py index fd1b7a581734..f816509257e8 100644 --- a/src/cryptography/__about__.py +++ b/src/cryptography/__about__.py @@ -22,10 +22,10 @@ ) __uri__ = "https://github.com/pyca/cryptography" -__version__ = "3.1" +__version__ = "3.3.2" __author__ = "The cryptography developers" __email__ = "cryptography-dev@python.org" __license__ = "BSD or Apache License, Version 2.0" -__copyright__ = "Copyright 2013-2019 {}".format(__author__) +__copyright__ = "Copyright 2013-2021 {}".format(__author__) diff --git a/src/cryptography/__init__.py b/src/cryptography/__init__.py index f16efce6ba78..465671eec826 100644 --- a/src/cryptography/__init__.py +++ b/src/cryptography/__init__.py @@ -34,15 +34,8 @@ if sys.version_info[0] == 2: warnings.warn( "Python 2 is no longer supported by the Python core team. Support for " - "it is now deprecated in cryptography, and will be removed in a " - "future release.", - CryptographyDeprecationWarning, - stacklevel=2, - ) -if sys.version_info[:2] == (3, 5): - warnings.warn( - "Python 3.5 support will be dropped in the next release of" - "cryptography. Please upgrade your Python.", + "it is now deprecated in cryptography, and will be removed in the " + "next release.", CryptographyDeprecationWarning, stacklevel=2, ) diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 97c7fd054495..45d4a1a1eec9 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -117,6 +117,7 @@ from cryptography.hazmat.bindings.openssl import binding from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import ( + dh, dsa, ec, ed25519, @@ -151,7 +152,7 @@ XTS, ) from cryptography.hazmat.primitives.kdf import scrypt -from cryptography.hazmat.primitives.serialization import ssh +from cryptography.hazmat.primitives.serialization import pkcs7, ssh from cryptography.x509 import ocsp @@ -623,8 +624,6 @@ def load_rsa_private_numbers(self, numbers): self.openssl_assert(res == 1) res = self._lib.RSA_set0_crt_params(rsa_cdata, dmp1, dmq1, iqmp) self.openssl_assert(res == 1) - res = self._lib.RSA_blinding_on(rsa_cdata, self._ffi.NULL) - self.openssl_assert(res == 1) evp_pkey = self._rsa_cdata_to_evp_pkey(rsa_cdata) return _RSAPrivateKey(self, rsa_cdata, evp_pkey) @@ -1370,8 +1369,9 @@ def load_pem_x509_certificate(self, data): if x509 == self._ffi.NULL: self._consume_errors() raise ValueError( - "Unable to load certificate. See https://cryptography.io/en/la" - "test/faq/#why-can-t-i-import-my-pem-file for more details." + "Unable to load certificate. See https://cryptography.io/en/" + "latest/faq.html#why-can-t-i-import-my-pem-file for more" + " details." ) x509 = self._ffi.gc(x509, self._lib.X509_free) @@ -1396,7 +1396,8 @@ def load_pem_x509_crl(self, data): self._consume_errors() raise ValueError( "Unable to load CRL. See https://cryptography.io/en/la" - "test/faq/#why-can-t-i-import-my-pem-file for more details." + "test/faq.html#why-can-t-i-import-my-pem-file for more" + " details." ) x509_crl = self._ffi.gc(x509_crl, self._lib.X509_CRL_free) @@ -1420,8 +1421,9 @@ def load_pem_x509_csr(self, data): if x509_req == self._ffi.NULL: self._consume_errors() raise ValueError( - "Unable to load request. See https://cryptography.io/en/la" - "test/faq/#why-can-t-i-import-my-pem-file for more details." + "Unable to load request. See https://cryptography.io/en/" + "latest/faq.html#why-can-t-i-import-my-pem-file for more" + " details." ) x509_req = self._ffi.gc(x509_req, self._lib.X509_REQ_free) @@ -1458,8 +1460,7 @@ def _load_key(self, openssl_read_func, convert_func, data, password): if evp_pkey == self._ffi.NULL: if userdata.error != 0: - errors = self._consume_errors() - self.openssl_assert(errors) + self._consume_errors() if userdata.error == -1: raise TypeError( "Password was not given but private key is encrypted" @@ -1490,8 +1491,11 @@ def _handle_key_loading_error(self): errors = self._consume_errors() if not errors: - raise ValueError("Could not deserialize key data.") - + raise ValueError( + "Could not deserialize key data. The data may be in an " + "incorrect format or it may be encrypted with an unsupported " + "algorithm." + ) elif errors[0]._lib_reason_match( self._lib.ERR_LIB_EVP, self._lib.EVP_R_BAD_DECRYPT ) or errors[0]._lib_reason_match( @@ -1500,16 +1504,6 @@ def _handle_key_loading_error(self): ): raise ValueError("Bad decrypt. Incorrect password?") - elif errors[0]._lib_reason_match( - self._lib.ERR_LIB_EVP, self._lib.EVP_R_UNKNOWN_PBE_ALGORITHM - ) or errors[0]._lib_reason_match( - self._lib.ERR_LIB_PEM, self._lib.PEM_R_UNSUPPORTED_ENCRYPTION - ): - raise UnsupportedAlgorithm( - "PEM data is encrypted with an unsupported cipher", - _Reasons.UNSUPPORTED_CIPHER, - ) - elif any( error._lib_reason_match( self._lib.ERR_LIB_EVP, @@ -1520,12 +1514,11 @@ def _handle_key_loading_error(self): raise ValueError("Unsupported public key algorithm.") else: - assert errors[0].lib in ( - self._lib.ERR_LIB_EVP, - self._lib.ERR_LIB_PEM, - self._lib.ERR_LIB_ASN1, + raise ValueError( + "Could not deserialize key data. The data may be in an " + "incorrect format or it may be encrypted with an unsupported " + "algorithm." ) - raise ValueError("Could not deserialize key data.") def elliptic_curve_supported(self, curve): try: @@ -1662,14 +1655,6 @@ def _ec_key_new_by_curve(self, curve): def _ec_key_new_by_curve_nid(self, curve_nid): ec_cdata = self._lib.EC_KEY_new_by_curve_name(curve_nid) self.openssl_assert(ec_cdata != self._ffi.NULL) - # Setting the ASN.1 flag to OPENSSL_EC_NAMED_CURVE is - # only necessary on OpenSSL 1.0.2t/u. Once we drop support for 1.0.2 - # we can remove this as it's done automatically when getting an EC_KEY - # from new_by_curve_name - # CRYPTOGRAPHY_OPENSSL_102U_OR_GREATER - self._lib.EC_KEY_set_asn1_flag( - ec_cdata, backend._lib.OPENSSL_EC_NAMED_CURVE - ) return self._ffi.gc(ec_cdata, self._lib.EC_KEY_free) def load_der_ocsp_request(self, data): @@ -2102,8 +2087,12 @@ def _parameter_bytes(self, encoding, format, cdata): return self._read_mem_bio(bio) def generate_dh_parameters(self, generator, key_size): - if key_size < 512: - raise ValueError("DH key_size must be at least 512 bits") + if key_size < dh._MIN_MODULUS_SIZE: + raise ValueError( + "DH key_size must be at least {} bits".format( + dh._MIN_MODULUS_SIZE + ) + ) if generator not in (2, 5): raise ValueError("DH generator must be 2 or 5") @@ -2336,7 +2325,7 @@ def x25519_generate_key(self): def x25519_supported(self): if self._fips_enabled: return False - return self._lib.CRYPTOGRAPHY_OPENSSL_110_OR_GREATER + return not self._lib.CRYPTOGRAPHY_IS_LIBRESSL def x448_load_public_bytes(self, data): if len(data) != 56: @@ -2690,6 +2679,85 @@ def _load_pkcs7_certificates(self, p7): return certs + def pkcs7_sign(self, builder, encoding, options): + bio = self._bytes_to_bio(builder._data) + init_flags = self._lib.PKCS7_PARTIAL + final_flags = 0 + + if len(builder._additional_certs) == 0: + certs = self._ffi.NULL + else: + certs = self._lib.sk_X509_new_null() + certs = self._ffi.gc(certs, self._lib.sk_X509_free) + for cert in builder._additional_certs: + res = self._lib.sk_X509_push(certs, cert._x509) + self.openssl_assert(res >= 1) + + if pkcs7.PKCS7Options.DetachedSignature in options: + # Don't embed the data in the PKCS7 structure + init_flags |= self._lib.PKCS7_DETACHED + final_flags |= self._lib.PKCS7_DETACHED + + # This just inits a structure for us. However, there + # are flags we need to set, joy. + p7 = self._lib.PKCS7_sign( + self._ffi.NULL, + self._ffi.NULL, + certs, + self._ffi.NULL, + init_flags, + ) + self.openssl_assert(p7 != self._ffi.NULL) + p7 = self._ffi.gc(p7, self._lib.PKCS7_free) + signer_flags = 0 + # These flags are configurable on a per-signature basis + # but we've deliberately chosen to make the API only allow + # setting it across all signatures for now. + if pkcs7.PKCS7Options.NoCapabilities in options: + signer_flags |= self._lib.PKCS7_NOSMIMECAP + elif pkcs7.PKCS7Options.NoAttributes in options: + signer_flags |= self._lib.PKCS7_NOATTR + + if pkcs7.PKCS7Options.NoCerts in options: + signer_flags |= self._lib.PKCS7_NOCERTS + + for certificate, private_key, hash_algorithm in builder._signers: + md = self._evp_md_non_null_from_algorithm(hash_algorithm) + p7signerinfo = self._lib.PKCS7_sign_add_signer( + p7, certificate._x509, private_key._evp_pkey, md, signer_flags + ) + self.openssl_assert(p7signerinfo != self._ffi.NULL) + + for option in options: + # DetachedSignature, NoCapabilities, and NoAttributes are already + # handled so we just need to check these last two options. + if option is pkcs7.PKCS7Options.Text: + final_flags |= self._lib.PKCS7_TEXT + elif option is pkcs7.PKCS7Options.Binary: + final_flags |= self._lib.PKCS7_BINARY + + bio_out = self._create_mem_bio_gc() + if encoding is serialization.Encoding.SMIME: + # This finalizes the structure + res = self._lib.SMIME_write_PKCS7( + bio_out, p7, bio.bio, final_flags + ) + elif encoding is serialization.Encoding.PEM: + res = self._lib.PKCS7_final(p7, bio.bio, final_flags) + self.openssl_assert(res == 1) + res = self._lib.PEM_write_bio_PKCS7_stream( + bio_out, p7, bio.bio, final_flags + ) + else: + assert encoding is serialization.Encoding.DER + # We need to call finalize here becauase i2d_PKCS7_bio does not + # finalize. + res = self._lib.PKCS7_final(p7, bio.bio, final_flags) + self.openssl_assert(res == 1) + res = self._lib.i2d_PKCS7_bio(bio_out, p7) + self.openssl_assert(res == 1) + return self._read_mem_bio(bio_out) + class GetCipherByName(object): def __init__(self, fmt): diff --git a/src/cryptography/hazmat/backends/openssl/ciphers.py b/src/cryptography/hazmat/backends/openssl/ciphers.py index 171605a683de..ad5dad3f7ed2 100644 --- a/src/cryptography/hazmat/backends/openssl/ciphers.py +++ b/src/cryptography/hazmat/backends/openssl/ciphers.py @@ -17,7 +17,7 @@ class _CipherContext(object): _ENCRYPT = 1 _DECRYPT = 0 - _MAX_CHUNK_SIZE = 2 ** 31 + _MAX_CHUNK_SIZE = 2 ** 30 - 1 def __init__(self, backend, cipher, mode, operation): self._backend = backend diff --git a/src/cryptography/hazmat/backends/openssl/decode_asn1.py b/src/cryptography/hazmat/backends/openssl/decode_asn1.py index 279b00ca5c10..cc9b8c0e34d9 100644 --- a/src/cryptography/hazmat/backends/openssl/decode_asn1.py +++ b/src/cryptography/hazmat/backends/openssl/decode_asn1.py @@ -64,7 +64,7 @@ def _decode_x509_name(backend, x509_name): for x in range(count): entry = backend._lib.X509_NAME_get_entry(x509_name, x) attribute = _decode_x509_name_entry(backend, entry) - set_id = backend._lib.Cryptography_X509_NAME_ENTRY_set(entry) + set_id = backend._lib.X509_NAME_ENTRY_set(entry) if set_id != prev_set_id: attributes.append({attribute}) else: diff --git a/src/cryptography/hazmat/backends/openssl/ec.py b/src/cryptography/hazmat/backends/openssl/ec.py index bf61bcf16b20..05d32baba662 100644 --- a/src/cryptography/hazmat/backends/openssl/ec.py +++ b/src/cryptography/hazmat/backends/openssl/ec.py @@ -40,18 +40,18 @@ def _ec_key_curve_sn(backend, ec_key): # an error for now. if nid == backend._lib.NID_undef: raise NotImplementedError( - "ECDSA keys with unnamed curves are unsupported " "at this time" + "ECDSA keys with unnamed curves are unsupported at this time" ) # This is like the above check, but it also catches the case where you # explicitly encoded a curve with the same parameters as a named curve. # Don't do that. if ( - backend._lib.CRYPTOGRAPHY_OPENSSL_102U_OR_GREATER + not backend._lib.CRYPTOGRAPHY_IS_LIBRESSL and backend._lib.EC_GROUP_get_asn1_flag(group) == 0 ): raise NotImplementedError( - "ECDSA keys with unnamed curves are unsupported " "at this time" + "ECDSA keys with unnamed curves are unsupported at this time" ) curve_name = backend._lib.OBJ_nid2sn(nid) diff --git a/src/cryptography/hazmat/backends/openssl/ed25519.py b/src/cryptography/hazmat/backends/openssl/ed25519.py index 75653373b3cf..13bec3af1094 100644 --- a/src/cryptography/hazmat/backends/openssl/ed25519.py +++ b/src/cryptography/hazmat/backends/openssl/ed25519.py @@ -50,10 +50,10 @@ def _raw_public_bytes(self): return self._backend._ffi.buffer(buf, _ED25519_KEY_SIZE)[:] def verify(self, signature, data): - evp_md_ctx = self._backend._lib.Cryptography_EVP_MD_CTX_new() + evp_md_ctx = self._backend._lib.EVP_MD_CTX_new() self._backend.openssl_assert(evp_md_ctx != self._backend._ffi.NULL) evp_md_ctx = self._backend._ffi.gc( - evp_md_ctx, self._backend._lib.Cryptography_EVP_MD_CTX_free + evp_md_ctx, self._backend._lib.EVP_MD_CTX_free ) res = self._backend._lib.EVP_DigestVerifyInit( evp_md_ctx, @@ -89,10 +89,10 @@ def public_key(self): return self._backend.ed25519_load_public_bytes(public_bytes) def sign(self, data): - evp_md_ctx = self._backend._lib.Cryptography_EVP_MD_CTX_new() + evp_md_ctx = self._backend._lib.EVP_MD_CTX_new() self._backend.openssl_assert(evp_md_ctx != self._backend._ffi.NULL) evp_md_ctx = self._backend._ffi.gc( - evp_md_ctx, self._backend._lib.Cryptography_EVP_MD_CTX_free + evp_md_ctx, self._backend._lib.EVP_MD_CTX_free ) res = self._backend._lib.EVP_DigestSignInit( evp_md_ctx, diff --git a/src/cryptography/hazmat/backends/openssl/ed448.py b/src/cryptography/hazmat/backends/openssl/ed448.py index 4a8dab1a8115..6512770e5b76 100644 --- a/src/cryptography/hazmat/backends/openssl/ed448.py +++ b/src/cryptography/hazmat/backends/openssl/ed448.py @@ -51,10 +51,10 @@ def _raw_public_bytes(self): return self._backend._ffi.buffer(buf, _ED448_KEY_SIZE)[:] def verify(self, signature, data): - evp_md_ctx = self._backend._lib.Cryptography_EVP_MD_CTX_new() + evp_md_ctx = self._backend._lib.EVP_MD_CTX_new() self._backend.openssl_assert(evp_md_ctx != self._backend._ffi.NULL) evp_md_ctx = self._backend._ffi.gc( - evp_md_ctx, self._backend._lib.Cryptography_EVP_MD_CTX_free + evp_md_ctx, self._backend._lib.EVP_MD_CTX_free ) res = self._backend._lib.EVP_DigestVerifyInit( evp_md_ctx, @@ -90,10 +90,10 @@ def public_key(self): return self._backend.ed448_load_public_bytes(public_bytes) def sign(self, data): - evp_md_ctx = self._backend._lib.Cryptography_EVP_MD_CTX_new() + evp_md_ctx = self._backend._lib.EVP_MD_CTX_new() self._backend.openssl_assert(evp_md_ctx != self._backend._ffi.NULL) evp_md_ctx = self._backend._ffi.gc( - evp_md_ctx, self._backend._lib.Cryptography_EVP_MD_CTX_free + evp_md_ctx, self._backend._lib.EVP_MD_CTX_free ) res = self._backend._lib.EVP_DigestSignInit( evp_md_ctx, diff --git a/src/cryptography/hazmat/backends/openssl/encode_asn1.py b/src/cryptography/hazmat/backends/openssl/encode_asn1.py index 88d709d21457..0a33200bbcc2 100644 --- a/src/cryptography/hazmat/backends/openssl/encode_asn1.py +++ b/src/cryptography/hazmat/backends/openssl/encode_asn1.py @@ -604,11 +604,21 @@ def _encode_general_subtree(backend, subtrees): gs = backend._lib.GENERAL_SUBTREE_new() gs.base = _encode_general_name(backend, name) res = backend._lib.sk_GENERAL_SUBTREE_push(general_subtrees, gs) - assert res >= 1 + backend.openssl_assert(res >= 1) return general_subtrees +def _encode_precert_signed_certificate_timestamps(backend, scts): + sct_stack = backend._lib.sk_SCT_new_null() + backend.openssl_assert(sct_stack != backend._ffi.NULL) + sct_stack = backend._ffi.gc(sct_stack, backend._lib.sk_SCT_free) + for sct in scts: + res = backend._lib.sk_SCT_push(sct_stack, sct._sct) + backend.openssl_assert(res >= 1) + return sct_stack + + def _encode_nonce(backend, nonce): return _encode_asn1_str_gc(backend, nonce.nonce) @@ -630,6 +640,9 @@ def _encode_nonce(backend, nonce): ExtensionOID.OCSP_NO_CHECK: _encode_ocsp_nocheck, ExtensionOID.NAME_CONSTRAINTS: _encode_name_constraints, ExtensionOID.POLICY_CONSTRAINTS: _encode_policy_constraints, + ExtensionOID.PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS: ( + _encode_precert_signed_certificate_timestamps + ), } _CRL_EXTENSION_ENCODE_HANDLERS = { diff --git a/src/cryptography/hazmat/backends/openssl/hashes.py b/src/cryptography/hazmat/backends/openssl/hashes.py index 44033993e166..764dce0ede60 100644 --- a/src/cryptography/hazmat/backends/openssl/hashes.py +++ b/src/cryptography/hazmat/backends/openssl/hashes.py @@ -18,9 +18,9 @@ def __init__(self, backend, algorithm, ctx=None): self._backend = backend if ctx is None: - ctx = self._backend._lib.Cryptography_EVP_MD_CTX_new() + ctx = self._backend._lib.EVP_MD_CTX_new() ctx = self._backend._ffi.gc( - ctx, self._backend._lib.Cryptography_EVP_MD_CTX_free + ctx, self._backend._lib.EVP_MD_CTX_free ) evp_md = self._backend._evp_md_from_algorithm(algorithm) if evp_md == self._backend._ffi.NULL: @@ -40,9 +40,9 @@ def __init__(self, backend, algorithm, ctx=None): algorithm = utils.read_only_property("_algorithm") def copy(self): - copied_ctx = self._backend._lib.Cryptography_EVP_MD_CTX_new() + copied_ctx = self._backend._lib.EVP_MD_CTX_new() copied_ctx = self._backend._ffi.gc( - copied_ctx, self._backend._lib.Cryptography_EVP_MD_CTX_free + copied_ctx, self._backend._lib.EVP_MD_CTX_free ) res = self._backend._lib.EVP_MD_CTX_copy_ex(copied_ctx, self._ctx) self._backend.openssl_assert(res != 0) diff --git a/src/cryptography/hazmat/backends/openssl/hmac.py b/src/cryptography/hazmat/backends/openssl/hmac.py index 5024223b219b..1cc9d99fec7b 100644 --- a/src/cryptography/hazmat/backends/openssl/hmac.py +++ b/src/cryptography/hazmat/backends/openssl/hmac.py @@ -21,11 +21,9 @@ def __init__(self, backend, key, algorithm, ctx=None): self._backend = backend if ctx is None: - ctx = self._backend._lib.Cryptography_HMAC_CTX_new() + ctx = self._backend._lib.HMAC_CTX_new() self._backend.openssl_assert(ctx != self._backend._ffi.NULL) - ctx = self._backend._ffi.gc( - ctx, self._backend._lib.Cryptography_HMAC_CTX_free - ) + ctx = self._backend._ffi.gc(ctx, self._backend._lib.HMAC_CTX_free) evp_md = self._backend._evp_md_from_algorithm(algorithm) if evp_md == self._backend._ffi.NULL: raise UnsupportedAlgorithm( @@ -46,10 +44,10 @@ def __init__(self, backend, key, algorithm, ctx=None): algorithm = utils.read_only_property("_algorithm") def copy(self): - copied_ctx = self._backend._lib.Cryptography_HMAC_CTX_new() + copied_ctx = self._backend._lib.HMAC_CTX_new() self._backend.openssl_assert(copied_ctx != self._backend._ffi.NULL) copied_ctx = self._backend._ffi.gc( - copied_ctx, self._backend._lib.Cryptography_HMAC_CTX_free + copied_ctx, self._backend._lib.HMAC_CTX_free ) res = self._backend._lib.HMAC_CTX_copy(copied_ctx, self._ctx) self._backend.openssl_assert(res != 0) diff --git a/src/cryptography/hazmat/backends/openssl/poly1305.py b/src/cryptography/hazmat/backends/openssl/poly1305.py index 17493ca60ce8..5699918b1726 100644 --- a/src/cryptography/hazmat/backends/openssl/poly1305.py +++ b/src/cryptography/hazmat/backends/openssl/poly1305.py @@ -30,10 +30,10 @@ def __init__(self, backend, key): self._evp_pkey = self._backend._ffi.gc( evp_pkey, self._backend._lib.EVP_PKEY_free ) - ctx = self._backend._lib.Cryptography_EVP_MD_CTX_new() + ctx = self._backend._lib.EVP_MD_CTX_new() self._backend.openssl_assert(ctx != self._backend._ffi.NULL) self._ctx = self._backend._ffi.gc( - ctx, self._backend._lib.Cryptography_EVP_MD_CTX_free + ctx, self._backend._lib.EVP_MD_CTX_free ) res = self._backend._lib.EVP_DigestSignInit( self._ctx, diff --git a/src/cryptography/hazmat/backends/openssl/rsa.py b/src/cryptography/hazmat/backends/openssl/rsa.py index 423f6878c124..82cd49c960ab 100644 --- a/src/cryptography/hazmat/backends/openssl/rsa.py +++ b/src/cryptography/hazmat/backends/openssl/rsa.py @@ -119,23 +119,19 @@ def _enc_dec_rsa_pkey_ctx(backend, key, data, padding_enum, padding): outlen = backend._ffi.new("size_t *", buf_size) buf = backend._ffi.new("unsigned char[]", buf_size) + # Everything from this line onwards is written with the goal of being as + # constant-time as is practical given the constraints of Python and our + # API. See Bleichenbacher's '98 attack on RSA, and its many many variants. + # As such, you should not attempt to change this (particularly to "clean it + # up") without understanding why it was written this way (see + # Chesterton's Fence), and without measuring to verify you have not + # introduced observable time differences. res = crypt(pkey_ctx, buf, outlen, data, len(data)) + resbuf = backend._ffi.buffer(buf)[: outlen[0]] + backend._lib.ERR_clear_error() if res <= 0: - _handle_rsa_enc_dec_error(backend, key) - - return backend._ffi.buffer(buf)[: outlen[0]] - - -def _handle_rsa_enc_dec_error(backend, key): - errors = backend._consume_errors_with_text() - if isinstance(key, _RSAPublicKey): - raise ValueError( - "Data too long for key size. Encrypt less data or use a " - "larger key size.", - errors, - ) - else: - raise ValueError("Decryption failed.", errors) + raise ValueError("Encryption/decryption failed.") + return resbuf def _rsa_sig_determine_padding(backend, key, padding, algorithm): @@ -146,6 +142,7 @@ def _rsa_sig_determine_padding(backend, key, padding, algorithm): backend.openssl_assert(pkey_size > 0) if isinstance(padding, PKCS1v15): + # Hash algorithm is ignored for PKCS1v15-padding, may be None. padding_enum = backend._lib.RSA_PKCS1_PADDING elif isinstance(padding, PSS): if not isinstance(padding._mgf, MGF1): @@ -154,6 +151,10 @@ def _rsa_sig_determine_padding(backend, key, padding, algorithm): _Reasons.UNSUPPORTED_MGF, ) + # PSS padding requires a hash algorithm + if not isinstance(algorithm, hashes.HashAlgorithm): + raise TypeError("Expected instance of hashes.HashAlgorithm.") + # Size of key in bytes - 2 is the maximum # PSS signature length (salt length is checked later) if pkey_size - algorithm.digest_size - 2 < 0: @@ -172,25 +173,37 @@ def _rsa_sig_determine_padding(backend, key, padding, algorithm): return padding_enum -def _rsa_sig_setup(backend, padding, algorithm, key, data, init_func): +# Hash algorithm can be absent (None) to initialize the context without setting +# any message digest algorithm. This is currently only valid for the PKCS1v15 +# padding type, where it means that the signature data is encoded/decoded +# as provided, without being wrapped in a DigestInfo structure. +def _rsa_sig_setup(backend, padding, algorithm, key, init_func): padding_enum = _rsa_sig_determine_padding(backend, key, padding, algorithm) - evp_md = backend._evp_md_non_null_from_algorithm(algorithm) pkey_ctx = backend._lib.EVP_PKEY_CTX_new(key._evp_pkey, backend._ffi.NULL) backend.openssl_assert(pkey_ctx != backend._ffi.NULL) pkey_ctx = backend._ffi.gc(pkey_ctx, backend._lib.EVP_PKEY_CTX_free) res = init_func(pkey_ctx) backend.openssl_assert(res == 1) - res = backend._lib.EVP_PKEY_CTX_set_signature_md(pkey_ctx, evp_md) - if res == 0: + if algorithm is not None: + evp_md = backend._evp_md_non_null_from_algorithm(algorithm) + res = backend._lib.EVP_PKEY_CTX_set_signature_md(pkey_ctx, evp_md) + if res == 0: + backend._consume_errors() + raise UnsupportedAlgorithm( + "{} is not supported by this backend for RSA signing.".format( + algorithm.name + ), + _Reasons.UNSUPPORTED_HASH, + ) + res = backend._lib.EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, padding_enum) + if res <= 0: backend._consume_errors() raise UnsupportedAlgorithm( - "{} is not supported by this backend for RSA signing.".format( - algorithm.name + "{} is not supported for the RSA signature operation.".format( + padding.name ), - _Reasons.UNSUPPORTED_HASH, + _Reasons.UNSUPPORTED_PADDING, ) - res = backend._lib.EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, padding_enum) - backend.openssl_assert(res > 0) if isinstance(padding, PSS): res = backend._lib.EVP_PKEY_CTX_set_rsa_pss_saltlen( pkey_ctx, _get_rsa_pss_salt_length(padding, key, algorithm) @@ -212,7 +225,6 @@ def _rsa_sig_sign(backend, padding, algorithm, private_key, data): padding, algorithm, private_key, - data, backend._lib.EVP_PKEY_sign_init, ) buflen = backend._ffi.new("size_t *") @@ -239,7 +251,6 @@ def _rsa_sig_verify(backend, padding, algorithm, public_key, signature, data): padding, algorithm, public_key, - data, backend._lib.EVP_PKEY_verify_init, ) res = backend._lib.EVP_PKEY_verify( @@ -254,6 +265,36 @@ def _rsa_sig_verify(backend, padding, algorithm, public_key, signature, data): raise InvalidSignature +def _rsa_sig_recover(backend, padding, algorithm, public_key, signature): + pkey_ctx = _rsa_sig_setup( + backend, + padding, + algorithm, + public_key, + backend._lib.EVP_PKEY_verify_recover_init, + ) + + # Attempt to keep the rest of the code in this function as constant/time + # as possible. See the comment in _enc_dec_rsa_pkey_ctx. Note that the + # outlen parameter is used even though its value may be undefined in the + # error case. Due to the tolerant nature of Python slicing this does not + # trigger any exceptions. + maxlen = backend._lib.EVP_PKEY_size(public_key._evp_pkey) + backend.openssl_assert(maxlen > 0) + buf = backend._ffi.new("unsigned char[]", maxlen) + buflen = backend._ffi.new("size_t *", maxlen) + res = backend._lib.EVP_PKEY_verify_recover( + pkey_ctx, buf, buflen, signature, len(signature) + ) + resbuf = backend._ffi.buffer(buf)[: buflen[0]] + backend._lib.ERR_clear_error() + # Assume that all parameter errors are handled during the setup phase and + # any error here is due to invalid signature. + if res != 1: + raise InvalidSignature + return resbuf + + @utils.register_interface(AsymmetricSignatureContext) class _RSASignatureContext(object): def __init__(self, backend, private_key, padding, algorithm): @@ -319,6 +360,11 @@ def __init__(self, backend, rsa_cdata, evp_pkey): errors = backend._consume_errors_with_text() raise ValueError("Invalid private key", errors) + # Blinding is on by default in many versions of OpenSSL, but let's + # just be conservative here. + res = backend._lib.RSA_blinding_on(rsa_cdata, backend._ffi.NULL) + backend.openssl_assert(res == 1) + self._backend = backend self._rsa_cdata = rsa_cdata self._evp_pkey = evp_pkey @@ -351,8 +397,6 @@ def public_key(self): ctx = self._backend._lib.RSAPublicKey_dup(self._rsa_cdata) self._backend.openssl_assert(ctx != self._backend._ffi.NULL) ctx = self._backend._ffi.gc(ctx, self._backend._lib.RSA_free) - res = self._backend._lib.RSA_blinding_on(ctx, self._backend._ffi.NULL) - self._backend.openssl_assert(res == 1) evp_pkey = self._backend._rsa_cdata_to_evp_pkey(ctx) return _RSAPublicKey(self._backend, ctx, evp_pkey) @@ -464,3 +508,9 @@ def verify(self, signature, data, padding, algorithm): return _rsa_sig_verify( self._backend, padding, algorithm, self, signature, data ) + + def recover_data_from_signature(self, signature, padding, algorithm): + _check_not_prehashed(algorithm) + return _rsa_sig_recover( + self._backend, padding, algorithm, self, signature + ) diff --git a/src/cryptography/hazmat/backends/openssl/utils.py b/src/cryptography/hazmat/backends/openssl/utils.py index ec0b947a44c6..3d697d1fb56f 100644 --- a/src/cryptography/hazmat/backends/openssl/utils.py +++ b/src/cryptography/hazmat/backends/openssl/utils.py @@ -52,7 +52,8 @@ def _check_not_prehashed(signature_algorithm): if isinstance(signature_algorithm, Prehashed): raise TypeError( "Prehashed is only supported in the sign and verify methods. " - "It cannot be used with signer or verifier." + "It cannot be used with signer, verifier or " + "recover_data_from_signature." ) diff --git a/src/cryptography/hazmat/bindings/openssl/_conditional.py b/src/cryptography/hazmat/bindings/openssl/_conditional.py index 9cf489acde06..ca50fed13414 100644 --- a/src/cryptography/hazmat/bindings/openssl/_conditional.py +++ b/src/cryptography/hazmat/bindings/openssl/_conditional.py @@ -74,12 +74,6 @@ def cryptography_has_tls_st(): ] -def cryptography_has_locking_callbacks(): - return [ - "Cryptography_setup_ssl_threads", - ] - - def cryptography_has_scrypt(): return [ "EVP_PBE_scrypt", @@ -106,11 +100,12 @@ def cryptography_has_sct(): "SCT_get0_signature", "SCT_get_timestamp", "SCT_set_source", + "sk_SCT_new_null", + "sk_SCT_free", "sk_SCT_num", "sk_SCT_value", - "SCT_LIST_free", "sk_SCT_push", - "sk_SCT_new_null", + "SCT_LIST_free", "SCT_new", "SCT_set1_log_id", "SCT_set_timestamp", @@ -126,20 +121,6 @@ def cryptography_has_x509_store_ctx_get_issuer(): ] -def cryptography_has_x25519(): - return [ - "EVP_PKEY_X25519", - "NID_X25519", - ] - - -def cryptography_has_x448(): - return [ - "EVP_PKEY_X448", - "NID_X448", - ] - - def cryptography_has_ed448(): return [ "EVP_PKEY_ED448", @@ -217,16 +198,6 @@ def cryptography_has_openssl_cleanup(): ] -def cryptography_has_cipher_details(): - return [ - "SSL_CIPHER_is_aead", - "SSL_CIPHER_get_cipher_nid", - "SSL_CIPHER_get_digest_nid", - "SSL_CIPHER_get_kx_nid", - "SSL_CIPHER_get_auth_nid", - ] - - def cryptography_has_tlsv13(): return [ "SSL_OP_NO_TLSv1_3", @@ -270,6 +241,10 @@ def cryptography_has_engine(): "ENGINE_free", "ENGINE_get_name", "Cryptography_add_osrandom_engine", + "ENGINE_ctrl_cmd_string", + "ENGINE_load_builtin_engines", + "ENGINE_load_private_key", + "ENGINE_load_public_key", ] @@ -287,6 +262,15 @@ def cryptography_has_srtp(): ] +def cryptography_has_get_proto_version(): + return [ + "SSL_CTX_get_min_proto_version", + "SSL_CTX_get_max_proto_version", + "SSL_get_min_proto_version", + "SSL_get_max_proto_version", + ] + + # This is a mapping of # {condition: function-returning-names-dependent-on-that-condition} so we can # loop over them and delete unsupported names at runtime. It will be removed @@ -304,7 +288,6 @@ def cryptography_has_srtp(): "Cryptography_HAS_SET_CERT_CB": cryptography_has_set_cert_cb, "Cryptography_HAS_SSL_ST": cryptography_has_ssl_st, "Cryptography_HAS_TLS_ST": cryptography_has_tls_st, - "Cryptography_HAS_LOCKING_CALLBACKS": cryptography_has_locking_callbacks, "Cryptography_HAS_SCRYPT": cryptography_has_scrypt, "Cryptography_HAS_EVP_PKEY_DHX": cryptography_has_evp_pkey_dhx, "Cryptography_HAS_MEM_FUNCTIONS": cryptography_has_mem_functions, @@ -312,8 +295,6 @@ def cryptography_has_srtp(): "Cryptography_HAS_X509_STORE_CTX_GET_ISSUER": ( cryptography_has_x509_store_ctx_get_issuer ), - "Cryptography_HAS_X25519": cryptography_has_x25519, - "Cryptography_HAS_X448": cryptography_has_x448, "Cryptography_HAS_ED448": cryptography_has_ed448, "Cryptography_HAS_ED25519": cryptography_has_ed25519, "Cryptography_HAS_POLY1305": cryptography_has_poly1305, @@ -328,7 +309,6 @@ def cryptography_has_srtp(): "Cryptography_HAS_PSK": cryptography_has_psk, "Cryptography_HAS_CUSTOM_EXT": cryptography_has_custom_ext, "Cryptography_HAS_OPENSSL_CLEANUP": cryptography_has_openssl_cleanup, - "Cryptography_HAS_CIPHER_DETAILS": cryptography_has_cipher_details, "Cryptography_HAS_TLSv1_3": cryptography_has_tlsv13, "Cryptography_HAS_KEYLOG": cryptography_has_keylog, "Cryptography_HAS_RAW_KEY": cryptography_has_raw_key, @@ -338,4 +318,5 @@ def cryptography_has_srtp(): "Cryptography_HAS_ENGINE": cryptography_has_engine, "Cryptography_HAS_VERIFIED_CHAIN": cryptography_has_verified_chain, "Cryptography_HAS_SRTP": cryptography_has_srtp, + "Cryptography_HAS_GET_PROTO_VERSION": cryptography_has_get_proto_version, } diff --git a/src/cryptography/hazmat/bindings/openssl/binding.py b/src/cryptography/hazmat/bindings/openssl/binding.py index 178a81e0d56c..7a84a340e43b 100644 --- a/src/cryptography/hazmat/bindings/openssl/binding.py +++ b/src/cryptography/hazmat/bindings/openssl/binding.py @@ -7,7 +7,6 @@ import collections import threading import types -import warnings import cryptography from cryptography import utils @@ -114,7 +113,6 @@ class Binding(object): ffi = ffi _lib_loaded = False _init_lock = threading.Lock() - _lock_init_lock = threading.Lock() def __init__(self): self._ensure_ffi_initialized() @@ -141,41 +139,11 @@ def _ensure_ffi_initialized(cls): cls.lib.SSL_library_init() # adds all ciphers/digests for EVP cls.lib.OpenSSL_add_all_algorithms() - # loads error strings for libcrypto and libssl functions - cls.lib.SSL_load_error_strings() cls._register_osrandom_engine() @classmethod def init_static_locks(cls): - with cls._lock_init_lock: - cls._ensure_ffi_initialized() - # Use Python's implementation if available, importing _ssl triggers - # the setup for this. - __import__("_ssl") - - if ( - not cls.lib.Cryptography_HAS_LOCKING_CALLBACKS - or cls.lib.CRYPTO_get_locking_callback() != cls.ffi.NULL - ): - return - - # If nothing else has setup a locking callback already, we set up - # our own - res = lib.Cryptography_setup_ssl_threads() - _openssl_assert(cls.lib, res == 1) - - -def _verify_openssl_version(lib): - if ( - lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 - and not lib.CRYPTOGRAPHY_IS_LIBRESSL - ): - warnings.warn( - "OpenSSL version 1.0.2 is no longer supported by the OpenSSL " - "project, please upgrade. The next version of cryptography will " - "drop support for it.", - utils.CryptographyDeprecationWarning, - ) + cls._ensure_ffi_initialized() def _verify_package_version(version): @@ -201,11 +169,4 @@ def _verify_package_version(version): _verify_package_version(cryptography.__version__) -# OpenSSL is not thread safe until the locks are initialized. We call this -# method in module scope so that it executes with the import lock. On -# Pythons < 3.4 this import lock is a global lock, which can prevent a race -# condition registering the OpenSSL locks. On Python 3.4+ the import lock -# is per module so this approach will not work. Binding.init_static_locks() - -_verify_openssl_version(Binding.lib) diff --git a/src/cryptography/hazmat/primitives/asymmetric/dh.py b/src/cryptography/hazmat/primitives/asymmetric/dh.py index cd9fbfab4600..74a311d5015a 100644 --- a/src/cryptography/hazmat/primitives/asymmetric/dh.py +++ b/src/cryptography/hazmat/primitives/asymmetric/dh.py @@ -12,6 +12,9 @@ from cryptography.hazmat.backends import _get_backend +_MIN_MODULUS_SIZE = 512 + + def generate_parameters(generator, key_size, backend=None): backend = _get_backend(backend) return backend.generate_dh_parameters(generator, key_size) @@ -95,6 +98,11 @@ def __init__(self, p, g, q=None): if g < 2: raise ValueError("DH generator must be 2 or greater") + if p.bit_length() < _MIN_MODULUS_SIZE: + raise ValueError( + "p (modulus) must be at least {}-bit".format(_MIN_MODULUS_SIZE) + ) + self._p = p self._g = g self._q = q diff --git a/src/cryptography/hazmat/primitives/asymmetric/rsa.py b/src/cryptography/hazmat/primitives/asymmetric/rsa.py index d8b8ddd914cc..ea16bbf66e66 100644 --- a/src/cryptography/hazmat/primitives/asymmetric/rsa.py +++ b/src/cryptography/hazmat/primitives/asymmetric/rsa.py @@ -106,6 +106,12 @@ def verify(self, signature, data, padding, algorithm): Verifies the signature of the data. """ + @abc.abstractmethod + def recover_data_from_signature(self, signature, padding, algorithm): + """ + Recovers the original data from the signature. + """ + RSAPublicKeyWithSerialization = RSAPublicKey diff --git a/src/cryptography/hazmat/primitives/ciphers/aead.py b/src/cryptography/hazmat/primitives/ciphers/aead.py index 4eddc1ee6e37..c8c93955ce01 100644 --- a/src/cryptography/hazmat/primitives/ciphers/aead.py +++ b/src/cryptography/hazmat/primitives/ciphers/aead.py @@ -170,5 +170,5 @@ def _check_params(self, nonce, data, associated_data): utils._check_byteslike("nonce", nonce) utils._check_bytes("data", data) utils._check_bytes("associated_data", associated_data) - if len(nonce) == 0: - raise ValueError("Nonce must be at least 1 byte") + if len(nonce) < 8 or len(nonce) > 128: + raise ValueError("Nonce must be between 8 and 128 bytes") diff --git a/src/cryptography/hazmat/primitives/ciphers/modes.py b/src/cryptography/hazmat/primitives/ciphers/modes.py index dcb24444214f..0ba0f2b5a176 100644 --- a/src/cryptography/hazmat/primitives/ciphers/modes.py +++ b/src/cryptography/hazmat/primitives/ciphers/modes.py @@ -196,12 +196,14 @@ class GCM(object): _MAX_AAD_BYTES = (2 ** 64) // 8 def __init__(self, initialization_vector, tag=None, min_tag_length=16): - # len(initialization_vector) must in [1, 2 ** 64), but it's impossible - # to actually construct a bytes object that large, so we don't check - # for it + # OpenSSL 3.0.0 constrains GCM IVs to [64, 1024] bits inclusive + # This is a sane limit anyway so we'll enforce it here. utils._check_byteslike("initialization_vector", initialization_vector) - if len(initialization_vector) == 0: - raise ValueError("initialization_vector must be at least 1 byte") + if len(initialization_vector) < 8 or len(initialization_vector) > 128: + raise ValueError( + "initialization_vector must be between 8 and 128 bytes (64 " + "and 1024 bits)." + ) self._initialization_vector = initialization_vector if tag is not None: utils._check_bytes("tag", tag) diff --git a/src/cryptography/hazmat/primitives/padding.py b/src/cryptography/hazmat/primitives/padding.py index 95913614cb2d..98abffbc08be 100644 --- a/src/cryptography/hazmat/primitives/padding.py +++ b/src/cryptography/hazmat/primitives/padding.py @@ -40,9 +40,12 @@ def _byte_padding_update(buffer_, data, block_size): if buffer_ is None: raise AlreadyFinalized("Context was already finalized.") - utils._check_bytes("data", data) + utils._check_byteslike("data", data) - buffer_ += data + # six.PY2: Only coerce non-bytes objects to avoid triggering bad behavior + # of future's newbytes type. Unconditionally call bytes() after Python 2 + # support is gone. + buffer_ += data if isinstance(data, bytes) else bytes(data) finished_blocks = len(buffer_) // (block_size // 8) @@ -64,9 +67,12 @@ def _byte_unpadding_update(buffer_, data, block_size): if buffer_ is None: raise AlreadyFinalized("Context was already finalized.") - utils._check_bytes("data", data) + utils._check_byteslike("data", data) - buffer_ += data + # six.PY2: Only coerce non-bytes objects to avoid triggering bad behavior + # of future's newbytes type. Unconditionally call bytes() after Python 2 + # support is gone. + buffer_ += data if isinstance(data, bytes) else bytes(data) finished_blocks = max(len(buffer_) // (block_size // 8) - 1, 0) diff --git a/src/cryptography/hazmat/primitives/serialization/base.py b/src/cryptography/hazmat/primitives/serialization/base.py index b2b403470f98..fc27235c5cf2 100644 --- a/src/cryptography/hazmat/primitives/serialization/base.py +++ b/src/cryptography/hazmat/primitives/serialization/base.py @@ -49,6 +49,7 @@ class Encoding(Enum): OpenSSH = "OpenSSH" Raw = "Raw" X962 = "ANSI X9.62" + SMIME = "S/MIME" class PrivateFormat(Enum): diff --git a/src/cryptography/hazmat/primitives/serialization/pkcs7.py b/src/cryptography/hazmat/primitives/serialization/pkcs7.py index fcdd1c9aa3c8..1e11e28ef5b3 100644 --- a/src/cryptography/hazmat/primitives/serialization/pkcs7.py +++ b/src/cryptography/hazmat/primitives/serialization/pkcs7.py @@ -4,7 +4,13 @@ from __future__ import absolute_import, division, print_function +from enum import Enum + +from cryptography import x509 from cryptography.hazmat.backends import _get_backend +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import ec, rsa +from cryptography.utils import _check_byteslike def load_pem_pkcs7_certificates(data): @@ -15,3 +21,112 @@ def load_pem_pkcs7_certificates(data): def load_der_pkcs7_certificates(data): backend = _get_backend(None) return backend.load_der_pkcs7_certificates(data) + + +class PKCS7SignatureBuilder(object): + def __init__(self, data=None, signers=[], additional_certs=[]): + self._data = data + self._signers = signers + self._additional_certs = additional_certs + + def set_data(self, data): + _check_byteslike("data", data) + if self._data is not None: + raise ValueError("data may only be set once") + + return PKCS7SignatureBuilder(data, self._signers) + + def add_signer(self, certificate, private_key, hash_algorithm): + if not isinstance( + hash_algorithm, + ( + hashes.SHA1, + hashes.SHA224, + hashes.SHA256, + hashes.SHA384, + hashes.SHA512, + ), + ): + raise TypeError( + "hash_algorithm must be one of hashes.SHA1, SHA224, " + "SHA256, SHA384, or SHA512" + ) + if not isinstance(certificate, x509.Certificate): + raise TypeError("certificate must be a x509.Certificate") + + if not isinstance( + private_key, (rsa.RSAPrivateKey, ec.EllipticCurvePrivateKey) + ): + raise TypeError("Only RSA & EC keys are supported at this time.") + + return PKCS7SignatureBuilder( + self._data, + self._signers + [(certificate, private_key, hash_algorithm)], + ) + + def add_certificate(self, certificate): + if not isinstance(certificate, x509.Certificate): + raise TypeError("certificate must be a x509.Certificate") + + return PKCS7SignatureBuilder( + self._data, self._signers, self._additional_certs + [certificate] + ) + + def sign(self, encoding, options, backend=None): + if len(self._signers) == 0: + raise ValueError("Must have at least one signer") + if self._data is None: + raise ValueError("You must add data to sign") + options = list(options) + if not all(isinstance(x, PKCS7Options) for x in options): + raise ValueError("options must be from the PKCS7Options enum") + if encoding not in ( + serialization.Encoding.PEM, + serialization.Encoding.DER, + serialization.Encoding.SMIME, + ): + raise ValueError( + "Must be PEM, DER, or SMIME from the Encoding enum" + ) + + # Text is a meaningless option unless it is accompanied by + # DetachedSignature + if ( + PKCS7Options.Text in options + and PKCS7Options.DetachedSignature not in options + ): + raise ValueError( + "When passing the Text option you must also pass " + "DetachedSignature" + ) + + if PKCS7Options.Text in options and encoding in ( + serialization.Encoding.DER, + serialization.Encoding.PEM, + ): + raise ValueError( + "The Text option is only available for SMIME serialization" + ) + + # No attributes implies no capabilities so we'll error if you try to + # pass both. + if ( + PKCS7Options.NoAttributes in options + and PKCS7Options.NoCapabilities in options + ): + raise ValueError( + "NoAttributes is a superset of NoCapabilities. Do not pass " + "both values." + ) + + backend = _get_backend(backend) + return backend.pkcs7_sign(self, encoding, options) + + +class PKCS7Options(Enum): + Text = "Add text/plain MIME type" + Binary = "Don't translate input data into canonical MIME format" + DetachedSignature = "Don't embed data in the PKCS7 structure" + NoCapabilities = "Don't embed SMIME capabilities" + NoAttributes = "Don't embed authenticatedAttributes" + NoCerts = "Don't embed signer certificate" diff --git a/tests/conftest.py b/tests/conftest.py index 4e3124fa76ea..ece7a11e716a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -11,7 +11,6 @@ from .utils import ( check_backend_support, load_wycheproof_tests, - skip_if_wycheproof_none, ) @@ -30,8 +29,7 @@ def pytest_addoption(parser): def pytest_generate_tests(metafunc): if "wycheproof" in metafunc.fixturenames: - wycheproof = metafunc.config.getoption("--wycheproof-root") - skip_if_wycheproof_none(wycheproof) + wycheproof = metafunc.config.getoption("--wycheproof-root", skip=True) testcases = [] marker = metafunc.definition.get_closest_marker("wycheproof_tests") diff --git a/tests/hazmat/backends/test_openssl.py b/tests/hazmat/backends/test_openssl.py index 2f7e7bebfd0c..eab868fad3a6 100644 --- a/tests/hazmat/backends/test_openssl.py +++ b/tests/hazmat/backends/test_openssl.py @@ -127,11 +127,6 @@ def test_evp_ciphers_registered(self): cipher = backend._lib.EVP_get_cipherbyname(b"aes-256-cbc") assert cipher != backend._ffi.NULL - def test_error_strings_loaded(self): - buf = backend._ffi.new("char[]", 256) - backend._lib.ERR_error_string_n(101183626, buf, len(buf)) - assert b"data not multiple of block length" in backend._ffi.string(buf) - def test_unknown_error_in_cipher_finalize(self): cipher = Cipher(AES(b"\0" * 16), CBC(b"\0" * 16), backend=backend) enc = cipher.encryptor() @@ -590,7 +585,7 @@ def test_numeric_string_x509_name_entry(self): x509.load_der_x509_certificate, backend, ) - if backend._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_102I: + if backend._lib.CRYPTOGRAPHY_IS_LIBRESSL: with pytest.raises(ValueError) as exc: cert.subject diff --git a/tests/hazmat/bindings/test_openssl.py b/tests/hazmat/bindings/test_openssl.py index a4f6ac015695..4bc046b80fe4 100644 --- a/tests/hazmat/bindings/test_openssl.py +++ b/tests/hazmat/bindings/test_openssl.py @@ -22,18 +22,6 @@ def test_binding_loads(self): assert binding.lib assert binding.ffi - def test_crypto_lock_init(self): - b = Binding() - - b.init_static_locks() - lock_cb = b.lib.CRYPTO_get_locking_callback() - if b.lib.CRYPTOGRAPHY_OPENSSL_110_OR_GREATER: - assert lock_cb == b.ffi.NULL - assert b.lib.Cryptography_HAS_LOCKING_CALLBACKS == 0 - else: - assert lock_cb != b.ffi.NULL - assert b.lib.Cryptography_HAS_LOCKING_CALLBACKS == 1 - def test_add_engine_more_than_once(self): b = Binding() b._register_osrandom_engine() @@ -85,7 +73,7 @@ def test_ssl_mode(self): def test_conditional_removal(self): b = Binding() - if b.lib.CRYPTOGRAPHY_OPENSSL_110_OR_GREATER: + if not b.lib.CRYPTOGRAPHY_IS_LIBRESSL: assert b.lib.TLS_ST_OK else: with pytest.raises(AttributeError): diff --git a/tests/hazmat/primitives/test_aead.py b/tests/hazmat/primitives/test_aead.py index 753c7c192bc9..270693182ed2 100644 --- a/tests/hazmat/primitives/test_aead.py +++ b/tests/hazmat/primitives/test_aead.py @@ -380,6 +380,9 @@ def test_data_too_large(self): def test_vectors(self, backend, vector): nonce = binascii.unhexlify(vector["iv"]) + if len(nonce) < 8: + pytest.skip("GCM does not support less than 64-bit IVs") + if backend._fips_enabled and len(nonce) != 12: # Red Hat disables non-96-bit IV support as part of its FIPS # patches. @@ -418,11 +421,17 @@ def test_params_not_bytes(self, nonce, data, associated_data, backend): with pytest.raises(TypeError): aesgcm.decrypt(nonce, data, associated_data) - def test_invalid_nonce_length(self, backend): + @pytest.mark.parametrize("length", [7, 129]) + def test_invalid_nonce_length(self, length, backend): + if backend._fips_enabled: + # Red Hat disables non-96-bit IV support as part of its FIPS + # patches. + pytest.skip("Non-96-bit IVs unsupported in FIPS mode.") + key = AESGCM.generate_key(128) aesgcm = AESGCM(key) with pytest.raises(ValueError): - aesgcm.encrypt(b"", b"hi", None) + aesgcm.encrypt(b"\x00" * length, b"hi", None) def test_bad_key(self, backend): with pytest.raises(TypeError): diff --git a/tests/hazmat/primitives/test_aes_gcm.py b/tests/hazmat/primitives/test_aes_gcm.py index f289f18b11cc..8b71d12300c9 100644 --- a/tests/hazmat/primitives/test_aes_gcm.py +++ b/tests/hazmat/primitives/test_aes_gcm.py @@ -195,3 +195,25 @@ def test_buffer_protocol(self, backend): dec.authenticate_additional_data(bytearray(b"foo")) pt = dec.update(ct) + dec.finalize() assert pt == data + + @pytest.mark.parametrize("size", [8, 128]) + def test_gcm_min_max_iv(self, size, backend): + if backend._fips_enabled: + # Red Hat disables non-96-bit IV support as part of its FIPS + # patches. + pytest.skip("Non-96-bit IVs unsupported in FIPS mode.") + + key = os.urandom(16) + iv = b"\x00" * size + + payload = b"data" + encryptor = base.Cipher(algorithms.AES(key), modes.GCM(iv)).encryptor() + ct = encryptor.update(payload) + encryptor.finalize() + tag = encryptor.tag + + decryptor = base.Cipher(algorithms.AES(key), modes.GCM(iv)).decryptor() + pt = decryptor.update(ct) + + decryptor.finalize_with_tag(tag) + assert pt == payload diff --git a/tests/hazmat/primitives/test_ciphers.py b/tests/hazmat/primitives/test_ciphers.py index 104e679e54a6..a9219fe99c15 100644 --- a/tests/hazmat/primitives/test_ciphers.py +++ b/tests/hazmat/primitives/test_ciphers.py @@ -72,6 +72,13 @@ def test_xts_wrong_key_size(self, backend): ciphers.Cipher(AES(b"0" * 16), modes.XTS(b"0" * 16), backend) +class TestGCM(object): + @pytest.mark.parametrize("size", [7, 129]) + def test_gcm_min_max(self, size): + with pytest.raises(ValueError): + modes.GCM(b"0" * size) + + class TestCamellia(object): @pytest.mark.parametrize( ("key", "keysize"), @@ -333,3 +340,12 @@ def test_update_into_auto_chunking(self, backend, monkeypatch): decbuf = bytearray(527) decprocessed = decryptor.update_into(buf[:processed], decbuf) assert decbuf[:decprocessed] == pt + + def test_max_chunk_size_fits_in_int32(self, backend): + # max chunk must fit in signed int32 or else a call large enough to + # cause chunking will result in the very OverflowError we want to + # avoid with chunking. + key = b"\x00" * 16 + c = ciphers.Cipher(AES(key), modes.ECB(), backend) + encryptor = c.encryptor() + backend._ffi.new("int *", encryptor._ctx._MAX_CHUNK_SIZE) diff --git a/tests/hazmat/primitives/test_dh.py b/tests/hazmat/primitives/test_dh.py index 63a7c642ef7a..bc5ed8fb0035 100644 --- a/tests/hazmat/primitives/test_dh.py +++ b/tests/hazmat/primitives/test_dh.py @@ -23,6 +23,19 @@ from ...doubles import DummyKeySerializationEncryption from ...utils import load_nist_vectors, load_vectors_from_file +# RFC 3526 +P_1536 = int( + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF", + 16, +) + def _skip_dhx_unsupported(backend, is_dhx): if not is_dhx: @@ -32,35 +45,39 @@ def _skip_dhx_unsupported(backend, is_dhx): def test_dh_parameternumbers(): - params = dh.DHParameterNumbers(65537, 2) + params = dh.DHParameterNumbers(P_1536, 2) - assert params.p == 65537 + assert params.p == P_1536 assert params.g == 2 with pytest.raises(TypeError): dh.DHParameterNumbers(None, 2) with pytest.raises(TypeError): - dh.DHParameterNumbers(65537, None) + dh.DHParameterNumbers(P_1536, None) with pytest.raises(TypeError): dh.DHParameterNumbers(None, None) with pytest.raises(ValueError): - dh.DHParameterNumbers(65537, 1) + dh.DHParameterNumbers(P_1536, 1) + + # p too small + with pytest.raises(ValueError): + dh.DHParameterNumbers(65537, 2) - params = dh.DHParameterNumbers(65537, 7, 1245) + params = dh.DHParameterNumbers(P_1536, 7, 1245) - assert params.p == 65537 + assert params.p == P_1536 assert params.g == 7 assert params.q == 1245 with pytest.raises(TypeError): - dh.DHParameterNumbers(65537, 2, "hello") + dh.DHParameterNumbers(P_1536, 2, "hello") def test_dh_numbers(): - params = dh.DHParameterNumbers(65537, 2) + params = dh.DHParameterNumbers(P_1536, 2) public = dh.DHPublicNumbers(1, params) @@ -86,20 +103,22 @@ def test_dh_numbers(): def test_dh_parameter_numbers_equality(): - assert dh.DHParameterNumbers(65537, 2) == dh.DHParameterNumbers(65537, 2) - assert dh.DHParameterNumbers(65537, 7, 12345) == dh.DHParameterNumbers( - 65537, 7, 12345 + assert dh.DHParameterNumbers(P_1536, 2) == dh.DHParameterNumbers(P_1536, 2) + assert dh.DHParameterNumbers(P_1536, 7, 12345) == dh.DHParameterNumbers( + P_1536, 7, 12345 ) - assert dh.DHParameterNumbers(6, 2) != dh.DHParameterNumbers(65537, 2) - assert dh.DHParameterNumbers(65537, 2, 123) != dh.DHParameterNumbers( - 65537, 2, 456 + assert dh.DHParameterNumbers(P_1536 + 2, 2) != dh.DHParameterNumbers( + P_1536, 2 ) - assert dh.DHParameterNumbers(65537, 5) != dh.DHParameterNumbers(65537, 2) - assert dh.DHParameterNumbers(65537, 2) != object() + assert dh.DHParameterNumbers(P_1536, 2, 123) != dh.DHParameterNumbers( + P_1536, 2, 456 + ) + assert dh.DHParameterNumbers(P_1536, 5) != dh.DHParameterNumbers(P_1536, 2) + assert dh.DHParameterNumbers(P_1536, 2) != object() def test_dh_private_numbers_equality(): - params = dh.DHParameterNumbers(65537, 2) + params = dh.DHParameterNumbers(P_1536, 2) public = dh.DHPublicNumbers(1, params) private = dh.DHPrivateNumbers(2, public) @@ -107,18 +126,18 @@ def test_dh_private_numbers_equality(): assert private != dh.DHPrivateNumbers(0, public) assert private != dh.DHPrivateNumbers(2, dh.DHPublicNumbers(0, params)) assert private != dh.DHPrivateNumbers( - 2, dh.DHPublicNumbers(1, dh.DHParameterNumbers(65537, 5)) + 2, dh.DHPublicNumbers(1, dh.DHParameterNumbers(P_1536, 5)) ) assert private != object() def test_dh_public_numbers_equality(): - params = dh.DHParameterNumbers(65537, 2) + params = dh.DHParameterNumbers(P_1536, 2) public = dh.DHPublicNumbers(1, params) assert public == dh.DHPublicNumbers(1, params) assert public != dh.DHPublicNumbers(0, params) - assert public != dh.DHPublicNumbers(1, dh.DHParameterNumbers(65537, 5)) + assert public != dh.DHPublicNumbers(1, dh.DHParameterNumbers(P_1536, 5)) assert public != object() @@ -132,6 +151,7 @@ def test_unsupported_generator_generate_dh(self, backend): with pytest.raises(ValueError): dh.generate_parameters(7, 512, backend) + @pytest.mark.skip_fips(reason="non-FIPS parameters") def test_dh_parameters_supported(self, backend): valid_p = int( b"907c7211ae61aaaba1825ff53b6cb71ac6df9f1a424c033f4a0a41ac42fad3a9" @@ -152,6 +172,12 @@ def test_dh_parameters_supported(self, backend): ) def test_dh_parameters_allows_rfc3526_groups(self, backend, vector): p = int_from_bytes(binascii.unhexlify(vector["p"]), "big") + if ( + backend._fips_enabled + and p.bit_length() < backend._fips_dh_min_modulus + ): + pytest.skip("modulus too small for FIPS mode") + params = dh.DHParameterNumbers(p, int(vector["g"])) param = params.parameters(backend) key = param.generate_private_key() @@ -161,6 +187,7 @@ def test_dh_parameters_allows_rfc3526_groups(self, backend, vector): roundtripped_key = key.private_numbers().private_key(backend) assert key.private_numbers() == roundtripped_key.private_numbers() + @pytest.mark.skip_fips(reason="non-FIPS parameters") @pytest.mark.parametrize( "vector", load_vectors_from_file( @@ -208,13 +235,13 @@ def test_convert_to_numbers(self, backend, with_q): deserialized_private, dh.DHPrivateKeyWithSerialization ) + @pytest.mark.skip_fips(reason="FIPS requires specific parameters") def test_numbers_unsupported_parameters(self, backend): - # p is set to 21 because when calling private_key we want it to - # fail the DH_check call OpenSSL does. Originally this was 23, but - # we are allowing p % 24 to == 23 with this PR (see #3768 for more) - # By setting it to 21 it fails later in DH_check in a primality check - # which triggers the code path we want to test - params = dh.DHParameterNumbers(21, 2) + # p is set to P_1536 + 1 because when calling private_key we want it to + # fail the DH_check call OpenSSL does, but we specifically want it to + # fail such that we don't get a DH_NOT_SUITABLE_GENERATOR. We can cause + # this by making sure p is not prime. + params = dh.DHParameterNumbers(P_1536 + 1, 2) public = dh.DHPublicNumbers(1, params) private = dh.DHPrivateNumbers(2, public) @@ -363,6 +390,16 @@ def test_bad_exchange(self, backend, vector): assert symkey1 != symkey2 + @pytest.mark.skip_fips(reason="key_size too small for FIPS") + def test_load_256bit_key_from_pkcs8(self, backend): + data = load_vectors_from_file( + os.path.join("asymmetric", "DH", "dh_key_256.pem"), + lambda pemfile: pemfile.read(), + mode="rb", + ) + key = serialization.load_pem_private_key(data, None, backend) + assert key.key_size == 256 + @pytest.mark.parametrize( "vector", load_vectors_from_file( @@ -375,6 +412,10 @@ def test_dh_vectors(self, backend, vector): and int(vector["p"]) < backend._fips_dh_min_modulus ): pytest.skip("modulus too small for FIPS mode") + + if int(vector["p"]).bit_length() < 512: + pytest.skip("DH keys less than 512 bits are unsupported") + parameters = dh.DHParameterNumbers(int(vector["p"]), int(vector["g"])) public = dh.DHPublicNumbers(int(vector["y"]), parameters) private = dh.DHPrivateNumbers(int(vector["x"]), public) @@ -383,6 +424,7 @@ def test_dh_vectors(self, backend, vector): assert int_from_bytes(symkey, "big") == int(vector["k"], 16) + @pytest.mark.skip_fips(reason="non-FIPS parameters") @pytest.mark.parametrize( "vector", load_vectors_from_file( @@ -445,6 +487,7 @@ def test_private_bytes_rejects_invalid(self, encoding, fmt, backend): with pytest.raises(ValueError): key.private_bytes(encoding, fmt, serialization.NoEncryption()) + @pytest.mark.skip_fips(reason="non-FIPS parameters") @pytest.mark.parametrize( ("key_path", "loader_func", "encoding", "is_dhx"), [ @@ -489,6 +532,7 @@ def test_private_bytes_match( ) assert serialized == key_bytes + @pytest.mark.skip_fips(reason="non-FIPS parameters") @pytest.mark.parametrize( ("key_path", "loader_func", "vec_path", "is_dhx"), [ diff --git a/tests/hazmat/primitives/test_padding.py b/tests/hazmat/primitives/test_padding.py index f66d0ee8521d..b15eb37539c5 100644 --- a/tests/hazmat/primitives/test_padding.py +++ b/tests/hazmat/primitives/test_padding.py @@ -43,6 +43,18 @@ def test_non_bytes(self): with pytest.raises(TypeError): unpadder.update(u"abc") + def test_zany_py2_bytes_subclass(self): + class mybytes(bytes): # noqa: N801 + def __str__(self): + return "broken" + + str(mybytes()) + padder = padding.PKCS7(128).padder() + padder.update(mybytes(b"abc")) + unpadder = padding.PKCS7(128).unpadder() + unpadder.update(mybytes(padder.finalize())) + assert unpadder.finalize() == b"abc" + @pytest.mark.parametrize( ("size", "unpadded", "padded"), [ @@ -109,6 +121,18 @@ def test_large_padding(self): assert data == b"" + def test_bytearray(self): + padder = padding.PKCS7(128).padder() + unpadded = bytearray(b"t" * 38) + padded = ( + padder.update(unpadded) + + padder.update(unpadded) + + padder.finalize() + ) + unpadder = padding.PKCS7(128).unpadder() + final = unpadder.update(padded) + unpadder.finalize() + assert final == unpadded + unpadded + class TestANSIX923(object): @pytest.mark.parametrize("size", [127, 4096, -2]) @@ -142,6 +166,18 @@ def test_non_bytes(self): with pytest.raises(TypeError): unpadder.update(u"abc") + def test_zany_py2_bytes_subclass(self): + class mybytes(bytes): # noqa: N801 + def __str__(self): + return "broken" + + str(mybytes()) + padder = padding.ANSIX923(128).padder() + padder.update(mybytes(b"abc")) + unpadder = padding.ANSIX923(128).unpadder() + unpadder.update(mybytes(padder.finalize())) + assert unpadder.finalize() == b"abc" + @pytest.mark.parametrize( ("size", "unpadded", "padded"), [ @@ -193,3 +229,15 @@ def test_use_after_finalize(self): unpadder.update(b"") with pytest.raises(AlreadyFinalized): unpadder.finalize() + + def test_bytearray(self): + padder = padding.ANSIX923(128).padder() + unpadded = bytearray(b"t" * 38) + padded = ( + padder.update(unpadded) + + padder.update(unpadded) + + padder.finalize() + ) + unpadder = padding.ANSIX923(128).unpadder() + final = unpadder.update(padded) + unpadder.finalize() + assert final == unpadded + unpadded diff --git a/tests/hazmat/primitives/test_pkcs7.py b/tests/hazmat/primitives/test_pkcs7.py index b7afe7512ac2..8b93cb6334ba 100644 --- a/tests/hazmat/primitives/test_pkcs7.py +++ b/tests/hazmat/primitives/test_pkcs7.py @@ -10,6 +10,8 @@ from cryptography import x509 from cryptography.exceptions import _Reasons +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import ed25519 from cryptography.hazmat.primitives.serialization import pkcs7 from .utils import load_vectors_from_file @@ -77,3 +79,597 @@ def test_load_pkcs7_unsupported_type(self): ), mode="rb", ) + + +# We have no public verification API and won't be adding one until we get +# some requirements from users so this function exists to give us basic +# verification for the signing tests. +def _pkcs7_verify(encoding, sig, msg, certs, options, backend): + sig_bio = backend._bytes_to_bio(sig) + if encoding is serialization.Encoding.DER: + p7 = backend._lib.d2i_PKCS7_bio(sig_bio.bio, backend._ffi.NULL) + elif encoding is serialization.Encoding.PEM: + p7 = backend._lib.PEM_read_bio_PKCS7( + sig_bio.bio, + backend._ffi.NULL, + backend._ffi.NULL, + backend._ffi.NULL, + ) + else: + p7 = backend._lib.SMIME_read_PKCS7(sig_bio.bio, backend._ffi.NULL) + backend.openssl_assert(p7 != backend._ffi.NULL) + p7 = backend._ffi.gc(p7, backend._lib.PKCS7_free) + flags = 0 + for option in options: + if option is pkcs7.PKCS7Options.Text: + flags |= backend._lib.PKCS7_TEXT + store = backend._lib.X509_STORE_new() + backend.openssl_assert(store != backend._ffi.NULL) + store = backend._ffi.gc(store, backend._lib.X509_STORE_free) + for cert in certs: + res = backend._lib.X509_STORE_add_cert(store, cert._x509) + backend.openssl_assert(res == 1) + if msg is None: + res = backend._lib.PKCS7_verify( + p7, + backend._ffi.NULL, + store, + backend._ffi.NULL, + backend._ffi.NULL, + flags, + ) + else: + msg_bio = backend._bytes_to_bio(msg) + res = backend._lib.PKCS7_verify( + p7, backend._ffi.NULL, store, msg_bio.bio, backend._ffi.NULL, flags + ) + backend.openssl_assert(res == 1) + + +def _load_cert_key(): + key = load_vectors_from_file( + os.path.join("x509", "custom", "ca", "ca_key.pem"), + lambda pemfile: serialization.load_pem_private_key( + pemfile.read(), None + ), + mode="rb", + ) + cert = load_vectors_from_file( + os.path.join("x509", "custom", "ca", "ca.pem"), + loader=lambda pemfile: x509.load_pem_x509_certificate(pemfile.read()), + mode="rb", + ) + return cert, key + + +class TestPKCS7Builder(object): + def test_invalid_data(self): + builder = pkcs7.PKCS7SignatureBuilder() + with pytest.raises(TypeError): + builder.set_data(u"not bytes") + + def test_set_data_twice(self): + builder = pkcs7.PKCS7SignatureBuilder().set_data(b"test") + with pytest.raises(ValueError): + builder.set_data(b"test") + + def test_sign_no_signer(self): + builder = pkcs7.PKCS7SignatureBuilder().set_data(b"test") + with pytest.raises(ValueError): + builder.sign(serialization.Encoding.SMIME, []) + + def test_sign_no_data(self): + cert, key = _load_cert_key() + builder = pkcs7.PKCS7SignatureBuilder().add_signer( + cert, key, hashes.SHA256() + ) + with pytest.raises(ValueError): + builder.sign(serialization.Encoding.SMIME, []) + + def test_unsupported_hash_alg(self): + cert, key = _load_cert_key() + with pytest.raises(TypeError): + pkcs7.PKCS7SignatureBuilder().add_signer( + cert, key, hashes.SHA512_256() + ) + + def test_not_a_cert(self): + cert, key = _load_cert_key() + with pytest.raises(TypeError): + pkcs7.PKCS7SignatureBuilder().add_signer( + b"notacert", key, hashes.SHA256() + ) + + @pytest.mark.supported( + only_if=lambda backend: backend.ed25519_supported(), + skip_message="Does not support ed25519.", + ) + def test_unsupported_key_type(self, backend): + cert, _ = _load_cert_key() + key = ed25519.Ed25519PrivateKey.generate() + with pytest.raises(TypeError): + pkcs7.PKCS7SignatureBuilder().add_signer( + cert, key, hashes.SHA256() + ) + + def test_sign_invalid_options(self): + cert, key = _load_cert_key() + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(b"test") + .add_signer(cert, key, hashes.SHA256()) + ) + with pytest.raises(ValueError): + builder.sign(serialization.Encoding.SMIME, [b"invalid"]) + + def test_sign_invalid_encoding(self): + cert, key = _load_cert_key() + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(b"test") + .add_signer(cert, key, hashes.SHA256()) + ) + with pytest.raises(ValueError): + builder.sign(serialization.Encoding.Raw, []) + + def test_sign_invalid_options_text_no_detached(self): + cert, key = _load_cert_key() + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(b"test") + .add_signer(cert, key, hashes.SHA256()) + ) + options = [pkcs7.PKCS7Options.Text] + with pytest.raises(ValueError): + builder.sign(serialization.Encoding.SMIME, options) + + def test_sign_invalid_options_text_der_encoding(self): + cert, key = _load_cert_key() + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(b"test") + .add_signer(cert, key, hashes.SHA256()) + ) + options = [ + pkcs7.PKCS7Options.Text, + pkcs7.PKCS7Options.DetachedSignature, + ] + with pytest.raises(ValueError): + builder.sign(serialization.Encoding.DER, options) + + def test_sign_invalid_options_no_attrs_and_no_caps(self): + cert, key = _load_cert_key() + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(b"test") + .add_signer(cert, key, hashes.SHA256()) + ) + options = [ + pkcs7.PKCS7Options.NoAttributes, + pkcs7.PKCS7Options.NoCapabilities, + ] + with pytest.raises(ValueError): + builder.sign(serialization.Encoding.SMIME, options) + + def test_smime_sign_detached(self, backend): + data = b"hello world" + cert, key = _load_cert_key() + options = [pkcs7.PKCS7Options.DetachedSignature] + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(cert, key, hashes.SHA256()) + ) + + sig = builder.sign(serialization.Encoding.SMIME, options) + sig_binary = builder.sign(serialization.Encoding.DER, options) + # We don't have a generic ASN.1 parser available to us so we instead + # will assert on specific byte sequences being present based on the + # parameters chosen above. + assert b"sha-256" in sig + # Detached signature means that the signed data is *not* embedded into + # the PKCS7 structure itself, but is present in the SMIME serialization + # as a separate section before the PKCS7 data. So we should expect to + # have data in sig but not in sig_binary + assert data in sig + _pkcs7_verify( + serialization.Encoding.SMIME, sig, data, [cert], options, backend + ) + assert data not in sig_binary + _pkcs7_verify( + serialization.Encoding.DER, + sig_binary, + data, + [cert], + options, + backend, + ) + + def test_sign_byteslike(self): + data = bytearray(b"hello world") + cert, key = _load_cert_key() + options = [pkcs7.PKCS7Options.DetachedSignature] + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(cert, key, hashes.SHA256()) + ) + + sig = builder.sign(serialization.Encoding.SMIME, options) + assert bytes(data) in sig + + def test_sign_pem(self, backend): + data = b"hello world" + cert, key = _load_cert_key() + options = [] + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(cert, key, hashes.SHA256()) + ) + + sig = builder.sign(serialization.Encoding.PEM, options) + _pkcs7_verify( + serialization.Encoding.PEM, + sig, + None, + [cert], + options, + backend, + ) + + @pytest.mark.parametrize( + ("hash_alg", "expected_value"), + [ + (hashes.SHA1(), b"\x06\x05+\x0e\x03\x02\x1a"), + (hashes.SHA256(), b"\x06\t`\x86H\x01e\x03\x04\x02\x01"), + (hashes.SHA384(), b"\x06\t`\x86H\x01e\x03\x04\x02\x02"), + (hashes.SHA512(), b"\x06\t`\x86H\x01e\x03\x04\x02\x03"), + ], + ) + def test_sign_alternate_digests_der( + self, hash_alg, expected_value, backend + ): + data = b"hello world" + cert, key = _load_cert_key() + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(cert, key, hash_alg) + ) + options = [] + sig = builder.sign(serialization.Encoding.DER, options) + assert expected_value in sig + _pkcs7_verify( + serialization.Encoding.DER, sig, None, [cert], options, backend + ) + + @pytest.mark.parametrize( + ("hash_alg", "expected_value"), + [ + (hashes.SHA1(), b"sha1"), + (hashes.SHA256(), b"sha-256"), + (hashes.SHA384(), b"sha-384"), + (hashes.SHA512(), b"sha-512"), + ], + ) + def test_sign_alternate_digests_detached(self, hash_alg, expected_value): + data = b"hello world" + cert, key = _load_cert_key() + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(cert, key, hash_alg) + ) + options = [pkcs7.PKCS7Options.DetachedSignature] + sig = builder.sign(serialization.Encoding.SMIME, options) + # When in detached signature mode the hash algorithm is stored as a + # byte string like "sha-384". + assert expected_value in sig + + def test_sign_attached(self, backend): + data = b"hello world" + cert, key = _load_cert_key() + options = [] + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(cert, key, hashes.SHA256()) + ) + + sig_binary = builder.sign(serialization.Encoding.DER, options) + # When not passing detached signature the signed data is embedded into + # the PKCS7 structure itself + assert data in sig_binary + _pkcs7_verify( + serialization.Encoding.DER, + sig_binary, + None, + [cert], + options, + backend, + ) + + def test_sign_binary(self, backend): + data = b"hello\nworld" + cert, key = _load_cert_key() + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(cert, key, hashes.SHA256()) + ) + options = [] + sig_no_binary = builder.sign(serialization.Encoding.DER, options) + sig_binary = builder.sign( + serialization.Encoding.DER, [pkcs7.PKCS7Options.Binary] + ) + # Binary prevents translation of LF to CR+LF (SMIME canonical form) + # so data should not be present in sig_no_binary, but should be present + # in sig_binary + assert data not in sig_no_binary + _pkcs7_verify( + serialization.Encoding.DER, + sig_no_binary, + None, + [cert], + options, + backend, + ) + assert data in sig_binary + _pkcs7_verify( + serialization.Encoding.DER, + sig_binary, + None, + [cert], + options, + backend, + ) + + def test_sign_smime_canonicalization(self, backend): + data = b"hello\nworld" + cert, key = _load_cert_key() + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(cert, key, hashes.SHA256()) + ) + + options = [] + sig_binary = builder.sign(serialization.Encoding.DER, options) + # LF gets converted to CR+LF (SMIME canonical form) + # so data should not be present in the sig + assert data not in sig_binary + assert b"hello\r\nworld" in sig_binary + _pkcs7_verify( + serialization.Encoding.DER, + sig_binary, + None, + [cert], + options, + backend, + ) + + def test_sign_text(self, backend): + data = b"hello world" + cert, key = _load_cert_key() + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(cert, key, hashes.SHA256()) + ) + + options = [ + pkcs7.PKCS7Options.Text, + pkcs7.PKCS7Options.DetachedSignature, + ] + sig_pem = builder.sign(serialization.Encoding.SMIME, options) + # The text option adds text/plain headers to the S/MIME message + # These headers are only relevant in SMIME mode, not binary, which is + # just the PKCS7 structure itself. + assert b"text/plain" in sig_pem + # When passing the Text option the header is prepended so the actual + # signed data is this. + signed_data = b"Content-Type: text/plain\r\n\r\nhello world" + _pkcs7_verify( + serialization.Encoding.SMIME, + sig_pem, + signed_data, + [cert], + options, + backend, + ) + + def test_sign_no_capabilities(self, backend): + data = b"hello world" + cert, key = _load_cert_key() + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(cert, key, hashes.SHA256()) + ) + + options = [pkcs7.PKCS7Options.NoCapabilities] + sig_binary = builder.sign(serialization.Encoding.DER, options) + # NoCapabilities removes the SMIMECapabilities attribute from the + # PKCS7 structure. This is an ASN.1 sequence with the + # OID 1.2.840.113549.1.9.15. It does NOT remove all authenticated + # attributes, so we verify that by looking for the signingTime OID. + + # 1.2.840.113549.1.9.15 SMIMECapabilities as an ASN.1 DER encoded OID + assert b"\x06\t*\x86H\x86\xf7\r\x01\t\x0f" not in sig_binary + # 1.2.840.113549.1.9.5 signingTime as an ASN.1 DER encoded OID + assert b"\x06\t*\x86H\x86\xf7\r\x01\t\x05" in sig_binary + _pkcs7_verify( + serialization.Encoding.DER, + sig_binary, + None, + [cert], + options, + backend, + ) + + def test_sign_no_attributes(self, backend): + data = b"hello world" + cert, key = _load_cert_key() + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(cert, key, hashes.SHA256()) + ) + + options = [pkcs7.PKCS7Options.NoAttributes] + sig_binary = builder.sign(serialization.Encoding.DER, options) + # NoAttributes removes all authenticated attributes, so we shouldn't + # find SMIMECapabilities or signingTime. + + # 1.2.840.113549.1.9.15 SMIMECapabilities as an ASN.1 DER encoded OID + assert b"\x06\t*\x86H\x86\xf7\r\x01\t\x0f" not in sig_binary + # 1.2.840.113549.1.9.5 signingTime as an ASN.1 DER encoded OID + assert b"\x06\t*\x86H\x86\xf7\r\x01\t\x05" not in sig_binary + _pkcs7_verify( + serialization.Encoding.DER, + sig_binary, + None, + [cert], + options, + backend, + ) + + def test_sign_no_certs(self, backend): + data = b"hello world" + cert, key = _load_cert_key() + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(cert, key, hashes.SHA256()) + ) + + options = [] + sig = builder.sign(serialization.Encoding.DER, options) + assert sig.count(cert.public_bytes(serialization.Encoding.DER)) == 1 + + options = [pkcs7.PKCS7Options.NoCerts] + sig_no = builder.sign(serialization.Encoding.DER, options) + assert sig_no.count(cert.public_bytes(serialization.Encoding.DER)) == 0 + + def test_multiple_signers(self, backend): + data = b"hello world" + cert, key = _load_cert_key() + rsa_key = load_vectors_from_file( + os.path.join("x509", "custom", "ca", "rsa_key.pem"), + lambda pemfile: serialization.load_pem_private_key( + pemfile.read(), None + ), + mode="rb", + ) + rsa_cert = load_vectors_from_file( + os.path.join("x509", "custom", "ca", "rsa_ca.pem"), + loader=lambda pemfile: x509.load_pem_x509_certificate( + pemfile.read() + ), + mode="rb", + ) + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(cert, key, hashes.SHA512()) + .add_signer(rsa_cert, rsa_key, hashes.SHA512()) + ) + options = [] + sig = builder.sign(serialization.Encoding.DER, options) + # There should be three SHA512 OIDs in this structure + assert sig.count(b"\x06\t`\x86H\x01e\x03\x04\x02\x03") == 3 + _pkcs7_verify( + serialization.Encoding.DER, + sig, + None, + [cert, rsa_cert], + options, + backend, + ) + + def test_multiple_signers_different_hash_algs(self, backend): + data = b"hello world" + cert, key = _load_cert_key() + rsa_key = load_vectors_from_file( + os.path.join("x509", "custom", "ca", "rsa_key.pem"), + lambda pemfile: serialization.load_pem_private_key( + pemfile.read(), None + ), + mode="rb", + ) + rsa_cert = load_vectors_from_file( + os.path.join("x509", "custom", "ca", "rsa_ca.pem"), + loader=lambda pemfile: x509.load_pem_x509_certificate( + pemfile.read() + ), + mode="rb", + ) + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(cert, key, hashes.SHA384()) + .add_signer(rsa_cert, rsa_key, hashes.SHA512()) + ) + options = [] + sig = builder.sign(serialization.Encoding.DER, options) + # There should be two SHA384 and two SHA512 OIDs in this structure + assert sig.count(b"\x06\t`\x86H\x01e\x03\x04\x02\x02") == 2 + assert sig.count(b"\x06\t`\x86H\x01e\x03\x04\x02\x03") == 2 + _pkcs7_verify( + serialization.Encoding.DER, + sig, + None, + [cert, rsa_cert], + options, + backend, + ) + + def test_add_additional_cert_not_a_cert(self, backend): + with pytest.raises(TypeError): + pkcs7.PKCS7SignatureBuilder().add_certificate(b"notacert") + + def test_add_additional_cert(self, backend): + data = b"hello world" + cert, key = _load_cert_key() + rsa_cert = load_vectors_from_file( + os.path.join("x509", "custom", "ca", "rsa_ca.pem"), + loader=lambda pemfile: x509.load_pem_x509_certificate( + pemfile.read() + ), + mode="rb", + ) + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(cert, key, hashes.SHA384()) + .add_certificate(rsa_cert) + ) + options = [] + sig = builder.sign(serialization.Encoding.DER, options) + assert ( + sig.count(rsa_cert.public_bytes(serialization.Encoding.DER)) == 1 + ) + + def test_add_multiple_additional_certs(self, backend): + data = b"hello world" + cert, key = _load_cert_key() + rsa_cert = load_vectors_from_file( + os.path.join("x509", "custom", "ca", "rsa_ca.pem"), + loader=lambda pemfile: x509.load_pem_x509_certificate( + pemfile.read() + ), + mode="rb", + ) + builder = ( + pkcs7.PKCS7SignatureBuilder() + .set_data(data) + .add_signer(cert, key, hashes.SHA384()) + .add_certificate(rsa_cert) + .add_certificate(rsa_cert) + ) + options = [] + sig = builder.sign(serialization.Encoding.DER, options) + assert ( + sig.count(rsa_cert.public_bytes(serialization.Encoding.DER)) == 2 + ) diff --git a/tests/hazmat/primitives/test_rsa.py b/tests/hazmat/primitives/test_rsa.py index bfb946ee5de4..61c481504ecc 100644 --- a/tests/hazmat/primitives/test_rsa.py +++ b/tests/hazmat/primitives/test_rsa.py @@ -730,6 +730,18 @@ def test_prehashed_unsupported_in_verifier_ctx(self, backend): asym_utils.Prehashed(hashes.SHA1()), ) + def test_prehashed_unsupported_in_signature_recover(self, backend): + private_key = RSA_KEY_512.private_key(backend) + public_key = private_key.public_key() + signature = private_key.sign( + b"sign me", padding.PKCS1v15(), hashes.SHA1() + ) + prehashed_alg = asym_utils.Prehashed(hashes.SHA1()) + with pytest.raises(TypeError): + public_key.recover_data_from_signature( + signature, padding.PKCS1v15(), prehashed_alg + ) + def test_corrupted_private_key(self, backend): with pytest.raises(ValueError): serialization.load_pem_private_key( @@ -759,12 +771,27 @@ def test_pkcs1v15_verification(self, pkcs1_example, backend): public_key = rsa.RSAPublicNumbers( e=public["public_exponent"], n=public["modulus"] ).public_key(backend) + signature = binascii.unhexlify(example["signature"]) + message = binascii.unhexlify(example["message"]) public_key.verify( - binascii.unhexlify(example["signature"]), - binascii.unhexlify(example["message"]), - padding.PKCS1v15(), - hashes.SHA1(), + signature, message, padding.PKCS1v15(), hashes.SHA1() + ) + + # Test digest recovery by providing hash + digest = hashes.Hash(hashes.SHA1()) + digest.update(message) + msg_digest = digest.finalize() + rec_msg_digest = public_key.recover_data_from_signature( + signature, padding.PKCS1v15(), hashes.SHA1() ) + assert msg_digest == rec_msg_digest + + # Test recovery of all data (full DigestInfo) with hash alg. as None + rec_sig_data = public_key.recover_data_from_signature( + signature, padding.PKCS1v15(), None + ) + assert len(rec_sig_data) > len(msg_digest) + assert msg_digest == rec_sig_data[-len(msg_digest) :] @pytest.mark.supported( only_if=lambda backend: backend.rsa_padding_supported( @@ -783,6 +810,17 @@ def test_invalid_pkcs1v15_signature_wrong_data(self, backend): signature, b"incorrect data", padding.PKCS1v15(), hashes.SHA1() ) + def test_invalid_pkcs1v15_signature_recover_wrong_hash_alg(self, backend): + private_key = RSA_KEY_512.private_key(backend) + public_key = private_key.public_key() + signature = private_key.sign( + b"sign me", padding.PKCS1v15(), hashes.SHA1() + ) + with pytest.raises(InvalidSignature): + public_key.recover_data_from_signature( + signature, padding.PKCS1v15(), hashes.SHA256() + ) + def test_invalid_signature_sequence_removed(self, backend): """ This test comes from wycheproof @@ -946,21 +984,19 @@ def test_invalid_pss_signature_wrong_key(self, backend): skip_message="Does not support PSS.", ) def test_invalid_pss_signature_data_too_large_for_modulus(self, backend): + # 2048 bit PSS signature signature = binascii.unhexlify( - b"cb43bde4f7ab89eb4a79c6e8dd67e0d1af60715da64429d90c716a490b799c29" - b"194cf8046509c6ed851052367a74e2e92d9b38947ed74332acb115a03fcc0222" + b"58750fc3d2f560d1f3e37c8e28bc8da6d3e93f5d58f8becd25b1c931eea30fea" + b"54cb17d44b90104a0aacb7fe9ffa2a59c5788435911d63de78178d21eb875ccd" + b"0b07121b641ed4fe6bcb1ca5060322765507b4f24bdba8a698a8e4e07e6bf2c4" + b"7a736abe5a912e85cd32f648f3e043b4385e8b612dcce342c5fddf18c524deb5" + b"6295b95f6dfa759b2896b793628a90f133e74c1ff7d3af43e3f7ee792df2e5b6" + b"a19e996ac3676884354899a437b3ae4e3ac91976c336c332a3b1db0d172b19cb" + b"40ad3d871296cfffb3c889ce74a179a3e290852c35d59525afe4b39dc907fad2" + b"ac462c50a488dca486031a3dc8c4cdbbc53e9f71d64732e1533a5d1249b833ce" ) - public_key = rsa.RSAPublicNumbers( - n=int( - b"381201f4905d67dfeb3dec131a0fbea773489227ec7a1448c3109189ac68" - b"5a95441be90866a14c4d2e139cd16db540ec6c7abab13ffff91443fd46a8" - b"960cbb7658ded26a5c95c86f6e40384e1c1239c63e541ba221191c4dd303" - b"231b42e33c6dbddf5ec9a746f09bf0c25d0f8d27f93ee0ae5c0d723348f4" - b"030d3581e13522", - 16, - ), - e=65537, - ).public_key(backend) + # 1024 bit key + public_key = RSA_KEY_1024.private_key(backend).public_key() with pytest.raises(InvalidSignature): public_key.verify( signature, @@ -972,6 +1008,27 @@ def test_invalid_pss_signature_data_too_large_for_modulus(self, backend): hashes.SHA1(), ) + def test_invalid_pss_signature_recover(self, backend): + private_key = RSA_KEY_1024.private_key(backend) + public_key = private_key.public_key() + pss_padding = padding.PSS( + mgf=padding.MGF1(algorithm=hashes.SHA1()), + salt_length=padding.PSS.MAX_LENGTH, + ) + signature = private_key.sign(b"sign me", pss_padding, hashes.SHA1()) + + # Hash algorithm can not be absent for PSS padding + with pytest.raises(TypeError): + public_key.recover_data_from_signature( + signature, pss_padding, None + ) + + # Signature data recovery not supported with PSS + with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_PADDING): + public_key.recover_data_from_signature( + signature, pss_padding, hashes.SHA1() + ) + @pytest.mark.supported( only_if=lambda backend: backend.rsa_padding_supported( padding.PKCS1v15() @@ -1896,6 +1953,9 @@ def test_rsa_public_numbers_create_key(self, backend): public_key = RSA_KEY_1024.public_numbers.public_key(backend) assert public_key + public_key = rsa.RSAPublicNumbers(n=10, e=3).public_key(backend) + assert public_key + def test_public_numbers_invalid_types(self): with pytest.raises(TypeError): rsa.RSAPublicNumbers(e=None, n=15) diff --git a/tests/hazmat/primitives/test_serialization.py b/tests/hazmat/primitives/test_serialization.py index 2f56711d5dab..32debd46c7d2 100644 --- a/tests/hazmat/primitives/test_serialization.py +++ b/tests/hazmat/primitives/test_serialization.py @@ -13,7 +13,7 @@ import six -from cryptography.exceptions import UnsupportedAlgorithm, _Reasons +from cryptography.exceptions import UnsupportedAlgorithm from cryptography.hazmat.backends.interfaces import ( DERSerializationBackend, DSABackend, @@ -55,7 +55,6 @@ load_vectors_from_file, ) from ...doubles import DummyKeySerializationEncryption -from ...utils import raises_unsupported_algorithm def _skip_fips_format(key_path, password, backend): @@ -771,7 +770,7 @@ def test_unsupported_key_encryption(self, backend): password = b"password" - with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_CIPHER): + with pytest.raises(ValueError): load_pem_private_key(key_data, password, backend) def test_corrupt_pkcs8_format(self, backend): @@ -966,7 +965,7 @@ def test_load_bad_oid_key(self, key_file, password, backend): ("key_file", "password"), [("bad-encryption-oid.pem", b"password")] ) def test_load_bad_encryption_oid_key(self, key_file, password, backend): - with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_CIPHER): + with pytest.raises(ValueError): load_vectors_from_file( os.path.join("asymmetric", "PKCS8", key_file), lambda pemfile: load_pem_private_key( @@ -1758,6 +1757,7 @@ def test_openssh_serialization_unsupported(self, backend): class TestDHSerialization(object): """Test all options with least-supported key type.""" + @pytest.mark.skip_fips(reason="non-FIPS parameters") def test_dh_public_key(self, backend): data = load_vectors_from_file( os.path.join("asymmetric", "DH", "dhkey.pem"), @@ -1789,6 +1789,7 @@ def test_dh_public_key(self, backend): with pytest.raises(ValueError): public_key.public_bytes(enc, fmt) + @pytest.mark.skip_fips(reason="non-FIPS parameters") def test_dh_private_key(self, backend): data = load_vectors_from_file( os.path.join("asymmetric", "DH", "dhkey.pem"), diff --git a/tests/hazmat/primitives/utils.py b/tests/hazmat/primitives/utils.py index 741e07d5c3cc..3f1eff66e00f 100644 --- a/tests/hazmat/primitives/utils.py +++ b/tests/hazmat/primitives/utils.py @@ -86,6 +86,10 @@ def test_aead(self, backend, params): def aead_test(backend, cipher_factory, mode_factory, params): + if mode_factory is GCM and len(params["iv"]) < 16: + # 16 because this is hex encoded data + pytest.skip("Less than 64-bit IVs are no longer supported") + if ( mode_factory is GCM and backend._fips_enabled diff --git a/tests/utils.py b/tests/utils.py index 5d98af00e337..497fde83f0a5 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -906,13 +906,6 @@ def has_flag(self, flag): return flag in self.testcase["flags"] -def skip_if_wycheproof_none(wycheproof): - # This is factored into its own function so we can easily test both - # branches - if wycheproof is None: - pytest.skip("--wycheproof-root not provided") - - def load_wycheproof_tests(wycheproof, test_file): path = os.path.join(wycheproof, "testvectors", test_file) with open(path) as f: diff --git a/tests/wycheproof/test_aes.py b/tests/wycheproof/test_aes.py index e33c01e99f54..9992095ae8c3 100644 --- a/tests/wycheproof/test_aes.py +++ b/tests/wycheproof/test_aes.py @@ -54,6 +54,11 @@ def test_aes_gcm(backend, wycheproof): msg = binascii.unhexlify(wycheproof.testcase["msg"]) ct = binascii.unhexlify(wycheproof.testcase["ct"]) tag = binascii.unhexlify(wycheproof.testcase["tag"]) + if len(iv) < 8 or len(iv) > 128: + pytest.skip( + "Less than 64-bit IVs (and greater than 1024-bit) are no longer " + "supported" + ) if backend._fips_enabled and len(iv) != 12: # Red Hat disables non-96-bit IV support as part of its FIPS # patches. @@ -73,9 +78,6 @@ def test_aes_gcm(backend, wycheproof): dec.authenticate_additional_data(aad) computed_msg = dec.update(ct) + dec.finalize() assert computed_msg == msg - elif len(iv) == 0: - with pytest.raises(ValueError): - Cipher(algorithms.AES(key), modes.GCM(iv), backend) else: dec = Cipher( algorithms.AES(key), @@ -97,6 +99,12 @@ def test_aes_gcm_aead_api(backend, wycheproof): msg = binascii.unhexlify(wycheproof.testcase["msg"]) ct = binascii.unhexlify(wycheproof.testcase["ct"]) tag = binascii.unhexlify(wycheproof.testcase["tag"]) + if len(iv) < 8 or len(iv) > 128: + pytest.skip( + "Less than 64-bit IVs (and greater than 1024-bit) are no longer " + "supported" + ) + if backend._fips_enabled and len(iv) != 12: # Red Hat disables non-96-bit IV support as part of its FIPS # patches. @@ -107,9 +115,6 @@ def test_aes_gcm_aead_api(backend, wycheproof): assert computed_ct == ct + tag computed_msg = aesgcm.decrypt(iv, ct + tag, aad) assert computed_msg == msg - elif len(iv) == 0: - with pytest.raises(ValueError): - aesgcm.encrypt(iv, msg, aad) else: with pytest.raises(InvalidTag): aesgcm.decrypt(iv, ct + tag, aad) diff --git a/tests/wycheproof/test_rsa.py b/tests/wycheproof/test_rsa.py index 1262b58853d3..926bb44e999f 100644 --- a/tests/wycheproof/test_rsa.py +++ b/tests/wycheproof/test_rsa.py @@ -35,12 +35,7 @@ def should_verify(backend, wycheproof): return True if wycheproof.acceptable: - if ( - backend._lib.CRYPTOGRAPHY_OPENSSL_110_OR_GREATER - or backend._lib.CRYPTOGRAPHY_IS_LIBRESSL - ) and wycheproof.has_flag("MissingNull"): - return False - return True + return not wycheproof.has_flag("MissingNull") return False @@ -165,16 +160,6 @@ def test_rsa_pss_signature(backend, wycheproof): @pytest.mark.requires_backend_interface(interface=RSABackend) -@pytest.mark.supported( - only_if=lambda backend: ( - backend._lib.CRYPTOGRAPHY_OPENSSL_110_OR_GREATER - or backend._lib.CRYPTOGRAPHY_IS_LIBRESSL - ), - skip_message=( - "A handful of these tests fail on OpenSSL 1.0.2 and since upstream " - "isn't maintaining it, they'll never be fixed." - ), -) @pytest.mark.wycheproof_tests( "rsa_oaep_2048_sha1_mgf1sha1_test.json", "rsa_oaep_2048_sha224_mgf1sha1_test.json", diff --git a/tests/wycheproof/test_utils.py b/tests/wycheproof/test_utils.py index 2cf3be08e97c..593d26bdec91 100644 --- a/tests/wycheproof/test_utils.py +++ b/tests/wycheproof/test_utils.py @@ -4,18 +4,9 @@ from __future__ import absolute_import, division, print_function -import pytest - -from ..utils import WycheproofTest, skip_if_wycheproof_none +from ..utils import WycheproofTest def test_wycheproof_test_repr(): wycheproof = WycheproofTest({}, {}, {"tcId": 3}) assert repr(wycheproof) == "" - - -def test_skip_if_wycheproof_none(): - with pytest.raises(pytest.skip.Exception): - skip_if_wycheproof_none(None) - - skip_if_wycheproof_none("abc") diff --git a/tests/x509/test_x509.py b/tests/x509/test_x509.py index 11c80816cff7..146619b9a84b 100644 --- a/tests/x509/test_x509.py +++ b/tests/x509/test_x509.py @@ -41,6 +41,7 @@ ) from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import ( + dh, dsa, ec, ed25519, @@ -51,6 +52,7 @@ from cryptography.hazmat.primitives.asymmetric.utils import ( decode_dss_signature, ) +from cryptography.utils import int_from_bytes from cryptography.x509.name import _ASN1Type from cryptography.x509.oid import ( AuthorityInformationAccessOID, @@ -65,7 +67,7 @@ from ..hazmat.primitives.fixtures_ec import EC_KEY_SECP256R1 from ..hazmat.primitives.fixtures_rsa import RSA_KEY_2048, RSA_KEY_512 from ..hazmat.primitives.test_ec import _skip_curve_unsupported -from ..utils import load_vectors_from_file +from ..utils import load_nist_vectors, load_vectors_from_file @utils.register_interface(x509.ExtensionType) @@ -1820,10 +1822,6 @@ def read_next_rdn_value_tag(reader): if ( # This only works correctly in OpenSSL 1.1.0f+ and 1.0.2l+ backend._lib.CRYPTOGRAPHY_OPENSSL_110F_OR_GREATER - or ( - backend._lib.CRYPTOGRAPHY_OPENSSL_102L_OR_GREATER - and not backend._lib.CRYPTOGRAPHY_OPENSSL_110_OR_GREATER - ) ): assert read_next_rdn_value_tag(subject) == PRINTABLE_STRING assert read_next_rdn_value_tag(issuer) == PRINTABLE_STRING @@ -5241,12 +5239,14 @@ class TestSignatureRejection(object): """Test if signing rejects DH keys properly.""" def load_key(self, backend): - data = load_vectors_from_file( - os.path.join("asymmetric", "DH", "dhkey.pem"), - lambda pemfile: pemfile.read(), - mode="rb", - ) - return serialization.load_pem_private_key(data, None, backend) + vector = load_vectors_from_file( + os.path.join("asymmetric", "DH", "rfc3526.txt"), + load_nist_vectors, + )[1] + p = int_from_bytes(binascii.unhexlify(vector["p"]), "big") + params = dh.DHParameterNumbers(p, int(vector["g"])) + param = params.parameters(backend) + return param.generate_private_key() def test_crt_signing_check(self, backend): issuer_private_key = self.load_key(backend) diff --git a/tests/x509/test_x509_ext.py b/tests/x509/test_x509_ext.py index 2cd216fb688a..8e2b402712ff 100644 --- a/tests/x509/test_x509_ext.py +++ b/tests/x509/test_x509_ext.py @@ -5679,10 +5679,35 @@ def test_simple(self, backend): ) @pytest.mark.supported( - only_if=lambda backend: ( - not backend._lib.CRYPTOGRAPHY_OPENSSL_110_OR_GREATER - ), - skip_message="Requires OpenSSL < 1.1.0", + only_if=lambda backend: (backend._lib.Cryptography_HAS_SCT), + skip_message="Requires CT support", + ) + def test_generate(self, backend): + cert = _load_cert( + os.path.join("x509", "badssl-sct.pem"), + x509.load_pem_x509_certificate, + backend, + ) + scts = cert.extensions.get_extension_for_class( + x509.PrecertificateSignedCertificateTimestamps + ).value + assert len(scts) == 1 + [sct] = scts + + private_key = RSA_KEY_2048.private_key(backend) + builder = _make_certbuilder(private_key).add_extension( + x509.PrecertificateSignedCertificateTimestamps([sct]), + critical=False, + ) + cert = builder.sign(private_key, hashes.SHA256(), backend) + ext = cert.extensions.get_extension_for_class( + x509.PrecertificateSignedCertificateTimestamps + ).value + assert list(ext) == [sct] + + @pytest.mark.supported( + only_if=lambda backend: backend._lib.CRYPTOGRAPHY_IS_LIBRESSL, + skip_message="Requires LibreSSL", ) def test_skips_scts_if_unsupported(self, backend): cert = _load_cert( diff --git a/tox.ini b/tox.ini index bc5de1ad9953..e6e04575bbc6 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] minversion = 2.4 -envlist = py27,pypy,py35,py36,py37,py38,docs,pep8,packaging +envlist = py27,pypy,py36,py37,py38,py39,docs,pep8,packaging isolated_build = True [testenv] @@ -8,7 +8,7 @@ extras = test ssh: ssh deps = - # This must be kept in sync with .travis/install.sh and .github/workflows/ci.yml + # This must be kept in sync with .github/workflows/ci.yml coverage ./vectors randomorder: pytest-randomly diff --git a/vectors/cryptography_vectors/__about__.py b/vectors/cryptography_vectors/__about__.py index 0ae2179b0c46..dc069d51ba86 100644 --- a/vectors/cryptography_vectors/__about__.py +++ b/vectors/cryptography_vectors/__about__.py @@ -20,10 +20,10 @@ __uri__ = "https://github.com/pyca/cryptography" -__version__ = "3.1" +__version__ = "3.3.2" __author__ = "The cryptography developers" __email__ = "cryptography-dev@python.org" __license__ = "BSD or Apache License, Version 2.0" -__copyright__ = "Copyright 2013-2019 %s" % __author__ +__copyright__ = "Copyright 2013-2021 %s" % __author__ diff --git a/vectors/cryptography_vectors/asymmetric/DH/dh_key_256.pem b/vectors/cryptography_vectors/asymmetric/DH/dh_key_256.pem new file mode 100644 index 000000000000..1c01dd3eaf7e --- /dev/null +++ b/vectors/cryptography_vectors/asymmetric/DH/dh_key_256.pem @@ -0,0 +1,4 @@ +-----BEGIN PRIVATE KEY----- +MFwCAQAwMwYJKoZIhvcNAQMBMCYCIQCBPg6BS+5nbb09nSjtc9NnNdIf9kVyNvaN +PWFFVgwPqwIBAgQiAiBmJ3qBbu72ZnUxnCrr8ujWFU7jWTcOjhsZSqobmiD6vA== +-----END PRIVATE KEY----- diff --git a/vectors/cryptography_vectors/ciphers/Blowfish/bf-cbc.txt b/vectors/cryptography_vectors/ciphers/Blowfish/bf-cbc.txt index 184d9565fd98..ad3fa0cf2fe6 100644 --- a/vectors/cryptography_vectors/ciphers/Blowfish/bf-cbc.txt +++ b/vectors/cryptography_vectors/ciphers/Blowfish/bf-cbc.txt @@ -1,4 +1,4 @@ -# Reformatted from https://www.schneier.com/code/vectors.txt +# Reformatted from https://www.schneier.com/wp-content/uploads/2015/12/vectors-2.txt # to look like the NIST vectors [ENCRYPT] diff --git a/vectors/cryptography_vectors/ciphers/Blowfish/bf-cfb.txt b/vectors/cryptography_vectors/ciphers/Blowfish/bf-cfb.txt index 8a326f500d46..cd2f58ff91df 100644 --- a/vectors/cryptography_vectors/ciphers/Blowfish/bf-cfb.txt +++ b/vectors/cryptography_vectors/ciphers/Blowfish/bf-cfb.txt @@ -1,4 +1,4 @@ -# Reformatted from https://www.schneier.com/code/vectors.txt +# Reformatted from https://www.schneier.com/wp-content/uploads/2015/12/vectors-2.txt # to look like the NIST vectors [ENCRYPT] diff --git a/vectors/cryptography_vectors/ciphers/Blowfish/bf-ecb.txt b/vectors/cryptography_vectors/ciphers/Blowfish/bf-ecb.txt index bb18a5a3f1bc..70c1c030803f 100644 --- a/vectors/cryptography_vectors/ciphers/Blowfish/bf-ecb.txt +++ b/vectors/cryptography_vectors/ciphers/Blowfish/bf-ecb.txt @@ -1,4 +1,4 @@ -# Reformatted from https://www.schneier.com/code/vectors.txt +# Reformatted from https://www.schneier.com/wp-content/uploads/2015/12/vectors-2.txt # to look like the NIST vectors [ENCRYPT] diff --git a/vectors/cryptography_vectors/ciphers/Blowfish/bf-ofb.txt b/vectors/cryptography_vectors/ciphers/Blowfish/bf-ofb.txt index 21a7421842f2..f87609a996ca 100644 --- a/vectors/cryptography_vectors/ciphers/Blowfish/bf-ofb.txt +++ b/vectors/cryptography_vectors/ciphers/Blowfish/bf-ofb.txt @@ -1,4 +1,4 @@ -# Reformatted from https://www.schneier.com/code/vectors.txt +# Reformatted from https://www.schneier.com/wp-content/uploads/2015/12/vectors-2.txt # to look like the NIST vectors [ENCRYPT] diff --git a/vectors/cryptography_vectors/x509/custom/ca/rsa_ca.pem b/vectors/cryptography_vectors/x509/custom/ca/rsa_ca.pem new file mode 100644 index 000000000000..089bcce10e72 --- /dev/null +++ b/vectors/cryptography_vectors/x509/custom/ca/rsa_ca.pem @@ -0,0 +1,28 @@ +-----BEGIN CERTIFICATE----- +MIIExzCCAq+gAwIBAgIJAOcS06ClbtbJMA0GCSqGSIb3DQEBCwUAMBoxGDAWBgNV +BAMMD2NyeXB0b2dyYXBoeSBDQTAeFw0yMDA5MTQyMTQwNDJaFw00ODAxMzEyMTQw +NDJaMBoxGDAWBgNVBAMMD2NyeXB0b2dyYXBoeSBDQTCCAiIwDQYJKoZIhvcNAQEB +BQADggIPADCCAgoCggIBANBIheRc1HT4MzV5GvUbDk9CFU6DTomRApNqRmizriRq +m6OY4Ht3d71BXog6/IBkqAnZ4/XJQ40G4sVDb52k11oPvfJ/F5pc+6UqPBL+QGzY +GkJoubAqXFpI6ow0qayFNQLv0T9o4yh0QQOoGvgCmv91qmitLrZNXu4U9S76G+Di +GST+QyMkMxj+VsGRsRRBufV1urcnvFWjU6Q2+cr2cp0mMAG96NTyIskYiJ8vL03W +z4DX4klO4X47fPmDnU/OMn4SbvMZ896j1L0J04S+uVThTkxQWcFcqXhX5qM8kzcj +JUmybFlbf150j3WiucW48K/j7fJ0x9q3iUo4Gva0coScglJWcgo/BBCwFDw8NVba +7npxSRMiaS3qTv0dEFcRnvByc+7hyGxxlWdTE9tHisUI1eZVk9P9ziqNOZKscY8Z +X1+/C4M9X69Y7A8I74F5dO27IRycEgOrSo2z1NhfSwbqJr9a2TBtRsFinn8rjKBI +zNn0E5p9jO1WjxtkcjHfXXpLN8FFMvoYI9l/K+ZWDm9sboaF8jrgozSc004AFemA +H79mmCGVRKXn1vDAo4DLC6p3NiBFYQcYbW9V+beGD6srsF6xJtuY/UwtPROLWSzu +CCrZ/4BlmpNsR0ehIFFvzEKjX6rR2yp3YKlguDbMBMKMpfSGxAFwcZ7OiaxR20UH +AgMBAAGjEDAOMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBADSveDS4 +y2V/N6Li2n9ChGNdCMr/45M0cl+GpL55aA36AWYMRLv0wip7MWV3yOj4mkjGBlTE +awKHH1FtetsE6B4a7M2hHhOXyXE60uUdptEx6ckGrJ1iyqu5cQUX1P+VnXbmOxfF +bl+Ugzjbgirx239rA4ezkDRuOvKcCbDOFV/gw3ZHfJ/IQeRXIQRl/y51wcnFUvFM +JEESYiijeDbEcY8r1/phmVQL0CO7WLMmTxlFj4X/TR3MTZWJQIap9GiLs5+n3QiO +jsZ3GuFOomB8oTebYkXniwbNu5hgLP/seRQzGA7B9VDZryAhCtvGgjtQh0eW2Qxt +sgmDJGOPKnKT3O5U0v3+IPLEYpe8JSzgAhhh6H1rAJRUNwP2gRcO4eOUJSkdl218 +fRNT0ILzosuWxwprER9ciMQF8q0JJKMhcfHRMH0S5mWVJAIkj68KY05oCy2zNyYa +oruopKSWXe0Bzr40znm40P7xIkui2BGQMlDPpbCaEfLsLqyctfbdmMlxac/QgIfY +TltrbqmI3MNy5uqGViGFpWPCB+kD8EsJF9nlKJXlu/i55qgUr/2/2CdeWlZDBP8A +1fdzmpYpWnwhE0KobzLS2z3AwDxiY/RSWUfypLZA0K/lpaEtYB6UHMDZ0/8WqgZV +gNucCuty0cA4Kf7eX1TlAKVwH8hTkVmJc2rX +-----END CERTIFICATE----- diff --git a/vectors/cryptography_vectors/x509/custom/ca/rsa_key.pem b/vectors/cryptography_vectors/x509/custom/ca/rsa_key.pem new file mode 100644 index 000000000000..97e39a501f20 --- /dev/null +++ b/vectors/cryptography_vectors/x509/custom/ca/rsa_key.pem @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDQSIXkXNR0+DM1 +eRr1Gw5PQhVOg06JkQKTakZos64kapujmOB7d3e9QV6IOvyAZKgJ2eP1yUONBuLF +Q2+dpNdaD73yfxeaXPulKjwS/kBs2BpCaLmwKlxaSOqMNKmshTUC79E/aOModEED +qBr4Apr/daporS62TV7uFPUu+hvg4hkk/kMjJDMY/lbBkbEUQbn1dbq3J7xVo1Ok +NvnK9nKdJjABvejU8iLJGIifLy9N1s+A1+JJTuF+O3z5g51PzjJ+Em7zGfPeo9S9 +CdOEvrlU4U5MUFnBXKl4V+ajPJM3IyVJsmxZW39edI91ornFuPCv4+3ydMfat4lK +OBr2tHKEnIJSVnIKPwQQsBQ8PDVW2u56cUkTImkt6k79HRBXEZ7wcnPu4chscZVn +UxPbR4rFCNXmVZPT/c4qjTmSrHGPGV9fvwuDPV+vWOwPCO+BeXTtuyEcnBIDq0qN +s9TYX0sG6ia/WtkwbUbBYp5/K4ygSMzZ9BOafYztVo8bZHIx3116SzfBRTL6GCPZ +fyvmVg5vbG6GhfI64KM0nNNOABXpgB+/ZpghlUSl59bwwKOAywuqdzYgRWEHGG1v +Vfm3hg+rK7BesSbbmP1MLT0Ti1ks7ggq2f+AZZqTbEdHoSBRb8xCo1+q0dsqd2Cp +YLg2zATCjKX0hsQBcHGezomsUdtFBwIDAQABAoICAQDH6YQRvwPwzTWhkn7MWU6v +xjbbJ+7e3T9CrNOttSBlNanzKU31U6KrFS4dxbgLqBEde3Rwud/LYZuRSPu9rLVC +bS+crF3EPJEQY2xLspu1nOn/abMoolAIHEp7jiR5QVWzXulRWmQFtSed0eEowJ9y +qMaKOAdI1RRToev/TfIqM/l8Z0ubVChzSdONcUAsuDU7ouc22r3K2Lv0Nwwkwc0a +hse3NEdg9JNsvs6LM2fM52w9N3ircjm+xmxatPft3HTcSucREIzg2hDb7K2HkOQj +0ykq2Eh97ml+56eocADBAEvO46FZVxf2WhxEBY8Xdz4VJMmDWJFmnZj5ksZWmrX6 +U5BfFY7DZvE2EpoZ5ph1Fm6dcXrJFkaZEyJLlzFKehXMipVenjCanIPpEEUvIz+p +m0QVoNJRj/GcNyIEZ0BCXedBOUWU4XE1pG4r6oZqwUvcjsVrqXP5kbJMVybiS6Kd +6T8ve+4qsn3ZvGRVKjInqf2WI0Wvum2sTF+4OAkYvFel9dKNjpYnnj4tLFc/EKWz +9+pE/Zz5fMOyMD9qXM6bdVkPjWjy1vXmNW4qFCZljrb395hTvsAPMsO6bbAM+lu6 +YcdOAf8k7awTb79kPMrPcbCygyKSGN9C9T3a/Nhrbr3TPi9SD9hC5Q8bL9uSHcR2 +hgRQcApxsfDRrGwy2lheEQKCAQEA/Hrynao+k6sYtlDc/ueCjb323EzsuhOxPqUZ +fKtGeFkJzKuaKTtymasvVpAAqJBEhTALrptGWlJQ0Y/EVaPpZ9pmk791EWNXdXsX +wwufbHxm6K9aOeogev8cd+B/9wUAQPQVotyRzCcOfbVe7t81cBNktqam5Zb9Y4Zr +qu63gBB1UttdmIF5qitl3JcFztlBjiza2UrqgVdKE+d9vLR84IBRy3dyQIOi6C1c +y37GNgObjx8ZcUVV54/KgvoVvDkvN6TEbUdC9eQz7FW7DA7MMVqyDvWZrSjBzVhK +2bTrd+Pi6S4n/ETvA6XRufHC8af4bdE2hzuq5VZO1kkgH37djwKCAQEA0y/YU0b4 +vCYpZ1MNhBFI6J9346DHD55Zu5dWFRqNkC0PiO6xEMUaUMbG4gxkiQPNT5WvddQs +EbRQTnd4FFdqB7XWoH+wERN7zjbT+BZVrHVC4gxEEy33s5oXGn7/ATxaowo7I4oq +15MwgZu3hBNxVUtuePZ6D9/ePNGOGOUtdMRrusmVX7gZEXxwvlLJXyVepl2V4JV1 +otI8EZCcoRhSfeYNEs4VhN0WmfMSV7ge0eFfVb6Lb+6PCcasYED8S0tBN2vjzvol +zCMv8skPATm7SopqBDoBPcXCHwN/gUFXHf/lrvE6bbeX1ZMxnRYKdQLLNYyQK9cr +nCUJXuNM21tVCQKCAQBapCkFwWDF0t8EVPOB78tG57QAUv2JsBgpzUvhHfwmqJCE +Efc+ZkE2Oea8xOX3nhN7XUxUWxpewr6Q/XQW6smYpye8UzfMDkYPvylAtKN/Zwnq +70kNEainf37Q6qAGJp14tCgwV89f44WoS7zRNQESQ2QczqeMNTCy0kdFDn6CU2ZL +YMWxQopTNVFUaEOFhympySCoceTOmm/VxX22iXVrg6XZzgAOeTO69s4hoFm4eoMW +Vqvjpmi4wT6K1w2GjWEOMPDz6ml3rX2WkxCbu5RDA7R4+mM5bzBkcBYvImyGliGY +ZSGlx3mnbZhlkQ3Tg+IESt+wnRM1Uk7rT0VhCUKxAoIBABWYuPibM2iaRnWoiqNM +2TXgyPPgRzsTqH2ElmsGEiACW6pXLohWf8Bu83u+ZLGWT/Kpjg3wqqkM1YGQuhjq +b49mSxKSvECiy3BlLvwZ3J0MSNCxDG0hsEkPovk0r4NC1soBi9awlH0DMlyuve+l +xVtBoYSBQC5LaICztWJaXXGpfJLXdo0ZWIbvQOBVuv4d5jYBMAiNgEAsW7Q4I6xd +vmHdmsyngo/ZxCvuLZwG2jAAai1slPnXXY1UYeBeBO72PS8bu2o5LpBXsNmVMhGg +A8U1rm3MOMBGbvmY8/sV4YDR4H0pch4yPja7HMHBtUQOCxXoz/2LvYv0RacMe5mb +F3ECggEAWxQZnT8pObxKrISZpHSKi54VxuLYbemS63Tdr4HE/KuiFAvbM6AeZOki +jbiMnqrCTOhJRS/i9HV78zSxRZZyVm961tnsjqMyaamX/S4yD7v3Vzu1mfsdVCa2 +Sl+JUUxsEgs/G3Fu6I/0TsCSn/HgNLM8b3f8TDkbpnOqKX165ddojXqSCfxjuYau +Szih/+jF1dz2/zBye1ARkLRdY/SzlzGl0cVn8bfkE0YEde7wvQ624Biy7r9i1o40 +7cy/8EQBR2FcXpOAZ7UgOqgGLNhXnd4FPsX4ldKOf5De8FErQOFirJ8pCUxFGr0U +fDWXtBuybAb5u+ZaVwHgqaaPCkKkVQ== +-----END PRIVATE KEY-----