Compare commits
1 Commits
main
...
ssl-featur
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
12d00aa166 |
1
.gitattributes
vendored
1
.gitattributes
vendored
@ -1 +0,0 @@
|
||||
*.toml text eol=lf
|
||||
354
.github/workflows/ci.yml
vendored
354
.github/workflows/ci.yml
vendored
@ -3,76 +3,61 @@ name: CI
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- master
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- master
|
||||
|
||||
env:
|
||||
RUSTFLAGS: -Dwarnings
|
||||
RUST_BACKTRACE: 1
|
||||
CARGO_INCREMENTAL: 0
|
||||
CARGO_PROFILE_DEV_DEBUG: 0
|
||||
|
||||
jobs:
|
||||
rustfmt:
|
||||
name: rustfmt
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rustfmt
|
||||
run: rustup default stable && rustup component add rustfmt
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Rust
|
||||
run: rustup update stable && rustup default stable
|
||||
- name: Check formatting
|
||||
run: cargo fmt --all -- --check
|
||||
|
||||
clippy:
|
||||
name: clippy
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CARGO_HOME: ${{ github.workspace }}/.cache/cargo
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
- name: Install Rust
|
||||
run: rustup toolchain add stable --no-self-update --component clippy && rustup default stable
|
||||
run: rustup update stable && rustup default stable
|
||||
- name: Get rust version
|
||||
id: rust-version
|
||||
shell: bash
|
||||
run: |
|
||||
echo "version=$(rustc --version)" >> $GITHUB_OUTPUT
|
||||
run: echo "::set-output name=version::$(rustc --version)"
|
||||
- name: Cache cargo index
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: |
|
||||
.cache/cargo/registry/index
|
||||
.cache/cargo/registry/cache
|
||||
key: index-${{ steps.rust-version.outputs.version }}-${{ hashFiles('Cargo.toml') }}
|
||||
enableCrossOsArchive: true
|
||||
path: ~/.cargo/registry/index
|
||||
key: index-${{ runner.os }}-${{ github.run_number }}
|
||||
restore-keys: |
|
||||
index-${{ runner.os }}-
|
||||
- name: Create lockfile
|
||||
run: cargo generate-lockfile
|
||||
- name: Cache cargo registry
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cargo/registry/cache
|
||||
key: registry-${{ runner.os }}-${{ steps.rust-version.outputs.version }}-${{ hashFiles('Cargo.lock') }}
|
||||
- name: Fetch dependencies
|
||||
run: cargo fetch
|
||||
- name: Cache target directory
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: target
|
||||
key: clippy-${{ steps.rust-version.outputs.version }}-${{ hashFiles('Cargo.lock') }}
|
||||
key: clippy-target-${{ runner.os }}-${{ steps.rust-version.outputs.version }}-${{ hashFiles('Cargo.lock') }}
|
||||
- name: Run clippy
|
||||
run: cargo clippy --all --all-targets --features rpk,mlkem
|
||||
- name: Check docs
|
||||
run: cargo doc --no-deps -p boring -p boring-sys -p hyper-boring -p tokio-boring --features rpk,underscore-wildcards
|
||||
env:
|
||||
CARGO_BUILD_RUSTDOCFLAGS: "--cfg=docsrs"
|
||||
RUSTC_BOOTSTRAP: 1
|
||||
DOCS_RS: 1
|
||||
- name: Cargo.toml boring versions consistency
|
||||
shell: bash
|
||||
run: |
|
||||
WORKSPACE_VERSION=$(grep -F '[workspace.package]' -A1 Cargo.toml | grep -F version | grep -Eo '".*"')
|
||||
if [[ -z "$WORKSPACE_VERSION" ]]; then echo 2>&1 "error: can't find boring version"; exit 1; fi
|
||||
if grep -E 'boring.* =' Cargo.toml | grep -vF "$WORKSPACE_VERSION"; then
|
||||
echo 2>&1 "error: boring dependencies must match workspace version $WORKSPACE_VERSION"
|
||||
exit 1
|
||||
fi
|
||||
run: cargo clippy --all --all-targets
|
||||
test:
|
||||
name: Test
|
||||
runs-on: ${{ matrix.os }}
|
||||
@ -80,7 +65,7 @@ jobs:
|
||||
matrix:
|
||||
thing:
|
||||
- stable
|
||||
- i686-mingw
|
||||
- macos-x86_64
|
||||
- arm-android
|
||||
- arm64-android
|
||||
- i686-android
|
||||
@ -91,210 +76,85 @@ jobs:
|
||||
- i686-linux
|
||||
- arm-linux
|
||||
- aarch64-linux
|
||||
- arm64-macos
|
||||
- x86_64-macos
|
||||
- x86_64-musl
|
||||
- x86_64-mingw
|
||||
- i686-msvc
|
||||
- x86_64-msvc
|
||||
include:
|
||||
- check_only: false
|
||||
- extra_test_args: ''
|
||||
- apt_packages: ''
|
||||
- custom_env: {}
|
||||
- thing: stable
|
||||
target: x86_64-unknown-linux-gnu
|
||||
rust: stable
|
||||
os: ubuntu-latest
|
||||
- thing: macos-x86_64
|
||||
target: x86_64-apple-darwin
|
||||
rust: stable
|
||||
os: macos-latest
|
||||
- thing: arm-android
|
||||
target: armv7-linux-androideabi
|
||||
target: arm-linux-androideabi
|
||||
rust: stable
|
||||
os: ubuntu-latest
|
||||
check_only: true
|
||||
- thing: arm64-android
|
||||
target: aarch64-linux-android
|
||||
rust: stable
|
||||
os: ubuntu-latest
|
||||
check_only: true
|
||||
- thing: i686-android
|
||||
target: i686-linux-android
|
||||
rust: stable
|
||||
os: ubuntu-latest
|
||||
check_only: true
|
||||
custom_env:
|
||||
CXXFLAGS: -msse2
|
||||
- thing: x86_64-android
|
||||
target: x86_64-linux-android
|
||||
rust: stable
|
||||
os: ubuntu-latest
|
||||
check_only: true
|
||||
- thing: aarch64-ios
|
||||
target: aarch64-apple-ios
|
||||
os: macos-latest
|
||||
check_only: true
|
||||
custom_env:
|
||||
IPHONEOS_DEPLOYMENT_TARGET: 17.5
|
||||
# It's... theoretically possible to run tests on iPhone Simulator,
|
||||
# but for now, make sure that BoringSSL only builds.
|
||||
- thing: aarch64-ios-sim
|
||||
target: aarch64-apple-ios-sim
|
||||
os: macos-latest
|
||||
check_only: true
|
||||
custom_env:
|
||||
IPHONEOS_DEPLOYMENT_TARGET: 17.5
|
||||
- thing: x86_64-ios
|
||||
target: x86_64-apple-ios
|
||||
os: macos-latest
|
||||
check_only: true
|
||||
custom_env:
|
||||
IPHONEOS_DEPLOYMENT_TARGET: 17.5
|
||||
- thing: i686-linux
|
||||
target: i686-unknown-linux-gnu
|
||||
rust: stable
|
||||
os: ubuntu-latest
|
||||
apt_packages: gcc-multilib g++-multilib
|
||||
custom_env:
|
||||
CXXFLAGS: -msse2
|
||||
- thing: arm-linux
|
||||
target: arm-unknown-linux-gnueabi
|
||||
rust: stable
|
||||
os: ubuntu-latest
|
||||
apt_packages: gcc-arm-linux-gnueabi g++-arm-linux-gnueabi
|
||||
check_only: true
|
||||
custom_env:
|
||||
CC: arm-linux-gnueabi-gcc
|
||||
CXX: arm-linux-gnueabi-g++
|
||||
CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABI_LINKER: arm-linux-gnueabi-g++
|
||||
- thing: aarch64-linux
|
||||
target: aarch64-unknown-linux-gnu
|
||||
rust: stable
|
||||
os: ubuntu-latest
|
||||
apt_packages: crossbuild-essential-arm64
|
||||
check_only: true
|
||||
custom_env:
|
||||
CC: aarch64-linux-gnu-gcc
|
||||
CXX: aarch64-linux-gnu-g++
|
||||
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-g++
|
||||
- thing: arm64-macos
|
||||
target: aarch64-apple-darwin
|
||||
- thing: x86_64-musl
|
||||
target: x86_64-unknown-linux-musl
|
||||
rust: stable
|
||||
os: macos-latest
|
||||
check_only: true
|
||||
- thing: x86_64-macos
|
||||
target: x86_64-apple-darwin
|
||||
rust: stable
|
||||
os: macos-latest
|
||||
os: ubuntu-latest
|
||||
- thing: x86_64-mingw
|
||||
target: x86_64-pc-windows-gnu
|
||||
rust: stable
|
||||
os: windows-latest
|
||||
check_only: true # tests are flaky for unclear reasons
|
||||
custom_env:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
C_INCLUDE_PATH: "C:\\msys64\\usr\\include"
|
||||
CPLUS_INCLUDE_PATH: "C:\\msys64\\usr\\include"
|
||||
LIBRARY_PATH: "C:\\msys64\\usr\\lib"
|
||||
RUSTC_BOOTSTRAP: 1 # for -Z checksum-freshness
|
||||
# CI's Windows doesn't have required root certs
|
||||
extra_test_args: --workspace --exclude tokio-boring --exclude hyper-boring -Z checksum-freshness
|
||||
- thing: i686-mingw
|
||||
target: i686-pc-windows-gnu
|
||||
rust: stable
|
||||
os: windows-latest
|
||||
check_only: true
|
||||
custom_env:
|
||||
RUSTC_BOOTSTRAP: 1 # for -Z checksum-freshness
|
||||
CMAKE_GENERATOR: "MinGW Makefiles"
|
||||
COLLECT_GCC: null
|
||||
# CI's Windows doesn't have required root certs
|
||||
extra_test_args: --workspace --exclude tokio-boring --exclude hyper-boring -Z checksum-freshness
|
||||
os: ubuntu-latest
|
||||
- thing: i686-msvc
|
||||
target: i686-pc-windows-msvc
|
||||
rust: stable-x86_64-msvc
|
||||
os: windows-latest
|
||||
custom_env:
|
||||
RUSTC_BOOTSTRAP: 1 # for -Z checksum-freshness
|
||||
CXXFLAGS: -msse2
|
||||
# CI's Windows doesn't have required root certs
|
||||
extra_test_args: --workspace --exclude tokio-boring --exclude hyper-boring -Z checksum-freshness
|
||||
- thing: x86_64-msvc
|
||||
target: x86_64-pc-windows-msvc
|
||||
rust: stable-x86_64-msvc
|
||||
os: windows-latest
|
||||
custom_env:
|
||||
RUSTC_BOOTSTRAP: 1 # for -Z checksum-freshness
|
||||
# CI's Windows doesn't have required root certs
|
||||
extra_test_args: --workspace --exclude tokio-boring --exclude hyper-boring -Z checksum-freshness
|
||||
- thing: x86_64-msvc-static
|
||||
target: x86_64-pc-windows-msvc
|
||||
rust: stable-x86_64-msvc
|
||||
os: windows-latest
|
||||
custom_env:
|
||||
RUSTC_BOOTSTRAP: 1 # for -Z checksum-freshness
|
||||
RUSTFLAGS: -Dwarnings -C target-feature=+crt-static
|
||||
# CI's Windows doesn't have required root certs
|
||||
extra_test_args: --workspace --exclude tokio-boring --exclude hyper-boring -Z checksum-freshness
|
||||
env:
|
||||
CARGO_HOME: ${{ github.workspace }}/.cache/cargo
|
||||
CARGO_BUILD_BUILD_DIR: ${{ github.workspace }}/.cache/build-dir
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
- name: Install Rust (rustup)
|
||||
run: rustup update ${{ matrix.rust }} --no-self-update && rustup default ${{ matrix.rust }} && rustup target add ${{ matrix.target }}
|
||||
shell: bash
|
||||
- name: Get rust version
|
||||
id: rust-version
|
||||
shell: bash
|
||||
run: |
|
||||
echo "version=$(rustc --version)" >> $GITHUB_OUTPUT
|
||||
- name: Prepopulate cargo index
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: |
|
||||
.cache/cargo/registry/index
|
||||
.cache/cargo/registry/cache
|
||||
key: index-${{ steps.rust-version.outputs.version }}-${{ hashFiles('Cargo.toml') }}
|
||||
enableCrossOsArchive: true
|
||||
- name: Install golang
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '>=1.22.0'
|
||||
- name: Install target-specific APT dependencies
|
||||
if: "matrix.apt_packages != ''"
|
||||
run: sudo apt update && sudo apt install -y ${{ matrix.apt_packages }}
|
||||
run: rustup update ${{ matrix.rust }} --no-self-update && rustup default ${{ matrix.rust }}
|
||||
shell: bash
|
||||
- run: rustup target add ${{ matrix.target }}
|
||||
- name: Install nasm
|
||||
if: startsWith(matrix.os, 'windows')
|
||||
run: choco install nasm
|
||||
shell: cmd
|
||||
- name: Setup 32-bit MSYS2
|
||||
if: matrix.thing == 'i686-mingw'
|
||||
uses: msys2/setup-msys2@v2
|
||||
id: msys2
|
||||
with:
|
||||
msystem: MINGW32
|
||||
path-type: inherit
|
||||
install: >-
|
||||
mingw-w64-i686-gcc
|
||||
mingw-w64-i686-cmake
|
||||
- name: Setup 32-bit MSYS2 Env vars
|
||||
if: matrix.thing == 'i686-mingw'
|
||||
shell: bash
|
||||
run: |
|
||||
MSYS_ROOT='${{ steps.msys2.outputs.msys2-location }}'
|
||||
test -d "$MSYS_ROOT\\mingw32\\bin"
|
||||
echo >> $GITHUB_PATH "$MSYS_ROOT\\mingw32\\bin"
|
||||
echo >> $GITHUB_PATH "$MSYS_ROOT\\usr\\bin"
|
||||
echo >> $GITHUB_ENV CC="$MSYS_ROOT\\mingw32\\bin\\gcc"
|
||||
echo >> $GITHUB_ENV CXX="$MSYS_ROOT\\mingw32\\bin\\g++"
|
||||
echo >> $GITHUB_ENV AR="$MSYS_ROOT\\mingw32\\bin\\ar"
|
||||
echo >> $GITHUB_ENV CFLAGS="-mlong-double-64 -I$MSYS_ROOT\\mingw32\\include"
|
||||
echo >> $GITHUB_ENV CXXFLAGS="-mlong-double-64 -I$MSYS_ROOT\\mingw32\\include"
|
||||
echo >> $GITHUB_ENV BINDGEN_EXTRA_CLANG_ARGS="-mlong-double-64 -I$MSYS_ROOT\\mingw32\\include"
|
||||
echo >> $GITHUB_ENV LIBRARY_PATH="$MSYS_ROOT\\mingw32\\lib"
|
||||
echo >> $GITHUB_ENV LDFLAGS="-L$MSYS_ROOT\\mingw32\\lib"
|
||||
- name: Install LLVM and Clang
|
||||
if: startsWith(matrix.os, 'windows')
|
||||
uses: KyleMayes/install-llvm-action@v1
|
||||
@ -304,134 +164,30 @@ jobs:
|
||||
- name: Set LIBCLANG_PATH
|
||||
if: startsWith(matrix.os, 'windows')
|
||||
run: echo "LIBCLANG_PATH=$((gcm clang).source -replace "clang.exe")" >> $env:GITHUB_ENV
|
||||
- name: Set Android Linker path
|
||||
if: endsWith(matrix.thing, '-android')
|
||||
run: echo "CARGO_TARGET_$(echo ${{ matrix.target }} | tr \\-a-z _A-Z)_LINKER=$ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/$(echo ${{ matrix.target }} | sed s/armv7/armv7a/)21-clang++" >> "$GITHUB_ENV"
|
||||
- name: Fetch deps
|
||||
run: cargo fetch --target ${{ matrix.target }}
|
||||
shell: bash
|
||||
env: ${{ matrix.custom_env }}
|
||||
# Windows builds are the slowest
|
||||
- name: Cache deps in Windows tests
|
||||
if: startsWith(matrix.os, 'windows')
|
||||
uses: actions/cache/restore@v4
|
||||
id: test-cache-restore
|
||||
with:
|
||||
path: .cache/build-dir
|
||||
key: wintest-${{ matrix.target }}-${{ hashFiles('Cargo.lock') }}
|
||||
- name: Build tests
|
||||
# We `build` because we want the linker to verify we are cross-compiling correctly for check-only targets.
|
||||
run: cargo build -vv --target ${{ matrix.target }} --tests ${{ matrix.extra_test_args }}
|
||||
shell: bash
|
||||
env: ${{ matrix.custom_env }}
|
||||
# By default it'd be saved after later cargo calls, which already invalidated the cache
|
||||
- name: Cache deps in Windows tests
|
||||
if: startsWith(matrix.os, 'windows')
|
||||
uses: actions/cache/save@v4
|
||||
with:
|
||||
path: .cache/build-dir
|
||||
key: ${{ steps.test-cache-restore.outputs.cache-primary-key }}
|
||||
- name: Run tests (skip=${{ matrix.check_only }})
|
||||
if: "!matrix.check_only"
|
||||
run: cargo test --target ${{ matrix.target }} ${{ matrix.extra_test_args }}
|
||||
shell: bash
|
||||
env: ${{ matrix.custom_env }}
|
||||
- name: Test boring-sys cargo publish
|
||||
# Running `cargo publish --dry-run` tests two things:
|
||||
#
|
||||
# 1. That `boring-sys` can build BoringSSL with just the files included
|
||||
# in the crates.io package (as determined by the `include` field in
|
||||
# the `Cargo.toml`).
|
||||
# 2. That the final `boring-sys` package size, including the BoringSSL
|
||||
# submodules, is not too large to be published to `crates.io`.
|
||||
#
|
||||
# Both of these may no longer be the case after updating the BoringSSL
|
||||
# submodules to a new revision, so it's important to test this on CI.
|
||||
run: cargo publish --dry-run --target ${{ matrix.target }} -p boring-sys
|
||||
shell: bash
|
||||
env: ${{ matrix.custom_env }}
|
||||
- if: "startsWith(matrix.os, 'windows') && !contains(matrix.target, 'ios')"
|
||||
# CI's Windows doesn't have require root certs
|
||||
run: cargo test --workspace --exclude tokio-boring --exclude hyper-boring
|
||||
name: Run tests (Windows)
|
||||
- if: "!startsWith(matrix.os, 'windows') && !contains(matrix.target, 'ios')"
|
||||
run: cargo test
|
||||
name: Run tests (not Windows)
|
||||
- if: "contains(matrix.target, 'ios')"
|
||||
# It's... theoretically possible to run tests on iPhone Simulator,
|
||||
# but for now, make sure that BoringSSL only builds.
|
||||
run: cargo check --target ${{ matrix.target }} --all-targets
|
||||
name: Check tests (iOS)
|
||||
|
||||
test-fips:
|
||||
name: Test FIPS integration
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
- name: Install Rust (rustup)
|
||||
run: rustup update stable --no-self-update && rustup default stable
|
||||
shell: bash
|
||||
- name: Install golang
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '>=1.22.0'
|
||||
- name: Run tests
|
||||
run: cargo test --features fips
|
||||
- name: Test boring-sys cargo publish (FIPS)
|
||||
# Running `cargo publish --dry-run` tests two things:
|
||||
#
|
||||
# 1. That `boring-sys` can build BoringSSL with just the files included
|
||||
# in the crates.io package (as determined by the `include` field in
|
||||
# the `Cargo.toml`).
|
||||
# 2. That the final `boring-sys` package size, including the BoringSSL
|
||||
# submodules, is not too large to be published to `crates.io`.
|
||||
#
|
||||
# Both of these may no longer be the case after updating the BoringSSL
|
||||
# submodules to a new revision, so it's important to test this on CI.
|
||||
run: cargo publish --dry-run -p boring-sys --features fips
|
||||
|
||||
cross-build:
|
||||
name: Cross build from macOS to Linux
|
||||
runs-on: macos-latest
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- target: x86_64-unknown-linux-gnu
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
- name: Install Rust (rustup)
|
||||
run: rustup toolchain install stable --no-self-update --profile minimal --target ${{ matrix.target }} && rustup default stable
|
||||
shell: bash
|
||||
- name: Install golang
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '>=1.22.0'
|
||||
- name: Install ${{ matrix.target }} toolchain
|
||||
run: brew tap messense/macos-cross-toolchains && brew install ${{ matrix.target }}
|
||||
- name: Set BORING_BSSL_SYSROOT
|
||||
run: echo "BORING_BSSL_SYSROOT=$(brew --prefix ${{ matrix.target }})/toolchain/${{ matrix.target }}/sysroot" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
- name: Set CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER
|
||||
run: echo "CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=${{ matrix.target }}-gcc" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
- name: Set CXXFLAGS
|
||||
run: echo "CXXFLAGS=-D__STDC_FORMAT_MACROS" >> $GITHUB_ENV
|
||||
- name: Build for ${{ matrix.target }}
|
||||
run: cargo build --target ${{ matrix.target }} --all-targets
|
||||
|
||||
test-features:
|
||||
name: Test features
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CARGO_INCREMENTAL: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
- name: Install Rust (rustup)
|
||||
run: rustup update stable --no-self-update && rustup default stable
|
||||
shell: bash
|
||||
- run: cargo check --no-default-features
|
||||
name: Check `--no-default-features`
|
||||
- run: cargo check --features mlkem,credential
|
||||
name: Check `mlkem,credential`
|
||||
- run: cargo test --features rpk
|
||||
name: Run `rpk` tests
|
||||
- run: cargo test --features underscore-wildcards
|
||||
name: Run `underscore-wildcards` tests
|
||||
- run: cargo test --features rpk,underscore-wildcards
|
||||
name: Run `rpk,underscore-wildcards` tests
|
||||
- run: cargo test --features rpk,underscore-wildcards,mlkem
|
||||
name: Run `rpk,underscore-wildcards` tests
|
||||
- name: Install Clang-7
|
||||
run: sudo apt-get install -y clang-7
|
||||
- run: cargo test --features fips
|
||||
name: Run tests
|
||||
|
||||
23
.github/workflows/semgrep.yml
vendored
23
.github/workflows/semgrep.yml
vendored
@ -1,23 +0,0 @@
|
||||
on:
|
||||
pull_request: {}
|
||||
workflow_dispatch: {}
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
name: Semgrep config
|
||||
jobs:
|
||||
semgrep:
|
||||
name: semgrep/ci
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}
|
||||
SEMGREP_URL: https://cloudflare.semgrep.dev
|
||||
SEMGREP_APP_URL: https://cloudflare.semgrep.dev
|
||||
SEMGREP_VERSION_CHECK_URL: https://cloudflare.semgrep.dev/api/check-version
|
||||
container:
|
||||
image: semgrep/semgrep
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: semgrep ci
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -2,3 +2,6 @@
|
||||
path = boring-sys/deps/boringssl
|
||||
url = https://github.com/google/boringssl.git
|
||||
ignore = dirty
|
||||
[submodule "boring-sys/deps/boringssl-fips"]
|
||||
path = boring-sys/deps/boringssl-fips
|
||||
url = https://github.com/google/boringssl.git
|
||||
|
||||
11
CHANGELOG.md
Normal file
11
CHANGELOG.md
Normal file
@ -0,0 +1,11 @@
|
||||
# Change Log
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [v2.0.0] - 2021-12-16
|
||||
|
||||
### Changed
|
||||
|
||||
* Updated `foreign-types` from 0.3 to 0.5. This is technically a breaking change if you used `foreign-types` in your own crate, but in practice this shouldn't have a large impact.
|
||||
* Removed unused `*Ref` structs; these served no purpose and were not useful.
|
||||
* Removed unused `tempdir` dependency
|
||||
43
Cargo.toml
43
Cargo.toml
@ -5,46 +5,3 @@ members = [
|
||||
"tokio-boring",
|
||||
"hyper-boring"
|
||||
]
|
||||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
version = "5.0.2"
|
||||
rust-version = "1.85"
|
||||
repository = "https://github.com/cloudflare/boring"
|
||||
edition = "2021"
|
||||
|
||||
[workspace.metadata.release]
|
||||
pre-release-commit-message = "Release {{version}}"
|
||||
shared-version = true
|
||||
tag-prefix = ""
|
||||
publish = false
|
||||
|
||||
[workspace.dependencies]
|
||||
boring-sys = { version = "5.0.2", path = "./boring-sys", default-features = false }
|
||||
boring = { version = "5.0.2", path = "./boring", default-features = false }
|
||||
tokio-boring = { version = "5.0.2", path = "./tokio-boring", default-features = false }
|
||||
|
||||
bindgen = { version = "0.72.0", default-features = false, features = ["runtime"] }
|
||||
bitflags = "2.9"
|
||||
brotli = "8.0"
|
||||
bytes = "1"
|
||||
cmake = "0.1.54"
|
||||
fs_extra = "1.3.0"
|
||||
fslock = "0.2"
|
||||
foreign-types = "0.5"
|
||||
libc = "0.2"
|
||||
hex = "0.4"
|
||||
rusty-hook = "^0.11"
|
||||
futures = "0.3"
|
||||
tokio = "1"
|
||||
anyhow = "1"
|
||||
antidote = "1.0.0"
|
||||
http = "1"
|
||||
http-body-util = "0.1.2"
|
||||
hyper = "1"
|
||||
hyper-util = "0.1.6"
|
||||
linked_hash_set = "0.1"
|
||||
openssl-macros = "0.1.1"
|
||||
tower = "0.4"
|
||||
tower-layer = "0.3"
|
||||
tower-service = "0.3"
|
||||
|
||||
48
README.md
48
README.md
@ -2,33 +2,37 @@
|
||||
|
||||
[](https://crates.io/crates/boring)
|
||||
|
||||
[BoringSSL](https://boringssl.googlesource.com/boringssl) is Google's fork of OpenSSL for Chrome/Chromium and Android.
|
||||
|
||||
This crate provides safe bindings for the Rust programming language and TLS adapters for [tokio](https://github.com/tokio-rs/tokio)
|
||||
BoringSSL bindings for the Rust programming language and TLS adapters for [tokio](https://github.com/tokio-rs/tokio)
|
||||
and [hyper](https://github.com/hyperium/hyper) built on top of it.
|
||||
|
||||
It supports [FIPS-compatible builds of BoringSSL](https://boringssl.googlesource.com/boringssl/+/master/crypto/fipsmodule/FIPS.md),
|
||||
as well as [Post-Quantum crypto](https://datatracker.ietf.org/doc/draft-ietf-tls-ecdhe-mlkem/)
|
||||
and [Raw Public Key](https://docs.rs/boring/latest/boring/ssl/struct.SslRef.html#method.peer_pubkey) extensions.
|
||||
[Documentation](https://docs.rs/boring).
|
||||
|
||||
## Documentation
|
||||
- Boring API: <https://docs.rs/boring>
|
||||
- tokio TLS adapters: <https://docs.rs/tokio-boring>
|
||||
- hyper HTTPS connector: <https://docs.rs/hyper-boring>
|
||||
- FFI bindings: <https://docs.rs/boring-sys>
|
||||
## Release Support
|
||||
|
||||
# Upgrading from `boring` v4
|
||||
By default, the crate statically links with the latest BoringSSL master branch.
|
||||
|
||||
* First update to boring 4.21 and ensure it builds without any deprecation warnings.
|
||||
* `pq-experimental` Cargo feature is no longer needed. Post-quantum crypto is enabled by default.
|
||||
* `fips-precompiled` Cargo feature has been merged into `fips`. Set `BORING_BSSL_FIPS_PATH` env var to use a precompiled library.
|
||||
* `fips-compat` Cargo feature has been renamed to `legacy-compat-deprecated` (4cb7e260a85b7)
|
||||
* `SslCurve` and `SslCurveNid` have been removed. Curve names are more stable and portable identifiers. Use `curve_name()` and `set_curves_list()`.
|
||||
* `Ssl::new_from_ref` -> `Ssl::new()`.
|
||||
* `X509Builder::append_extension2` -> `X509Builder::append_extension`.
|
||||
* `X509Store` is now cheaply cloneable, but immutable. `SslContextBuilder.cert_store_mut()` can't be used after `.set_cert_store()`. If you need `.cert_store_mut()`, either don't overwrite the default store, or use `.set_cert_store_builder()`.
|
||||
* `X509StoreBuilder::add_cert` takes a reference.
|
||||
* `hyper` 0.x support has been removed. Use `hyper` 1.x.
|
||||
## Support for pre-built binaries
|
||||
|
||||
While this crate can build BoringSSL on its own, you may want to provide pre-built binaries instead.
|
||||
To do so, specify the environment variable `BORING_BSSL_PATH` with the path to the binaries.
|
||||
|
||||
You can also provide specific headers by setting `BORING_BSSL_INCLUDE_PATH`.
|
||||
|
||||
_Notes_: The crate will look for headers in the `$BORING_BSSL_INCLUDE_PATH/openssl/` folder, make sure to place your headers there.
|
||||
|
||||
_Warning_: When providing a different version of BoringSSL make sure to use a compatible one, the crate relies on the presence of certain functions.
|
||||
|
||||
## Building with a FIPS-validated module
|
||||
|
||||
Only BoringCrypto module version ae223d6138807a13006342edfeef32e813246b39, as
|
||||
certified with [certificate
|
||||
3678](https://csrc.nist.gov/projects/cryptographic-module-validation-program/certificate/3678)
|
||||
is supported by this crate. Support is enabled by this crate's `fips` feature.
|
||||
|
||||
`boring-sys` comes with a test that FIPS is enabled/disabled depending on the feature flag. You can run it as follows:
|
||||
```bash
|
||||
$ cargo test --features fips fips::is_enabled
|
||||
```
|
||||
|
||||
## Contribution
|
||||
|
||||
|
||||
2474
RELEASE_NOTES
2474
RELEASE_NOTES
File diff suppressed because it is too large
Load Diff
17
boring-sys/CHANGELOG.md
Normal file
17
boring-sys/CHANGELOG.md
Normal file
@ -0,0 +1,17 @@
|
||||
# Change Log
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [v2.0.0] - 2021-12-16
|
||||
|
||||
### Added
|
||||
|
||||
* Allow using pre-built binaries of `bssl` using the `BORING_BSSL_PATH` env variable
|
||||
* Automatically fetch the `boringssl` submodule if it doesn't yet exist
|
||||
|
||||
### Changed
|
||||
|
||||
* Removed unused `PasswordCallback` type
|
||||
* Disable unused bindgen dependencies
|
||||
* Update `bindgen` and `bytes` dependencies
|
||||
*
|
||||
@ -1,79 +1,38 @@
|
||||
[package]
|
||||
name = "boring-sys"
|
||||
version = { workspace = true }
|
||||
version = "2.0.0"
|
||||
authors = ["Alex Crichton <alex@alexcrichton.com>",
|
||||
"Steven Fackler <sfackler@gmail.com>",
|
||||
"Ivan Nikulin <ifaaan@gmail.com>"]
|
||||
license = "MIT"
|
||||
description = "FFI bindings to BoringSSL"
|
||||
repository = { workspace = true }
|
||||
repository = "https://github.com/cloudflare/boring"
|
||||
documentation = "https://docs.rs/boring-sys"
|
||||
links = "boringssl"
|
||||
build = "build/main.rs"
|
||||
readme = "README.md"
|
||||
categories = ["cryptography", "external-ffi-bindings"]
|
||||
keywords = ["tls", "boringssl", "openssl", "fips", "ml-kem"]
|
||||
edition = { workspace = true }
|
||||
rust-version = { workspace = true }
|
||||
edition = "2018"
|
||||
include = [
|
||||
"/*.md",
|
||||
"/*.toml",
|
||||
"/LICENSE-MIT",
|
||||
"/cmake/*.cmake",
|
||||
"/deps/boringssl/**/*.[chS]",
|
||||
"/deps/boringssl/**/*.inc",
|
||||
"/deps/boringssl/**/*.asm",
|
||||
"/deps/boringssl/**/*.pl",
|
||||
"/deps/boringssl/**/*.go",
|
||||
"/deps/boringssl/**/*.cmake",
|
||||
"/deps/boringssl/**/go.mod",
|
||||
"/deps/boringssl/**/go.sum",
|
||||
"/deps/boringssl/crypto/obj/obj_mac.num",
|
||||
"/deps/boringssl/crypto/obj/objects.txt",
|
||||
"/deps/boringssl/crypto/err/*.errordata",
|
||||
"/deps/boringssl/**/*.bzl",
|
||||
"/deps/boringssl/**/*.cc",
|
||||
"/deps/boringssl/src/**/*.cc",
|
||||
"/deps/boringssl/**/CMakeLists.txt",
|
||||
"/deps/boringssl/**/sources.cmake",
|
||||
"/deps/boringssl/**/util/go_tests.txt",
|
||||
"/deps/boringssl/LICENSE",
|
||||
"/build/*",
|
||||
"/build.rs",
|
||||
"/src",
|
||||
"/patches",
|
||||
]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["rpk", "underscore-wildcards", "mlkem"]
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
[build-dependencies]
|
||||
bindgen = { version = "0.60", default-features = false, features = ["runtime"] }
|
||||
cmake = "0.1"
|
||||
|
||||
[features]
|
||||
# Compile boringssl using the FIPS build flag if building boringssl from
|
||||
# scratch.
|
||||
#
|
||||
# See
|
||||
# https://boringssl.googlesource.com/boringssl/+/master/crypto/fipsmodule/FIPS.md
|
||||
# for instructions and more details on the boringssl FIPS flag.
|
||||
default = ["ssl"]
|
||||
ssl = []
|
||||
|
||||
# Use a FIPS-validated version of boringssl.
|
||||
fips = []
|
||||
|
||||
# Enables Raw public key API (https://datatracker.ietf.org/doc/html/rfc7250)
|
||||
rpk = []
|
||||
|
||||
# Require mlkem.h
|
||||
mlkem = []
|
||||
|
||||
# Applies a patch (`patches/underscore-wildcards.patch`) to enable
|
||||
# `ffi::X509_CHECK_FLAG_UNDERSCORE_WILDCARDS`. This feature is necessary in
|
||||
# order to compile the bindings for the default branch of boringSSL
|
||||
# (`deps/boringssl`). Alternatively, a version of boringSSL that implements the
|
||||
# same feature set can be provided by setting
|
||||
# `BORING_BSSL{,_FIPS}_SOURCE_PATH`.
|
||||
underscore-wildcards = []
|
||||
|
||||
[build-dependencies]
|
||||
bindgen = { workspace = true }
|
||||
cmake = { workspace = true }
|
||||
fs_extra = { workspace = true }
|
||||
fslock = { workspace = true }
|
||||
|
||||
[lints.rust]
|
||||
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(const_fn)'] }
|
||||
|
||||
@ -1,17 +0,0 @@
|
||||
# Low-level bindings to BoringSSL
|
||||
|
||||
[BoringSSL](https://boringssl.googlesource.com/boringssl) is Google's fork of OpenSSL for Chrome/Chromium and Android.
|
||||
|
||||
This crate builds the BoringSSL library (or optionally links a pre-built version) and generates FFI bindings for it.
|
||||
It supports [FIPS-compatible builds of BoringSSL](https://boringssl.googlesource.com/boringssl/+/master/crypto/fipsmodule/FIPS.md),
|
||||
as well as [Post-Quantum crypto](https://datatracker.ietf.org/doc/draft-ietf-tls-ecdhe-mlkem/)
|
||||
and [Raw Public Key](https://docs.rs/boring/latest/boring/ssl/struct.SslRef.html#method.peer_pubkey) extensions.
|
||||
|
||||
To use BoringSSL from Rust, prefer the [higher-level safe API](https://docs.rs/boring).
|
||||
|
||||
## Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally
|
||||
submitted for inclusion in the work by you, as defined in the Apache-2.0
|
||||
license, shall be dual licensed under the terms of both the Apache License,
|
||||
Version 2.0 and the MIT license without any additional terms or conditions.
|
||||
1
boring-sys/README.md
Symbolic link
1
boring-sys/README.md
Symbolic link
@ -0,0 +1 @@
|
||||
../README.md
|
||||
456
boring-sys/build.rs
Normal file
456
boring-sys/build.rs
Normal file
@ -0,0 +1,456 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
|
||||
// NOTE: this build script is adopted from quiche (https://github.com/cloudflare/quiche)
|
||||
|
||||
// Additional parameters for Android build of BoringSSL.
|
||||
//
|
||||
// Android NDK < 18 with GCC.
|
||||
const CMAKE_PARAMS_ANDROID_NDK_OLD_GCC: &[(&str, &[(&str, &str)])] = &[
|
||||
(
|
||||
"aarch64",
|
||||
&[("ANDROID_TOOLCHAIN_NAME", "aarch64-linux-android-4.9")],
|
||||
),
|
||||
(
|
||||
"arm",
|
||||
&[("ANDROID_TOOLCHAIN_NAME", "arm-linux-androideabi-4.9")],
|
||||
),
|
||||
(
|
||||
"x86",
|
||||
&[("ANDROID_TOOLCHAIN_NAME", "x86-linux-android-4.9")],
|
||||
),
|
||||
(
|
||||
"x86_64",
|
||||
&[("ANDROID_TOOLCHAIN_NAME", "x86_64-linux-android-4.9")],
|
||||
),
|
||||
];
|
||||
|
||||
// Android NDK >= 19.
|
||||
const CMAKE_PARAMS_ANDROID_NDK: &[(&str, &[(&str, &str)])] = &[
|
||||
("aarch64", &[("ANDROID_ABI", "arm64-v8a")]),
|
||||
("arm", &[("ANDROID_ABI", "armeabi-v7a")]),
|
||||
("x86", &[("ANDROID_ABI", "x86")]),
|
||||
("x86_64", &[("ANDROID_ABI", "x86_64")]),
|
||||
];
|
||||
|
||||
fn cmake_params_android() -> &'static [(&'static str, &'static str)] {
|
||||
let arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap();
|
||||
let cmake_params_android = if cfg!(feature = "ndk-old-gcc") {
|
||||
CMAKE_PARAMS_ANDROID_NDK_OLD_GCC
|
||||
} else {
|
||||
CMAKE_PARAMS_ANDROID_NDK
|
||||
};
|
||||
for (android_arch, params) in cmake_params_android {
|
||||
if *android_arch == arch {
|
||||
return *params;
|
||||
}
|
||||
}
|
||||
&[]
|
||||
}
|
||||
|
||||
const CMAKE_PARAMS_IOS: &[(&str, &[(&str, &str)])] = &[
|
||||
(
|
||||
"aarch64-apple-ios",
|
||||
&[
|
||||
("CMAKE_OSX_ARCHITECTURES", "arm64"),
|
||||
("CMAKE_OSX_SYSROOT", "iphoneos"),
|
||||
],
|
||||
),
|
||||
(
|
||||
"aarch64-apple-ios-sim",
|
||||
&[
|
||||
("CMAKE_OSX_ARCHITECTURES", "arm64"),
|
||||
("CMAKE_OSX_SYSROOT", "iphonesimulator"),
|
||||
],
|
||||
),
|
||||
(
|
||||
"x86_64-apple-ios",
|
||||
&[
|
||||
("CMAKE_OSX_ARCHITECTURES", "x86_64"),
|
||||
("CMAKE_OSX_SYSROOT", "iphonesimulator"),
|
||||
],
|
||||
),
|
||||
];
|
||||
|
||||
fn cmake_params_ios() -> &'static [(&'static str, &'static str)] {
|
||||
let target = std::env::var("TARGET").unwrap();
|
||||
for (ios_target, params) in CMAKE_PARAMS_IOS {
|
||||
if *ios_target == target {
|
||||
return *params;
|
||||
}
|
||||
}
|
||||
&[]
|
||||
}
|
||||
|
||||
fn get_ios_sdk_name() -> &'static str {
|
||||
for (name, value) in cmake_params_ios() {
|
||||
if *name == "CMAKE_OSX_SYSROOT" {
|
||||
return *value;
|
||||
}
|
||||
}
|
||||
let target = std::env::var("TARGET").unwrap();
|
||||
panic!("cannot find iOS SDK for {} in CMAKE_PARAMS_IOS", target);
|
||||
}
|
||||
|
||||
/// Returns the platform-specific output path for lib.
|
||||
///
|
||||
/// MSVC generator on Windows place static libs in a target sub-folder,
|
||||
/// so adjust library location based on platform and build target.
|
||||
/// See issue: https://github.com/alexcrichton/cmake-rs/issues/18
|
||||
fn get_boringssl_platform_output_path() -> String {
|
||||
if cfg!(windows) {
|
||||
// Code under this branch should match the logic in cmake-rs
|
||||
let debug_env_var = std::env::var("DEBUG").expect("DEBUG variable not defined in env");
|
||||
|
||||
let deb_info = match &debug_env_var[..] {
|
||||
"false" => false,
|
||||
"true" => true,
|
||||
unknown => panic!("Unknown DEBUG={} env var.", unknown),
|
||||
};
|
||||
|
||||
let opt_env_var =
|
||||
std::env::var("OPT_LEVEL").expect("OPT_LEVEL variable not defined in env");
|
||||
|
||||
let subdir = match &opt_env_var[..] {
|
||||
"0" => "Debug",
|
||||
"1" | "2" | "3" => {
|
||||
if deb_info {
|
||||
"RelWithDebInfo"
|
||||
} else {
|
||||
"Release"
|
||||
}
|
||||
}
|
||||
"s" | "z" => "MinSizeRel",
|
||||
unknown => panic!("Unknown OPT_LEVEL={} env var.", unknown),
|
||||
};
|
||||
|
||||
subdir.to_string()
|
||||
} else {
|
||||
"".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "fips")]
|
||||
const BORING_SSL_PATH: &str = "deps/boringssl-fips";
|
||||
#[cfg(not(feature = "fips"))]
|
||||
const BORING_SSL_PATH: &str = "deps/boringssl";
|
||||
|
||||
/// Returns a new cmake::Config for building BoringSSL.
|
||||
///
|
||||
/// It will add platform-specific parameters if needed.
|
||||
fn get_boringssl_cmake_config() -> cmake::Config {
|
||||
let arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap();
|
||||
let os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
|
||||
let pwd = std::env::current_dir().unwrap();
|
||||
|
||||
let mut boringssl_cmake = cmake::Config::new(BORING_SSL_PATH);
|
||||
|
||||
// Add platform-specific parameters.
|
||||
match os.as_ref() {
|
||||
"android" => {
|
||||
// We need ANDROID_NDK_HOME to be set properly.
|
||||
println!("cargo:rerun-if-env-changed=ANDROID_NDK_HOME");
|
||||
let android_ndk_home = std::env::var("ANDROID_NDK_HOME")
|
||||
.expect("Please set ANDROID_NDK_HOME for Android build");
|
||||
let android_ndk_home = std::path::Path::new(&android_ndk_home);
|
||||
for (name, value) in cmake_params_android() {
|
||||
eprintln!("android arch={} add {}={}", arch, name, value);
|
||||
boringssl_cmake.define(name, value);
|
||||
}
|
||||
let toolchain_file = android_ndk_home.join("build/cmake/android.toolchain.cmake");
|
||||
let toolchain_file = toolchain_file.to_str().unwrap();
|
||||
eprintln!("android toolchain={}", toolchain_file);
|
||||
boringssl_cmake.define("CMAKE_TOOLCHAIN_FILE", toolchain_file);
|
||||
|
||||
// 21 is the minimum level tested. You can give higher value.
|
||||
boringssl_cmake.define("ANDROID_NATIVE_API_LEVEL", "21");
|
||||
boringssl_cmake.define("ANDROID_STL", "c++_shared");
|
||||
|
||||
boringssl_cmake
|
||||
}
|
||||
|
||||
"ios" => {
|
||||
for (name, value) in cmake_params_ios() {
|
||||
eprintln!("ios arch={} add {}={}", arch, name, value);
|
||||
boringssl_cmake.define(name, value);
|
||||
}
|
||||
|
||||
// Bitcode is always on.
|
||||
let bitcode_cflag = "-fembed-bitcode";
|
||||
|
||||
// Hack for Xcode 10.1.
|
||||
let target_cflag = if arch == "x86_64" {
|
||||
"-target x86_64-apple-ios-simulator"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
|
||||
let cflag = format!("{} {}", bitcode_cflag, target_cflag);
|
||||
|
||||
boringssl_cmake.define("CMAKE_ASM_FLAGS", &cflag);
|
||||
boringssl_cmake.cflag(&cflag);
|
||||
|
||||
boringssl_cmake
|
||||
}
|
||||
|
||||
_ => {
|
||||
// Configure BoringSSL for building on 32-bit non-windows platforms.
|
||||
if arch == "x86" && os != "windows" {
|
||||
boringssl_cmake.define(
|
||||
"CMAKE_TOOLCHAIN_FILE",
|
||||
pwd.join(BORING_SSL_PATH)
|
||||
.join("src/util/32-bit-toolchain.cmake")
|
||||
.as_os_str(),
|
||||
);
|
||||
}
|
||||
|
||||
boringssl_cmake
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Verify that the toolchains match https://csrc.nist.gov/CSRC/media/projects/cryptographic-module-validation-program/documents/security-policies/140sp3678.pdf
|
||||
/// See "Installation Instructions" under section 12.1.
|
||||
// TODO: maybe this should also verify the Go and Ninja versions? But those haven't been an issue in practice ...
|
||||
fn verify_fips_clang_version() -> (&'static str, &'static str) {
|
||||
fn version(tool: &str) -> String {
|
||||
let output = match Command::new(tool).arg("--version").output() {
|
||||
Ok(o) => o,
|
||||
Err(e) => {
|
||||
eprintln!("warning: missing {}, trying other compilers: {}", tool, e);
|
||||
// NOTE: hard-codes that the loop below checks the version
|
||||
return String::new();
|
||||
}
|
||||
};
|
||||
assert!(output.status.success());
|
||||
let output = std::str::from_utf8(&output.stdout).expect("invalid utf8 output");
|
||||
output.lines().next().expect("empty output").to_string()
|
||||
}
|
||||
|
||||
const REQUIRED_CLANG_VERSION: &str = "7.0.1";
|
||||
for (cc, cxx) in [
|
||||
("clang-7", "clang++-7"),
|
||||
("clang", "clang++"),
|
||||
("cc", "c++"),
|
||||
] {
|
||||
let cc_version = version(cc);
|
||||
if cc_version.contains(REQUIRED_CLANG_VERSION) {
|
||||
assert!(
|
||||
version(cxx).contains(REQUIRED_CLANG_VERSION),
|
||||
"mismatched versions of cc and c++"
|
||||
);
|
||||
return (cc, cxx);
|
||||
} else if cc == "cc" {
|
||||
panic!(
|
||||
"unsupported clang version \"{}\": FIPS requires clang {}",
|
||||
cc_version, REQUIRED_CLANG_VERSION
|
||||
);
|
||||
} else if !cc_version.is_empty() {
|
||||
eprintln!(
|
||||
"warning: FIPS requires clang version {}, skipping incompatible version \"{}\"",
|
||||
REQUIRED_CLANG_VERSION, cc_version
|
||||
);
|
||||
}
|
||||
}
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn get_extra_clang_args_for_bindgen() -> Vec<String> {
|
||||
let os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
|
||||
|
||||
let mut params = Vec::new();
|
||||
|
||||
// Add platform-specific parameters.
|
||||
#[allow(clippy::single_match)]
|
||||
match os.as_ref() {
|
||||
"ios" => {
|
||||
use std::io::Write;
|
||||
// When cross-compiling for iOS, tell bindgen to use iOS sysroot,
|
||||
// and *don't* use system headers of the host macOS.
|
||||
let sdk = get_ios_sdk_name();
|
||||
let output = std::process::Command::new("xcrun")
|
||||
.args(["--show-sdk-path", "--sdk", sdk])
|
||||
.output()
|
||||
.unwrap();
|
||||
if !output.status.success() {
|
||||
if let Some(exit_code) = output.status.code() {
|
||||
eprintln!("xcrun failed: exit code {}", exit_code);
|
||||
} else {
|
||||
eprintln!("xcrun failed: killed");
|
||||
}
|
||||
std::io::stderr().write_all(&output.stderr).unwrap();
|
||||
// Uh... let's try anyway, I guess?
|
||||
return params;
|
||||
}
|
||||
let mut sysroot = String::from_utf8(output.stdout).unwrap();
|
||||
// There is typically a newline at the end which confuses clang.
|
||||
sysroot.truncate(sysroot.trim_end().len());
|
||||
params.push("-isysroot".to_string());
|
||||
params.push(sysroot);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
params
|
||||
}
|
||||
|
||||
fn main() {
|
||||
use std::env;
|
||||
|
||||
println!("cargo:rerun-if-env-changed=BORING_BSSL_PATH");
|
||||
let bssl_dir = std::env::var("BORING_BSSL_PATH").unwrap_or_else(|_| {
|
||||
if !Path::new(BORING_SSL_PATH).join("CMakeLists.txt").exists() {
|
||||
println!("cargo:warning=fetching boringssl git submodule");
|
||||
// fetch the boringssl submodule
|
||||
let status = Command::new("git")
|
||||
.args(&[
|
||||
"submodule",
|
||||
"update",
|
||||
"--init",
|
||||
"--recursive",
|
||||
BORING_SSL_PATH,
|
||||
])
|
||||
.status();
|
||||
if !status.map_or(false, |status| status.success()) {
|
||||
panic!("failed to fetch submodule - consider running `git submodule update --init --recursive deps/boringssl` yourself");
|
||||
}
|
||||
}
|
||||
|
||||
let mut cfg = get_boringssl_cmake_config();
|
||||
|
||||
if cfg!(feature = "fuzzing") {
|
||||
cfg.cxxflag("-DBORINGSSL_UNSAFE_DETERMINISTIC_MODE")
|
||||
.cxxflag("-DBORINGSSL_UNSAFE_FUZZER_MODE");
|
||||
}
|
||||
if cfg!(feature = "fips") {
|
||||
let (clang, clangxx) = verify_fips_clang_version();
|
||||
cfg.define("CMAKE_C_COMPILER", clang);
|
||||
cfg.define("CMAKE_CXX_COMPILER", clangxx);
|
||||
cfg.define("CMAKE_ASM_COMPILER", clang);
|
||||
cfg.define("FIPS", "1");
|
||||
}
|
||||
|
||||
if cfg!(feature = "ssl") {
|
||||
cfg.build_target("ssl").build();
|
||||
}
|
||||
cfg.build_target("crypto").build().display().to_string()
|
||||
});
|
||||
|
||||
let build_path = get_boringssl_platform_output_path();
|
||||
if cfg!(feature = "fips") {
|
||||
println!(
|
||||
"cargo:rustc-link-search=native={}/build/crypto/{}",
|
||||
bssl_dir, build_path
|
||||
);
|
||||
println!(
|
||||
"cargo:rustc-link-search=native={}/build/ssl/{}",
|
||||
bssl_dir, build_path
|
||||
);
|
||||
} else {
|
||||
println!(
|
||||
"cargo:rustc-link-search=native={}/build/{}",
|
||||
bssl_dir, build_path
|
||||
);
|
||||
}
|
||||
|
||||
println!("cargo:rustc-link-lib=static=crypto");
|
||||
if cfg!(feature = "ssl") {
|
||||
println!("cargo:rustc-link-lib=static=ssl");
|
||||
}
|
||||
|
||||
// MacOS: Allow cdylib to link with undefined symbols
|
||||
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
|
||||
if target_os == "macos" {
|
||||
println!("cargo:rustc-cdylib-link-arg=-Wl,-undefined,dynamic_lookup");
|
||||
}
|
||||
|
||||
println!("cargo:rerun-if-env-changed=BORING_BSSL_INCLUDE_PATH");
|
||||
let include_path = std::env::var("BORING_BSSL_INCLUDE_PATH").unwrap_or_else(|_| {
|
||||
if cfg!(feature = "fips") {
|
||||
format!("{}/include", BORING_SSL_PATH)
|
||||
} else {
|
||||
format!("{}/src/include", BORING_SSL_PATH)
|
||||
}
|
||||
});
|
||||
|
||||
let mut builder = bindgen::Builder::default()
|
||||
.derive_copy(true)
|
||||
.derive_debug(true)
|
||||
.derive_default(true)
|
||||
.derive_eq(true)
|
||||
.default_enum_style(bindgen::EnumVariation::NewType { is_bitfield: false })
|
||||
.default_macro_constant_type(bindgen::MacroTypeVariation::Signed)
|
||||
.generate_comments(true)
|
||||
.fit_macro_constants(false)
|
||||
.size_t_is_usize(true)
|
||||
.layout_tests(true)
|
||||
.prepend_enum_name(true)
|
||||
.rustfmt_bindings(true)
|
||||
.clang_args(get_extra_clang_args_for_bindgen())
|
||||
.clang_args(&["-I", &include_path]);
|
||||
|
||||
let target = std::env::var("TARGET").unwrap();
|
||||
match target.as_ref() {
|
||||
// bindgen produces alignment tests that cause undefined behavior [1]
|
||||
// when applied to explicitly unaligned types like OSUnalignedU64.
|
||||
//
|
||||
// There is no way to disable these tests for only some types
|
||||
// and it's not nice to suppress warnings for the entire crate,
|
||||
// so let's disable all alignment tests and hope for the best.
|
||||
//
|
||||
// [1]: https://github.com/rust-lang/rust-bindgen/issues/1651
|
||||
"aarch64-apple-ios" | "aarch64-apple-ios-sim" => {
|
||||
builder = builder.layout_tests(false);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let headers = [
|
||||
"aes.h",
|
||||
"asn1_mac.h",
|
||||
"asn1t.h",
|
||||
#[cfg(not(feature = "fips"))]
|
||||
"blake2.h",
|
||||
"blowfish.h",
|
||||
"cast.h",
|
||||
"chacha.h",
|
||||
"cmac.h",
|
||||
"cpu.h",
|
||||
"curve25519.h",
|
||||
"des.h",
|
||||
"dtls1.h",
|
||||
"hkdf.h",
|
||||
"hrss.h",
|
||||
"md4.h",
|
||||
"md5.h",
|
||||
"obj_mac.h",
|
||||
"objects.h",
|
||||
"opensslv.h",
|
||||
"ossl_typ.h",
|
||||
"pkcs12.h",
|
||||
"poly1305.h",
|
||||
"rand.h",
|
||||
"rc4.h",
|
||||
"ripemd.h",
|
||||
"siphash.h",
|
||||
"srtp.h",
|
||||
#[cfg(not(feature = "fips"))]
|
||||
"trust_token.h",
|
||||
"x509v3.h",
|
||||
];
|
||||
for header in &headers {
|
||||
builder = builder.header(
|
||||
Path::new(&include_path)
|
||||
.join("openssl")
|
||||
.join(header)
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
let bindings = builder.generate().expect("Unable to generate bindings");
|
||||
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||
bindings
|
||||
.write_to_file(out_path.join("bindings.rs"))
|
||||
.expect("Couldn't write bindings!");
|
||||
}
|
||||
@ -1,191 +0,0 @@
|
||||
use std::env;
|
||||
use std::ffi::OsString;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub(crate) struct Config {
|
||||
pub(crate) manifest_dir: PathBuf,
|
||||
pub(crate) out_dir: PathBuf,
|
||||
pub(crate) is_bazel: bool,
|
||||
pub(crate) host: String,
|
||||
pub(crate) target: String,
|
||||
pub(crate) target_arch: String,
|
||||
pub(crate) target_os: String,
|
||||
pub(crate) unix: bool,
|
||||
pub(crate) target_env: String,
|
||||
pub(crate) target_features: Vec<String>,
|
||||
pub(crate) features: Features,
|
||||
pub(crate) env: Env,
|
||||
}
|
||||
|
||||
pub(crate) struct Features {
|
||||
pub(crate) fips: bool,
|
||||
pub(crate) rpk: bool,
|
||||
pub(crate) underscore_wildcards: bool,
|
||||
}
|
||||
|
||||
pub(crate) struct Env {
|
||||
pub(crate) path: Option<PathBuf>,
|
||||
pub(crate) include_path: Option<PathBuf>,
|
||||
pub(crate) source_path: Option<PathBuf>,
|
||||
pub(crate) assume_patched: bool,
|
||||
pub(crate) sysroot: Option<PathBuf>,
|
||||
pub(crate) compiler_external_toolchain: Option<PathBuf>,
|
||||
pub(crate) debug: Option<OsString>,
|
||||
pub(crate) opt_level: Option<OsString>,
|
||||
pub(crate) android_ndk_home: Option<PathBuf>,
|
||||
pub(crate) cmake_toolchain_file: Option<PathBuf>,
|
||||
pub(crate) cpp_runtime_lib: Option<OsString>,
|
||||
pub(crate) docs_rs: bool,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub(crate) fn from_env() -> Self {
|
||||
let manifest_dir = env::var_os("CARGO_MANIFEST_DIR").unwrap().into();
|
||||
let out_dir = env::var_os("OUT_DIR").unwrap().into();
|
||||
let host = env::var("HOST").unwrap();
|
||||
let target = env::var("TARGET").unwrap();
|
||||
let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
|
||||
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
|
||||
let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap();
|
||||
let unix = env::var("CARGO_CFG_UNIX").is_ok();
|
||||
|
||||
let target_features = env::var("CARGO_CFG_TARGET_FEATURE")
|
||||
.unwrap_or_default()
|
||||
.split(',')
|
||||
.map(|s| s.to_owned())
|
||||
.collect();
|
||||
|
||||
let features = Features::from_env();
|
||||
let env = Env::from_env(&host, &target, features.is_fips_like());
|
||||
|
||||
let is_bazel = env
|
||||
.source_path
|
||||
.as_ref()
|
||||
.is_some_and(|path| path.join("src").exists());
|
||||
|
||||
let config = Self {
|
||||
manifest_dir,
|
||||
out_dir,
|
||||
is_bazel,
|
||||
host,
|
||||
target,
|
||||
target_arch,
|
||||
target_os,
|
||||
unix,
|
||||
target_env,
|
||||
target_features,
|
||||
features,
|
||||
env,
|
||||
};
|
||||
|
||||
config.check_feature_compatibility();
|
||||
|
||||
config
|
||||
}
|
||||
|
||||
fn check_feature_compatibility(&self) {
|
||||
if self.features.fips && self.features.rpk {
|
||||
panic!("`fips` and `rpk` features are mutually exclusive");
|
||||
}
|
||||
|
||||
let is_precompiled_native_lib = self.env.path.is_some();
|
||||
let is_external_native_lib_source =
|
||||
!is_precompiled_native_lib && self.env.source_path.is_none();
|
||||
|
||||
if self.env.assume_patched && is_external_native_lib_source {
|
||||
panic!(
|
||||
"`BORING_BSSL_{{,_FIPS}}_ASSUME_PATCHED` env variable is supposed to be used with\
|
||||
`BORING_BSSL{{,_FIPS}}_PATH` or `BORING_BSSL{{,_FIPS}}_SOURCE_PATH` env variables"
|
||||
);
|
||||
}
|
||||
|
||||
let features_with_patches_enabled = self.features.rpk || self.features.underscore_wildcards;
|
||||
|
||||
let patches_required = features_with_patches_enabled && !self.env.assume_patched;
|
||||
|
||||
if is_precompiled_native_lib && patches_required {
|
||||
println!(
|
||||
"cargo:warning=precompiled BoringSSL was provided, so patches will be ignored"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Features {
|
||||
fn from_env() -> Self {
|
||||
let fips = env::var_os("CARGO_FEATURE_FIPS").is_some();
|
||||
let rpk = env::var_os("CARGO_FEATURE_RPK").is_some();
|
||||
let underscore_wildcards = env::var_os("CARGO_FEATURE_UNDERSCORE_WILDCARDS").is_some();
|
||||
|
||||
Self {
|
||||
fips,
|
||||
rpk,
|
||||
underscore_wildcards,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn is_fips_like(&self) -> bool {
|
||||
self.fips
|
||||
}
|
||||
}
|
||||
|
||||
impl Env {
|
||||
fn from_env(host: &str, target: &str, is_fips_like: bool) -> Self {
|
||||
let target_with_underscores = target.replace('-', "_");
|
||||
|
||||
// Logic stolen from cmake-rs.
|
||||
let target_var = |name: &str| {
|
||||
let kind = if host == target { "HOST" } else { "TARGET" };
|
||||
|
||||
// TODO(rmehra): look for just `name` first, as most people just set that
|
||||
var(&format!("{name}_{target}"))
|
||||
.or_else(|| var(&format!("{name}_{target_with_underscores}")))
|
||||
.or_else(|| var(&format!("{kind}_{name}")))
|
||||
.or_else(|| var(name))
|
||||
};
|
||||
|
||||
let boringssl_var = |name: &str| {
|
||||
const BORING_BSSL_PREFIX: &str = "BORING_BSSL_";
|
||||
const BORING_BSSL_FIPS_PREFIX: &str = "BORING_BSSL_FIPS_";
|
||||
|
||||
// The passed name is the non-fips version of the environment variable,
|
||||
// to help look for them in the repository.
|
||||
assert!(name.starts_with(BORING_BSSL_PREFIX));
|
||||
|
||||
let non_fips = target_var(name);
|
||||
if is_fips_like {
|
||||
let fips_name = name.replace(BORING_BSSL_PREFIX, BORING_BSSL_FIPS_PREFIX);
|
||||
let fips = target_var(&fips_name);
|
||||
if fips.is_none() && non_fips.is_some() {
|
||||
println!("cargo:warning=env var {name} ignored, because FIPS is enabled. Set {fips_name} instead.");
|
||||
}
|
||||
fips
|
||||
} else {
|
||||
non_fips
|
||||
}
|
||||
};
|
||||
|
||||
Self {
|
||||
path: boringssl_var("BORING_BSSL_PATH").map(PathBuf::from), // gets BORING_BSSL_FIPS_PATH if fips is enabled
|
||||
include_path: boringssl_var("BORING_BSSL_INCLUDE_PATH").map(PathBuf::from), // gets BORING_BSSL_FIPS_INCLUDE_PATH if fips is enabled
|
||||
source_path: boringssl_var("BORING_BSSL_SOURCE_PATH").map(PathBuf::from), // gets BORING_BSSL_FIPS_SOURCE_PATH if fips is enabled
|
||||
assume_patched: boringssl_var("BORING_BSSL_ASSUME_PATCHED") // gets BORING_BSSL_FIPS_ASSUME_PATCHED if fips is enabled
|
||||
.is_some_and(|v| !v.is_empty()),
|
||||
sysroot: boringssl_var("BORING_BSSL_SYSROOT").map(PathBuf::from), // gets BORING_BSSL_FIPS_SYSROOT if fips is enabled
|
||||
compiler_external_toolchain: boringssl_var("BORING_BSSL_COMPILER_EXTERNAL_TOOLCHAIN") // gets BORING_BSSL_FIPS_COMPILER_EXTERNAL_TOOLCHAIN if fips is enabled
|
||||
.map(PathBuf::from),
|
||||
debug: target_var("DEBUG"),
|
||||
opt_level: target_var("OPT_LEVEL"),
|
||||
android_ndk_home: target_var("ANDROID_NDK_HOME").map(Into::into),
|
||||
cmake_toolchain_file: target_var("CMAKE_TOOLCHAIN_FILE").map(Into::into),
|
||||
cpp_runtime_lib: target_var("BORING_BSSL_RUST_CPPLIB"),
|
||||
docs_rs: var("DOCS_RS").is_some(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn var(name: &str) -> Option<OsString> {
|
||||
println!("cargo:rerun-if-env-changed={name}");
|
||||
|
||||
env::var_os(name)
|
||||
}
|
||||
@ -1,767 +0,0 @@
|
||||
use fslock::LockFile;
|
||||
use std::ffi::OsString;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::io::Write;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Command, Output};
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use crate::config::Config;
|
||||
|
||||
mod config;
|
||||
|
||||
fn should_use_cmake_cross_compilation(config: &Config) -> bool {
|
||||
if config.host == config.target {
|
||||
return false;
|
||||
}
|
||||
match config.target_os.as_str() {
|
||||
"macos" | "ios" => {
|
||||
// Cross-compiling for Apple platforms on macOS is supported using the normal Xcode
|
||||
// tools, along with the settings from `cmake_params_apple`.
|
||||
!config.host.ends_with("-darwin")
|
||||
}
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
// Android NDK >= 19.
|
||||
const CMAKE_PARAMS_ANDROID_NDK: &[(&str, &[(&str, &str)])] = &[
|
||||
("aarch64", &[("ANDROID_ABI", "arm64-v8a")]),
|
||||
("arm", &[("ANDROID_ABI", "armeabi-v7a")]),
|
||||
("x86", &[("ANDROID_ABI", "x86")]),
|
||||
("x86_64", &[("ANDROID_ABI", "x86_64")]),
|
||||
];
|
||||
|
||||
fn cmake_params_android(config: &Config) -> &'static [(&'static str, &'static str)] {
|
||||
for (android_arch, params) in CMAKE_PARAMS_ANDROID_NDK {
|
||||
if *android_arch == config.target_arch {
|
||||
return params;
|
||||
}
|
||||
}
|
||||
&[]
|
||||
}
|
||||
|
||||
const CMAKE_PARAMS_APPLE: &[(&str, &[(&str, &str)])] = &[
|
||||
// iOS
|
||||
(
|
||||
"aarch64-apple-ios",
|
||||
&[
|
||||
("CMAKE_OSX_ARCHITECTURES", "arm64"),
|
||||
("CMAKE_OSX_SYSROOT", "iphoneos"),
|
||||
("CMAKE_MACOSX_BUNDLE", "OFF"),
|
||||
],
|
||||
),
|
||||
(
|
||||
"aarch64-apple-ios-sim",
|
||||
&[
|
||||
("CMAKE_OSX_ARCHITECTURES", "arm64"),
|
||||
("CMAKE_OSX_SYSROOT", "iphonesimulator"),
|
||||
("CMAKE_MACOSX_BUNDLE", "OFF"),
|
||||
],
|
||||
),
|
||||
(
|
||||
"x86_64-apple-ios",
|
||||
&[
|
||||
("CMAKE_OSX_ARCHITECTURES", "x86_64"),
|
||||
("CMAKE_OSX_SYSROOT", "iphonesimulator"),
|
||||
("CMAKE_MACOSX_BUNDLE", "OFF"),
|
||||
],
|
||||
),
|
||||
// macOS
|
||||
(
|
||||
"aarch64-apple-darwin",
|
||||
&[
|
||||
("CMAKE_OSX_ARCHITECTURES", "arm64"),
|
||||
("CMAKE_OSX_SYSROOT", "macosx"),
|
||||
],
|
||||
),
|
||||
(
|
||||
"x86_64-apple-darwin",
|
||||
&[
|
||||
("CMAKE_OSX_ARCHITECTURES", "x86_64"),
|
||||
("CMAKE_OSX_SYSROOT", "macosx"),
|
||||
],
|
||||
),
|
||||
];
|
||||
|
||||
fn cmake_params_apple(config: &Config) -> &'static [(&'static str, &'static str)] {
|
||||
for (next_target, params) in CMAKE_PARAMS_APPLE {
|
||||
if *next_target == config.target {
|
||||
return params;
|
||||
}
|
||||
}
|
||||
&[]
|
||||
}
|
||||
|
||||
fn get_apple_sdk_name(config: &Config) -> &'static str {
|
||||
for (name, value) in cmake_params_apple(config) {
|
||||
if *name == "CMAKE_OSX_SYSROOT" {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
panic!(
|
||||
"cannot find SDK for {} in CMAKE_PARAMS_APPLE",
|
||||
config.target
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns an absolute path to the BoringSSL source.
|
||||
fn get_boringssl_source_path(config: &Config) -> &Path {
|
||||
static SOURCE_PATH: OnceLock<PathBuf> = OnceLock::new();
|
||||
|
||||
SOURCE_PATH.get_or_init(|| {
|
||||
if let Some(src_path) = &config.env.source_path {
|
||||
if !src_path.exists() {
|
||||
println!(
|
||||
"cargo:warning=boringssl source path doesn't exist: {}",
|
||||
src_path.display()
|
||||
);
|
||||
}
|
||||
return src_path.into();
|
||||
}
|
||||
|
||||
let submodule_dir = "boringssl";
|
||||
|
||||
let src_path = config.out_dir.join(submodule_dir);
|
||||
|
||||
let submodule_path = config.manifest_dir.join("deps").join(submodule_dir);
|
||||
|
||||
if !submodule_path.join("CMakeLists.txt").exists() {
|
||||
println!("cargo:warning=fetching boringssl git submodule");
|
||||
|
||||
run_command(
|
||||
Command::new("git")
|
||||
.args(["submodule", "update", "--init", "--recursive"])
|
||||
.arg(&submodule_path),
|
||||
)
|
||||
.expect("git submodule update");
|
||||
}
|
||||
|
||||
let _ = fs::remove_dir_all(&src_path);
|
||||
fs_extra::dir::copy(submodule_path, &config.out_dir, &Default::default())
|
||||
.inspect_err(|_| {
|
||||
let _ = fs::remove_dir_all(&config.out_dir);
|
||||
})
|
||||
.expect("copying failed. Try running `cargo clean`");
|
||||
|
||||
// NOTE: .git can be both file and dir, depening on whether it was copied from a submodule
|
||||
// or created by the patches code.
|
||||
let src_git_path = src_path.join(".git");
|
||||
let _ = fs::remove_file(&src_git_path);
|
||||
let _ = fs::remove_dir_all(&src_git_path);
|
||||
|
||||
src_path
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the platform-specific output path for lib.
|
||||
///
|
||||
/// MSVC generator on Windows place static libs in a target sub-folder,
|
||||
/// so adjust library location based on platform and build target.
|
||||
/// See issue: <https://github.com/alexcrichton/cmake-rs/issues/18>
|
||||
fn msvc_lib_subdir(config: &Config) -> Option<&'static str> {
|
||||
if config.target.ends_with("-msvc") {
|
||||
// Code under this branch should match the logic in cmake-rs
|
||||
let debug_env_var = config
|
||||
.env
|
||||
.debug
|
||||
.as_ref()
|
||||
.expect("DEBUG variable not defined in env");
|
||||
|
||||
let deb_info = match debug_env_var.to_str() {
|
||||
Some("false") => false,
|
||||
Some("true") => true,
|
||||
_ => panic!("Unknown DEBUG={debug_env_var:?} env var."),
|
||||
};
|
||||
|
||||
let opt_env_var = config
|
||||
.env
|
||||
.opt_level
|
||||
.as_ref()
|
||||
.expect("OPT_LEVEL variable not defined in env");
|
||||
|
||||
let subdir = match opt_env_var.to_str() {
|
||||
Some("0") => "Debug",
|
||||
Some("1" | "2" | "3") => {
|
||||
if deb_info {
|
||||
"RelWithDebInfo"
|
||||
} else {
|
||||
"Release"
|
||||
}
|
||||
}
|
||||
Some("s" | "z") => "MinSizeRel",
|
||||
_ => panic!("Unknown OPT_LEVEL={opt_env_var:?} env var."),
|
||||
};
|
||||
|
||||
Some(subdir)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a new `cmake::Config` for building BoringSSL.
|
||||
///
|
||||
/// It will add platform-specific parameters if needed.
|
||||
fn get_boringssl_cmake_config(config: &Config) -> cmake::Config {
|
||||
let src_path = get_boringssl_source_path(config);
|
||||
let mut boringssl_cmake = cmake::Config::new(src_path);
|
||||
|
||||
if config.env.cmake_toolchain_file.is_some() {
|
||||
return boringssl_cmake;
|
||||
}
|
||||
|
||||
if config.target_os == "windows" {
|
||||
// Explicitly use the non-debug CRT.
|
||||
// This is required now because newest BoringSSL requires CMake 3.22 which
|
||||
// uses the new logic with CMAKE_MSVC_RUNTIME_LIBRARY introduced in CMake 3.15.
|
||||
// https://github.com/rust-lang/cmake-rs/pull/30#issuecomment-2969758499
|
||||
if config.target_features.iter().any(|f| f == "crt-static") {
|
||||
boringssl_cmake.define("CMAKE_MSVC_RUNTIME_LIBRARY", "MultiThreaded");
|
||||
} else {
|
||||
boringssl_cmake.define("CMAKE_MSVC_RUNTIME_LIBRARY", "MultiThreadedDLL");
|
||||
}
|
||||
}
|
||||
|
||||
if config.host == config.target {
|
||||
return boringssl_cmake;
|
||||
}
|
||||
|
||||
if should_use_cmake_cross_compilation(config) {
|
||||
boringssl_cmake
|
||||
.define("CMAKE_CROSSCOMPILING", "true")
|
||||
.define("CMAKE_C_COMPILER_TARGET", &config.target)
|
||||
.define("CMAKE_CXX_COMPILER_TARGET", &config.target)
|
||||
.define("CMAKE_ASM_COMPILER_TARGET", &config.target);
|
||||
}
|
||||
|
||||
if let Some(sysroot) = &config.env.sysroot {
|
||||
boringssl_cmake.define("CMAKE_SYSROOT", sysroot);
|
||||
}
|
||||
|
||||
if let Some(toolchain) = &config.env.compiler_external_toolchain {
|
||||
boringssl_cmake
|
||||
.define("CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN", toolchain)
|
||||
.define("CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN", toolchain)
|
||||
.define("CMAKE_ASM_COMPILER_EXTERNAL_TOOLCHAIN", toolchain);
|
||||
}
|
||||
|
||||
// Add platform-specific parameters for cross-compilation.
|
||||
match &*config.target_os {
|
||||
"android" => {
|
||||
// We need ANDROID_NDK_HOME to be set properly.
|
||||
let android_ndk_home = config
|
||||
.env
|
||||
.android_ndk_home
|
||||
.as_ref()
|
||||
.expect("Please set ANDROID_NDK_HOME for Android build");
|
||||
for (name, value) in cmake_params_android(config) {
|
||||
eprintln!("android arch={} add {}={}", config.target_arch, name, value);
|
||||
boringssl_cmake.define(name, value);
|
||||
}
|
||||
let toolchain_file = android_ndk_home.join("build/cmake/android.toolchain.cmake");
|
||||
let toolchain_file = toolchain_file.to_str().unwrap();
|
||||
eprintln!("android toolchain={toolchain_file}");
|
||||
boringssl_cmake.define("CMAKE_TOOLCHAIN_FILE", toolchain_file);
|
||||
|
||||
// 21 is the minimum level tested. You can give higher value.
|
||||
boringssl_cmake.define("CMAKE_SYSTEM_VERSION", "21");
|
||||
boringssl_cmake.define("CMAKE_ANDROID_STL_TYPE", "c++_shared");
|
||||
}
|
||||
|
||||
"macos" => {
|
||||
for (name, value) in cmake_params_apple(config) {
|
||||
eprintln!("macos arch={} add {}={}", config.target_arch, name, value);
|
||||
boringssl_cmake.define(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
"ios" => {
|
||||
for (name, value) in cmake_params_apple(config) {
|
||||
eprintln!("ios arch={} add {}={}", config.target_arch, name, value);
|
||||
boringssl_cmake.define(name, value);
|
||||
}
|
||||
|
||||
// Bitcode is always on.
|
||||
let bitcode_cflag = "-fembed-bitcode";
|
||||
|
||||
// Hack for Xcode 10.1.
|
||||
let target_cflag = if config.target_arch == "x86_64" {
|
||||
"-target x86_64-apple-ios-simulator"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
|
||||
let cflag = format!("{bitcode_cflag} {target_cflag}");
|
||||
boringssl_cmake.define("CMAKE_ASM_FLAGS", &cflag);
|
||||
boringssl_cmake.cflag(&cflag);
|
||||
}
|
||||
|
||||
"windows" => {
|
||||
if config.host.contains("windows") {
|
||||
// BoringSSL's CMakeLists.txt isn't set up for cross-compiling using Visual Studio.
|
||||
// Disable assembly support so that it at least builds.
|
||||
boringssl_cmake.define("OPENSSL_NO_ASM", "YES");
|
||||
}
|
||||
}
|
||||
|
||||
"linux" => match &*config.target_arch {
|
||||
"x86" => {
|
||||
boringssl_cmake.define(
|
||||
"CMAKE_TOOLCHAIN_FILE",
|
||||
// `src_path` can be a path relative to the manifest dir, but
|
||||
// cmake hates that.
|
||||
config
|
||||
.manifest_dir
|
||||
.join(src_path)
|
||||
.join("util/32-bit-toolchain.cmake")
|
||||
.as_os_str(),
|
||||
);
|
||||
}
|
||||
"aarch64" => {
|
||||
boringssl_cmake.define(
|
||||
"CMAKE_TOOLCHAIN_FILE",
|
||||
config
|
||||
.manifest_dir
|
||||
.join("cmake/aarch64-linux.cmake")
|
||||
.as_os_str(),
|
||||
);
|
||||
}
|
||||
"arm" => {
|
||||
boringssl_cmake.define(
|
||||
"CMAKE_TOOLCHAIN_FILE",
|
||||
config
|
||||
.manifest_dir
|
||||
.join("cmake/armv7-linux.cmake")
|
||||
.as_os_str(),
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
println!(
|
||||
"cargo:warning=no toolchain file configured by boring-sys for {}",
|
||||
config.target
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
_ => {}
|
||||
}
|
||||
|
||||
boringssl_cmake
|
||||
}
|
||||
|
||||
fn pick_best_android_ndk_toolchain(toolchains_dir: &Path) -> std::io::Result<OsString> {
|
||||
let toolchains = std::fs::read_dir(toolchains_dir)?.collect::<Result<Vec<_>, _>>()?;
|
||||
// First look for one of the toolchains that Google has documented.
|
||||
// https://developer.android.com/ndk/guides/other_build_systems
|
||||
for known_toolchain in ["linux-x86_64", "darwin-x86_64", "windows-x86_64"] {
|
||||
if let Some(toolchain) = toolchains
|
||||
.iter()
|
||||
.find(|entry| entry.file_name() == known_toolchain)
|
||||
{
|
||||
return Ok(toolchain.file_name());
|
||||
}
|
||||
}
|
||||
// Then fall back to any subdirectory, in case Google has added support for a new host.
|
||||
// (Maybe there's a linux-aarch64 toolchain now.)
|
||||
if let Some(toolchain) = toolchains
|
||||
.into_iter()
|
||||
.find(|entry| entry.file_type().map(|ty| ty.is_dir()).unwrap_or(false))
|
||||
{
|
||||
return Ok(toolchain.file_name());
|
||||
}
|
||||
// Finally give up.
|
||||
Err(std::io::Error::new(
|
||||
std::io::ErrorKind::NotFound,
|
||||
"no subdirectories at given path",
|
||||
))
|
||||
}
|
||||
|
||||
fn get_extra_clang_args_for_bindgen(config: &Config) -> Vec<String> {
|
||||
let mut params = Vec::new();
|
||||
|
||||
// Add platform-specific parameters.
|
||||
match &*config.target_os {
|
||||
"ios" | "macos" => {
|
||||
// When cross-compiling for Apple targets, tell bindgen to use SDK sysroot,
|
||||
// and *don't* use system headers of the host macOS.
|
||||
let sdk = get_apple_sdk_name(config);
|
||||
match run_command(Command::new("xcrun").args(["--show-sdk-path", "--sdk", sdk])) {
|
||||
Ok(output) => {
|
||||
let sysroot = std::str::from_utf8(&output.stdout).expect("xcrun output");
|
||||
params.push("-isysroot".to_string());
|
||||
// There is typically a newline at the end which confuses clang.
|
||||
params.push(sysroot.trim_end().to_string());
|
||||
}
|
||||
Err(e) => {
|
||||
println!("cargo:warning={e}");
|
||||
// Uh... let's try anyway, I guess?
|
||||
}
|
||||
}
|
||||
}
|
||||
"android" => {
|
||||
let mut android_sysroot = config
|
||||
.env
|
||||
.android_ndk_home
|
||||
.clone()
|
||||
.expect("Please set ANDROID_NDK_HOME for Android build");
|
||||
|
||||
android_sysroot.extend(["toolchains", "llvm", "prebuilt"]);
|
||||
|
||||
match pick_best_android_ndk_toolchain(&android_sysroot) {
|
||||
Ok(toolchain) => {
|
||||
android_sysroot.push(toolchain);
|
||||
android_sysroot.push("sysroot");
|
||||
params.push("--sysroot".to_string());
|
||||
params.push(android_sysroot.into_os_string().into_string().unwrap());
|
||||
}
|
||||
Err(e) => {
|
||||
println!("cargo:warning=failed to find prebuilt Android NDK toolchain for bindgen: {e}");
|
||||
// Uh... let's try anyway, I guess?
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
params
|
||||
}
|
||||
|
||||
fn ensure_patches_applied(config: &Config) -> io::Result<()> {
|
||||
if config.env.assume_patched || config.env.path.is_some() {
|
||||
println!(
|
||||
"cargo:warning=skipping git patches application, provided\
|
||||
native BoringSSL is expected to have the patches included"
|
||||
);
|
||||
return Ok(());
|
||||
} else if config.env.source_path.is_some()
|
||||
&& (config.features.rpk || config.features.underscore_wildcards)
|
||||
{
|
||||
panic!(
|
||||
"BORING_BSSL_ASSUME_PATCHED must be set when setting
|
||||
BORING_BSSL_SOURCE_PATH and using any of the following
|
||||
features: rpk, underscore-wildcards"
|
||||
);
|
||||
}
|
||||
|
||||
let mut lock_file = LockFile::open(&config.out_dir.join(".patch_lock"))?;
|
||||
let src_path = get_boringssl_source_path(config);
|
||||
let has_git = src_path.join(".git").exists();
|
||||
|
||||
lock_file.lock()?;
|
||||
|
||||
// NOTE: init git in the copied files, so we can apply patches
|
||||
if !has_git {
|
||||
run_command(Command::new("git").arg("init").current_dir(src_path))?;
|
||||
}
|
||||
|
||||
println!("cargo:warning=applying post quantum crypto patch to boringssl");
|
||||
apply_patch(config, "boring-pq.patch")?;
|
||||
|
||||
if config.features.rpk {
|
||||
println!("cargo:warning=applying RPK patch to boringssl");
|
||||
apply_patch(config, "rpk.patch")?;
|
||||
}
|
||||
|
||||
if config.features.underscore_wildcards {
|
||||
println!("cargo:warning=applying underscore wildcards patch to boringssl");
|
||||
apply_patch(config, "underscore-wildcards.patch")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn apply_patch(config: &Config, patch_name: &str) -> io::Result<()> {
|
||||
let src_path = get_boringssl_source_path(config);
|
||||
#[cfg(not(windows))]
|
||||
let cmd_path = config
|
||||
.manifest_dir
|
||||
.join("patches")
|
||||
.join(patch_name)
|
||||
.canonicalize()?;
|
||||
|
||||
#[cfg(windows)]
|
||||
let cmd_path = config.manifest_dir.join("patches").join(patch_name);
|
||||
|
||||
let mut args = vec!["apply", "-v", "--whitespace=fix"];
|
||||
|
||||
// non-bazel versions of BoringSSL have no src/ dir
|
||||
if config.is_bazel {
|
||||
args.push("-p2");
|
||||
}
|
||||
|
||||
run_command(
|
||||
Command::new("git")
|
||||
.args(&args)
|
||||
.arg(cmd_path)
|
||||
.current_dir(src_path),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_command(command: &mut Command) -> io::Result<Output> {
|
||||
let out = command.output().map_err(|e| {
|
||||
io::Error::new(
|
||||
e.kind(),
|
||||
format!(
|
||||
"can't run {}: {e}\n{command:?} failed",
|
||||
command.get_program().to_string_lossy(),
|
||||
),
|
||||
)
|
||||
})?;
|
||||
|
||||
std::io::stderr().write_all(&out.stderr)?;
|
||||
std::io::stdout().write_all(&out.stdout)?;
|
||||
|
||||
if !out.status.success() {
|
||||
let err = match out.status.code() {
|
||||
Some(code) => format!("{command:?} exited with status: {code}"),
|
||||
None => format!("{command:?} was terminated by signal"),
|
||||
};
|
||||
|
||||
return Err(io::Error::other(err));
|
||||
}
|
||||
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
fn build_boringssl_or_get_prebuilt(config: &Config) -> &Path {
|
||||
static BUILD_SOURCE_PATH: OnceLock<PathBuf> = OnceLock::new();
|
||||
|
||||
BUILD_SOURCE_PATH.get_or_init(|| {
|
||||
if let Some(path) = &config.env.path {
|
||||
if !path.exists() {
|
||||
println!("cargo:warning=built path doesn't exist: {}", path.display());
|
||||
}
|
||||
return path.into();
|
||||
}
|
||||
|
||||
let mut cfg = get_boringssl_cmake_config(config);
|
||||
|
||||
let num_jobs = std::env::var("NUM_JOBS").ok().or_else(|| {
|
||||
std::thread::available_parallelism()
|
||||
.ok()
|
||||
.map(|t| t.to_string())
|
||||
});
|
||||
if let Some(num_jobs) = num_jobs {
|
||||
cfg.env("CMAKE_BUILD_PARALLEL_LEVEL", num_jobs);
|
||||
}
|
||||
|
||||
if config.features.fips {
|
||||
cfg.define("CMAKE_C_COMPILER", "clang")
|
||||
.define("CMAKE_CXX_COMPILER", "clang++")
|
||||
.define("CMAKE_ASM_COMPILER", "clang")
|
||||
.define("FIPS", "1");
|
||||
}
|
||||
|
||||
cfg.build_target("ssl").build();
|
||||
let path = cfg.build_target("crypto").build();
|
||||
let build_dir = path.join("build");
|
||||
if build_dir.exists() {
|
||||
build_dir
|
||||
} else {
|
||||
path
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn get_cpp_runtime_lib(config: &Config) -> Option<String> {
|
||||
if let Some(ref cpp_lib) = config.env.cpp_runtime_lib {
|
||||
return cpp_lib.clone().into_string().ok();
|
||||
}
|
||||
|
||||
match &*config.target_os {
|
||||
"macos" | "ios" | "freebsd" | "openbsd" | "android" => Some("c++".into()),
|
||||
_ if config.unix || config.target_env == "gnu" => Some("stdc++".into()),
|
||||
// TODO(rmehra): figure out how to do this for windows
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let config = Config::from_env();
|
||||
ensure_patches_applied(&config).unwrap();
|
||||
if !config.env.docs_rs {
|
||||
emit_link_directives(&config);
|
||||
}
|
||||
generate_bindings(&config);
|
||||
}
|
||||
|
||||
fn emit_link_directives(config: &Config) {
|
||||
let bssl_dir = build_boringssl_or_get_prebuilt(config);
|
||||
let msvc_lib_subdir = msvc_lib_subdir(config);
|
||||
|
||||
let subdirs =
|
||||
if config.is_bazel || (config.features.is_fips_like() && config.env.path.is_some()) {
|
||||
&["lib"][..]
|
||||
} else {
|
||||
&["lib", "crypto", "ssl", ""][..]
|
||||
};
|
||||
|
||||
for subdir in subdirs {
|
||||
let dir = bssl_dir.join(subdir);
|
||||
let dir = msvc_lib_subdir
|
||||
.map(|s| dir.join(s))
|
||||
.filter(|d| d.exists())
|
||||
.unwrap_or(dir);
|
||||
println!("cargo:rustc-link-search=native={}", dir.display());
|
||||
}
|
||||
|
||||
if let Some(cpp_lib) = get_cpp_runtime_lib(config) {
|
||||
println!("cargo:rustc-link-lib={cpp_lib}");
|
||||
}
|
||||
println!("cargo:rustc-link-lib=static=crypto");
|
||||
println!("cargo:rustc-link-lib=static=ssl");
|
||||
|
||||
if config.target_os == "windows" {
|
||||
// Rust 1.87.0 compat - https://github.com/rust-lang/rust/pull/138233
|
||||
println!("cargo:rustc-link-lib=advapi32");
|
||||
}
|
||||
}
|
||||
|
||||
fn check_include_path(path: PathBuf) -> Result<PathBuf, String> {
|
||||
if path.join("openssl").join("x509v3.h").exists() {
|
||||
Ok(path)
|
||||
} else {
|
||||
Err(format!(
|
||||
"Include path {} {}",
|
||||
path.display(),
|
||||
if !path.exists() {
|
||||
"does not exist"
|
||||
} else {
|
||||
"does not have expected openssl/x509v3.h"
|
||||
}
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_bindings(config: &Config) {
|
||||
let include_path = config.env.include_path.clone().unwrap_or_else(|| {
|
||||
if let Some(bssl_path) = &config.env.path {
|
||||
return check_include_path(bssl_path.join("include"))
|
||||
.expect("config has invalid include path");
|
||||
}
|
||||
|
||||
let src_path = get_boringssl_source_path(config);
|
||||
check_include_path(src_path.join("include"))
|
||||
.or_else(|_| check_include_path(src_path.join("src").join("include")))
|
||||
.expect("can't find usable include path")
|
||||
});
|
||||
|
||||
let target_rust_version =
|
||||
bindgen::RustTarget::stable(77, 0).expect("bindgen does not recognize target rust version");
|
||||
|
||||
let mut builder = bindgen::Builder::default()
|
||||
.rust_target(target_rust_version) // bindgen MSRV is 1.70, so this is enough
|
||||
.derive_copy(true)
|
||||
.derive_debug(true)
|
||||
.derive_default(true)
|
||||
.derive_eq(false)
|
||||
.derive_partialeq(false)
|
||||
.default_enum_style(bindgen::EnumVariation::NewType {
|
||||
is_bitfield: false,
|
||||
is_global: false,
|
||||
})
|
||||
.default_macro_constant_type(bindgen::MacroTypeVariation::Signed)
|
||||
.generate_comments(true)
|
||||
.fit_macro_constants(false)
|
||||
.size_t_is_usize(true)
|
||||
.layout_tests(config.env.debug.is_some())
|
||||
.prepend_enum_name(true)
|
||||
.blocklist_type("max_align_t") // Not supported by bindgen on all targets, not used by BoringSSL
|
||||
.clang_args(get_extra_clang_args_for_bindgen(config))
|
||||
.clang_arg("-I")
|
||||
.clang_arg(include_path.display().to_string());
|
||||
|
||||
if let Some(sysroot) = &config.env.sysroot {
|
||||
builder = builder
|
||||
.clang_arg("--sysroot")
|
||||
.clang_arg(sysroot.display().to_string());
|
||||
|
||||
// we need to add special platform header file with env for support cross building
|
||||
let target_include_dir = sysroot.join(format!(
|
||||
"usr/include/{}-{}-{}",
|
||||
config.target_arch, config.target_os, config.target_env
|
||||
));
|
||||
if target_include_dir.is_dir() {
|
||||
builder = builder
|
||||
.clang_arg("-I")
|
||||
.clang_arg(target_include_dir.display().to_string());
|
||||
}
|
||||
}
|
||||
|
||||
let must_have_headers = [
|
||||
"aes.h",
|
||||
"asn1_mac.h",
|
||||
"asn1t.h",
|
||||
"blake2.h",
|
||||
"blowfish.h",
|
||||
"cast.h",
|
||||
"chacha.h",
|
||||
"cmac.h",
|
||||
"cpu.h",
|
||||
"curve25519.h",
|
||||
"des.h",
|
||||
"dtls1.h",
|
||||
"err.h",
|
||||
"hkdf.h",
|
||||
"hpke.h",
|
||||
"ossl_typ.h",
|
||||
"pkcs12.h",
|
||||
"poly1305.h",
|
||||
"x509v3.h",
|
||||
];
|
||||
let headers = [
|
||||
"hmac.h",
|
||||
"hrss.h",
|
||||
"md4.h",
|
||||
"md5.h",
|
||||
"mlkem.h",
|
||||
"obj_mac.h",
|
||||
"objects.h",
|
||||
"opensslv.h",
|
||||
"rand.h",
|
||||
"rc4.h",
|
||||
"ripemd.h",
|
||||
"siphash.h",
|
||||
"srtp.h",
|
||||
"trust_token.h",
|
||||
];
|
||||
for (i, header) in must_have_headers.into_iter().chain(headers).enumerate() {
|
||||
let header_path = include_path.join("openssl").join(header);
|
||||
if header_path.exists() {
|
||||
builder = builder.header(header_path.to_str().unwrap());
|
||||
} else {
|
||||
let err = format!("'openssl/{header}' is missing from '{}'. The include path may be incorrect or contain an outdated version of OpenSSL/BoringSSL", include_path.display());
|
||||
let required = i < must_have_headers.len();
|
||||
println!(
|
||||
"cargo::{}={err}",
|
||||
if required { "error" } else { "warning" }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let bindings = builder.generate().expect("Unable to generate bindings");
|
||||
let mut source_code = Vec::new();
|
||||
bindings
|
||||
.write(Box::new(&mut source_code))
|
||||
.expect("Couldn't serialize bindings!");
|
||||
ensure_err_lib_enum_is_named(&mut source_code);
|
||||
fs::write(config.out_dir.join("bindings.rs"), source_code).expect("Couldn't write bindings!");
|
||||
}
|
||||
|
||||
/// err.h has anonymous `enum { ERR_LIB_NONE = 1 }`, which makes a dodgy `_bindgen_ty_1` name
|
||||
fn ensure_err_lib_enum_is_named(source_code: &mut Vec<u8>) {
|
||||
let src = String::from_utf8_lossy(source_code);
|
||||
let enum_type = src
|
||||
.split_once("ERR_LIB_SSL:")
|
||||
.and_then(|(_, def)| Some(def.split_once('=')?.0))
|
||||
.unwrap_or("_bindgen_ty_1");
|
||||
|
||||
source_code.extend_from_slice(
|
||||
format!("\n/// Newtype for [`ERR_LIB_SSL`] constants\npub use {enum_type} as ErrLib;\n")
|
||||
.as_bytes(),
|
||||
);
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(CMAKE_SYSTEM_PROCESSOR aarch64)
|
||||
# Rely on environment variables to set the compiler and include paths.
|
||||
@ -1,3 +0,0 @@
|
||||
set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(CMAKE_SYSTEM_PROCESSOR armv7)
|
||||
# Rely on environment variables to set the compiler and include paths.
|
||||
@ -1 +1 @@
|
||||
Subproject commit 91a66a59b6c1435120ff83e245d7719411294386
|
||||
Subproject commit f1c75347daa2ea81a941e953f2263e0a4d970c8d
|
||||
1
boring-sys/deps/boringssl-fips
Submodule
1
boring-sys/deps/boringssl-fips
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit ae223d6138807a13006342edfeef32e813246b39
|
||||
@ -1,706 +0,0 @@
|
||||
From cb5689e091f515fc8a42ceaff08d702333e505ed Mon Sep 17 00:00:00 2001
|
||||
From: Anthony Ramine <aramine@cloudflare.com>
|
||||
Date: Wed, 3 Dec 2025 11:10:16 +0100
|
||||
Subject: [PATCH] Add additional post-quantum key agreements
|
||||
|
||||
This patch adds:
|
||||
|
||||
1. Enable X25519MLKEM768 by default.
|
||||
|
||||
2. Supports for P256Kyber768Draft00 under 0xfe32, which we temporarily
|
||||
need for compliance reasons. (Note that this is not the codepoint
|
||||
allocated for that exchange in the IANA table.)
|
||||
Enables by default and in FIPS mode.
|
||||
|
||||
3. Add SSL(_CTX)_use_second_keyshare. By default BoringSSL will send a
|
||||
non post-quantum and a post-quantum keyshare if available. These
|
||||
functions allow one to change the behaviour to only send a single
|
||||
keyshare.
|
||||
---
|
||||
crypto/obj/obj_dat.h | 6 +-
|
||||
crypto/obj/obj_mac.num | 1 +
|
||||
crypto/obj/objects.txt | 1 +
|
||||
include/openssl/nid.h | 3 +
|
||||
include/openssl/ssl.h | 15 ++++
|
||||
ssl/extensions.cc | 26 ++++---
|
||||
ssl/internal.h | 12 ++-
|
||||
ssl/ssl_key_share.cc | 111 +++++++++++++++++++++++++++-
|
||||
ssl/ssl_lib.cc | 16 +++-
|
||||
ssl/ssl_test.cc | 19 ++++-
|
||||
ssl/test/runner/basic_tests.go | 2 +
|
||||
ssl/test/runner/cbc_tests.go | 3 +
|
||||
ssl/test/runner/common.go | 2 +-
|
||||
ssl/test/runner/curve_tests.go | 28 +++----
|
||||
ssl/test/runner/ech_tests.go | 24 +++++-
|
||||
ssl/test/runner/extension_tests.go | 3 +-
|
||||
ssl/test/runner/key_update_tests.go | 6 +-
|
||||
tool/client.cc | 9 +++
|
||||
18 files changed, 245 insertions(+), 42 deletions(-)
|
||||
|
||||
diff --git a/crypto/obj/obj_dat.h b/crypto/obj/obj_dat.h
|
||||
index d8b86dcd2..6dd49ec36 100644
|
||||
--- a/crypto/obj/obj_dat.h
|
||||
+++ b/crypto/obj/obj_dat.h
|
||||
@@ -15,7 +15,7 @@
|
||||
// This file is generated by crypto/obj/objects.go.
|
||||
|
||||
|
||||
-#define NUM_NID 971
|
||||
+#define NUM_NID 972
|
||||
|
||||
static const uint8_t kObjectData[] = {
|
||||
/* NID_rsadsi */
|
||||
@@ -8799,6 +8799,8 @@ static const ASN1_OBJECT kObjects[NUM_NID] = {
|
||||
{"id-ml-dsa-87", "ML-DSA-87", NID_ML_DSA_87, 9, &kObjectData[6223], 0},
|
||||
{"id-alg-ml-kem-768", "ML-KEM-768", NID_ML_KEM_768, 9, &kObjectData[6232],
|
||||
0},
|
||||
+ {"P256Kyber768Draft00", "P256Kyber768Draft00", NID_P256Kyber768Draft00, 0,
|
||||
+ NULL, 0},
|
||||
};
|
||||
|
||||
static const uint16_t kNIDsInShortNameOrder[] = {
|
||||
@@ -8931,6 +8933,7 @@ static const uint16_t kNIDsInShortNameOrder[] = {
|
||||
18 /* OU */,
|
||||
749 /* Oakley-EC2N-3 */,
|
||||
750 /* Oakley-EC2N-4 */,
|
||||
+ 971 /* P256Kyber768Draft00 */,
|
||||
9 /* PBE-MD2-DES */,
|
||||
168 /* PBE-MD2-RC2-64 */,
|
||||
10 /* PBE-MD5-DES */,
|
||||
@@ -9854,6 +9857,7 @@ static const uint16_t kNIDsInLongNameOrder[] = {
|
||||
366 /* OCSP Nonce */,
|
||||
371 /* OCSP Service Locator */,
|
||||
180 /* OCSP Signing */,
|
||||
+ 971 /* P256Kyber768Draft00 */,
|
||||
161 /* PBES2 */,
|
||||
69 /* PBKDF2 */,
|
||||
162 /* PBMAC1 */,
|
||||
diff --git a/crypto/obj/obj_mac.num b/crypto/obj/obj_mac.num
|
||||
index ae863e29d..7231b9a58 100644
|
||||
--- a/crypto/obj/obj_mac.num
|
||||
+++ b/crypto/obj/obj_mac.num
|
||||
@@ -958,3 +958,4 @@ ML_DSA_44 967
|
||||
ML_DSA_65 968
|
||||
ML_DSA_87 969
|
||||
ML_KEM_768 970
|
||||
+P256Kyber768Draft00 971
|
||||
diff --git a/crypto/obj/objects.txt b/crypto/obj/objects.txt
|
||||
index 1e0cb76db..e8b249dfd 100644
|
||||
--- a/crypto/obj/objects.txt
|
||||
+++ b/crypto/obj/objects.txt
|
||||
@@ -1340,6 +1340,7 @@ secg-scheme 14 3 : dhSinglePass-cofactorDH-sha512kdf-scheme
|
||||
|
||||
# NIDs for post quantum hybrid KEMs in TLS (no corresponding OIDs).
|
||||
: X25519Kyber768Draft00
|
||||
+ : P256Kyber768Draft00
|
||||
: X25519MLKEM768
|
||||
|
||||
# See RFC 8410.
|
||||
diff --git a/include/openssl/nid.h b/include/openssl/nid.h
|
||||
index 83a1cf592..7265f15f6 100644
|
||||
--- a/include/openssl/nid.h
|
||||
+++ b/include/openssl/nid.h
|
||||
@@ -5508,6 +5508,9 @@ extern "C" {
|
||||
#define OBJ_ML_KEM_768 2L, 16L, 840L, 1L, 101L, 3L, 4L, 4L, 2L
|
||||
#define OBJ_ENC_ML_KEM_768 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x04, 0x02
|
||||
|
||||
+#define SN_P256Kyber768Draft00 "P256Kyber768Draft00"
|
||||
+#define NID_P256Kyber768Draft00 971
|
||||
+
|
||||
|
||||
#if defined(__cplusplus)
|
||||
} /* extern C */
|
||||
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
|
||||
index ff68ba69e..0730e769a 100644
|
||||
--- a/include/openssl/ssl.h
|
||||
+++ b/include/openssl/ssl.h
|
||||
@@ -2550,6 +2550,7 @@ OPENSSL_EXPORT size_t SSL_CTX_get_num_tickets(const SSL_CTX *ctx);
|
||||
#define SSL_GROUP_X25519_MLKEM768 0x11ec
|
||||
#define SSL_GROUP_X25519_KYBER768_DRAFT00 0x6399
|
||||
#define SSL_GROUP_MLKEM1024 0x0202
|
||||
+#define SSL_GROUP_P256_KYBER768_DRAFT00 0xfe32
|
||||
|
||||
// SSL_CTX_set1_group_ids sets the preferred groups for |ctx| to |group_ids|.
|
||||
// Each element of |group_ids| should be a unique one of the |SSL_GROUP_*|
|
||||
@@ -5964,6 +5965,20 @@ OPENSSL_EXPORT int SSL_CTX_set1_curves_list(SSL_CTX *ctx, const char *curves);
|
||||
// SSL_set1_curves_list calls |SSL_set1_groups_list|.
|
||||
OPENSSL_EXPORT int SSL_set1_curves_list(SSL *ssl, const char *curves);
|
||||
|
||||
+// By default, a client will send both a non post-quantum and a post-quantum
|
||||
+// keyshare if available.
|
||||
+//
|
||||
+// SSL_use_second_keyshare controls this behaviour. If |enabled| is 0, then
|
||||
+// a client using |ssl| will only send one keyshare.
|
||||
+OPENSSL_EXPORT void SSL_use_second_keyshare(SSL *ssl, int enabled);
|
||||
+
|
||||
+// By default, a client will send both a non post-quantum and a post-quantum
|
||||
+// keyshare if available.
|
||||
+//
|
||||
+// SSL_CTX_use_second_keyshare controls this behaviour. If |enabled| is 0, then
|
||||
+// a client using |ctx| will only send one keyshare.
|
||||
+OPENSSL_EXPORT void SSL_CTX_use_second_keyshare(SSL_CTX *ctx, int enabled);
|
||||
+
|
||||
// TLSEXT_nid_unknown is a constant used in OpenSSL for
|
||||
// |SSL_get_negotiated_group| to return an unrecognized group. BoringSSL never
|
||||
// returns this value, but we define this constant for compatibility.
|
||||
diff --git a/ssl/extensions.cc b/ssl/extensions.cc
|
||||
index c5f90688c..e0514fed3 100644
|
||||
--- a/ssl/extensions.cc
|
||||
+++ b/ssl/extensions.cc
|
||||
@@ -101,6 +101,7 @@ static bool tls1_check_duplicate_extensions(const CBS *cbs) {
|
||||
static bool is_post_quantum_group(uint16_t id) {
|
||||
switch (id) {
|
||||
case SSL_GROUP_X25519_KYBER768_DRAFT00:
|
||||
+ case SSL_GROUP_P256_KYBER768_DRAFT00:
|
||||
case SSL_GROUP_X25519_MLKEM768:
|
||||
case SSL_GROUP_MLKEM1024:
|
||||
return true;
|
||||
@@ -2241,18 +2242,21 @@ bool ssl_setup_key_shares(SSL_HANDSHAKE *hs, uint16_t override_group_id) {
|
||||
if (!default_key_shares.TryPushBack(supported_group_list[0])) {
|
||||
return false;
|
||||
}
|
||||
- // We'll try to include one post-quantum and one classical initial key
|
||||
- // share.
|
||||
- for (size_t i = 1; i < supported_group_list.size(); i++) {
|
||||
- if (is_post_quantum_group(default_key_shares[0]) ==
|
||||
- is_post_quantum_group(supported_group_list[i])) {
|
||||
- continue;
|
||||
- }
|
||||
- if (!default_key_shares.TryPushBack(supported_group_list[i])) {
|
||||
- return false;
|
||||
+
|
||||
+ if (!ssl->config->disable_second_keyshare) {
|
||||
+ // We'll try to include one post-quantum and one classical initial key
|
||||
+ // share.
|
||||
+ for (size_t i = 1; i < supported_group_list.size(); i++) {
|
||||
+ if (is_post_quantum_group(default_key_shares[0]) ==
|
||||
+ is_post_quantum_group(supported_group_list[i])) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ if (!default_key_shares.TryPushBack(supported_group_list[i])) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ assert(default_key_shares[1] != default_key_shares[0]);
|
||||
+ break;
|
||||
}
|
||||
- assert(default_key_shares[1] != default_key_shares[0]);
|
||||
- break;
|
||||
}
|
||||
selected_key_shares.emplace(default_key_shares);
|
||||
}
|
||||
diff --git a/ssl/internal.h b/ssl/internal.h
|
||||
index a69505b47..1f5ce51e6 100644
|
||||
--- a/ssl/internal.h
|
||||
+++ b/ssl/internal.h
|
||||
@@ -955,7 +955,7 @@ struct NamedGroup {
|
||||
Span<const NamedGroup> NamedGroups();
|
||||
|
||||
// kNumNamedGroups is the number of supported groups.
|
||||
-constexpr size_t kNumNamedGroups = 7u;
|
||||
+constexpr size_t kNumNamedGroups = 8u;
|
||||
|
||||
// DefaultSupportedGroupIds returns the list of IDs for the default groups that
|
||||
// are supported when the caller hasn't explicitly configured supported groups.
|
||||
@@ -3388,6 +3388,11 @@ struct SSL_CONFIG {
|
||||
// permute_extensions is whether to permute extensions when sending messages.
|
||||
bool permute_extensions : 1;
|
||||
|
||||
+ // As a client by default we will send a non post-quantum share and
|
||||
+ // a post-quantum share if available. If disable_second_keyshare is set,
|
||||
+ // we will only send the most preferred keyshare.
|
||||
+ bool disable_second_keyshare : 1;
|
||||
+
|
||||
// aes_hw_override if set indicates we should override checking for aes
|
||||
// hardware support, and use the value in aes_hw_override_value instead.
|
||||
bool aes_hw_override : 1;
|
||||
@@ -4015,6 +4020,11 @@ struct ssl_ctx_st : public bssl::RefCounted<ssl_ctx_st> {
|
||||
// permute_extensions is whether to permute extensions when sending messages.
|
||||
bool permute_extensions : 1;
|
||||
|
||||
+ // As a client by default we will send a non post-quantum share and
|
||||
+ // a post-quantum share if available. If disable_second_keyshare is set,
|
||||
+ // we will only send the most preferred keyshare.
|
||||
+ bool disable_second_keyshare : 1;
|
||||
+
|
||||
// allow_unknown_alpn_protos is whether the client allows unsolicited ALPN
|
||||
// protocols from the peer.
|
||||
bool allow_unknown_alpn_protos : 1;
|
||||
diff --git a/ssl/ssl_key_share.cc b/ssl/ssl_key_share.cc
|
||||
index d155b5527..4fb08906b 100644
|
||||
--- a/ssl/ssl_key_share.cc
|
||||
+++ b/ssl/ssl_key_share.cc
|
||||
@@ -193,6 +193,109 @@ class X25519KeyShare : public SSLKeyShare {
|
||||
uint8_t private_key_[32];
|
||||
};
|
||||
|
||||
+class P256Kyber768Draft00KeyShare : public SSLKeyShare {
|
||||
+ public:
|
||||
+ P256Kyber768Draft00KeyShare()
|
||||
+ : ecks_(EC_group_p256(), SSL_GROUP_SECP256R1) {}
|
||||
+
|
||||
+ uint16_t GroupID() const override {
|
||||
+ return SSL_GROUP_P256_KYBER768_DRAFT00;
|
||||
+ }
|
||||
+
|
||||
+ bool Generate(CBB *out) override {
|
||||
+ uint8_t kyber_public_key[KYBER_PUBLIC_KEY_BYTES];
|
||||
+ KYBER_generate_key(kyber_public_key, &kyber_private_key_);
|
||||
+
|
||||
+ if(!ecks_.Generate(out) ||
|
||||
+ !CBB_add_bytes(out, kyber_public_key, sizeof(kyber_public_key))) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ bool Encap(CBB *out_ciphertext, Array<uint8_t> *out_secret,
|
||||
+ uint8_t *out_alert, Span<const uint8_t> peer_key) override {
|
||||
+ Array<uint8_t> ec_secret;
|
||||
+
|
||||
+ *out_alert = SSL_AD_INTERNAL_ERROR;
|
||||
+
|
||||
+ if(peer_key.size() != p256_share_size + KYBER_PUBLIC_KEY_BYTES) {
|
||||
+ *out_alert = SSL_AD_ILLEGAL_PARAMETER;
|
||||
+ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ if (!ecks_.Encap(out_ciphertext, &ec_secret, out_alert,
|
||||
+ peer_key.subspan(0, p256_share_size))) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ KYBER_public_key peer_kyber_pub;
|
||||
+ CBS peer_kyber_cbs;
|
||||
+ CBS_init(&peer_kyber_cbs, peer_key.data() + p256_share_size,
|
||||
+ KYBER_PUBLIC_KEY_BYTES);
|
||||
+
|
||||
+ if (!KYBER_parse_public_key(&peer_kyber_pub, &peer_kyber_cbs)) {
|
||||
+ *out_alert = SSL_AD_ILLEGAL_PARAMETER;
|
||||
+ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ uint8_t kyber_ciphertext[KYBER_CIPHERTEXT_BYTES];
|
||||
+ Array<uint8_t> secret;
|
||||
+ if (!secret.InitForOverwrite(p256_secret_size + KYBER_SHARED_SECRET_BYTES)) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ OPENSSL_memcpy(secret.data(), ec_secret.data(), ec_secret.size());
|
||||
+ KYBER_encap(kyber_ciphertext, secret.data() + p256_secret_size,
|
||||
+ &peer_kyber_pub);
|
||||
+
|
||||
+ if(!CBB_add_bytes(out_ciphertext, kyber_ciphertext,
|
||||
+ sizeof(kyber_ciphertext))) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ *out_secret = std::move(secret);
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ bool Decap(Array<uint8_t> *out_secret, uint8_t *out_alert,
|
||||
+ Span<const uint8_t> ciphertext) override {
|
||||
+ *out_alert = SSL_AD_INTERNAL_ERROR;
|
||||
+
|
||||
+ Array<uint8_t> ec_secret;
|
||||
+
|
||||
+ if (ciphertext.size() != p256_share_size + KYBER_CIPHERTEXT_BYTES) {
|
||||
+ *out_alert = SSL_AD_ILLEGAL_PARAMETER;
|
||||
+ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ if (!ecks_.Decap(&ec_secret, out_alert,
|
||||
+ ciphertext.subspan(0, p256_share_size))) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ Array<uint8_t> secret;
|
||||
+ if (!secret.InitForOverwrite(p256_secret_size + KYBER_SHARED_SECRET_BYTES)) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ OPENSSL_memcpy(secret.data(), ec_secret.data(), ec_secret.size());
|
||||
+ KYBER_decap(secret.data() + p256_secret_size,
|
||||
+ ciphertext.data() + p256_share_size, &kyber_private_key_);
|
||||
+ *out_secret = std::move(secret);
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ private:
|
||||
+ ECKeyShare ecks_;
|
||||
+ KYBER_private_key kyber_private_key_;
|
||||
+
|
||||
+ static constexpr size_t p256_share_size = 65;
|
||||
+ static constexpr size_t p256_secret_size = 32;
|
||||
+};
|
||||
+
|
||||
// draft-tls-westerbaan-xyber768d00-03
|
||||
class X25519Kyber768KeyShare : public SSLKeyShare {
|
||||
public:
|
||||
@@ -441,9 +544,11 @@ constexpr NamedGroup kNamedGroups[] = {
|
||||
{NID_secp521r1, SSL_GROUP_SECP521R1, "P-521", "secp521r1"},
|
||||
{NID_X25519, SSL_GROUP_X25519, "X25519", "x25519"},
|
||||
{NID_X25519Kyber768Draft00, SSL_GROUP_X25519_KYBER768_DRAFT00,
|
||||
- "X25519Kyber768Draft00", ""},
|
||||
+ "X25519Kyber768Draft00", "Xyber768D00"},
|
||||
{NID_X25519MLKEM768, SSL_GROUP_X25519_MLKEM768, "X25519MLKEM768", ""},
|
||||
{NID_ML_KEM_1024, SSL_GROUP_MLKEM1024, "MLKEM1024", ""},
|
||||
+ {NID_P256Kyber768Draft00, SSL_GROUP_P256_KYBER768_DRAFT00,
|
||||
+ "P256Kyber768Draft00", "P256Kyber768D00"},
|
||||
};
|
||||
|
||||
static_assert(std::size(kNamedGroups) == kNumNamedGroups,
|
||||
@@ -455,6 +560,8 @@ Span<const NamedGroup> NamedGroups() { return kNamedGroups; }
|
||||
|
||||
Span<const uint16_t> DefaultSupportedGroupIds() {
|
||||
static const uint16_t kDefaultSupportedGroupIds[] = {
|
||||
+ SSL_GROUP_X25519_MLKEM768,
|
||||
+ SSL_GROUP_P256_KYBER768_DRAFT00,
|
||||
SSL_GROUP_X25519,
|
||||
SSL_GROUP_SECP256R1,
|
||||
SSL_GROUP_SECP384R1,
|
||||
@@ -478,6 +585,8 @@ UniquePtr<SSLKeyShare> SSLKeyShare::Create(uint16_t group_id) {
|
||||
return MakeUnique<X25519MLKEM768KeyShare>();
|
||||
case SSL_GROUP_MLKEM1024:
|
||||
return MakeUnique<MLKEM1024KeyShare>();
|
||||
+ case SSL_GROUP_P256_KYBER768_DRAFT00:
|
||||
+ return MakeUnique<P256Kyber768Draft00KeyShare>();
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
diff --git a/ssl/ssl_lib.cc b/ssl/ssl_lib.cc
|
||||
index f64b103fb..fe5bb9bc7 100644
|
||||
--- a/ssl/ssl_lib.cc
|
||||
+++ b/ssl/ssl_lib.cc
|
||||
@@ -397,6 +397,7 @@ ssl_ctx_st::ssl_ctx_st(const SSL_METHOD *ssl_method)
|
||||
channel_id_enabled(false),
|
||||
grease_enabled(false),
|
||||
permute_extensions(false),
|
||||
+ disable_second_keyshare(false),
|
||||
allow_unknown_alpn_protos(false),
|
||||
false_start_allowed_without_alpn(false),
|
||||
handoff(false),
|
||||
@@ -527,6 +528,7 @@ SSL *SSL_new(SSL_CTX *ctx) {
|
||||
ssl->config->retain_only_sha256_of_client_certs =
|
||||
ctx->retain_only_sha256_of_client_certs;
|
||||
ssl->config->permute_extensions = ctx->permute_extensions;
|
||||
+ ssl->config->disable_second_keyshare = ctx->disable_second_keyshare;
|
||||
ssl->config->aes_hw_override = ctx->aes_hw_override;
|
||||
ssl->config->aes_hw_override_value = ctx->aes_hw_override_value;
|
||||
ssl->config->compliance_policy = ctx->compliance_policy;
|
||||
@@ -586,6 +588,7 @@ SSL_CONFIG::SSL_CONFIG(SSL *ssl_arg)
|
||||
jdk11_workaround(false),
|
||||
quic_use_legacy_codepoint(false),
|
||||
permute_extensions(false),
|
||||
+ disable_second_keyshare(false),
|
||||
alps_use_new_codepoint(true) {
|
||||
assert(ssl);
|
||||
}
|
||||
@@ -3331,6 +3334,15 @@ int SSL_set1_curves_list(SSL *ssl, const char *curves) {
|
||||
return SSL_set1_groups_list(ssl, curves);
|
||||
}
|
||||
|
||||
+void SSL_use_second_keyshare(SSL *ssl, int enabled) {
|
||||
+ ssl->config->disable_second_keyshare = !enabled;
|
||||
+}
|
||||
+
|
||||
+void SSL_CTX_use_second_keyshare(SSL_CTX *ctx, int enabled) {
|
||||
+ ctx->disable_second_keyshare = !enabled;
|
||||
+}
|
||||
+
|
||||
+
|
||||
namespace fips202205 {
|
||||
|
||||
// (References are to SP 800-52r2):
|
||||
@@ -3342,7 +3354,9 @@ namespace fips202205 {
|
||||
// Section 3.3.1
|
||||
// "The server shall be configured to only use cipher suites that are
|
||||
// composed entirely of NIST approved algorithms"
|
||||
-static const uint16_t kGroups[] = {SSL_GROUP_SECP256R1, SSL_GROUP_SECP384R1};
|
||||
+static const uint16_t kGroups[] = {
|
||||
+ SSL_GROUP_P256_KYBER768_DRAFT00,
|
||||
+ SSL_GROUP_SECP256R1, SSL_GROUP_SECP384R1};
|
||||
|
||||
static const uint16_t kSigAlgs[] = {
|
||||
SSL_SIGN_RSA_PKCS1_SHA256,
|
||||
diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc
|
||||
index 779a2c37a..36a0cab3b 100644
|
||||
--- a/ssl/ssl_test.cc
|
||||
+++ b/ssl/ssl_test.cc
|
||||
@@ -506,6 +506,14 @@ static const CurveTest kCurveTests[] = {
|
||||
"MLKEM1024:X25519MLKEM768",
|
||||
{SSL_GROUP_MLKEM1024, SSL_GROUP_X25519_MLKEM768},
|
||||
},
|
||||
+ {
|
||||
+ "P256Kyber768Draft00",
|
||||
+ {SSL_GROUP_P256_KYBER768_DRAFT00},
|
||||
+ },
|
||||
+ {
|
||||
+ "P-256:P256Kyber768Draft00",
|
||||
+ {SSL_GROUP_SECP256R1, SSL_GROUP_P256_KYBER768_DRAFT00},
|
||||
+ },
|
||||
|
||||
{
|
||||
"P-256:P-384:P-521:X25519",
|
||||
@@ -668,7 +676,9 @@ TEST(SSLTest, CurveRules) {
|
||||
}
|
||||
|
||||
TEST(SSLTest, DefaultCurves) {
|
||||
- const uint16_t kDefaults[] = {SSL_GROUP_X25519, SSL_GROUP_SECP256R1,
|
||||
+ const uint16_t kDefaults[] = {SSL_GROUP_X25519_MLKEM768,
|
||||
+ SSL_GROUP_P256_KYBER768_DRAFT00,
|
||||
+ SSL_GROUP_X25519, SSL_GROUP_SECP256R1,
|
||||
SSL_GROUP_SECP384R1};
|
||||
|
||||
// Test the group ID APIs.
|
||||
@@ -1522,6 +1532,9 @@ static bool GetClientHello(SSL *ssl, std::vector<uint8_t> *out) {
|
||||
static size_t GetClientHelloLen(uint16_t max_version, uint16_t session_version,
|
||||
size_t ticket_len) {
|
||||
bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
|
||||
+ // RTG-3417 bas: we need to disable PQ here so that the small ClientHello
|
||||
+ // padding tests properly tests things.
|
||||
+ SSL_CTX_set1_curves_list(ctx.get(), "X25519");
|
||||
bssl::UniquePtr<SSL_SESSION> session =
|
||||
CreateSessionWithTicket(session_version, ticket_len);
|
||||
if (!ctx || !session) {
|
||||
@@ -6815,7 +6828,9 @@ TEST(SSLTest, ApplyHandoffRemovesUnsupportedCurves) {
|
||||
|
||||
// The default list of groups is used before applying the handoff.
|
||||
EXPECT_THAT(server->config->supported_group_list,
|
||||
- ElementsAreArray({SSL_GROUP_X25519, SSL_GROUP_SECP256R1,
|
||||
+ ElementsAreArray({SSL_GROUP_X25519_MLKEM768,
|
||||
+ SSL_GROUP_P256_KYBER768_DRAFT00,
|
||||
+ SSL_GROUP_X25519, SSL_GROUP_SECP256R1,
|
||||
SSL_GROUP_SECP384R1}));
|
||||
ASSERT_TRUE(SSL_apply_handoff(server.get(), handoff));
|
||||
EXPECT_EQ(1u, server->config->supported_group_list.size());
|
||||
diff --git a/ssl/test/runner/basic_tests.go b/ssl/test/runner/basic_tests.go
|
||||
index 08de8fa5f..dd945fa49 100644
|
||||
--- a/ssl/test/runner/basic_tests.go
|
||||
+++ b/ssl/test/runner/basic_tests.go
|
||||
@@ -129,6 +129,7 @@ read alert 1 0
|
||||
`write hs 1
|
||||
read hs 3
|
||||
write hs 1
|
||||
+write hs 1
|
||||
read hs 2
|
||||
read hs 11
|
||||
read hs 12
|
||||
@@ -1956,6 +1957,7 @@ read alert 1 0
|
||||
write hs 2
|
||||
write hs 8
|
||||
write hs 11
|
||||
+write hs 11
|
||||
write hs 15
|
||||
write hs 20
|
||||
read hs 20
|
||||
diff --git a/ssl/test/runner/cbc_tests.go b/ssl/test/runner/cbc_tests.go
|
||||
index 6f49d12af..5e970b2b5 100644
|
||||
--- a/ssl/test/runner/cbc_tests.go
|
||||
+++ b/ssl/test/runner/cbc_tests.go
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
package runner
|
||||
|
||||
+import "strconv"
|
||||
+
|
||||
func addCBCPaddingTests() {
|
||||
testCases = append(testCases, testCase{
|
||||
name: "MaxCBCPadding",
|
||||
@@ -104,6 +106,7 @@ func addCBCSplittingTests() {
|
||||
"-partial-write",
|
||||
// BoringSSL disables 3DES by default.
|
||||
"-cipher", "ALL:3DES",
|
||||
+ "-curves", strconv.Itoa(int(CurveX25519)),
|
||||
},
|
||||
})
|
||||
}
|
||||
diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go
|
||||
index 7dbde72c9..9d18d9d45 100644
|
||||
--- a/ssl/test/runner/common.go
|
||||
+++ b/ssl/test/runner/common.go
|
||||
@@ -2095,7 +2095,7 @@ type ProtocolBugs struct {
|
||||
FailIfHelloRetryRequested bool
|
||||
|
||||
// FailIfPostQuantumOffered will cause a server to reject a ClientHello if
|
||||
- // post-quantum curves are supported.
|
||||
+ // post-quantum curves are not supported.
|
||||
FailIfPostQuantumOffered bool
|
||||
|
||||
// ExpectKeyShares, if not nil, lists (in order) the curves that a ClientHello
|
||||
diff --git a/ssl/test/runner/curve_tests.go b/ssl/test/runner/curve_tests.go
|
||||
index 8e7b0a45b..556bf314d 100644
|
||||
--- a/ssl/test/runner/curve_tests.go
|
||||
+++ b/ssl/test/runner/curve_tests.go
|
||||
@@ -579,17 +579,6 @@ func addCurveTests() {
|
||||
})
|
||||
}
|
||||
|
||||
- // ML-KEM and Kyber should not be offered by default as a client.
|
||||
- testCases = append(testCases, testCase{
|
||||
- name: "PostQuantumNotEnabledByDefaultInClients",
|
||||
- config: Config{
|
||||
- MinVersion: VersionTLS13,
|
||||
- Bugs: ProtocolBugs{
|
||||
- FailIfPostQuantumOffered: true,
|
||||
- },
|
||||
- },
|
||||
- })
|
||||
-
|
||||
for _, curve := range testCurves {
|
||||
if !isMLKEMGroup(curve.id) {
|
||||
continue
|
||||
@@ -679,18 +668,19 @@ func addCurveTests() {
|
||||
})
|
||||
}
|
||||
|
||||
- // As a server, ML-KEMs and Kyber are not yet supported by default.
|
||||
+ // If ML-KEM is offered, both X25519 and ML-KEM should have a key-share.
|
||||
testCases = append(testCases, testCase{
|
||||
- testType: serverTest,
|
||||
- name: "PostQuantumNotEnabledByDefaultForAServer",
|
||||
+ name: "NotJustMLKEMKeyShare",
|
||||
config: Config{
|
||||
- MinVersion: VersionTLS13,
|
||||
- CurvePreferences: []CurveID{CurveX25519MLKEM768, CurveMLKEM1024, CurveX25519Kyber768, CurveX25519},
|
||||
- DefaultCurves: []CurveID{CurveX25519MLKEM768, CurveMLKEM1024, CurveX25519Kyber768},
|
||||
+ MinVersion: VersionTLS13,
|
||||
+ Bugs: ProtocolBugs{
|
||||
+ ExpectedKeyShares: []CurveID{CurveX25519MLKEM768, CurveX25519},
|
||||
+ },
|
||||
},
|
||||
flags: []string{
|
||||
- "-server-preference",
|
||||
- "-expect-curve-id", strconv.Itoa(int(CurveX25519)),
|
||||
+ "-curves", strconv.Itoa(int(CurveX25519MLKEM768)),
|
||||
+ "-curves", strconv.Itoa(int(CurveX25519)),
|
||||
+ "-expect-curve-id", strconv.Itoa(int(CurveX25519MLKEM768)),
|
||||
},
|
||||
})
|
||||
|
||||
diff --git a/ssl/test/runner/ech_tests.go b/ssl/test/runner/ech_tests.go
|
||||
index 2cd3c10d3..f19d8d20a 100644
|
||||
--- a/ssl/test/runner/ech_tests.go
|
||||
+++ b/ssl/test/runner/ech_tests.go
|
||||
@@ -451,7 +451,8 @@ func addEncryptedClientHelloTests() {
|
||||
expectMsgCallback += clientAndServerHello
|
||||
}
|
||||
// EncryptedExtensions onwards.
|
||||
- expectMsgCallback += `write hs 8
|
||||
+ if protocol != dtls {
|
||||
+ expectMsgCallback += `write hs 8
|
||||
write hs 11
|
||||
write hs 15
|
||||
write hs 20
|
||||
@@ -462,6 +463,20 @@ write hs 4
|
||||
read ack
|
||||
read ack
|
||||
`
|
||||
+ } else {
|
||||
+ expectMsgCallback += `write hs 8
|
||||
+write hs 11
|
||||
+write hs 11
|
||||
+write hs 15
|
||||
+write hs 20
|
||||
+read hs 20
|
||||
+write ack
|
||||
+write hs 4
|
||||
+write hs 4
|
||||
+read ack
|
||||
+read ack
|
||||
+`
|
||||
+ }
|
||||
if protocol != dtls {
|
||||
expectMsgCallback = strings.ReplaceAll(expectMsgCallback, "write ack\n", "")
|
||||
expectMsgCallback = strings.ReplaceAll(expectMsgCallback, "read ack\n", "")
|
||||
@@ -2349,8 +2364,11 @@ read ack
|
||||
|
||||
// Test the message callback is correctly reported, with and without
|
||||
// HelloRetryRequest.
|
||||
- clientAndServerHello := "write clienthelloinner\nwrite hs 1\nread hs 2\n"
|
||||
- clientAndServerHelloInitial := clientAndServerHello
|
||||
+ clientAndServerHelloInitial := "write clienthelloinner\nwrite hs 1\nwrite hs 1\nread hs 2\n"
|
||||
+ clientAndServerHello := "write clienthelloinner\nwrite hs 1\nread hs 2\n"
|
||||
+ if protocol != dtls {
|
||||
+ clientAndServerHelloInitial = clientAndServerHello
|
||||
+ }
|
||||
if protocol == tls {
|
||||
clientAndServerHelloInitial += "write ccs\n"
|
||||
}
|
||||
diff --git a/ssl/test/runner/extension_tests.go b/ssl/test/runner/extension_tests.go
|
||||
index d6adb7759..4eb80aa8e 100644
|
||||
--- a/ssl/test/runner/extension_tests.go
|
||||
+++ b/ssl/test/runner/extension_tests.go
|
||||
@@ -16,6 +16,7 @@ package runner
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
+ "strconv"
|
||||
)
|
||||
|
||||
func addExtensionTests() {
|
||||
@@ -1967,7 +1968,7 @@ func addExtensionTests() {
|
||||
// This hostname just needs to be long enough to push the
|
||||
// ClientHello into F5's danger zone between 256 and 511 bytes
|
||||
// long.
|
||||
- flags: []string{"-host-name", "01234567890123456789012345678901234567890123456789012345678901234567890123456789.com"},
|
||||
+ flags: []string{"-host-name", "01234567890123456789012345678901234567890123456789012345678901234567890123456789.com", "-curves", strconv.Itoa(int(CurveX25519))},
|
||||
})
|
||||
|
||||
// Test that illegal extensions in TLS 1.3 are rejected by the client if
|
||||
diff --git a/ssl/test/runner/key_update_tests.go b/ssl/test/runner/key_update_tests.go
|
||||
index 0a9053038..5ce709589 100644
|
||||
--- a/ssl/test/runner/key_update_tests.go
|
||||
+++ b/ssl/test/runner/key_update_tests.go
|
||||
@@ -14,7 +14,10 @@
|
||||
|
||||
package runner
|
||||
|
||||
-import "slices"
|
||||
+import (
|
||||
+ "slices"
|
||||
+ "strconv"
|
||||
+)
|
||||
|
||||
func addKeyUpdateTests() {
|
||||
// TLS tests.
|
||||
@@ -295,6 +298,7 @@ func addKeyUpdateTests() {
|
||||
},
|
||||
},
|
||||
shimSendsKeyUpdateBeforeRead: true,
|
||||
+ flags: []string{"-curves", strconv.Itoa(int(CurveX25519))},
|
||||
})
|
||||
|
||||
// Test that shim responds to KeyUpdate requests.
|
||||
diff --git a/tool/client.cc b/tool/client.cc
|
||||
index 0839d4880..be9b79259 100644
|
||||
--- a/tool/client.cc
|
||||
+++ b/tool/client.cc
|
||||
@@ -125,6 +125,11 @@ static const struct argument kArguments[] = {
|
||||
kBooleanArgument,
|
||||
"Permute extensions in handshake messages",
|
||||
},
|
||||
+ {
|
||||
+ "-disable-second-keyshare",
|
||||
+ kBooleanArgument,
|
||||
+ "Do not send a second keyshare",
|
||||
+ },
|
||||
{
|
||||
"-test-resumption", kBooleanArgument,
|
||||
"Connect to the server twice. The first connection is closed once a "
|
||||
@@ -538,6 +543,10 @@ bool Client(const std::vector<std::string> &args) {
|
||||
SSL_CTX_set_permute_extensions(ctx.get(), 1);
|
||||
}
|
||||
|
||||
+ if (args_map.count("-disable-second-keyshare") != 0) {
|
||||
+ SSL_CTX_use_second_keyshare(ctx.get(), 0);
|
||||
+ }
|
||||
+
|
||||
if (args_map.count("-root-certs") != 0) {
|
||||
if (!SSL_CTX_load_verify_locations(
|
||||
ctx.get(), args_map["-root-certs"].c_str(), nullptr)) {
|
||||
--
|
||||
2.40.0
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,79 +0,0 @@
|
||||
From 2128aa4382ba668e2c4f77bf18da719b2ad0087e Mon Sep 17 00:00:00 2001
|
||||
From: Anthony Ramine <aramine@cloudflare.com>
|
||||
Date: Fri, 5 Dec 2025 08:19:56 +0100
|
||||
Subject: [PATCH] Introduce X509_CHECK_FLAG_UNDERSCORE_WILDCARDS
|
||||
|
||||
---
|
||||
crypto/x509/v3_utl.cc | 4 +++-
|
||||
crypto/x509/x509_test.cc | 25 +++++++++++++++++++++++++
|
||||
include/openssl/x509.h | 3 +++
|
||||
3 files changed, 31 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/crypto/x509/v3_utl.cc b/crypto/x509/v3_utl.cc
|
||||
index 015bbcad2..2b9b63430 100644
|
||||
--- a/crypto/x509/v3_utl.cc
|
||||
+++ b/crypto/x509/v3_utl.cc
|
||||
@@ -740,7 +740,9 @@ static int wildcard_match(const unsigned char *prefix, size_t prefix_len,
|
||||
// Check that the part matched by the wildcard contains only
|
||||
// permitted characters and only matches a single label.
|
||||
for (p = wildcard_start; p != wildcard_end; ++p) {
|
||||
- if (!OPENSSL_isalnum(*p) && *p != '-') {
|
||||
+ if (!OPENSSL_isalnum(*p) && *p != '-' &&
|
||||
+ !(*p == '_' &&
|
||||
+ (flags & X509_CHECK_FLAG_UNDERSCORE_WILDCARDS))) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
diff --git a/crypto/x509/x509_test.cc b/crypto/x509/x509_test.cc
|
||||
index c6ce62dd1..f284f421f 100644
|
||||
--- a/crypto/x509/x509_test.cc
|
||||
+++ b/crypto/x509/x509_test.cc
|
||||
@@ -5209,6 +5209,31 @@ TEST(X509Test, Names) {
|
||||
/*invalid_emails=*/{},
|
||||
/*flags=*/0,
|
||||
},
|
||||
+
|
||||
+ // Underscores in DNS names are forbidden by default.
|
||||
+ {
|
||||
+ /*cert_subject=*/{},
|
||||
+ /*cert_dns_names=*/{"*.example.com"},
|
||||
+ /*cert_emails=*/{},
|
||||
+ /*valid_dns_names=*/{},
|
||||
+ /*invalid_dns_names=*/{"not_allowed.example.com"},
|
||||
+ /*valid_emails=*/{},
|
||||
+ /*invalid_emails=*/{},
|
||||
+ /*flags=*/0,
|
||||
+ },
|
||||
+
|
||||
+ // Underscores in DNS names can be allowed with the right flag.
|
||||
+ {
|
||||
+ /*cert_subject=*/{},
|
||||
+ /*cert_dns_names=*/{"*.example.com"},
|
||||
+ /*cert_emails=*/{},
|
||||
+ /*valid_dns_names=*/{"now_allowed.example.com"},
|
||||
+ /*invalid_dns_names=*/{},
|
||||
+ /*valid_emails=*/{},
|
||||
+ /*invalid_emails=*/{},
|
||||
+ /*flags=*/X509_CHECK_FLAG_UNDERSCORE_WILDCARDS,
|
||||
+ },
|
||||
+
|
||||
};
|
||||
|
||||
size_t i = 0;
|
||||
diff --git a/include/openssl/x509.h b/include/openssl/x509.h
|
||||
index 926f365f4..cc538cceb 100644
|
||||
--- a/include/openssl/x509.h
|
||||
+++ b/include/openssl/x509.h
|
||||
@@ -3359,6 +3359,9 @@ OPENSSL_EXPORT int X509_VERIFY_PARAM_add1_host(X509_VERIFY_PARAM *param,
|
||||
// enabled when subjectAltNames is missing.
|
||||
#define X509_CHECK_FLAG_NEVER_CHECK_SUBJECT 0x20
|
||||
|
||||
+// X509_CHECK_FLAG_UNDERSCORE_WILDCARDS allows underscores in DNS wildcard matches.
|
||||
+#define X509_CHECK_FLAG_UNDERSCORE_WILDCARDS 0x40
|
||||
+
|
||||
// X509_VERIFY_PARAM_set_hostflags sets the name-checking flags on |param| to
|
||||
// |flags|. |flags| should be a combination of |X509_CHECK_FLAG_*| constants.
|
||||
OPENSSL_EXPORT void X509_VERIFY_PARAM_set_hostflags(X509_VERIFY_PARAM *param,
|
||||
--
|
||||
2.40.0
|
||||
|
||||
@ -15,26 +15,9 @@ use std::convert::TryInto;
|
||||
use std::ffi::c_void;
|
||||
use std::os::raw::{c_char, c_int, c_uint, c_ulong};
|
||||
|
||||
#[allow(
|
||||
clippy::useless_transmute,
|
||||
clippy::derive_partial_eq_without_eq,
|
||||
clippy::ptr_offset_with_cast,
|
||||
dead_code
|
||||
)]
|
||||
mod generated {
|
||||
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
||||
}
|
||||
|
||||
// explicitly require presence of some symbols to check if the bindings worked
|
||||
pub use generated::{ssl_compliance_policy_t, ERR_add_error_data, SSL_set1_groups}; // if these are missing, your include path is incorrect or has a wrong version of boringssl
|
||||
pub use generated::{BIO_new, OPENSSL_free, SSL_ERROR_NONE}; // if these are missing, your include path is incorrect
|
||||
#[cfg(feature = "fips")]
|
||||
pub use generated::{FIPS_mode, SSL_CTX_set_compliance_policy}; // your include path is incorrect or has a version of boringssl without FIPS support
|
||||
#[cfg(feature = "mlkem")]
|
||||
pub use generated::{MLKEM768_encap, MLKEM768_private_key_from_seed}; // your include path is incorrect or has a version of boringssl without mlkem support
|
||||
#[cfg(feature = "rpk")]
|
||||
pub use generated::{SSL_CREDENTIAL_new_raw_public_key, SSL_CREDENTIAL_set1_spki}; // your include path is incorrect or has a version of boringssl without rpk support
|
||||
|
||||
pub use generated::*;
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
@ -42,28 +25,60 @@ pub type BN_ULONG = u64;
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
pub type BN_ULONG = u32;
|
||||
|
||||
#[must_use]
|
||||
pub const fn ERR_PACK(l: c_int, f: c_int, r: c_int) -> c_ulong {
|
||||
((l as c_ulong & 0x0FF) << 24) | ((f as c_ulong & 0xFFF) << 12) | (r as c_ulong & 0xFFF)
|
||||
#[cfg(const_fn)]
|
||||
macro_rules! const_fn {
|
||||
($(pub const fn $name:ident($($arg:ident: $t:ty),*) -> $ret:ty $b:block)*) => {
|
||||
$(
|
||||
pub const fn $name($($arg: $t),*) -> $ret $b
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn ERR_GET_LIB(l: c_uint) -> c_int {
|
||||
((l >> 24) & 0x0FF) as c_int
|
||||
#[cfg(not(const_fn))]
|
||||
macro_rules! const_fn {
|
||||
($(pub const fn $name:ident($($arg:ident: $t:ty),*) -> $ret:ty $b:block)*) => {
|
||||
$(
|
||||
pub fn $name($($arg: $t),*) -> $ret $b
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn ERR_GET_FUNC(l: c_uint) -> c_int {
|
||||
((l >> 12) & 0xFFF) as c_int
|
||||
}
|
||||
const_fn! {
|
||||
pub const fn ERR_PACK(l: c_int, f: c_int, r: c_int) -> c_ulong {
|
||||
((l as c_ulong & 0x0FF) << 24) |
|
||||
((f as c_ulong & 0xFFF) << 12) |
|
||||
(r as c_ulong & 0xFFF)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn ERR_GET_REASON(l: c_uint) -> c_int {
|
||||
(l & 0xFFF) as c_int
|
||||
pub const fn ERR_GET_LIB(l: c_uint) -> c_int {
|
||||
((l >> 24) & 0x0FF) as c_int
|
||||
}
|
||||
|
||||
pub const fn ERR_GET_FUNC(l: c_uint) -> c_int {
|
||||
((l >> 12) & 0xFFF) as c_int
|
||||
}
|
||||
|
||||
pub const fn ERR_GET_REASON(l: c_uint) -> c_int {
|
||||
(l & 0xFFF) as c_int
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init() {
|
||||
unsafe {
|
||||
CRYPTO_library_init();
|
||||
#[cfg(feature = "ssl")]
|
||||
{
|
||||
use std::ptr;
|
||||
use std::sync::Once;
|
||||
|
||||
// explicitly initialize to work around https://github.com/openssl/openssl/issues/3505
|
||||
static INIT: Once = Once::new();
|
||||
|
||||
let init_options = OPENSSL_INIT_LOAD_SSL_STRINGS;
|
||||
|
||||
INIT.call_once(|| {
|
||||
assert_eq!(
|
||||
unsafe { OPENSSL_init_ssl(init_options.try_into().unwrap(), ptr::null_mut()) },
|
||||
1
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,66 +1,30 @@
|
||||
[package]
|
||||
name = "boring"
|
||||
version = { workspace = true }
|
||||
version = "2.0.0"
|
||||
authors = ["Steven Fackler <sfackler@gmail.com>", "Ivan Nikulin <ifaaan@gmail.com>"]
|
||||
license = "Apache-2.0"
|
||||
description = "BoringSSL bindings"
|
||||
repository = { workspace = true }
|
||||
repository = "https://github.com/cloudflare/boring"
|
||||
documentation = "https://docs.rs/boring"
|
||||
readme = "README.md"
|
||||
keywords = ["tls", "ssl", "dtls", "post-quantum", "fips"]
|
||||
keywords = ["crypto", "tls", "ssl", "dtls"]
|
||||
categories = ["cryptography", "api-bindings"]
|
||||
edition = { workspace = true }
|
||||
rust-version = { workspace = true }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["rpk", "underscore-wildcards"]
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
# Use a FIPS-validated version of BoringSSL.
|
||||
fips = ["boring-sys/fips"]
|
||||
|
||||
# **DO NOT USE** This will be removed without warning in future releases.
|
||||
legacy-compat-deprecated = []
|
||||
|
||||
# **DO NOT USE** This will be removed without warning in future releases.
|
||||
# PQ is always enabled. This feature is a no-op, only for backwards compatibility.
|
||||
pq-experimental = []
|
||||
|
||||
# Interface for ML-KEM (FIPS 203) post-quantum key encapsulation. Does not affect ciphers used in TLS.
|
||||
mlkem = []
|
||||
|
||||
# SslCredential API
|
||||
credential = []
|
||||
|
||||
# Enables Raw public key API (https://datatracker.ietf.org/doc/html/rfc7250)
|
||||
# This feature is necessary in order to compile the bindings for the
|
||||
# default branch of boringSSL. Alternatively, a version of boringSSL that
|
||||
# implements the same feature set can be provided by setting
|
||||
# `BORING_BSSL{,_FIPS}_SOURCE_PATH` and `BORING_BSSL{,_FIPS}_ASSUME_PATCHED`.
|
||||
rpk = ["credential", "boring-sys/rpk"]
|
||||
|
||||
# Applies a patch to enable `ffi::X509_CHECK_FLAG_UNDERSCORE_WILDCARDS`. This
|
||||
# feature is necessary in order to compile the bindings for the default branch
|
||||
# of boringSSL. Alternatively, a version of boringSSL that implements the same
|
||||
# feature set can be provided by setting `BORING_BSSL{,_FIPS}_SOURCE_PATH` and
|
||||
# `BORING_BSSL{,_FIPS}_ASSUME_PATCHED`.
|
||||
underscore-wildcards = ["boring-sys/underscore-wildcards"]
|
||||
|
||||
# **DO NOT USE** This will be removed without warning in future releases.
|
||||
# Alias for 'fips', only for backwards compatibility.
|
||||
fips-precompiled = ["fips"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
bitflags = { workspace = true }
|
||||
foreign-types = { workspace = true }
|
||||
openssl-macros = { workspace = true }
|
||||
libc = { workspace = true }
|
||||
boring-sys = { workspace = true }
|
||||
bitflags = "1.0"
|
||||
foreign-types = "0.5"
|
||||
lazy_static = "1"
|
||||
libc = "0.2"
|
||||
boring-sys = { version = ">=1.1.0,<3.0.0", path = "../boring-sys", default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
hex = { workspace = true }
|
||||
rusty-hook = { workspace = true }
|
||||
brotli = { workspace = true }
|
||||
hex = "0.4"
|
||||
rusty-hook = "^0.11"
|
||||
|
||||
[features]
|
||||
default = ["ssl"]
|
||||
ssl = ["boring-sys/ssl"]
|
||||
|
||||
# Use a FIPS-validated version of boringssl.
|
||||
fips = ["boring-sys/fips"]
|
||||
|
||||
@ -13,7 +13,7 @@ use boring::x509::extension::{
|
||||
AuthorityKeyIdentifier, BasicConstraints, KeyUsage, SubjectAlternativeName,
|
||||
SubjectKeyIdentifier,
|
||||
};
|
||||
use boring::x509::{X509NameBuilder, X509Ref, X509Req, X509ReqBuilder, X509};
|
||||
use boring::x509::{X509NameBuilder, X509Ref, X509Req, X509ReqBuilder, X509VerifyResult, X509};
|
||||
|
||||
/// Make a CA certificate and private key
|
||||
fn mk_ca_cert() -> Result<(X509, PKey<Private>), ErrorStack> {
|
||||
@ -43,19 +43,18 @@ fn mk_ca_cert() -> Result<(X509, PKey<Private>), ErrorStack> {
|
||||
let not_after = Asn1Time::days_from_now(365)?;
|
||||
cert_builder.set_not_after(¬_after)?;
|
||||
|
||||
cert_builder.append_extension(BasicConstraints::new().critical().ca().build()?.as_ref())?;
|
||||
cert_builder.append_extension(BasicConstraints::new().critical().ca().build()?)?;
|
||||
cert_builder.append_extension(
|
||||
KeyUsage::new()
|
||||
.critical()
|
||||
.key_cert_sign()
|
||||
.crl_sign()
|
||||
.build()?
|
||||
.as_ref(),
|
||||
.build()?,
|
||||
)?;
|
||||
|
||||
let subject_key_identifier =
|
||||
SubjectKeyIdentifier::new().build(&cert_builder.x509v3_context(None, None))?;
|
||||
cert_builder.append_extension(&subject_key_identifier)?;
|
||||
cert_builder.append_extension(subject_key_identifier)?;
|
||||
|
||||
cert_builder.sign(&privkey, MessageDigest::sha256())?;
|
||||
let cert = cert_builder.build();
|
||||
@ -82,6 +81,7 @@ fn mk_request(privkey: &PKey<Private>) -> Result<X509Req, ErrorStack> {
|
||||
}
|
||||
|
||||
/// Make a certificate and private key signed by the given CA cert and private key
|
||||
#[cfg_attr(feature = "fips", allow(unreachable_code, unused_variables))]
|
||||
fn mk_ca_signed_cert(
|
||||
ca_cert: &X509Ref,
|
||||
ca_privkey: &PKeyRef<Private>,
|
||||
@ -99,7 +99,15 @@ fn mk_ca_signed_cert(
|
||||
serial.to_asn1_integer()?
|
||||
};
|
||||
cert_builder.set_serial_number(&serial_number)?;
|
||||
|
||||
#[cfg(not(feature = "fips"))]
|
||||
cert_builder.set_subject_name(req.subject_name())?;
|
||||
#[cfg(feature = "fips")]
|
||||
{
|
||||
eprintln!("mk_certs not supported with FIPS module");
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
cert_builder.set_issuer_name(ca_cert.subject_name())?;
|
||||
cert_builder.set_pubkey(&privkey)?;
|
||||
let not_before = Asn1Time::days_from_now(0)?;
|
||||
@ -107,7 +115,7 @@ fn mk_ca_signed_cert(
|
||||
let not_after = Asn1Time::days_from_now(365)?;
|
||||
cert_builder.set_not_after(¬_after)?;
|
||||
|
||||
cert_builder.append_extension(BasicConstraints::new().build()?.as_ref())?;
|
||||
cert_builder.append_extension(BasicConstraints::new().build()?)?;
|
||||
|
||||
cert_builder.append_extension(
|
||||
KeyUsage::new()
|
||||
@ -115,25 +123,24 @@ fn mk_ca_signed_cert(
|
||||
.non_repudiation()
|
||||
.digital_signature()
|
||||
.key_encipherment()
|
||||
.build()?
|
||||
.as_ref(),
|
||||
.build()?,
|
||||
)?;
|
||||
|
||||
let subject_key_identifier =
|
||||
SubjectKeyIdentifier::new().build(&cert_builder.x509v3_context(Some(ca_cert), None))?;
|
||||
cert_builder.append_extension(&subject_key_identifier)?;
|
||||
cert_builder.append_extension(subject_key_identifier)?;
|
||||
|
||||
let auth_key_identifier = AuthorityKeyIdentifier::new()
|
||||
.keyid(false)
|
||||
.issuer(false)
|
||||
.build(&cert_builder.x509v3_context(Some(ca_cert), None))?;
|
||||
cert_builder.append_extension(&auth_key_identifier)?;
|
||||
cert_builder.append_extension(auth_key_identifier)?;
|
||||
|
||||
let subject_alt_name = SubjectAlternativeName::new()
|
||||
.dns("*.example.com")
|
||||
.dns("hello.com")
|
||||
.build(&cert_builder.x509v3_context(Some(ca_cert), None))?;
|
||||
cert_builder.append_extension(&subject_alt_name)?;
|
||||
cert_builder.append_extension(subject_alt_name)?;
|
||||
|
||||
cert_builder.sign(ca_privkey, MessageDigest::sha256())?;
|
||||
let cert = cert_builder.build();
|
||||
@ -147,9 +154,9 @@ fn real_main() -> Result<(), ErrorStack> {
|
||||
|
||||
// Verify that this cert was issued by this ca
|
||||
match ca_cert.issued(&cert) {
|
||||
Ok(()) => println!("Certificate verified!"),
|
||||
Err(ver_err) => println!("Failed to verify certificate: {ver_err}"),
|
||||
}
|
||||
X509VerifyResult::OK => println!("Certificate verified!"),
|
||||
ver_err => println!("Failed to verify certificate: {}", ver_err),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -157,6 +164,6 @@ fn real_main() -> Result<(), ErrorStack> {
|
||||
fn main() {
|
||||
match real_main() {
|
||||
Ok(()) => println!("Finished."),
|
||||
Err(e) => println!("Error: {e}"),
|
||||
}
|
||||
Err(e) => println!("Error: {}", e),
|
||||
};
|
||||
}
|
||||
|
||||
@ -38,10 +38,8 @@
|
||||
//! ```
|
||||
//!
|
||||
use crate::ffi;
|
||||
use libc::c_int;
|
||||
use openssl_macros::corresponds;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::ptr;
|
||||
use libc::{c_int, c_uint, size_t};
|
||||
use std::{mem, ptr};
|
||||
|
||||
/// Provides Error handling for parsing keys.
|
||||
#[derive(Debug)]
|
||||
@ -56,19 +54,19 @@ impl AesKey {
|
||||
/// # Failure
|
||||
///
|
||||
/// Returns an error if the key is not 128, 192, or 256 bits.
|
||||
#[corresponds(AES_set_encrypt_key)]
|
||||
#[allow(deprecated)] // https://github.com/rust-lang/rust/issues/63566
|
||||
pub fn new_encrypt(key: &[u8]) -> Result<AesKey, KeyError> {
|
||||
unsafe {
|
||||
assert!(key.len() <= c_int::MAX as usize / 8);
|
||||
assert!(key.len() <= c_int::max_value() as usize / 8);
|
||||
|
||||
let mut aes_key = MaybeUninit::uninit();
|
||||
let mut aes_key = mem::uninitialized();
|
||||
let r = ffi::AES_set_encrypt_key(
|
||||
key.as_ptr(),
|
||||
(key.len() * 8).try_into().map_err(|_| KeyError(()))?,
|
||||
aes_key.as_mut_ptr(),
|
||||
key.as_ptr() as *const _,
|
||||
key.len() as c_uint * 8,
|
||||
&mut aes_key,
|
||||
);
|
||||
if r == 0 {
|
||||
Ok(AesKey(aes_key.assume_init()))
|
||||
Ok(AesKey(aes_key))
|
||||
} else {
|
||||
Err(KeyError(()))
|
||||
}
|
||||
@ -80,20 +78,20 @@ impl AesKey {
|
||||
/// # Failure
|
||||
///
|
||||
/// Returns an error if the key is not 128, 192, or 256 bits.
|
||||
#[corresponds(AES_set_decrypt_key)]
|
||||
#[allow(deprecated)] // https://github.com/rust-lang/rust/issues/63566
|
||||
pub fn new_decrypt(key: &[u8]) -> Result<AesKey, KeyError> {
|
||||
unsafe {
|
||||
assert!(key.len() <= c_int::MAX as usize / 8);
|
||||
assert!(key.len() <= c_int::max_value() as usize / 8);
|
||||
|
||||
let mut aes_key = MaybeUninit::uninit();
|
||||
let mut aes_key = mem::uninitialized();
|
||||
let r = ffi::AES_set_decrypt_key(
|
||||
key.as_ptr(),
|
||||
(key.len() * 8).try_into().map_err(|_| KeyError(()))?,
|
||||
aes_key.as_mut_ptr(),
|
||||
key.as_ptr() as *const _,
|
||||
key.len() as c_uint * 8,
|
||||
&mut aes_key,
|
||||
);
|
||||
|
||||
if r == 0 {
|
||||
Ok(AesKey(aes_key.assume_init()))
|
||||
Ok(AesKey(aes_key))
|
||||
} else {
|
||||
Err(KeyError(()))
|
||||
}
|
||||
@ -114,7 +112,6 @@ impl AesKey {
|
||||
///
|
||||
/// Panics if either `out` or `in_` do not have sizes that are a multiple of 8, or if
|
||||
/// `out` is not 8 bytes longer than `in_`
|
||||
#[corresponds(AES_wrap_key)]
|
||||
pub fn wrap_key(
|
||||
key: &AesKey,
|
||||
iv: Option<[u8; 8]>,
|
||||
@ -125,11 +122,12 @@ pub fn wrap_key(
|
||||
assert!(out.len() >= in_.len() + 8); // Ciphertext is 64 bits longer (see 2.2.1)
|
||||
|
||||
let written = ffi::AES_wrap_key(
|
||||
std::ptr::addr_of!(key.0).cast_mut(), // this is safe, the implementation only uses the key as a const pointer.
|
||||
iv.as_ref().map_or(ptr::null(), |iv| iv.as_ptr()),
|
||||
out.as_mut_ptr(),
|
||||
in_.as_ptr(),
|
||||
in_.len(),
|
||||
&key.0 as *const _ as *mut _, // this is safe, the implementation only uses the key as a const pointer.
|
||||
iv.as_ref()
|
||||
.map_or(ptr::null(), |iv| iv.as_ptr() as *const _),
|
||||
out.as_ptr() as *mut _,
|
||||
in_.as_ptr() as *const _,
|
||||
in_.len() as size_t,
|
||||
);
|
||||
if written <= 0 {
|
||||
Err(KeyError(()))
|
||||
@ -152,7 +150,6 @@ pub fn wrap_key(
|
||||
///
|
||||
/// Panics if either `out` or `in_` do not have sizes that are a multiple of 8, or
|
||||
/// if `in_` is not 8 bytes longer than `out`
|
||||
#[corresponds(AES_unwrap_key)]
|
||||
pub fn unwrap_key(
|
||||
key: &AesKey,
|
||||
iv: Option<[u8; 8]>,
|
||||
@ -163,11 +160,12 @@ pub fn unwrap_key(
|
||||
assert!(out.len() + 8 <= in_.len());
|
||||
|
||||
let written = ffi::AES_unwrap_key(
|
||||
std::ptr::addr_of!(key.0).cast_mut(), // this is safe, the implementation only uses the key as a const pointer.
|
||||
iv.as_ref().map_or(ptr::null(), |iv| iv.as_ptr().cast()),
|
||||
out.as_ptr().cast_mut(),
|
||||
in_.as_ptr().cast(),
|
||||
in_.len(),
|
||||
&key.0 as *const _ as *mut _, // this is safe, the implementation only uses the key as a const pointer.
|
||||
iv.as_ref()
|
||||
.map_or(ptr::null(), |iv| iv.as_ptr() as *const _),
|
||||
out.as_ptr() as *mut _,
|
||||
in_.as_ptr() as *const _,
|
||||
in_.len() as size_t,
|
||||
);
|
||||
|
||||
if written <= 0 {
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
//! ```
|
||||
use crate::ffi;
|
||||
use foreign_types::{ForeignType, ForeignTypeRef};
|
||||
use libc::{c_int, c_long, time_t};
|
||||
use libc::{c_char, c_int, c_long, time_t};
|
||||
use std::cmp::Ordering;
|
||||
use std::ffi::CString;
|
||||
use std::fmt;
|
||||
@ -38,10 +38,8 @@ use crate::bio::MemBio;
|
||||
use crate::bn::{BigNum, BigNumRef};
|
||||
use crate::error::ErrorStack;
|
||||
use crate::nid::Nid;
|
||||
use crate::stack::Stackable;
|
||||
use crate::string::OpensslString;
|
||||
use crate::{cvt, cvt_p};
|
||||
use openssl_macros::corresponds;
|
||||
|
||||
foreign_type_and_impl_send_sync! {
|
||||
type CType = ffi::ASN1_GENERALIZEDTIME;
|
||||
@ -63,94 +61,20 @@ foreign_type_and_impl_send_sync! {
|
||||
|
||||
impl fmt::Display for Asn1GeneralizedTimeRef {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let bio = MemBio::new().ok();
|
||||
let msg = bio
|
||||
.as_ref()
|
||||
.and_then(|mem_bio| unsafe {
|
||||
cvt(ffi::ASN1_GENERALIZEDTIME_print(
|
||||
mem_bio.as_ptr(),
|
||||
self.as_ptr(),
|
||||
))
|
||||
.ok()?;
|
||||
str::from_utf8(mem_bio.get_buf()).ok()
|
||||
})
|
||||
.unwrap_or("error");
|
||||
f.write_str(msg)
|
||||
}
|
||||
}
|
||||
|
||||
/// The type of an ASN.1 value.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct Asn1Type(c_int);
|
||||
|
||||
#[allow(missing_docs)] // no need to document the constants
|
||||
impl Asn1Type {
|
||||
pub const EOC: Asn1Type = Asn1Type(ffi::V_ASN1_EOC);
|
||||
|
||||
pub const BOOLEAN: Asn1Type = Asn1Type(ffi::V_ASN1_BOOLEAN);
|
||||
|
||||
pub const INTEGER: Asn1Type = Asn1Type(ffi::V_ASN1_INTEGER);
|
||||
|
||||
pub const BIT_STRING: Asn1Type = Asn1Type(ffi::V_ASN1_BIT_STRING);
|
||||
|
||||
pub const OCTET_STRING: Asn1Type = Asn1Type(ffi::V_ASN1_OCTET_STRING);
|
||||
|
||||
pub const NULL: Asn1Type = Asn1Type(ffi::V_ASN1_NULL);
|
||||
|
||||
pub const OBJECT: Asn1Type = Asn1Type(ffi::V_ASN1_OBJECT);
|
||||
|
||||
pub const OBJECT_DESCRIPTOR: Asn1Type = Asn1Type(ffi::V_ASN1_OBJECT_DESCRIPTOR);
|
||||
|
||||
pub const EXTERNAL: Asn1Type = Asn1Type(ffi::V_ASN1_EXTERNAL);
|
||||
|
||||
pub const REAL: Asn1Type = Asn1Type(ffi::V_ASN1_REAL);
|
||||
|
||||
pub const ENUMERATED: Asn1Type = Asn1Type(ffi::V_ASN1_ENUMERATED);
|
||||
|
||||
pub const UTF8STRING: Asn1Type = Asn1Type(ffi::V_ASN1_UTF8STRING);
|
||||
|
||||
pub const SEQUENCE: Asn1Type = Asn1Type(ffi::V_ASN1_SEQUENCE);
|
||||
|
||||
pub const SET: Asn1Type = Asn1Type(ffi::V_ASN1_SET);
|
||||
|
||||
pub const NUMERICSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_NUMERICSTRING);
|
||||
|
||||
pub const PRINTABLESTRING: Asn1Type = Asn1Type(ffi::V_ASN1_PRINTABLESTRING);
|
||||
|
||||
pub const T61STRING: Asn1Type = Asn1Type(ffi::V_ASN1_T61STRING);
|
||||
|
||||
pub const TELETEXSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_TELETEXSTRING);
|
||||
|
||||
pub const VIDEOTEXSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_VIDEOTEXSTRING);
|
||||
|
||||
pub const IA5STRING: Asn1Type = Asn1Type(ffi::V_ASN1_IA5STRING);
|
||||
|
||||
pub const UTCTIME: Asn1Type = Asn1Type(ffi::V_ASN1_UTCTIME);
|
||||
|
||||
pub const GENERALIZEDTIME: Asn1Type = Asn1Type(ffi::V_ASN1_GENERALIZEDTIME);
|
||||
|
||||
pub const GRAPHICSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_GRAPHICSTRING);
|
||||
|
||||
pub const ISO64STRING: Asn1Type = Asn1Type(ffi::V_ASN1_ISO64STRING);
|
||||
|
||||
pub const VISIBLESTRING: Asn1Type = Asn1Type(ffi::V_ASN1_VISIBLESTRING);
|
||||
|
||||
pub const GENERALSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_GENERALSTRING);
|
||||
|
||||
pub const UNIVERSALSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_UNIVERSALSTRING);
|
||||
|
||||
pub const BMPSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_BMPSTRING);
|
||||
|
||||
/// Constructs an `Asn1Type` from a raw OpenSSL value.
|
||||
#[must_use]
|
||||
pub fn from_raw(value: c_int) -> Self {
|
||||
Asn1Type(value)
|
||||
}
|
||||
|
||||
/// Returns the raw OpenSSL value represented by this type.
|
||||
#[must_use]
|
||||
pub fn as_raw(&self) -> c_int {
|
||||
self.0
|
||||
unsafe {
|
||||
let mem_bio = match MemBio::new() {
|
||||
Err(_) => return f.write_str("error"),
|
||||
Ok(m) => m,
|
||||
};
|
||||
let print_result = cvt(ffi::ASN1_GENERALIZEDTIME_print(
|
||||
mem_bio.as_ptr(),
|
||||
self.as_ptr(),
|
||||
));
|
||||
match print_result {
|
||||
Err(_) => f.write_str("error"),
|
||||
Ok(_) => f.write_str(str::from_utf8_unchecked(mem_bio.get_buf())),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -189,7 +113,10 @@ foreign_type_and_impl_send_sync! {
|
||||
|
||||
impl Asn1TimeRef {
|
||||
/// Find difference between two times
|
||||
#[corresponds(ASN1_TIME_diff)]
|
||||
///
|
||||
/// This corresponds to [`ASN1_TIME_diff`].
|
||||
///
|
||||
/// [`ASN1_TIME_diff`]: https://www.openssl.org/docs/man1.1.0/crypto/ASN1_TIME_diff.html
|
||||
pub fn diff(&self, compare: &Self) -> Result<TimeDiff, ErrorStack> {
|
||||
let mut days = 0;
|
||||
let mut secs = 0;
|
||||
@ -204,7 +131,12 @@ impl Asn1TimeRef {
|
||||
}
|
||||
|
||||
/// Compare two times
|
||||
#[corresponds(ASN1_TIME_compare)]
|
||||
///
|
||||
/// This corresponds to [`ASN1_TIME_compare`] but is implemented using [`diff`] so that it is
|
||||
/// also supported on older versions of OpenSSL.
|
||||
///
|
||||
/// [`ASN1_TIME_compare`]: https://www.openssl.org/docs/man1.1.1/man3/ASN1_TIME_compare.html
|
||||
/// [`diff`]: struct.Asn1TimeRef.html#method.diff
|
||||
pub fn compare(&self, other: &Self) -> Result<Ordering, ErrorStack> {
|
||||
let d = self.diff(other)?;
|
||||
if d.days > 0 || d.secs > 0 {
|
||||
@ -234,7 +166,7 @@ impl PartialEq<Asn1Time> for Asn1TimeRef {
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<Asn1Time> for &Asn1TimeRef {
|
||||
impl<'a> PartialEq<Asn1Time> for &'a Asn1TimeRef {
|
||||
fn eq(&self, other: &Asn1Time) -> bool {
|
||||
self.diff(other)
|
||||
.map(|t| t.days == 0 && t.secs == 0)
|
||||
@ -254,7 +186,7 @@ impl PartialOrd<Asn1Time> for Asn1TimeRef {
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<Asn1Time> for &Asn1TimeRef {
|
||||
impl<'a> PartialOrd<Asn1Time> for &'a Asn1TimeRef {
|
||||
fn partial_cmp(&self, other: &Asn1Time) -> Option<Ordering> {
|
||||
self.compare(other).ok()
|
||||
}
|
||||
@ -283,7 +215,6 @@ impl fmt::Debug for Asn1TimeRef {
|
||||
}
|
||||
|
||||
impl Asn1Time {
|
||||
#[corresponds(ASN1_TIME_new)]
|
||||
fn new() -> Result<Asn1Time, ErrorStack> {
|
||||
ffi::init();
|
||||
|
||||
@ -293,7 +224,6 @@ impl Asn1Time {
|
||||
}
|
||||
}
|
||||
|
||||
#[corresponds(X509_gmtime_adj)]
|
||||
fn from_period(period: c_long) -> Result<Asn1Time, ErrorStack> {
|
||||
ffi::init();
|
||||
|
||||
@ -305,30 +235,28 @@ impl Asn1Time {
|
||||
|
||||
/// Creates a new time on specified interval in days from now
|
||||
pub fn days_from_now(days: u32) -> Result<Asn1Time, ErrorStack> {
|
||||
// the type varies between platforms, so both into() and try_into() trigger Clippy lints
|
||||
Self::from_period((days * 60 * 60 * 24) as _)
|
||||
Asn1Time::from_period(days as c_long * 60 * 60 * 24)
|
||||
}
|
||||
|
||||
/// Creates a new time from the specified `time_t` value
|
||||
#[corresponds(ASN1_TIME_set)]
|
||||
pub fn from_unix(time: time_t) -> Result<Asn1Time, ErrorStack> {
|
||||
ffi::init();
|
||||
|
||||
unsafe {
|
||||
// for higher musl version, need to convert i32 to i64
|
||||
// https://github.com/rust-lang/libc/issues/1848
|
||||
#[allow(clippy::useless_conversion)]
|
||||
let handle = cvt_p(ffi::ASN1_TIME_set(ptr::null_mut(), time.into()))?;
|
||||
let handle = cvt_p(ffi::ASN1_TIME_set(ptr::null_mut(), time))?;
|
||||
Ok(Asn1Time::from_ptr(handle))
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new time corresponding to the specified ASN1 time string.
|
||||
#[corresponds(ASN1_TIME_set_string)]
|
||||
///
|
||||
/// This corresponds to [`ASN1_TIME_set_string`].
|
||||
///
|
||||
/// [`ASN1_TIME_set_string`]: https://www.openssl.org/docs/manmaster/man3/ASN1_TIME_set_string.html
|
||||
#[allow(clippy::should_implement_trait)]
|
||||
pub fn from_str(s: &str) -> Result<Asn1Time, ErrorStack> {
|
||||
unsafe {
|
||||
let s = CString::new(s).map_err(ErrorStack::internal_error)?;
|
||||
let s = CString::new(s).unwrap();
|
||||
|
||||
let time = Asn1Time::new()?;
|
||||
cvt(ffi::ASN1_TIME_set_string(time.as_ptr(), s.as_ptr()))?;
|
||||
@ -399,7 +327,6 @@ impl Asn1StringRef {
|
||||
/// ASN.1 strings may utilize UTF-16, ASCII, BMP, or UTF8. This is important to
|
||||
/// consume the string in a meaningful way without knowing the underlying
|
||||
/// format.
|
||||
#[corresponds(ASN1_STRING_to_UTF8)]
|
||||
pub fn as_utf8(&self) -> Result<OpensslString, ErrorStack> {
|
||||
unsafe {
|
||||
let mut ptr = ptr::null_mut();
|
||||
@ -408,7 +335,7 @@ impl Asn1StringRef {
|
||||
return Err(ErrorStack::get());
|
||||
}
|
||||
|
||||
Ok(OpensslString::from_ptr(ptr.cast()))
|
||||
Ok(OpensslString::from_ptr(ptr as *mut c_char))
|
||||
}
|
||||
}
|
||||
|
||||
@ -418,21 +345,16 @@ impl Asn1StringRef {
|
||||
/// strings in rust, it is preferable to use [`as_utf8`]
|
||||
///
|
||||
/// [`as_utf8`]: struct.Asn1String.html#method.as_utf8
|
||||
#[corresponds(ASN1_STRING_get0_data)]
|
||||
#[must_use]
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
unsafe { slice::from_raw_parts(ASN1_STRING_get0_data(self.as_ptr()), self.len()) }
|
||||
}
|
||||
|
||||
/// Returns the number of bytes in the string.
|
||||
#[corresponds(ASN1_STRING_length)]
|
||||
#[must_use]
|
||||
pub fn len(&self) -> usize {
|
||||
unsafe { ffi::ASN1_STRING_length(self.as_ptr()) as usize }
|
||||
}
|
||||
|
||||
/// Determines if the string is empty.
|
||||
#[must_use]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
@ -477,16 +399,17 @@ impl Asn1Integer {
|
||||
}
|
||||
|
||||
impl Asn1IntegerRef {
|
||||
#[allow(clippy::unnecessary_cast)]
|
||||
#[allow(missing_docs)]
|
||||
#[deprecated(since = "0.10.6", note = "use to_bn instead")]
|
||||
#[must_use]
|
||||
pub fn get(&self) -> i64 {
|
||||
unsafe { crate::ffi::ASN1_INTEGER_get(self.as_ptr()) as i64 }
|
||||
}
|
||||
|
||||
/// Converts the integer to a `BigNum`.
|
||||
#[corresponds(ASN1_INTEGER_to_BN)]
|
||||
///
|
||||
/// This corresponds to [`ASN1_INTEGER_to_BN`].
|
||||
///
|
||||
/// [`ASN1_INTEGER_to_BN`]: https://www.openssl.org/docs/man1.1.0/crypto/ASN1_INTEGER_get.html
|
||||
pub fn to_bn(&self) -> Result<BigNum, ErrorStack> {
|
||||
unsafe {
|
||||
cvt_p(crate::ffi::ASN1_INTEGER_to_BN(
|
||||
@ -500,15 +423,12 @@ impl Asn1IntegerRef {
|
||||
/// Sets the ASN.1 value to the value of a signed 32-bit integer, for larger numbers
|
||||
/// see [`bn`].
|
||||
///
|
||||
/// OpenSSL documentation at [`ASN1_INTEGER_set`]
|
||||
///
|
||||
/// [`bn`]: ../bn/struct.BigNumRef.html#method.to_asn1_integer
|
||||
#[corresponds(ASN1_INTEGER_set)]
|
||||
/// [`ASN1_INTEGER_set`]: https://www.openssl.org/docs/man1.1.0/crypto/ASN1_INTEGER_set.html
|
||||
pub fn set(&mut self, value: i32) -> Result<(), ErrorStack> {
|
||||
unsafe {
|
||||
cvt(crate::ffi::ASN1_INTEGER_set(
|
||||
self.as_ptr(),
|
||||
c_long::from(value),
|
||||
))
|
||||
}
|
||||
unsafe { cvt(crate::ffi::ASN1_INTEGER_set(self.as_ptr(), value as c_long)).map(|_| ()) }
|
||||
}
|
||||
}
|
||||
|
||||
@ -526,34 +446,16 @@ foreign_type_and_impl_send_sync! {
|
||||
|
||||
impl Asn1BitStringRef {
|
||||
/// Returns the Asn1BitString as a slice.
|
||||
#[corresponds(ASN1_STRING_get0_data)]
|
||||
#[must_use]
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
unsafe {
|
||||
let ptr = ASN1_STRING_get0_data(self.as_ptr().cast());
|
||||
if ptr.is_null() {
|
||||
return &[];
|
||||
}
|
||||
slice::from_raw_parts(ptr, self.len())
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the Asn1BitString as a str, if possible.
|
||||
#[corresponds(ASN1_STRING_get0_data)]
|
||||
#[must_use]
|
||||
pub fn to_str(&self) -> Option<&str> {
|
||||
str::from_utf8(self.as_slice()).ok()
|
||||
unsafe { slice::from_raw_parts(ASN1_STRING_get0_data(self.as_ptr() as *mut _), self.len()) }
|
||||
}
|
||||
|
||||
/// Returns the number of bytes in the string.
|
||||
#[corresponds(ASN1_STRING_length)]
|
||||
#[must_use]
|
||||
pub fn len(&self) -> usize {
|
||||
unsafe { ffi::ASN1_STRING_length(self.as_ptr().cast_const()) as usize }
|
||||
unsafe { ffi::ASN1_STRING_length(self.as_ptr() as *const _) as usize }
|
||||
}
|
||||
|
||||
/// Determines if the string is empty.
|
||||
#[must_use]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
@ -579,19 +481,19 @@ foreign_type_and_impl_send_sync! {
|
||||
pub struct Asn1Object;
|
||||
}
|
||||
|
||||
impl Stackable for Asn1Object {
|
||||
type StackType = ffi::stack_st_ASN1_OBJECT;
|
||||
}
|
||||
|
||||
impl Asn1Object {
|
||||
/// Constructs an ASN.1 Object Identifier from a string representation of the OID.
|
||||
#[corresponds(OBJ_txt2obj)]
|
||||
/// Constructs an ASN.1 Object Identifier from a string representation of
|
||||
/// the OID.
|
||||
///
|
||||
/// This corresponds to [`OBJ_txt2obj`].
|
||||
///
|
||||
/// [`OBJ_txt2obj`]: https://www.openssl.org/docs/man1.1.0/man3/OBJ_txt2obj.html
|
||||
#[allow(clippy::should_implement_trait)]
|
||||
pub fn from_str(txt: &str) -> Result<Asn1Object, ErrorStack> {
|
||||
unsafe {
|
||||
ffi::init();
|
||||
let txt = CString::new(txt).map_err(ErrorStack::internal_error)?;
|
||||
let obj: *mut ffi::ASN1_OBJECT = cvt_p(ffi::OBJ_txt2obj(txt.as_ptr(), 0))?;
|
||||
let txt = CString::new(txt).unwrap();
|
||||
let obj: *mut ffi::ASN1_OBJECT = cvt_p(ffi::OBJ_txt2obj(txt.as_ptr() as *const _, 0))?;
|
||||
Ok(Asn1Object::from_ptr(obj))
|
||||
}
|
||||
}
|
||||
@ -599,38 +501,26 @@ impl Asn1Object {
|
||||
|
||||
impl Asn1ObjectRef {
|
||||
/// Returns the NID associated with this OID.
|
||||
#[must_use]
|
||||
pub fn nid(&self) -> Nid {
|
||||
unsafe { Nid::from_raw(ffi::OBJ_obj2nid(self.as_ptr())) }
|
||||
}
|
||||
|
||||
/// Returns the numerical string OID of this object.
|
||||
///
|
||||
/// This corresponds to [`OBJ_obj2txt`] with `no_name = 1`.
|
||||
///
|
||||
/// [`OBJ_obj2txt`]: https://www.openssl.org/docs/man1.1.1/man3/OBJ_obj2txt.html
|
||||
pub fn oid_string(&self) -> String {
|
||||
self.to_text(true)
|
||||
}
|
||||
|
||||
// To promote this to `pub`, the call-site parameter meaning ought to be clearer
|
||||
fn to_text(&self, no_name: bool) -> String {
|
||||
unsafe {
|
||||
let mut buf = [0u8; 80];
|
||||
let len = ffi::OBJ_obj2txt(
|
||||
buf.as_mut_ptr().cast(),
|
||||
buf.len() as c_int,
|
||||
self.as_ptr(),
|
||||
no_name as c_int,
|
||||
);
|
||||
String::from_utf8_lossy(&buf[..len as usize]).into_owned()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Asn1ObjectRef {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.write_str(&self.to_text(false))
|
||||
unsafe {
|
||||
let mut buf = [0; 80];
|
||||
let len = ffi::OBJ_obj2txt(
|
||||
buf.as_mut_ptr() as *mut _,
|
||||
buf.len() as c_int,
|
||||
self.as_ptr(),
|
||||
0,
|
||||
);
|
||||
match str::from_utf8(&buf[..len as usize]) {
|
||||
Err(_) => fmt.write_str("error"),
|
||||
Ok(s) => fmt.write_str(s),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -733,13 +623,4 @@ mod tests {
|
||||
.map(|object| object.to_string())
|
||||
.expect_err("parsing invalid OID should fail");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn object_to_text() {
|
||||
let oid = "2.16.840.1.101.3.4.2.1";
|
||||
let object = Asn1Object::from_str(oid).unwrap();
|
||||
assert_eq!(object.to_text(false), Nid::SHA256.long_name().unwrap());
|
||||
assert_eq!(object.to_text(true), oid.to_string());
|
||||
assert_eq!(object.oid_string(), oid.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,21 +3,22 @@ use crate::cvt_n;
|
||||
use crate::error::ErrorStack;
|
||||
use crate::ffi;
|
||||
use libc::c_int;
|
||||
use openssl_macros::corresponds;
|
||||
|
||||
/// Encodes a slice of bytes to a base64 string.
|
||||
///
|
||||
/// This corresponds to [`EVP_EncodeBlock`].
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the input length or computed output length overflow a signed C integer.
|
||||
#[corresponds(EVP_EncodeBlock)]
|
||||
#[must_use]
|
||||
///
|
||||
/// [`EVP_EncodeBlock`]: https://www.openssl.org/docs/man1.1.1/man3/EVP_DecodeBlock.html
|
||||
pub fn encode_block(src: &[u8]) -> String {
|
||||
assert!(src.len() <= c_int::MAX as usize);
|
||||
assert!(src.len() <= c_int::max_value() as usize);
|
||||
let src_len = src.len();
|
||||
|
||||
let len = encoded_len(src_len).unwrap();
|
||||
let mut out = Vec::with_capacity(len);
|
||||
let mut out = Vec::with_capacity(len as usize);
|
||||
|
||||
// SAFETY: `encoded_len` ensures space for 4 output characters
|
||||
// for every 3 input bytes including padding and nul terminator.
|
||||
@ -25,17 +26,20 @@ pub fn encode_block(src: &[u8]) -> String {
|
||||
// `EVP_EncodeBlock` will only write to not read from `out`.
|
||||
unsafe {
|
||||
let out_len = ffi::EVP_EncodeBlock(out.as_mut_ptr(), src.as_ptr(), src_len);
|
||||
out.set_len(out_len);
|
||||
out.set_len(out_len as usize);
|
||||
String::from_utf8_unchecked(out)
|
||||
}
|
||||
}
|
||||
|
||||
/// Decodes a base64-encoded string to bytes.
|
||||
///
|
||||
/// This corresponds to [`EVP_DecodeBlock`].
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the input length or computed output length overflow a signed C integer.
|
||||
#[corresponds(EVP_DecodeBlock)]
|
||||
///
|
||||
/// [`EVP_DecodeBlock`]: https://www.openssl.org/docs/man1.1.1/man3/EVP_DecodeBlock.html
|
||||
pub fn decode_block(src: &str) -> Result<Vec<u8>, ErrorStack> {
|
||||
let src = src.trim();
|
||||
|
||||
@ -44,11 +48,11 @@ pub fn decode_block(src: &str) -> Result<Vec<u8>, ErrorStack> {
|
||||
return Ok(vec![]);
|
||||
}
|
||||
|
||||
assert!(src.len() <= c_int::MAX as usize);
|
||||
assert!(src.len() <= c_int::max_value() as usize);
|
||||
let src_len = src.len();
|
||||
|
||||
let len = decoded_len(src_len).unwrap();
|
||||
let mut out = Vec::with_capacity(len);
|
||||
let mut out = Vec::with_capacity(len as usize);
|
||||
|
||||
// SAFETY: `decoded_len` ensures space for 3 output bytes
|
||||
// for every 4 input characters including padding.
|
||||
@ -102,7 +106,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_encode_block() {
|
||||
assert_eq!(String::new(), encode_block(b""));
|
||||
assert_eq!("".to_string(), encode_block(b""));
|
||||
assert_eq!("Zg==".to_string(), encode_block(b"f"));
|
||||
assert_eq!("Zm8=".to_string(), encode_block(b"fo"));
|
||||
assert_eq!("Zm9v".to_string(), encode_block(b"foo"));
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
use crate::ffi;
|
||||
use crate::ffi::BIO_new_mem_buf;
|
||||
use libc::c_int;
|
||||
use std::marker::PhantomData;
|
||||
use std::ptr;
|
||||
use std::slice;
|
||||
|
||||
use crate::cvt_p;
|
||||
use crate::error::ErrorStack;
|
||||
use crate::ffi;
|
||||
use crate::ffi::BIO_new_mem_buf;
|
||||
use crate::try_int;
|
||||
|
||||
pub struct MemBioSlice<'a>(*mut ffi::BIO, PhantomData<&'a [u8]>);
|
||||
|
||||
impl Drop for MemBioSlice<'_> {
|
||||
impl<'a> Drop for MemBioSlice<'a> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
ffi::BIO_free_all(self.0);
|
||||
@ -22,7 +22,13 @@ impl<'a> MemBioSlice<'a> {
|
||||
pub fn new(buf: &'a [u8]) -> Result<MemBioSlice<'a>, ErrorStack> {
|
||||
ffi::init();
|
||||
|
||||
let bio = unsafe { cvt_p(BIO_new_mem_buf(buf.as_ptr().cast(), try_int(buf.len())?))? };
|
||||
assert!(buf.len() <= c_int::max_value() as usize);
|
||||
let bio = unsafe {
|
||||
cvt_p(BIO_new_mem_buf(
|
||||
buf.as_ptr() as *const _,
|
||||
buf.len() as c_int,
|
||||
))?
|
||||
};
|
||||
|
||||
Ok(MemBioSlice(bio, PhantomData))
|
||||
}
|
||||
@ -58,10 +64,7 @@ impl MemBio {
|
||||
unsafe {
|
||||
let mut ptr = ptr::null_mut();
|
||||
let len = ffi::BIO_get_mem_data(self.0, &mut ptr);
|
||||
if ptr.is_null() || len < 0 {
|
||||
return &[];
|
||||
}
|
||||
slice::from_raw_parts(ptr.cast_const().cast(), len as usize)
|
||||
slice::from_raw_parts(ptr as *const _ as *const _, len as usize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
340
boring/src/bn.rs
340
boring/src/bn.rs
@ -24,7 +24,7 @@
|
||||
//! [`BIGNUM`]: https://wiki.openssl.org/index.php/Manual:Bn_internal(3)
|
||||
use crate::ffi;
|
||||
use foreign_types::{ForeignType, ForeignTypeRef};
|
||||
use libc::c_int;
|
||||
use libc::{c_int, size_t};
|
||||
use std::cmp::Ordering;
|
||||
use std::ffi::CString;
|
||||
use std::ops::{Add, Deref, Div, Mul, Neg, Rem, Shl, Shr, Sub};
|
||||
@ -35,7 +35,6 @@ use crate::error::ErrorStack;
|
||||
use crate::ffi::BN_is_negative;
|
||||
use crate::string::OpensslString;
|
||||
use crate::{cvt, cvt_n, cvt_p};
|
||||
use openssl_macros::corresponds;
|
||||
|
||||
/// Options for the most significant bits of a randomly generated `BigNum`.
|
||||
pub struct MsbOption(c_int);
|
||||
@ -70,7 +69,10 @@ foreign_type_and_impl_send_sync! {
|
||||
|
||||
impl BigNumContext {
|
||||
/// Returns a new `BigNumContext`.
|
||||
#[corresponds(BN_CTX_new)]
|
||||
///
|
||||
/// See OpenSSL documentation at [`BN_CTX_new`].
|
||||
///
|
||||
/// [`BN_CTX_new`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_CTX_new.html
|
||||
pub fn new() -> Result<BigNumContext, ErrorStack> {
|
||||
unsafe {
|
||||
ffi::init();
|
||||
@ -89,7 +91,7 @@ foreign_type_and_impl_send_sync! {
|
||||
/// with [`new`]. Perform standard mathematics on large numbers using
|
||||
/// methods from [`Dref<Target = BigNumRef>`]
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_new`].
|
||||
/// OpenSSL documenation at [`BN_new`].
|
||||
///
|
||||
/// [`new`]: struct.BigNum.html#method.new
|
||||
/// [`Dref<Target = BigNumRef>`]: struct.BigNum.html#deref-methods
|
||||
@ -113,36 +115,51 @@ impl BigNumRef {
|
||||
/// Erases the memory used by this `BigNum`, resetting its value to 0.
|
||||
///
|
||||
/// This can be used to destroy sensitive data such as keys when they are no longer needed.
|
||||
#[corresponds(BN_clear)]
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_clear`]
|
||||
///
|
||||
/// [`BN_clear`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_clear.html
|
||||
pub fn clear(&mut self) {
|
||||
unsafe { ffi::BN_clear(self.as_ptr()) }
|
||||
}
|
||||
|
||||
/// Adds a `u32` to `self`.
|
||||
#[corresponds(BN_add_word)]
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_add_word`]
|
||||
///
|
||||
/// [`BN_add_word`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_add_word.html
|
||||
pub fn add_word(&mut self, w: u32) -> Result<(), ErrorStack> {
|
||||
unsafe { cvt(ffi::BN_add_word(self.as_ptr(), ffi::BN_ULONG::from(w))) }
|
||||
unsafe { cvt(ffi::BN_add_word(self.as_ptr(), w as ffi::BN_ULONG)).map(|_| ()) }
|
||||
}
|
||||
|
||||
/// Subtracts a `u32` from `self`.
|
||||
#[corresponds(BN_sub_word)]
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_sub_word`]
|
||||
///
|
||||
/// [`BN_sub_word`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_sub_word.html
|
||||
pub fn sub_word(&mut self, w: u32) -> Result<(), ErrorStack> {
|
||||
unsafe { cvt(ffi::BN_sub_word(self.as_ptr(), ffi::BN_ULONG::from(w))) }
|
||||
unsafe { cvt(ffi::BN_sub_word(self.as_ptr(), w as ffi::BN_ULONG)).map(|_| ()) }
|
||||
}
|
||||
|
||||
/// Multiplies a `u32` by `self`.
|
||||
#[corresponds(BN_mul_word)]
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_mul_word`]
|
||||
///
|
||||
/// [`BN_mul_word`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_mul_word.html
|
||||
pub fn mul_word(&mut self, w: u32) -> Result<(), ErrorStack> {
|
||||
unsafe { cvt(ffi::BN_mul_word(self.as_ptr(), ffi::BN_ULONG::from(w))) }
|
||||
unsafe { cvt(ffi::BN_mul_word(self.as_ptr(), w as ffi::BN_ULONG)).map(|_| ()) }
|
||||
}
|
||||
|
||||
/// Divides `self` by a `u32`, returning the remainder.
|
||||
#[corresponds(BN_div_word)]
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_div_word`]
|
||||
///
|
||||
/// [`BN_div_word`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_div_word.html
|
||||
#[allow(clippy::useless_conversion)]
|
||||
pub fn div_word(&mut self, w: u32) -> Result<u64, ErrorStack> {
|
||||
unsafe {
|
||||
let r = ffi::BN_div_word(self.as_ptr(), w.into());
|
||||
if r == ffi::BN_ULONG::MAX {
|
||||
if r == ffi::BN_ULONG::max_value() {
|
||||
Err(ErrorStack::get())
|
||||
} else {
|
||||
Ok(r.into())
|
||||
@ -151,12 +168,15 @@ impl BigNumRef {
|
||||
}
|
||||
|
||||
/// Returns the result of `self` modulo `w`.
|
||||
#[corresponds(BN_mod_word)]
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_mod_word`]
|
||||
///
|
||||
/// [`BN_mod_word`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_mod_word.html
|
||||
#[allow(clippy::useless_conversion)]
|
||||
pub fn mod_word(&self, w: u32) -> Result<u64, ErrorStack> {
|
||||
unsafe {
|
||||
let r = ffi::BN_mod_word(self.as_ptr(), w.into());
|
||||
if r == ffi::BN_ULONG::MAX {
|
||||
if r == ffi::BN_ULONG::max_value() {
|
||||
Err(ErrorStack::get())
|
||||
} else {
|
||||
Ok(r.into())
|
||||
@ -166,39 +186,53 @@ impl BigNumRef {
|
||||
|
||||
/// Places a cryptographically-secure pseudo-random nonnegative
|
||||
/// number less than `self` in `rnd`.
|
||||
#[corresponds(BN_rand_range)]
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_rand_range`]
|
||||
///
|
||||
/// [`BN_rand_range`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_rand_range.html
|
||||
pub fn rand_range(&self, rnd: &mut BigNumRef) -> Result<(), ErrorStack> {
|
||||
unsafe { cvt(ffi::BN_rand_range(rnd.as_ptr(), self.as_ptr())) }
|
||||
unsafe { cvt(ffi::BN_rand_range(rnd.as_ptr(), self.as_ptr())).map(|_| ()) }
|
||||
}
|
||||
|
||||
/// The cryptographically weak counterpart to `rand_in_range`.
|
||||
#[corresponds(BN_pseudo_rand_range)]
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_pseudo_rand_range`]
|
||||
///
|
||||
/// [`BN_pseudo_rand_range`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_pseudo_rand_range.html
|
||||
pub fn pseudo_rand_range(&self, rnd: &mut BigNumRef) -> Result<(), ErrorStack> {
|
||||
unsafe { cvt(ffi::BN_pseudo_rand_range(rnd.as_ptr(), self.as_ptr())) }
|
||||
unsafe { cvt(ffi::BN_pseudo_rand_range(rnd.as_ptr(), self.as_ptr())).map(|_| ()) }
|
||||
}
|
||||
|
||||
/// Sets bit `n`. Equivalent to `self |= (1 << n)`.
|
||||
///
|
||||
/// When setting a bit outside of `self`, it is expanded.
|
||||
#[corresponds(BN_set_bit)]
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_set_bit`]
|
||||
///
|
||||
/// [`BN_set_bit`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_set_bit.html
|
||||
#[allow(clippy::useless_conversion)]
|
||||
pub fn set_bit(&mut self, n: i32) -> Result<(), ErrorStack> {
|
||||
unsafe { cvt(ffi::BN_set_bit(self.as_ptr(), n.into())) }
|
||||
unsafe { cvt(ffi::BN_set_bit(self.as_ptr(), n.into())).map(|_| ()) }
|
||||
}
|
||||
|
||||
/// Clears bit `n`, setting it to 0. Equivalent to `self &= ~(1 << n)`.
|
||||
///
|
||||
/// When clearing a bit outside of `self`, an error is returned.
|
||||
#[corresponds(BN_clear_bit)]
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_clear_bit`]
|
||||
///
|
||||
/// [`BN_clear_bit`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_clear_bit.html
|
||||
#[allow(clippy::useless_conversion)]
|
||||
pub fn clear_bit(&mut self, n: i32) -> Result<(), ErrorStack> {
|
||||
unsafe { cvt(ffi::BN_clear_bit(self.as_ptr(), n.into())) }
|
||||
unsafe { cvt(ffi::BN_clear_bit(self.as_ptr(), n.into())).map(|_| ()) }
|
||||
}
|
||||
|
||||
/// Returns `true` if the `n`th bit of `self` is set to 1, `false` otherwise.
|
||||
#[corresponds(BN_is_bit_set)]
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_is_bit_set`]
|
||||
///
|
||||
/// [`BN_is_bit_set`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_is_bit_set.html
|
||||
#[allow(clippy::useless_conversion)]
|
||||
#[must_use]
|
||||
pub fn is_bit_set(&self, n: i32) -> bool {
|
||||
unsafe { ffi::BN_is_bit_set(self.as_ptr(), n.into()) == 1 }
|
||||
}
|
||||
@ -206,69 +240,94 @@ impl BigNumRef {
|
||||
/// Truncates `self` to the lowest `n` bits.
|
||||
///
|
||||
/// An error occurs if `self` is already shorter than `n` bits.
|
||||
#[corresponds(BN_mask_bits)]
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_mask_bits`]
|
||||
///
|
||||
/// [`BN_mask_bits`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_mask_bits.html
|
||||
#[allow(clippy::useless_conversion)]
|
||||
pub fn mask_bits(&mut self, n: i32) -> Result<(), ErrorStack> {
|
||||
unsafe { cvt(ffi::BN_mask_bits(self.as_ptr(), n.into())) }
|
||||
unsafe { cvt(ffi::BN_mask_bits(self.as_ptr(), n.into())).map(|_| ()) }
|
||||
}
|
||||
|
||||
/// Places `a << 1` in `self`. Equivalent to `self * 2`.
|
||||
#[corresponds(BN_lshift1)]
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_lshift1`]
|
||||
///
|
||||
/// [`BN_lshift1`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_lshift1.html
|
||||
pub fn lshift1(&mut self, a: &BigNumRef) -> Result<(), ErrorStack> {
|
||||
unsafe { cvt(ffi::BN_lshift1(self.as_ptr(), a.as_ptr())) }
|
||||
unsafe { cvt(ffi::BN_lshift1(self.as_ptr(), a.as_ptr())).map(|_| ()) }
|
||||
}
|
||||
|
||||
/// Places `a >> 1` in `self`. Equivalent to `self / 2`.
|
||||
#[corresponds(BN_rshift1)]
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_rshift1`]
|
||||
///
|
||||
/// [`BN_rshift1`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_rshift1.html
|
||||
pub fn rshift1(&mut self, a: &BigNumRef) -> Result<(), ErrorStack> {
|
||||
unsafe { cvt(ffi::BN_rshift1(self.as_ptr(), a.as_ptr())) }
|
||||
unsafe { cvt(ffi::BN_rshift1(self.as_ptr(), a.as_ptr())).map(|_| ()) }
|
||||
}
|
||||
|
||||
/// Places `a + b` in `self`. [`core::ops::Add`] is also implemented for `BigNumRef`.
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_add`]
|
||||
///
|
||||
/// [`core::ops::Add`]: struct.BigNumRef.html#method.add
|
||||
#[corresponds(BN_add)]
|
||||
/// [`BN_add`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_add.html
|
||||
pub fn checked_add(&mut self, a: &BigNumRef, b: &BigNumRef) -> Result<(), ErrorStack> {
|
||||
unsafe { cvt(ffi::BN_add(self.as_ptr(), a.as_ptr(), b.as_ptr())) }
|
||||
unsafe { cvt(ffi::BN_add(self.as_ptr(), a.as_ptr(), b.as_ptr())).map(|_| ()) }
|
||||
}
|
||||
|
||||
/// Places `a - b` in `self`. [`core::ops::Sub`] is also implemented for `BigNumRef`.
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_sub`]
|
||||
///
|
||||
/// [`core::ops::Sub`]: struct.BigNumRef.html#method.sub
|
||||
#[corresponds(BN_sub)]
|
||||
/// [`BN_sub`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_sub.html
|
||||
pub fn checked_sub(&mut self, a: &BigNumRef, b: &BigNumRef) -> Result<(), ErrorStack> {
|
||||
unsafe { cvt(ffi::BN_sub(self.as_ptr(), a.as_ptr(), b.as_ptr())) }
|
||||
unsafe { cvt(ffi::BN_sub(self.as_ptr(), a.as_ptr(), b.as_ptr())).map(|_| ()) }
|
||||
}
|
||||
|
||||
/// Places `a << n` in `self`. Equivalent to `a * 2 ^ n`.
|
||||
#[corresponds(BN_lshift)]
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_lshift`]
|
||||
///
|
||||
/// [`BN_lshift`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_lshift.html
|
||||
#[allow(clippy::useless_conversion)]
|
||||
pub fn lshift(&mut self, a: &BigNumRef, n: i32) -> Result<(), ErrorStack> {
|
||||
unsafe { cvt(ffi::BN_lshift(self.as_ptr(), a.as_ptr(), n.into())) }
|
||||
unsafe { cvt(ffi::BN_lshift(self.as_ptr(), a.as_ptr(), n.into())).map(|_| ()) }
|
||||
}
|
||||
|
||||
/// Places `a >> n` in `self`. Equivalent to `a / 2 ^ n`.
|
||||
#[corresponds(BN_rshift)]
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_rshift`]
|
||||
///
|
||||
/// [`BN_rshift`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_rshift.html
|
||||
#[allow(clippy::useless_conversion)]
|
||||
pub fn rshift(&mut self, a: &BigNumRef, n: i32) -> Result<(), ErrorStack> {
|
||||
unsafe { cvt(ffi::BN_rshift(self.as_ptr(), a.as_ptr(), n.into())) }
|
||||
unsafe { cvt(ffi::BN_rshift(self.as_ptr(), a.as_ptr(), n.into())).map(|_| ()) }
|
||||
}
|
||||
|
||||
/// Creates a new BigNum with the same value.
|
||||
#[corresponds(BN_dup)]
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_dup`]
|
||||
///
|
||||
/// [`BN_dup`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_dup.html
|
||||
pub fn to_owned(&self) -> Result<BigNum, ErrorStack> {
|
||||
unsafe { cvt_p(ffi::BN_dup(self.as_ptr())).map(|b| BigNum::from_ptr(b)) }
|
||||
}
|
||||
|
||||
/// Sets the sign of `self`. Pass true to set `self` to a negative. False sets
|
||||
/// `self` positive.
|
||||
#[corresponds(BN_set_negative)]
|
||||
pub fn set_negative(&mut self, negative: bool) {
|
||||
unsafe { ffi::BN_set_negative(self.as_ptr(), c_int::from(negative)) }
|
||||
unsafe { ffi::BN_set_negative(self.as_ptr(), negative as c_int) }
|
||||
}
|
||||
|
||||
/// Compare the absolute values of `self` and `oth`.
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_ucmp`]
|
||||
///
|
||||
/// [`BN_ucmp`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_ucmp.html
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -279,28 +338,25 @@ impl BigNumRef {
|
||||
///
|
||||
/// assert_eq!(s.ucmp(&o), Ordering::Equal);
|
||||
/// ```
|
||||
#[corresponds(BN_ucmp)]
|
||||
#[must_use]
|
||||
pub fn ucmp(&self, oth: &BigNumRef) -> Ordering {
|
||||
unsafe { ffi::BN_ucmp(self.as_ptr(), oth.as_ptr()).cmp(&0) }
|
||||
}
|
||||
|
||||
/// Returns `true` if `self` is negative.
|
||||
#[corresponds(BN_is_negative)]
|
||||
#[must_use]
|
||||
pub fn is_negative(&self) -> bool {
|
||||
unsafe { BN_is_negative(self.as_ptr()) == 1 }
|
||||
}
|
||||
|
||||
/// Returns the number of significant bits in `self`.
|
||||
#[corresponds(BN_num_bits)]
|
||||
#[must_use]
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_num_bits`]
|
||||
///
|
||||
/// [`BN_num_bits`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_num_bits.html
|
||||
pub fn num_bits(&self) -> i32 {
|
||||
unsafe { ffi::BN_num_bits(self.as_ptr()) as i32 }
|
||||
}
|
||||
|
||||
/// Returns the size of `self` in bytes. Implemented natively.
|
||||
#[must_use]
|
||||
pub fn num_bytes(&self) -> i32 {
|
||||
(self.num_bits() + 7) / 8
|
||||
}
|
||||
@ -328,8 +384,10 @@ impl BigNumRef {
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_rand`]
|
||||
///
|
||||
/// [`constants`]: index.html#constants
|
||||
#[corresponds(BN_rand)]
|
||||
/// [`BN_rand`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_rand.html
|
||||
#[allow(clippy::useless_conversion)]
|
||||
pub fn rand(&mut self, bits: i32, msb: MsbOption, odd: bool) -> Result<(), ErrorStack> {
|
||||
unsafe {
|
||||
@ -337,13 +395,17 @@ impl BigNumRef {
|
||||
self.as_ptr(),
|
||||
bits.into(),
|
||||
msb.0,
|
||||
c_int::from(odd),
|
||||
odd as c_int,
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// The cryptographically weak counterpart to `rand`. Not suitable for key generation.
|
||||
#[corresponds(BN_pseudo_rand)]
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_psuedo_rand`]
|
||||
///
|
||||
/// [`BN_psuedo_rand`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_pseudo_rand.html
|
||||
#[allow(clippy::useless_conversion)]
|
||||
pub fn pseudo_rand(&mut self, bits: i32, msb: MsbOption, odd: bool) -> Result<(), ErrorStack> {
|
||||
unsafe {
|
||||
@ -351,8 +413,9 @@ impl BigNumRef {
|
||||
self.as_ptr(),
|
||||
bits.into(),
|
||||
msb.0,
|
||||
c_int::from(odd),
|
||||
odd as c_int,
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
@ -379,7 +442,10 @@ impl BigNumRef {
|
||||
/// Ok((big))
|
||||
/// }
|
||||
/// ```
|
||||
#[corresponds(BN_generate_prime_ex)]
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_generate_prime_ex`]
|
||||
///
|
||||
/// [`BN_generate_prime_ex`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_generate_prime_ex.html
|
||||
pub fn generate_prime(
|
||||
&mut self,
|
||||
bits: i32,
|
||||
@ -390,20 +456,23 @@ impl BigNumRef {
|
||||
unsafe {
|
||||
cvt(ffi::BN_generate_prime_ex(
|
||||
self.as_ptr(),
|
||||
c_int::from(bits),
|
||||
c_int::from(safe),
|
||||
bits as c_int,
|
||||
safe as c_int,
|
||||
add.map(|n| n.as_ptr()).unwrap_or(ptr::null_mut()),
|
||||
rem.map(|n| n.as_ptr()).unwrap_or(ptr::null_mut()),
|
||||
ptr::null_mut(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Places the result of `a * b` in `self`.
|
||||
/// [`core::ops::Mul`] is also implemented for `BigNumRef`.
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_mul`]
|
||||
///
|
||||
/// [`core::ops::Mul`]: struct.BigNumRef.html#method.mul
|
||||
#[corresponds(BN_mul)]
|
||||
/// [`BN_mul`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_mul.html
|
||||
pub fn checked_mul(
|
||||
&mut self,
|
||||
a: &BigNumRef,
|
||||
@ -417,14 +486,17 @@ impl BigNumRef {
|
||||
b.as_ptr(),
|
||||
ctx.as_ptr(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Places the result of `a / b` in `self`. The remainder is discarded.
|
||||
/// [`core::ops::Div`] is also implemented for `BigNumRef`.
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_div`]
|
||||
///
|
||||
/// [`core::ops::Div`]: struct.BigNumRef.html#method.div
|
||||
#[corresponds(BN_div)]
|
||||
/// [`BN_div`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_div.html
|
||||
pub fn checked_div(
|
||||
&mut self,
|
||||
a: &BigNumRef,
|
||||
@ -439,11 +511,15 @@ impl BigNumRef {
|
||||
b.as_ptr(),
|
||||
ctx.as_ptr(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Places the result of `a % b` in `self`.
|
||||
#[corresponds(BN_div)]
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_div`]
|
||||
///
|
||||
/// [`BN_div`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_div.html
|
||||
pub fn checked_rem(
|
||||
&mut self,
|
||||
a: &BigNumRef,
|
||||
@ -458,11 +534,15 @@ impl BigNumRef {
|
||||
b.as_ptr(),
|
||||
ctx.as_ptr(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Places the result of `a / b` in `self` and `a % b` in `rem`.
|
||||
#[corresponds(BN_div)]
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_div`]
|
||||
///
|
||||
/// [`BN_div`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_div.html
|
||||
pub fn div_rem(
|
||||
&mut self,
|
||||
rem: &mut BigNumRef,
|
||||
@ -478,18 +558,25 @@ impl BigNumRef {
|
||||
b.as_ptr(),
|
||||
ctx.as_ptr(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Places the result of `a²` in `self`.
|
||||
#[corresponds(BN_sqr)]
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_sqr`]
|
||||
///
|
||||
/// [`BN_sqr`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_sqr.html
|
||||
pub fn sqr(&mut self, a: &BigNumRef, ctx: &mut BigNumContextRef) -> Result<(), ErrorStack> {
|
||||
unsafe { cvt(ffi::BN_sqr(self.as_ptr(), a.as_ptr(), ctx.as_ptr())) }
|
||||
unsafe { cvt(ffi::BN_sqr(self.as_ptr(), a.as_ptr(), ctx.as_ptr())).map(|_| ()) }
|
||||
}
|
||||
|
||||
/// Places the result of `a mod m` in `self`. As opposed to `div_rem`
|
||||
/// the result is non-negative.
|
||||
#[corresponds(BN_nnmod)]
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_nnmod`]
|
||||
///
|
||||
/// [`BN_nnmod`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_nnmod.html
|
||||
pub fn nnmod(
|
||||
&mut self,
|
||||
a: &BigNumRef,
|
||||
@ -503,11 +590,15 @@ impl BigNumRef {
|
||||
m.as_ptr(),
|
||||
ctx.as_ptr(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Places the result of `(a + b) mod m` in `self`.
|
||||
#[corresponds(BN_mod_add)]
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_mod_add`]
|
||||
///
|
||||
/// [`BN_mod_add`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_mod_add.html
|
||||
pub fn mod_add(
|
||||
&mut self,
|
||||
a: &BigNumRef,
|
||||
@ -523,11 +614,15 @@ impl BigNumRef {
|
||||
m.as_ptr(),
|
||||
ctx.as_ptr(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Places the result of `(a - b) mod m` in `self`.
|
||||
#[corresponds(BN_mod_sub)]
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_mod_sub`]
|
||||
///
|
||||
/// [`BN_mod_sub`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_mod_sub.html
|
||||
pub fn mod_sub(
|
||||
&mut self,
|
||||
a: &BigNumRef,
|
||||
@ -543,11 +638,15 @@ impl BigNumRef {
|
||||
m.as_ptr(),
|
||||
ctx.as_ptr(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Places the result of `(a * b) mod m` in `self`.
|
||||
#[corresponds(BN_mod_mul)]
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_mod_mul`]
|
||||
///
|
||||
/// [`BN_mod_mul`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_mod_mul.html
|
||||
pub fn mod_mul(
|
||||
&mut self,
|
||||
a: &BigNumRef,
|
||||
@ -563,11 +662,15 @@ impl BigNumRef {
|
||||
m.as_ptr(),
|
||||
ctx.as_ptr(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Places the result of `a² mod m` in `self`.
|
||||
#[corresponds(BN_mod_sqr)]
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_mod_sqr`]
|
||||
///
|
||||
/// [`BN_mod_sqr`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_mod_sqr.html
|
||||
pub fn mod_sqr(
|
||||
&mut self,
|
||||
a: &BigNumRef,
|
||||
@ -581,11 +684,15 @@ impl BigNumRef {
|
||||
m.as_ptr(),
|
||||
ctx.as_ptr(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Places the result of `a^p` in `self`.
|
||||
#[corresponds(BN_exp)]
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_exp`]
|
||||
///
|
||||
/// [`BN_exp`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_exp.html
|
||||
pub fn exp(
|
||||
&mut self,
|
||||
a: &BigNumRef,
|
||||
@ -599,11 +706,15 @@ impl BigNumRef {
|
||||
p.as_ptr(),
|
||||
ctx.as_ptr(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Places the result of `a^p mod m` in `self`.
|
||||
#[corresponds(BN_mod_exp)]
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_mod_exp`]
|
||||
///
|
||||
/// [`BN_mod_exp`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_mod_exp.html
|
||||
pub fn mod_exp(
|
||||
&mut self,
|
||||
a: &BigNumRef,
|
||||
@ -619,11 +730,11 @@ impl BigNumRef {
|
||||
m.as_ptr(),
|
||||
ctx.as_ptr(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Places the inverse of `a` modulo `n` in `self`.
|
||||
#[corresponds(BN_mod_inverse)]
|
||||
pub fn mod_inverse(
|
||||
&mut self,
|
||||
a: &BigNumRef,
|
||||
@ -642,7 +753,10 @@ impl BigNumRef {
|
||||
}
|
||||
|
||||
/// Places the greatest common denominator of `a` and `b` in `self`.
|
||||
#[corresponds(BN_gcd)]
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_gcd`]
|
||||
///
|
||||
/// [`BN_gcd`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_gcd.html
|
||||
pub fn gcd(
|
||||
&mut self,
|
||||
a: &BigNumRef,
|
||||
@ -656,6 +770,7 @@ impl BigNumRef {
|
||||
b.as_ptr(),
|
||||
ctx.as_ptr(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
@ -663,10 +778,13 @@ impl BigNumRef {
|
||||
///
|
||||
/// Performs a Miller-Rabin probabilistic primality test with `checks` iterations.
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_is_prime_ex`]
|
||||
///
|
||||
/// [`BN_is_prime_ex`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_is_prime_ex.html
|
||||
///
|
||||
/// # Return Value
|
||||
///
|
||||
/// Returns `true` if `self` is prime with an error probability of less than `0.25 ^ checks`.
|
||||
#[corresponds(BN_is_prime_ex)]
|
||||
#[allow(clippy::useless_conversion)]
|
||||
pub fn is_prime(&self, checks: i32, ctx: &mut BigNumContextRef) -> Result<bool, ErrorStack> {
|
||||
unsafe {
|
||||
@ -686,10 +804,13 @@ impl BigNumRef {
|
||||
/// Then, like `is_prime`, performs a Miller-Rabin probabilistic primality test with `checks`
|
||||
/// iterations.
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_is_prime_fasttest_ex`]
|
||||
///
|
||||
/// [`BN_is_prime_fasttest_ex`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_is_prime_fasttest_ex.html
|
||||
///
|
||||
/// # Return Value
|
||||
///
|
||||
/// Returns `true` if `self` is prime with an error probability of less than `0.25 ^ checks`.
|
||||
#[corresponds(BN_is_prime_fasttest_ex)]
|
||||
#[allow(clippy::useless_conversion)]
|
||||
pub fn is_prime_fasttest(
|
||||
&self,
|
||||
@ -702,7 +823,7 @@ impl BigNumRef {
|
||||
self.as_ptr(),
|
||||
checks.into(),
|
||||
ctx.as_ptr(),
|
||||
c_int::from(do_trial_division),
|
||||
do_trial_division as c_int,
|
||||
ptr::null_mut(),
|
||||
))
|
||||
.map(|r| r != 0)
|
||||
@ -721,8 +842,6 @@ impl BigNumRef {
|
||||
/// let s_vec = s.to_vec();
|
||||
/// assert_eq!(BigNum::from_slice(&s_vec).unwrap(), r);
|
||||
/// ```
|
||||
#[corresponds(BN_bn2bin)]
|
||||
#[must_use]
|
||||
pub fn to_vec(&self) -> Vec<u8> {
|
||||
let size = self.num_bytes() as usize;
|
||||
let mut v = Vec::with_capacity(size);
|
||||
@ -771,7 +890,6 @@ impl BigNumRef {
|
||||
///
|
||||
/// assert_eq!(&**s.to_dec_str().unwrap(), "-12345");
|
||||
/// ```
|
||||
#[corresponds(BN_bn2dec)]
|
||||
pub fn to_dec_str(&self) -> Result<OpensslString, ErrorStack> {
|
||||
unsafe {
|
||||
let buf = cvt_p(ffi::BN_bn2dec(self.as_ptr()))?;
|
||||
@ -787,7 +905,6 @@ impl BigNumRef {
|
||||
///
|
||||
/// assert_eq!(&**s.to_hex_str().unwrap(), "-99ff");
|
||||
/// ```
|
||||
#[corresponds(BN_bn2hex)]
|
||||
pub fn to_hex_str(&self) -> Result<OpensslString, ErrorStack> {
|
||||
unsafe {
|
||||
let buf = cvt_p(ffi::BN_bn2hex(self.as_ptr()))?;
|
||||
@ -796,7 +913,6 @@ impl BigNumRef {
|
||||
}
|
||||
|
||||
/// Returns an `Asn1Integer` containing the value of `self`.
|
||||
#[corresponds(BN_to_ASN1_INTEGER)]
|
||||
pub fn to_asn1_integer(&self) -> Result<Asn1Integer, ErrorStack> {
|
||||
unsafe {
|
||||
cvt_p(ffi::BN_to_ASN1_INTEGER(self.as_ptr(), ptr::null_mut()))
|
||||
@ -807,7 +923,6 @@ impl BigNumRef {
|
||||
|
||||
impl BigNum {
|
||||
/// Creates a new `BigNum` with the value 0.
|
||||
#[corresponds(BN_new)]
|
||||
pub fn new() -> Result<BigNum, ErrorStack> {
|
||||
unsafe {
|
||||
ffi::init();
|
||||
@ -817,33 +932,42 @@ impl BigNum {
|
||||
}
|
||||
|
||||
/// Creates a new `BigNum` with the given value.
|
||||
#[corresponds(BN_set_word)]
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_set_word`]
|
||||
///
|
||||
/// [`BN_set_word`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_set_word.html
|
||||
pub fn from_u32(n: u32) -> Result<BigNum, ErrorStack> {
|
||||
BigNum::new().and_then(|v| unsafe {
|
||||
cvt(ffi::BN_set_word(v.as_ptr(), ffi::BN_ULONG::from(n))).map(|_| v)
|
||||
cvt(ffi::BN_set_word(v.as_ptr(), n as ffi::BN_ULONG)).map(|_| v)
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates a `BigNum` from a decimal string.
|
||||
#[corresponds(BN_dec2bn)]
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_dec2bn`]
|
||||
///
|
||||
/// [`BN_dec2bn`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_dec2bn.html
|
||||
pub fn from_dec_str(s: &str) -> Result<BigNum, ErrorStack> {
|
||||
unsafe {
|
||||
ffi::init();
|
||||
let c_str = CString::new(s.as_bytes()).map_err(ErrorStack::internal_error)?;
|
||||
let c_str = CString::new(s.as_bytes()).unwrap();
|
||||
let mut bn = ptr::null_mut();
|
||||
cvt(ffi::BN_dec2bn(&mut bn, c_str.as_ptr()))?;
|
||||
cvt(ffi::BN_dec2bn(&mut bn, c_str.as_ptr() as *const _))?;
|
||||
Ok(BigNum::from_ptr(bn))
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a `BigNum` from a hexadecimal string.
|
||||
#[corresponds(BN_hex2bn)]
|
||||
///
|
||||
/// OpenSSL documentation at [`BN_hex2bn`]
|
||||
///
|
||||
/// [`BN_hex2bn`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_hex2bn.html
|
||||
pub fn from_hex_str(s: &str) -> Result<BigNum, ErrorStack> {
|
||||
unsafe {
|
||||
ffi::init();
|
||||
let c_str = CString::new(s.as_bytes()).map_err(ErrorStack::internal_error)?;
|
||||
let c_str = CString::new(s.as_bytes()).unwrap();
|
||||
let mut bn = ptr::null_mut();
|
||||
cvt(ffi::BN_hex2bn(&mut bn, c_str.as_ptr()))?;
|
||||
cvt(ffi::BN_hex2bn(&mut bn, c_str.as_ptr() as *const _))?;
|
||||
Ok(BigNum::from_ptr(bn))
|
||||
}
|
||||
}
|
||||
@ -860,12 +984,16 @@ impl BigNum {
|
||||
///
|
||||
/// assert_eq!(bignum, BigNum::from_u32(0x120034).unwrap());
|
||||
/// ```
|
||||
#[corresponds(BN_bin2bn)]
|
||||
pub fn from_slice(n: &[u8]) -> Result<BigNum, ErrorStack> {
|
||||
unsafe {
|
||||
ffi::init();
|
||||
assert!(n.len() <= c_int::MAX as usize);
|
||||
cvt_p(ffi::BN_bin2bn(n.as_ptr(), n.len(), ptr::null_mut())).map(|p| BigNum::from_ptr(p))
|
||||
assert!(n.len() <= c_int::max_value() as usize);
|
||||
cvt_p(ffi::BN_bin2bn(
|
||||
n.as_ptr(),
|
||||
n.len() as size_t,
|
||||
ptr::null_mut(),
|
||||
))
|
||||
.map(|p| BigNum::from_ptr(p))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -914,7 +1042,7 @@ impl PartialEq<BigNumRef> for BigNumRef {
|
||||
|
||||
impl PartialEq<BigNum> for BigNumRef {
|
||||
fn eq(&self, oth: &BigNum) -> bool {
|
||||
self.eq(&**oth)
|
||||
self.eq(oth.deref())
|
||||
}
|
||||
}
|
||||
|
||||
@ -942,7 +1070,7 @@ impl PartialOrd<BigNumRef> for BigNumRef {
|
||||
|
||||
impl PartialOrd<BigNum> for BigNumRef {
|
||||
fn partial_cmp(&self, oth: &BigNum) -> Option<Ordering> {
|
||||
Some(self.cmp(&**oth))
|
||||
Some(self.cmp(oth.deref()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -954,7 +1082,7 @@ impl Ord for BigNumRef {
|
||||
|
||||
impl PartialOrd for BigNum {
|
||||
fn partial_cmp(&self, oth: &BigNum) -> Option<Ordering> {
|
||||
Some(self.cmp(oth))
|
||||
self.deref().partial_cmp(oth.deref())
|
||||
}
|
||||
}
|
||||
|
||||
@ -966,7 +1094,7 @@ impl PartialOrd<BigNumRef> for BigNum {
|
||||
|
||||
impl Ord for BigNum {
|
||||
fn cmp(&self, oth: &BigNum) -> Ordering {
|
||||
self.deref().cmp(&**oth)
|
||||
self.deref().cmp(oth.deref())
|
||||
}
|
||||
}
|
||||
|
||||
@ -998,7 +1126,7 @@ macro_rules! delegate {
|
||||
};
|
||||
}
|
||||
|
||||
impl Add<&BigNumRef> for &BigNumRef {
|
||||
impl<'a, 'b> Add<&'b BigNumRef> for &'a BigNumRef {
|
||||
type Output = BigNum;
|
||||
|
||||
fn add(self, oth: &BigNumRef) -> BigNum {
|
||||
@ -1010,7 +1138,7 @@ impl Add<&BigNumRef> for &BigNumRef {
|
||||
|
||||
delegate!(Add, add);
|
||||
|
||||
impl Sub<&BigNumRef> for &BigNumRef {
|
||||
impl<'a, 'b> Sub<&'b BigNumRef> for &'a BigNumRef {
|
||||
type Output = BigNum;
|
||||
|
||||
fn sub(self, oth: &BigNumRef) -> BigNum {
|
||||
@ -1022,7 +1150,7 @@ impl Sub<&BigNumRef> for &BigNumRef {
|
||||
|
||||
delegate!(Sub, sub);
|
||||
|
||||
impl Mul<&BigNumRef> for &BigNumRef {
|
||||
impl<'a, 'b> Mul<&'b BigNumRef> for &'a BigNumRef {
|
||||
type Output = BigNum;
|
||||
|
||||
fn mul(self, oth: &BigNumRef) -> BigNum {
|
||||
@ -1035,7 +1163,7 @@ impl Mul<&BigNumRef> for &BigNumRef {
|
||||
|
||||
delegate!(Mul, mul);
|
||||
|
||||
impl<'b> Div<&'b BigNumRef> for &BigNumRef {
|
||||
impl<'a, 'b> Div<&'b BigNumRef> for &'a BigNumRef {
|
||||
type Output = BigNum;
|
||||
|
||||
fn div(self, oth: &'b BigNumRef) -> BigNum {
|
||||
@ -1048,7 +1176,7 @@ impl<'b> Div<&'b BigNumRef> for &BigNumRef {
|
||||
|
||||
delegate!(Div, div);
|
||||
|
||||
impl<'b> Rem<&'b BigNumRef> for &BigNumRef {
|
||||
impl<'a, 'b> Rem<&'b BigNumRef> for &'a BigNumRef {
|
||||
type Output = BigNum;
|
||||
|
||||
fn rem(self, oth: &'b BigNumRef) -> BigNum {
|
||||
@ -1061,7 +1189,7 @@ impl<'b> Rem<&'b BigNumRef> for &BigNumRef {
|
||||
|
||||
delegate!(Rem, rem);
|
||||
|
||||
impl Shl<i32> for &BigNumRef {
|
||||
impl<'a> Shl<i32> for &'a BigNumRef {
|
||||
type Output = BigNum;
|
||||
|
||||
fn shl(self, n: i32) -> BigNum {
|
||||
@ -1071,7 +1199,7 @@ impl Shl<i32> for &BigNumRef {
|
||||
}
|
||||
}
|
||||
|
||||
impl Shl<i32> for &BigNum {
|
||||
impl<'a> Shl<i32> for &'a BigNum {
|
||||
type Output = BigNum;
|
||||
|
||||
fn shl(self, n: i32) -> BigNum {
|
||||
@ -1079,7 +1207,7 @@ impl Shl<i32> for &BigNum {
|
||||
}
|
||||
}
|
||||
|
||||
impl Shr<i32> for &BigNumRef {
|
||||
impl<'a> Shr<i32> for &'a BigNumRef {
|
||||
type Output = BigNum;
|
||||
|
||||
fn shr(self, n: i32) -> BigNum {
|
||||
@ -1089,7 +1217,7 @@ impl Shr<i32> for &BigNumRef {
|
||||
}
|
||||
}
|
||||
|
||||
impl Shr<i32> for &BigNum {
|
||||
impl<'a> Shr<i32> for &'a BigNum {
|
||||
type Output = BigNum;
|
||||
|
||||
fn shr(self, n: i32) -> BigNum {
|
||||
@ -1097,7 +1225,7 @@ impl Shr<i32> for &BigNum {
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for &BigNumRef {
|
||||
impl<'a> Neg for &'a BigNumRef {
|
||||
type Output = BigNum;
|
||||
|
||||
fn neg(self) -> BigNum {
|
||||
@ -1105,7 +1233,7 @@ impl Neg for &BigNumRef {
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for &BigNum {
|
||||
impl<'a> Neg for &'a BigNum {
|
||||
type Output = BigNum;
|
||||
|
||||
fn neg(self) -> BigNum {
|
||||
|
||||
@ -19,7 +19,6 @@ impl ConfMethod {
|
||||
}
|
||||
|
||||
/// Convert to raw pointer.
|
||||
#[must_use]
|
||||
pub fn as_ptr(&self) -> *mut c_void {
|
||||
self.0
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
//! Shared secret derivation.
|
||||
use crate::ffi;
|
||||
use foreign_types::ForeignTypeRef;
|
||||
use openssl_macros::corresponds;
|
||||
use std::marker::PhantomData;
|
||||
use std::ptr;
|
||||
|
||||
@ -12,21 +11,16 @@ use crate::{cvt, cvt_p};
|
||||
/// A type used to derive a shared secret between two keys.
|
||||
pub struct Deriver<'a>(*mut ffi::EVP_PKEY_CTX, PhantomData<&'a ()>);
|
||||
|
||||
unsafe impl Sync for Deriver<'_> {}
|
||||
unsafe impl Send for Deriver<'_> {}
|
||||
|
||||
impl Drop for Deriver<'_> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
ffi::EVP_PKEY_CTX_free(self.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
unsafe impl<'a> Sync for Deriver<'a> {}
|
||||
unsafe impl<'a> Send for Deriver<'a> {}
|
||||
|
||||
#[allow(clippy::len_without_is_empty)]
|
||||
impl<'a> Deriver<'a> {
|
||||
/// Creates a new `Deriver` using the provided private key.
|
||||
#[corresponds(EVP_PKEY_derive_init)]
|
||||
///
|
||||
/// This corresponds to [`EVP_PKEY_derive_init`].
|
||||
///
|
||||
/// [`EVP_PKEY_derive_init`]: https://www.openssl.org/docs/man1.0.2/crypto/EVP_PKEY_derive_init.html
|
||||
pub fn new<T>(key: &'a PKeyRef<T>) -> Result<Deriver<'a>, ErrorStack>
|
||||
where
|
||||
T: HasPrivate,
|
||||
@ -39,18 +33,25 @@ impl<'a> Deriver<'a> {
|
||||
}
|
||||
|
||||
/// Sets the peer key used for secret derivation.
|
||||
#[corresponds(EVP_PKEY_derive_set_peer)]
|
||||
///
|
||||
/// This corresponds to [`EVP_PKEY_derive_set_peer`]:
|
||||
///
|
||||
/// [`EVP_PKEY_derive_set_peer`]: https://www.openssl.org/docs/man1.0.2/crypto/EVP_PKEY_derive_init.html
|
||||
pub fn set_peer<T>(&mut self, key: &'a PKeyRef<T>) -> Result<(), ErrorStack>
|
||||
where
|
||||
T: HasPublic,
|
||||
{
|
||||
unsafe { cvt(ffi::EVP_PKEY_derive_set_peer(self.0, key.as_ptr())) }
|
||||
unsafe { cvt(ffi::EVP_PKEY_derive_set_peer(self.0, key.as_ptr())).map(|_| ()) }
|
||||
}
|
||||
|
||||
/// Returns the size of the shared secret.
|
||||
///
|
||||
/// It can be used to size the buffer passed to [`Deriver::derive`].
|
||||
#[corresponds(EVP_PKEY_derive)]
|
||||
///
|
||||
/// This corresponds to [`EVP_PKEY_derive`].
|
||||
///
|
||||
/// [`Deriver::derive`]: #method.derive
|
||||
/// [`EVP_PKEY_derive`]: https://www.openssl.org/docs/man1.0.2/crypto/EVP_PKEY_derive_init.html
|
||||
pub fn len(&mut self) -> Result<usize, ErrorStack> {
|
||||
unsafe {
|
||||
let mut len = 0;
|
||||
@ -61,10 +62,20 @@ impl<'a> Deriver<'a> {
|
||||
/// Derives a shared secret between the two keys, writing it into the buffer.
|
||||
///
|
||||
/// Returns the number of bytes written.
|
||||
#[corresponds(EVP_PKEY_derive)]
|
||||
///
|
||||
/// This corresponds to [`EVP_PKEY_derive`].
|
||||
///
|
||||
/// [`EVP_PKEY_derive`]: https://www.openssl.org/docs/man1.0.2/crypto/EVP_PKEY_derive_init.html
|
||||
pub fn derive(&mut self, buf: &mut [u8]) -> Result<usize, ErrorStack> {
|
||||
let mut len = buf.len();
|
||||
unsafe { cvt(ffi::EVP_PKEY_derive(self.0, buf.as_mut_ptr(), &mut len)).map(|_| len) }
|
||||
unsafe {
|
||||
cvt(ffi::EVP_PKEY_derive(
|
||||
self.0,
|
||||
buf.as_mut_ptr() as *mut _,
|
||||
&mut len,
|
||||
))
|
||||
.map(|_| len)
|
||||
}
|
||||
}
|
||||
|
||||
/// A convenience function which derives a shared secret and returns it in a new buffer.
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
use crate::error::ErrorStack;
|
||||
use crate::ffi;
|
||||
use foreign_types::{ForeignType, ForeignTypeRef};
|
||||
use openssl_macros::corresponds;
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
|
||||
@ -26,14 +25,20 @@ where
|
||||
/// Serializes the parameters into a PEM-encoded PKCS#3 DHparameter structure.
|
||||
///
|
||||
/// The output will have a header of `-----BEGIN DH PARAMETERS-----`.
|
||||
#[corresponds(PEM_write_bio_DHparams)]
|
||||
///
|
||||
/// This corresponds to [`PEM_write_bio_DHparams`].
|
||||
///
|
||||
/// [`PEM_write_bio_DHparams`]: https://www.openssl.org/docs/manmaster/man3/PEM_write_bio_DHparams.html
|
||||
params_to_pem,
|
||||
ffi::PEM_write_bio_DHparams
|
||||
}
|
||||
|
||||
to_der! {
|
||||
/// Serializes the parameters into a DER-encoded PKCS#3 `DHparameter` structure.
|
||||
#[corresponds(i2d_DHparams)]
|
||||
/// Serializes the parameters into a DER-encoded PKCS#3 DHparameter structure.
|
||||
///
|
||||
/// This corresponds to [`i2d_DHparams`].
|
||||
///
|
||||
/// [`i2d_DHparams`]: https://www.openssl.org/docs/man1.1.0/crypto/i2d_DHparams.html
|
||||
params_to_der,
|
||||
ffi::i2d_DHparams
|
||||
}
|
||||
@ -53,7 +58,10 @@ impl Dh<Params> {
|
||||
/// Deserializes a PEM-encoded PKCS#3 DHpararameters structure.
|
||||
///
|
||||
/// The input should have a header of `-----BEGIN DH PARAMETERS-----`.
|
||||
#[corresponds(PEM_read_bio_DHparams)]
|
||||
///
|
||||
/// This corresponds to [`PEM_read_bio_DHparams`].
|
||||
///
|
||||
/// [`PEM_read_bio_DHparams`]: https://www.openssl.org/docs/man1.0.2/crypto/PEM_read_bio_DHparams.html
|
||||
params_from_pem,
|
||||
Dh<Params>,
|
||||
ffi::PEM_read_bio_DHparams
|
||||
@ -61,7 +69,10 @@ impl Dh<Params> {
|
||||
|
||||
from_der! {
|
||||
/// Deserializes a DER-encoded PKCS#3 DHparameters structure.
|
||||
#[corresponds(d2i_DHparams)]
|
||||
///
|
||||
/// This corresponds to [`d2i_DHparams`].
|
||||
///
|
||||
/// [`d2i_DHparams`]: https://www.openssl.org/docs/man1.1.0/crypto/d2i_DHparams.html
|
||||
params_from_der,
|
||||
Dh<Params>,
|
||||
ffi::d2i_DHparams,
|
||||
@ -71,6 +82,7 @@ impl Dh<Params> {
|
||||
|
||||
use crate::ffi::DH_set0_pqg;
|
||||
|
||||
#[cfg(feature = "ssl")] // Many of these tests use SslContext to verify the result.
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::bn::BigNum;
|
||||
|
||||
@ -5,18 +5,16 @@
|
||||
//! using the private key that can be validated with the public key but not be generated
|
||||
//! without the private key.
|
||||
|
||||
use crate::ffi;
|
||||
use foreign_types::{ForeignType, ForeignTypeRef};
|
||||
use libc::c_uint;
|
||||
use openssl_macros::corresponds;
|
||||
use std::fmt;
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
|
||||
use crate::bn::{BigNum, BigNumRef};
|
||||
use crate::error::ErrorStack;
|
||||
use crate::ffi;
|
||||
use crate::pkey::{HasParams, HasPrivate, HasPublic, Private, Public};
|
||||
use crate::try_int;
|
||||
use crate::{cvt, cvt_p};
|
||||
|
||||
generic_foreign_type_and_impl_send_sync! {
|
||||
@ -86,25 +84,30 @@ where
|
||||
/// Serialies the public key into a PEM-encoded SubjectPublicKeyInfo structure.
|
||||
///
|
||||
/// The output will have a header of `-----BEGIN PUBLIC KEY-----`.
|
||||
#[corresponds(PEM_write_bio_DSA_PUBKEY)]
|
||||
///
|
||||
/// This corresponds to [`PEM_write_bio_DSA_PUBKEY`].
|
||||
///
|
||||
/// [`PEM_write_bio_DSA_PUBKEY`]: https://www.openssl.org/docs/man1.1.0/crypto/PEM_write_bio_DSA_PUBKEY.html
|
||||
public_key_to_pem,
|
||||
ffi::PEM_write_bio_DSA_PUBKEY
|
||||
}
|
||||
|
||||
to_der! {
|
||||
/// Serializes the public key into a DER-encoded SubjectPublicKeyInfo structure.
|
||||
#[corresponds(i2d_DSA_PUBKEY)]
|
||||
///
|
||||
/// This corresponds to [`i2d_DSA_PUBKEY`].
|
||||
///
|
||||
/// [`i2d_DSA_PUBKEY`]: https://www.openssl.org/docs/man1.1.0/crypto/i2d_DSA_PUBKEY.html
|
||||
public_key_to_der,
|
||||
ffi::i2d_DSA_PUBKEY
|
||||
}
|
||||
|
||||
/// Returns a reference to the public key component of `self`.
|
||||
#[must_use]
|
||||
pub fn pub_key(&self) -> &BigNumRef {
|
||||
unsafe {
|
||||
let mut pub_key = ptr::null();
|
||||
DSA_get0_key(self.as_ptr(), &mut pub_key, ptr::null_mut());
|
||||
BigNumRef::from_ptr(pub_key.cast_mut())
|
||||
BigNumRef::from_ptr(pub_key as *mut _)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -117,23 +120,28 @@ where
|
||||
/// Serializes the private key to a PEM-encoded DSAPrivateKey structure.
|
||||
///
|
||||
/// The output will have a header of `-----BEGIN DSA PRIVATE KEY-----`.
|
||||
#[corresponds(PEM_write_bio_DSAPrivateKey)]
|
||||
///
|
||||
/// This corresponds to [`PEM_write_bio_DSAPrivateKey`].
|
||||
///
|
||||
/// [`PEM_write_bio_DSAPrivateKey`]: https://www.openssl.org/docs/man1.1.0/crypto/PEM_write_bio_DSAPrivateKey.html
|
||||
private_key_to_pem,
|
||||
/// Serializes the private key to a PEM-encoded encrypted DSAPrivateKey structure.
|
||||
///
|
||||
/// The output will have a header of `-----BEGIN DSA PRIVATE KEY-----`.
|
||||
#[corresponds(PEM_write_bio_DSAPrivateKey)]
|
||||
///
|
||||
/// This corresponds to [`PEM_write_bio_DSAPrivateKey`].
|
||||
///
|
||||
/// [`PEM_write_bio_DSAPrivateKey`]: https://www.openssl.org/docs/man1.1.0/crypto/PEM_write_bio_DSAPrivateKey.html
|
||||
private_key_to_pem_passphrase,
|
||||
ffi::PEM_write_bio_DSAPrivateKey
|
||||
}
|
||||
|
||||
/// Returns a reference to the private key component of `self`.
|
||||
#[must_use]
|
||||
pub fn priv_key(&self) -> &BigNumRef {
|
||||
unsafe {
|
||||
let mut priv_key = ptr::null();
|
||||
DSA_get0_key(self.as_ptr(), ptr::null_mut(), &mut priv_key);
|
||||
BigNumRef::from_ptr(priv_key.cast_mut())
|
||||
BigNumRef::from_ptr(priv_key as *mut _)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -143,39 +151,38 @@ where
|
||||
T: HasParams,
|
||||
{
|
||||
/// Returns the maximum size of the signature output by `self` in bytes.
|
||||
#[corresponds(DSA_size)]
|
||||
#[must_use]
|
||||
///
|
||||
/// OpenSSL documentation at [`DSA_size`]
|
||||
///
|
||||
/// [`DSA_size`]: https://www.openssl.org/docs/man1.1.0/crypto/DSA_size.html
|
||||
pub fn size(&self) -> u32 {
|
||||
unsafe { ffi::DSA_size(self.as_ptr()) as u32 }
|
||||
}
|
||||
|
||||
/// Returns the DSA prime parameter of `self`.
|
||||
#[must_use]
|
||||
pub fn p(&self) -> &BigNumRef {
|
||||
unsafe {
|
||||
let mut p = ptr::null();
|
||||
DSA_get0_pqg(self.as_ptr(), &mut p, ptr::null_mut(), ptr::null_mut());
|
||||
BigNumRef::from_ptr(p.cast_mut())
|
||||
BigNumRef::from_ptr(p as *mut _)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the DSA sub-prime parameter of `self`.
|
||||
#[must_use]
|
||||
pub fn q(&self) -> &BigNumRef {
|
||||
unsafe {
|
||||
let mut q = ptr::null();
|
||||
DSA_get0_pqg(self.as_ptr(), ptr::null_mut(), &mut q, ptr::null_mut());
|
||||
BigNumRef::from_ptr(q.cast_mut())
|
||||
BigNumRef::from_ptr(q as *mut _)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the DSA base parameter of `self`.
|
||||
#[must_use]
|
||||
pub fn g(&self) -> &BigNumRef {
|
||||
unsafe {
|
||||
let mut g = ptr::null();
|
||||
DSA_get0_pqg(self.as_ptr(), ptr::null_mut(), ptr::null_mut(), &mut g);
|
||||
BigNumRef::from_ptr(g.cast_mut())
|
||||
BigNumRef::from_ptr(g as *mut _)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -196,7 +203,7 @@ impl Dsa<Private> {
|
||||
let dsa = Dsa::from_ptr(cvt_p(ffi::DSA_new())?);
|
||||
cvt(ffi::DSA_generate_parameters_ex(
|
||||
dsa.0,
|
||||
c_uint::from(bits),
|
||||
bits as c_uint,
|
||||
ptr::null(),
|
||||
0,
|
||||
ptr::null_mut(),
|
||||
@ -237,7 +244,10 @@ impl Dsa<Public> {
|
||||
/// Decodes a PEM-encoded SubjectPublicKeyInfo structure containing a DSA key.
|
||||
///
|
||||
/// The input should have a header of `-----BEGIN PUBLIC KEY-----`.
|
||||
#[corresponds(PEM_read_bio_DSA_PUBKEY)]
|
||||
///
|
||||
/// This corresponds to [`PEM_read_bio_DSA_PUBKEY`].
|
||||
///
|
||||
/// [`PEM_read_bio_DSA_PUBKEY`]: https://www.openssl.org/docs/man1.0.2/crypto/PEM_read_bio_DSA_PUBKEY.html
|
||||
public_key_from_pem,
|
||||
Dsa<Public>,
|
||||
ffi::PEM_read_bio_DSA_PUBKEY
|
||||
@ -245,7 +255,10 @@ impl Dsa<Public> {
|
||||
|
||||
from_der! {
|
||||
/// Decodes a DER-encoded SubjectPublicKeyInfo structure containing a DSA key.
|
||||
#[corresponds(d2i_DSA_PUBKEY)]
|
||||
///
|
||||
/// This corresponds to [`d2i_DSA_PUBKEY`].
|
||||
///
|
||||
/// [`d2i_DSA_PUBKEY`]: https://www.openssl.org/docs/man1.0.2/crypto/d2i_DSA_PUBKEY.html
|
||||
public_key_from_der,
|
||||
Dsa<Public>,
|
||||
ffi::d2i_DSA_PUBKEY,
|
||||
@ -267,7 +280,8 @@ impl Dsa<Public> {
|
||||
let dsa = Dsa::from_ptr(cvt_p(ffi::DSA_new())?);
|
||||
cvt(DSA_set0_pqg(dsa.0, p.as_ptr(), q.as_ptr(), g.as_ptr()))?;
|
||||
mem::forget((p, q, g));
|
||||
cvt(DSA_set0_key(dsa.0, pub_key.into_ptr(), ptr::null_mut()))?;
|
||||
cvt(DSA_set0_key(dsa.0, pub_key.as_ptr(), ptr::null_mut()))?;
|
||||
mem::forget(pub_key);
|
||||
Ok(dsa)
|
||||
}
|
||||
}
|
||||
@ -301,7 +315,7 @@ mod test {
|
||||
let mut ctx = BigNumContext::new().unwrap();
|
||||
let mut calc = BigNum::new().unwrap();
|
||||
calc.mod_exp(g, priv_key, p, &mut ctx).unwrap();
|
||||
assert_eq!(&calc, pub_key);
|
||||
assert_eq!(&calc, pub_key)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
200
boring/src/ec.rs
200
boring/src/ec.rs
@ -15,18 +15,16 @@
|
||||
//! [`EcGroup`]: struct.EcGroup.html
|
||||
//! [`Nid`]: ../nid/struct.Nid.html
|
||||
//! [Eliptic Curve Cryptography]: https://wiki.openssl.org/index.php/Elliptic_Curve_Cryptography
|
||||
use crate::ffi;
|
||||
use foreign_types::{ForeignType, ForeignTypeRef};
|
||||
use libc::c_int;
|
||||
use openssl_macros::corresponds;
|
||||
use std::fmt;
|
||||
use std::ptr;
|
||||
|
||||
use crate::bn::{BigNumContextRef, BigNumRef};
|
||||
use crate::error::ErrorStack;
|
||||
use crate::ffi;
|
||||
use crate::nid::Nid;
|
||||
use crate::pkey::{HasParams, HasPrivate, HasPublic, Params, Private, Public};
|
||||
use crate::try_int;
|
||||
use crate::{cvt, cvt_n, cvt_p, init};
|
||||
|
||||
/// Compressed or Uncompressed conversion
|
||||
@ -104,7 +102,7 @@ foreign_type_and_impl_send_sync! {
|
||||
/// Prime fields use the formula `y^2 mod p = x^3 + ax + b mod p`. Binary
|
||||
/// fields use the formula `y^2 + xy = x^3 + ax^2 + b`. Named curves have
|
||||
/// assured security. To prevent accidental vulnerabilities, they should
|
||||
/// be preferred.
|
||||
/// be prefered.
|
||||
///
|
||||
/// [wiki]: https://wiki.openssl.org/index.php/Command_Line_Elliptic_Curve_Operations
|
||||
/// [`Nid`]: ../nid/index.html
|
||||
@ -113,7 +111,10 @@ foreign_type_and_impl_send_sync! {
|
||||
|
||||
impl EcGroup {
|
||||
/// Returns the group of a standard named curve.
|
||||
#[corresponds(EC_GROUP_new)]
|
||||
///
|
||||
/// OpenSSL documentation at [`EC_GROUP_new`].
|
||||
///
|
||||
/// [`EC_GROUP_new`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_GROUP_new.html
|
||||
pub fn from_curve_name(nid: Nid) -> Result<EcGroup, ErrorStack> {
|
||||
unsafe {
|
||||
init();
|
||||
@ -144,11 +145,15 @@ impl EcGroupRef {
|
||||
b.as_ptr(),
|
||||
ctx.as_ptr(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Places the cofactor of the group in the provided `BigNum`.
|
||||
#[corresponds(EC_GROUP_get_cofactor)]
|
||||
///
|
||||
/// OpenSSL documentation at [`EC_GROUP_get_cofactor`]
|
||||
///
|
||||
/// [`EC_GROUP_get_cofactor`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_GROUP_get_cofactor.html
|
||||
pub fn cofactor(
|
||||
&self,
|
||||
cofactor: &mut BigNumRef,
|
||||
@ -160,36 +165,45 @@ impl EcGroupRef {
|
||||
cofactor.as_ptr(),
|
||||
ctx.as_ptr(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the degree of the curve.
|
||||
#[corresponds(EC_GROUP_get_degree)]
|
||||
#[allow(clippy::unnecessary_cast)]
|
||||
#[must_use]
|
||||
///
|
||||
/// OpenSSL documentation at [`EC_GROUP_get_degree`]
|
||||
///
|
||||
/// [`EC_GROUP_get_degree`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_GROUP_get_degree.html
|
||||
pub fn degree(&self) -> u32 {
|
||||
unsafe { ffi::EC_GROUP_get_degree(self.as_ptr()) as u32 }
|
||||
}
|
||||
|
||||
/// Returns the number of bits in the group order.
|
||||
#[corresponds(EC_GROUP_order_bits)]
|
||||
#[must_use]
|
||||
///
|
||||
/// OpenSSL documentation at [`EC_GROUP_order_bits`]
|
||||
///
|
||||
/// [`EC_GROUP_order_bits`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_GROUP_order_bits.html
|
||||
pub fn order_bits(&self) -> u32 {
|
||||
unsafe { ffi::EC_GROUP_order_bits(self.as_ptr()) as u32 }
|
||||
}
|
||||
|
||||
/// Returns the generator for the given curve as a [`EcPoint`].
|
||||
#[corresponds(EC_GROUP_get0_generator)]
|
||||
#[must_use]
|
||||
///
|
||||
/// OpenSSL documentation at [`EC_GROUP_get0_generator`]
|
||||
///
|
||||
/// [`EC_GROUP_get0_generator`]: https://www.openssl.org/docs/man1.1.0/man3/EC_GROUP_get0_generator.html
|
||||
pub fn generator(&self) -> &EcPointRef {
|
||||
unsafe {
|
||||
let ptr = ffi::EC_GROUP_get0_generator(self.as_ptr());
|
||||
EcPointRef::from_ptr(ptr.cast_mut())
|
||||
EcPointRef::from_ptr(ptr as *mut _)
|
||||
}
|
||||
}
|
||||
|
||||
/// Places the order of the curve in the provided `BigNum`.
|
||||
#[corresponds(EC_GROUP_get_order)]
|
||||
///
|
||||
/// OpenSSL documentation at [`EC_GROUP_get_order`]
|
||||
///
|
||||
/// [`EC_GROUP_get_order`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_GROUP_get_order.html
|
||||
pub fn order(
|
||||
&self,
|
||||
order: &mut BigNumRef,
|
||||
@ -201,6 +215,7 @@ impl EcGroupRef {
|
||||
order.as_ptr(),
|
||||
ctx.as_ptr(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
@ -216,8 +231,10 @@ impl EcGroupRef {
|
||||
}
|
||||
|
||||
/// Returns the name of the curve, if a name is associated.
|
||||
#[corresponds(EC_GROUP_get_curve_name)]
|
||||
#[must_use]
|
||||
///
|
||||
/// OpenSSL documentation at [`EC_GROUP_get_curve_name`]
|
||||
///
|
||||
/// [`EC_GROUP_get_curve_name`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_GROUP_get_curve_name.html
|
||||
pub fn curve_name(&self) -> Option<Nid> {
|
||||
let nid = unsafe { ffi::EC_GROUP_get_curve_name(self.as_ptr()) };
|
||||
if nid > 0 {
|
||||
@ -242,7 +259,10 @@ foreign_type_and_impl_send_sync! {
|
||||
|
||||
impl EcPointRef {
|
||||
/// Computes `a + b`, storing the result in `self`.
|
||||
#[corresponds(EC_POINT_add)]
|
||||
///
|
||||
/// OpenSSL documentation at [`EC_POINT_add`]
|
||||
///
|
||||
/// [`EC_POINT_add`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_POINT_add.html
|
||||
pub fn add(
|
||||
&mut self,
|
||||
group: &EcGroupRef,
|
||||
@ -258,17 +278,22 @@ impl EcPointRef {
|
||||
b.as_ptr(),
|
||||
ctx.as_ptr(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes `q * m`, storing the result in `self`.
|
||||
#[corresponds(EC_POINT_mul)]
|
||||
///
|
||||
/// OpenSSL documentation at [`EC_POINT_mul`]
|
||||
///
|
||||
/// [`EC_POINT_mul`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_POINT_mul.html
|
||||
pub fn mul(
|
||||
&mut self,
|
||||
group: &EcGroupRef,
|
||||
q: &EcPointRef,
|
||||
m: &BigNumRef,
|
||||
ctx: &mut BigNumContextRef,
|
||||
// FIXME should be &mut
|
||||
ctx: &BigNumContextRef,
|
||||
) -> Result<(), ErrorStack> {
|
||||
unsafe {
|
||||
cvt(ffi::EC_POINT_mul(
|
||||
@ -279,6 +304,7 @@ impl EcPointRef {
|
||||
m.as_ptr(),
|
||||
ctx.as_ptr(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
@ -287,7 +313,8 @@ impl EcPointRef {
|
||||
&mut self,
|
||||
group: &EcGroupRef,
|
||||
n: &BigNumRef,
|
||||
ctx: &mut BigNumContextRef,
|
||||
// FIXME should be &mut
|
||||
ctx: &BigNumContextRef,
|
||||
) -> Result<(), ErrorStack> {
|
||||
unsafe {
|
||||
cvt(ffi::EC_POINT_mul(
|
||||
@ -298,6 +325,7 @@ impl EcPointRef {
|
||||
ptr::null(),
|
||||
ctx.as_ptr(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
@ -319,11 +347,15 @@ impl EcPointRef {
|
||||
m.as_ptr(),
|
||||
ctx.as_ptr(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Inverts `self`.
|
||||
#[corresponds(EC_POINT_invert)]
|
||||
///
|
||||
/// OpenSSL documentation at [`EC_POINT_invert`]
|
||||
///
|
||||
/// [`EC_POINT_invert`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_POINT_invert.html
|
||||
pub fn invert(&mut self, group: &EcGroupRef, ctx: &BigNumContextRef) -> Result<(), ErrorStack> {
|
||||
unsafe {
|
||||
cvt(ffi::EC_POINT_invert(
|
||||
@ -331,11 +363,15 @@ impl EcPointRef {
|
||||
self.as_ptr(),
|
||||
ctx.as_ptr(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Serializes the point to a binary representation.
|
||||
#[corresponds(EC_POINT_point2oct)]
|
||||
///
|
||||
/// OpenSSL documentation at [`EC_POINT_point2oct`]
|
||||
///
|
||||
/// [`EC_POINT_point2oct`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_POINT_point2oct.html
|
||||
pub fn to_bytes(
|
||||
&self,
|
||||
group: &EcGroupRef,
|
||||
@ -372,7 +408,10 @@ impl EcPointRef {
|
||||
}
|
||||
|
||||
/// Creates a new point on the specified curve with the same value.
|
||||
#[corresponds(EC_POINT_dup)]
|
||||
///
|
||||
/// OpenSSL documentation at [`EC_POINT_dup`]
|
||||
///
|
||||
/// [`EC_POINT_dup`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_POINT_dup.html
|
||||
pub fn to_owned(&self, group: &EcGroupRef) -> Result<EcPoint, ErrorStack> {
|
||||
unsafe {
|
||||
cvt_p(ffi::EC_POINT_dup(self.as_ptr(), group.as_ptr())).map(|p| EcPoint::from_ptr(p))
|
||||
@ -403,7 +442,10 @@ impl EcPointRef {
|
||||
|
||||
/// Place affine coordinates of a curve over a prime field in the provided
|
||||
/// `x` and `y` `BigNum`s
|
||||
#[corresponds(EC_POINT_get_affine_coordinates_GFp)]
|
||||
///
|
||||
/// OpenSSL documentation at [`EC_POINT_get_affine_coordinates_GFp`]
|
||||
///
|
||||
/// [`EC_POINT_get_affine_coordinates_GFp`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_POINT_get_affine_coordinates_GFp.html
|
||||
pub fn affine_coordinates_gfp(
|
||||
&self,
|
||||
group: &EcGroupRef,
|
||||
@ -419,19 +461,26 @@ impl EcPointRef {
|
||||
y.as_ptr(),
|
||||
ctx.as_ptr(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EcPoint {
|
||||
/// Creates a new point on the specified curve.
|
||||
#[corresponds(EC_POINT_new)]
|
||||
///
|
||||
/// OpenSSL documentation at [`EC_POINT_new`]
|
||||
///
|
||||
/// [`EC_POINT_new`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_POINT_new.html
|
||||
pub fn new(group: &EcGroupRef) -> Result<EcPoint, ErrorStack> {
|
||||
unsafe { cvt_p(ffi::EC_POINT_new(group.as_ptr())).map(|p| EcPoint::from_ptr(p)) }
|
||||
}
|
||||
|
||||
/// Creates point from a binary representation
|
||||
#[corresponds(EC_POINT_oct2point)]
|
||||
///
|
||||
/// OpenSSL documentation at [`EC_POINT_oct2point`]
|
||||
///
|
||||
/// [`EC_POINT_oct2point`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_POINT_oct2point.html
|
||||
pub fn from_bytes(
|
||||
group: &EcGroupRef,
|
||||
buf: &[u8],
|
||||
@ -457,6 +506,9 @@ generic_foreign_type_and_impl_send_sync! {
|
||||
|
||||
/// Public and optional Private key on the given curve
|
||||
///
|
||||
/// OpenSSL documentation at [`EC_KEY_new`]
|
||||
///
|
||||
/// [`EC_KEY_new`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_KEY_new.html
|
||||
pub struct EcKey<T>;
|
||||
|
||||
/// Reference to [`EcKey`]
|
||||
@ -473,30 +525,41 @@ where
|
||||
/// Serializes the private key to a PEM-encoded ECPrivateKey structure.
|
||||
///
|
||||
/// The output will have a header of `-----BEGIN EC PRIVATE KEY-----`.
|
||||
#[corresponds(PEM_write_bio_ECPrivateKey)]
|
||||
///
|
||||
/// This corresponds to [`PEM_write_bio_ECPrivateKey`].
|
||||
///
|
||||
/// [`PEM_write_bio_ECPrivateKey`]: https://www.openssl.org/docs/man1.1.0/crypto/PEM_write_bio_ECPrivateKey.html
|
||||
private_key_to_pem,
|
||||
/// Serializes the private key to a PEM-encoded encrypted ECPrivateKey structure.
|
||||
///
|
||||
/// The output will have a header of `-----BEGIN EC PRIVATE KEY-----`.
|
||||
#[corresponds(PEM_write_bio_ECPrivateKey)]
|
||||
///
|
||||
/// This corresponds to [`PEM_write_bio_ECPrivateKey`].
|
||||
///
|
||||
/// [`PEM_write_bio_ECPrivateKey`]: https://www.openssl.org/docs/man1.1.0/crypto/PEM_write_bio_ECPrivateKey.html
|
||||
private_key_to_pem_passphrase,
|
||||
ffi::PEM_write_bio_ECPrivateKey
|
||||
}
|
||||
|
||||
to_der! {
|
||||
/// Serializes the private key into a DER-encoded ECPrivateKey structure.
|
||||
#[corresponds(i2d_ECPrivateKey)]
|
||||
///
|
||||
/// This corresponds to [`i2d_ECPrivateKey`].
|
||||
///
|
||||
/// [`i2d_ECPrivateKey`]: https://www.openssl.org/docs/man1.0.2/crypto/d2i_ECPrivate_key.html
|
||||
private_key_to_der,
|
||||
ffi::i2d_ECPrivateKey
|
||||
}
|
||||
|
||||
/// Return [`EcPoint`] associated with the private key
|
||||
#[corresponds(EC_KEY_get0_private_key)]
|
||||
#[must_use]
|
||||
///
|
||||
/// OpenSSL documentation at [`EC_KEY_get0_private_key`]
|
||||
///
|
||||
/// [`EC_KEY_get0_private_key`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_KEY_get0_private_key.html
|
||||
pub fn private_key(&self) -> &BigNumRef {
|
||||
unsafe {
|
||||
let ptr = ffi::EC_KEY_get0_private_key(self.as_ptr());
|
||||
BigNumRef::from_ptr(ptr.cast_mut())
|
||||
BigNumRef::from_ptr(ptr as *mut _)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -506,12 +569,14 @@ where
|
||||
T: HasPublic,
|
||||
{
|
||||
/// Returns the public key.
|
||||
#[corresponds(EC_KEY_get0_public_key)]
|
||||
#[must_use]
|
||||
///
|
||||
/// OpenSSL documentation at [`EC_KEY_get0_public_key`]
|
||||
///
|
||||
/// [`EC_KEY_get0_public_key`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_KEY_get0_public_key.html
|
||||
pub fn public_key(&self) -> &EcPointRef {
|
||||
unsafe {
|
||||
let ptr = ffi::EC_KEY_get0_public_key(self.as_ptr());
|
||||
EcPointRef::from_ptr(ptr.cast_mut())
|
||||
EcPointRef::from_ptr(ptr as *mut _)
|
||||
}
|
||||
}
|
||||
|
||||
@ -519,14 +584,20 @@ where
|
||||
/// Serialies the public key into a PEM-encoded SubjectPublicKeyInfo structure.
|
||||
///
|
||||
/// The output will have a header of `-----BEGIN PUBLIC KEY-----`.
|
||||
#[corresponds(PEM_write_bio_EC_PUBKEY)]
|
||||
///
|
||||
/// This corresponds to [`PEM_write_bio_EC_PUBKEY`].
|
||||
///
|
||||
/// [`PEM_write_bio_EC_PUBKEY`]: https://www.openssl.org/docs/man1.1.0/crypto/PEM_write_bio_EC_PUBKEY.html
|
||||
public_key_to_pem,
|
||||
ffi::PEM_write_bio_EC_PUBKEY
|
||||
}
|
||||
|
||||
to_der! {
|
||||
/// Serializes the public key into a DER-encoded SubjectPublicKeyInfo structure.
|
||||
#[corresponds(i2d_EC_PUBKEY)]
|
||||
///
|
||||
/// This corresponds to [`i2d_EC_PUBKEY`].
|
||||
///
|
||||
/// [`i2d_EC_PUBKEY`]: https://www.openssl.org/docs/man1.1.0/crypto/i2d_EC_PUBKEY.html
|
||||
public_key_to_der,
|
||||
ffi::i2d_EC_PUBKEY
|
||||
}
|
||||
@ -537,19 +608,24 @@ where
|
||||
T: HasParams,
|
||||
{
|
||||
/// Return [`EcGroup`] of the `EcKey`
|
||||
#[corresponds(EC_KEY_get0_group)]
|
||||
#[must_use]
|
||||
///
|
||||
/// OpenSSL documentation at [`EC_KEY_get0_group`]
|
||||
///
|
||||
/// [`EC_KEY_get0_group`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_KEY_get0_group.html
|
||||
pub fn group(&self) -> &EcGroupRef {
|
||||
unsafe {
|
||||
let ptr = ffi::EC_KEY_get0_group(self.as_ptr());
|
||||
EcGroupRef::from_ptr(ptr.cast_mut())
|
||||
EcGroupRef::from_ptr(ptr as *mut _)
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks the key for validity.
|
||||
#[corresponds(EC_KEY_check_key)]
|
||||
///
|
||||
/// OpenSSL documenation at [`EC_KEY_check_key`]
|
||||
///
|
||||
/// [`EC_KEY_check_key`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_KEY_check_key.html
|
||||
pub fn check_key(&self) -> Result<(), ErrorStack> {
|
||||
unsafe { cvt(ffi::EC_KEY_check_key(self.as_ptr())) }
|
||||
unsafe { cvt(ffi::EC_KEY_check_key(self.as_ptr())).map(|_| ()) }
|
||||
}
|
||||
}
|
||||
|
||||
@ -570,7 +646,10 @@ impl EcKey<Params> {
|
||||
///
|
||||
/// It will not have an associated public or private key. This kind of key is primarily useful
|
||||
/// to be provided to the `set_tmp_ecdh` methods on `Ssl` and `SslContextBuilder`.
|
||||
#[corresponds(EC_KEY_new_by_curve_name)]
|
||||
///
|
||||
/// OpenSSL documenation at [`EC_KEY_new_by_curve_name`]
|
||||
///
|
||||
/// [`EC_KEY_new_by_curve_name`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_KEY_new_by_curve_name.html
|
||||
pub fn from_curve_name(nid: Nid) -> Result<EcKey<Params>, ErrorStack> {
|
||||
unsafe {
|
||||
init();
|
||||
@ -579,7 +658,10 @@ impl EcKey<Params> {
|
||||
}
|
||||
|
||||
/// Constructs an `EcKey` corresponding to a curve.
|
||||
#[corresponds(EC_KEY_set_group)]
|
||||
///
|
||||
/// This corresponds to [`EC_KEY_set_group`].
|
||||
///
|
||||
/// [`EC_KEY_set_group`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_KEY_new.html
|
||||
pub fn from_group(group: &EcGroupRef) -> Result<EcKey<Params>, ErrorStack> {
|
||||
unsafe {
|
||||
cvt_p(ffi::EC_KEY_new())
|
||||
@ -660,7 +742,10 @@ impl EcKey<Public> {
|
||||
/// Decodes a PEM-encoded SubjectPublicKeyInfo structure containing a EC key.
|
||||
///
|
||||
/// The input should have a header of `-----BEGIN PUBLIC KEY-----`.
|
||||
#[corresponds(PEM_read_bio_EC_PUBKEY)]
|
||||
///
|
||||
/// This corresponds to [`PEM_read_bio_EC_PUBKEY`].
|
||||
///
|
||||
/// [`PEM_read_bio_EC_PUBKEY`]: https://www.openssl.org/docs/man1.1.0/crypto/PEM_read_bio_EC_PUBKEY.html
|
||||
public_key_from_pem,
|
||||
EcKey<Public>,
|
||||
ffi::PEM_read_bio_EC_PUBKEY
|
||||
@ -668,7 +753,10 @@ impl EcKey<Public> {
|
||||
|
||||
from_der! {
|
||||
/// Decodes a DER-encoded SubjectPublicKeyInfo structure containing a EC key.
|
||||
#[corresponds(d2i_EC_PUBKEY)]
|
||||
///
|
||||
/// This corresponds to [`d2i_EC_PUBKEY`].
|
||||
///
|
||||
/// [`d2i_EC_PUBKEY`]: https://www.openssl.org/docs/man1.1.0/crypto/d2i_EC_PUBKEY.html
|
||||
public_key_from_der,
|
||||
EcKey<Public>,
|
||||
ffi::d2i_EC_PUBKEY,
|
||||
@ -722,13 +810,15 @@ impl EcKey<Private> {
|
||||
/// Deserializes a private key from a PEM-encoded ECPrivateKey structure.
|
||||
///
|
||||
/// The input should have a header of `-----BEGIN EC PRIVATE KEY-----`.
|
||||
#[corresponds(PEM_read_bio_ECPrivateKey)]
|
||||
///
|
||||
/// This corresponds to `PEM_read_bio_ECPrivateKey`.
|
||||
private_key_from_pem,
|
||||
|
||||
/// Deserializes a private key from a PEM-encoded encrypted ECPrivateKey structure.
|
||||
///
|
||||
/// The input should have a header of `-----BEGIN EC PRIVATE KEY-----`.
|
||||
#[corresponds(PEM_read_bio_ECPrivateKey)]
|
||||
///
|
||||
/// This corresponds to `PEM_read_bio_ECPrivateKey`.
|
||||
private_key_from_pem_passphrase,
|
||||
|
||||
/// Deserializes a private key from a PEM-encoded encrypted ECPrivateKey structure.
|
||||
@ -736,7 +826,8 @@ impl EcKey<Private> {
|
||||
/// The callback should fill the password into the provided buffer and return its length.
|
||||
///
|
||||
/// The input should have a header of `-----BEGIN EC PRIVATE KEY-----`.
|
||||
#[corresponds(PEM_read_bio_ECPrivateKey)]
|
||||
///
|
||||
/// This corresponds to `PEM_read_bio_ECPrivateKey`.
|
||||
private_key_from_pem_callback,
|
||||
EcKey<Private>,
|
||||
ffi::PEM_read_bio_ECPrivateKey
|
||||
@ -744,7 +835,10 @@ impl EcKey<Private> {
|
||||
|
||||
from_der! {
|
||||
/// Decodes a DER-encoded elliptic curve private key structure.
|
||||
#[corresponds(d2i_ECPrivateKey)]
|
||||
///
|
||||
/// This corresponds to [`d2i_ECPrivateKey`].
|
||||
///
|
||||
/// [`d2i_ECPrivateKey`]: https://www.openssl.org/docs/man1.0.2/crypto/d2i_ECPrivate_key.html
|
||||
private_key_from_der,
|
||||
EcKey<Private>,
|
||||
ffi::d2i_ECPrivateKey,
|
||||
@ -837,7 +931,7 @@ mod test {
|
||||
let mut ctx = BigNumContext::new().unwrap();
|
||||
let mut public_key = EcPoint::new(&group).unwrap();
|
||||
public_key
|
||||
.mul_generator(&group, key.private_key(), &mut ctx)
|
||||
.mul_generator(&group, key.private_key(), &ctx)
|
||||
.unwrap();
|
||||
assert!(public_key.eq(&group, key.public_key(), &mut ctx).unwrap());
|
||||
}
|
||||
@ -849,7 +943,7 @@ mod test {
|
||||
let one = BigNum::from_u32(1).unwrap();
|
||||
let mut ctx = BigNumContext::new().unwrap();
|
||||
let mut ecp = EcPoint::new(&group).unwrap();
|
||||
ecp.mul_generator(&group, &one, &mut ctx).unwrap();
|
||||
ecp.mul_generator(&group, &one, &ctx).unwrap();
|
||||
assert!(ecp.eq(&group, gen, &mut ctx).unwrap());
|
||||
}
|
||||
|
||||
|
||||
@ -2,8 +2,7 @@
|
||||
|
||||
use crate::ffi;
|
||||
use foreign_types::{ForeignType, ForeignTypeRef};
|
||||
use libc::c_int;
|
||||
use openssl_macros::corresponds;
|
||||
use libc::{c_int, size_t};
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
|
||||
@ -27,37 +26,46 @@ foreign_type_and_impl_send_sync! {
|
||||
|
||||
impl EcdsaSig {
|
||||
/// Computes a digital signature of the hash value `data` using the private EC key eckey.
|
||||
#[corresponds(ECDSA_do_sign)]
|
||||
///
|
||||
/// OpenSSL documentation at [`ECDSA_do_sign`]
|
||||
///
|
||||
/// [`ECDSA_do_sign`]: https://www.openssl.org/docs/man1.1.0/crypto/ECDSA_do_sign.html
|
||||
pub fn sign<T>(data: &[u8], eckey: &EcKeyRef<T>) -> Result<EcdsaSig, ErrorStack>
|
||||
where
|
||||
T: HasPrivate,
|
||||
{
|
||||
unsafe {
|
||||
assert!(data.len() <= c_int::MAX as usize);
|
||||
assert!(data.len() <= c_int::max_value() as usize);
|
||||
let sig = cvt_p(ffi::ECDSA_do_sign(
|
||||
data.as_ptr(),
|
||||
data.len(),
|
||||
data.len() as size_t,
|
||||
eckey.as_ptr(),
|
||||
))?;
|
||||
Ok(EcdsaSig::from_ptr(sig))
|
||||
Ok(EcdsaSig::from_ptr(sig as *mut _))
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a new `EcdsaSig` by setting the `r` and `s` values associated with a
|
||||
/// ECDSA signature.
|
||||
#[corresponds(ECDSA_SIG_set0)]
|
||||
///
|
||||
/// OpenSSL documentation at [`ECDSA_SIG_set0`]
|
||||
///
|
||||
/// [`ECDSA_SIG_set0`]: https://www.openssl.org/docs/man1.1.0/crypto/ECDSA_SIG_set0.html
|
||||
pub fn from_private_components(r: BigNum, s: BigNum) -> Result<EcdsaSig, ErrorStack> {
|
||||
unsafe {
|
||||
let sig = cvt_p(ffi::ECDSA_SIG_new())?;
|
||||
ECDSA_SIG_set0(sig, r.as_ptr(), s.as_ptr());
|
||||
mem::forget((r, s));
|
||||
Ok(EcdsaSig::from_ptr(sig))
|
||||
Ok(EcdsaSig::from_ptr(sig as *mut _))
|
||||
}
|
||||
}
|
||||
|
||||
from_der! {
|
||||
/// Decodes a DER-encoded ECDSA signature.
|
||||
#[corresponds(d2i_ECDSA_SIG)]
|
||||
///
|
||||
/// This corresponds to [`d2i_ECDSA_SIG`].
|
||||
///
|
||||
/// [`d2i_ECDSA_SIG`]: https://www.openssl.org/docs/man1.1.0/crypto/d2i_ECDSA_SIG.html
|
||||
from_der,
|
||||
EcdsaSig,
|
||||
ffi::d2i_ECDSA_SIG,
|
||||
@ -68,22 +76,28 @@ impl EcdsaSig {
|
||||
impl EcdsaSigRef {
|
||||
to_der! {
|
||||
/// Serializes the ECDSA signature into a DER-encoded ECDSASignature structure.
|
||||
#[corresponds(i2d_ECDSA_SIG)]
|
||||
///
|
||||
/// This corresponds to [`i2d_ECDSA_SIG`].
|
||||
///
|
||||
/// [`i2d_ECDSA_SIG`]: https://www.openssl.org/docs/man1.1.0/crypto/i2d_ECDSA_SIG.html
|
||||
to_der,
|
||||
ffi::i2d_ECDSA_SIG
|
||||
}
|
||||
|
||||
/// Verifies if the signature is a valid ECDSA signature using the given public key.
|
||||
#[corresponds(ECDSA_do_verify)]
|
||||
///
|
||||
/// OpenSSL documentation at [`ECDSA_do_verify`]
|
||||
///
|
||||
/// [`ECDSA_do_verify`]: https://www.openssl.org/docs/man1.1.0/crypto/ECDSA_do_verify.html
|
||||
pub fn verify<T>(&self, data: &[u8], eckey: &EcKeyRef<T>) -> Result<bool, ErrorStack>
|
||||
where
|
||||
T: HasPublic,
|
||||
{
|
||||
unsafe {
|
||||
assert!(data.len() <= c_int::MAX as usize);
|
||||
assert!(data.len() <= c_int::max_value() as usize);
|
||||
cvt_n(ffi::ECDSA_do_verify(
|
||||
data.as_ptr(),
|
||||
data.len(),
|
||||
data.len() as size_t,
|
||||
self.as_ptr(),
|
||||
eckey.as_ptr(),
|
||||
))
|
||||
@ -92,24 +106,28 @@ impl EcdsaSigRef {
|
||||
}
|
||||
|
||||
/// Returns internal component: `r` of an `EcdsaSig`. (See X9.62 or FIPS 186-2)
|
||||
#[corresponds(ECDSA_SIG_get0)]
|
||||
#[must_use]
|
||||
///
|
||||
/// OpenSSL documentation at [`ECDSA_SIG_get0`]
|
||||
///
|
||||
/// [`ECDSA_SIG_get0`]: https://www.openssl.org/docs/man1.1.0/crypto/ECDSA_SIG_get0.html
|
||||
pub fn r(&self) -> &BigNumRef {
|
||||
unsafe {
|
||||
let mut r = ptr::null();
|
||||
ECDSA_SIG_get0(self.as_ptr(), &mut r, ptr::null_mut());
|
||||
BigNumRef::from_ptr(r.cast_mut())
|
||||
BigNumRef::from_ptr(r as *mut _)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns internal components: `s` of an `EcdsaSig`. (See X9.62 or FIPS 186-2)
|
||||
#[corresponds(ECDSA_SIG_get0)]
|
||||
#[must_use]
|
||||
///
|
||||
/// OpenSSL documentation at [`ECDSA_SIG_get0`]
|
||||
///
|
||||
/// [`ECDSA_SIG_get0`]: https://www.openssl.org/docs/man1.1.0/crypto/ECDSA_SIG_get0.html
|
||||
pub fn s(&self) -> &BigNumRef {
|
||||
unsafe {
|
||||
let mut s = ptr::null();
|
||||
ECDSA_SIG_get0(self.as_ptr(), ptr::null_mut(), &mut s);
|
||||
BigNumRef::from_ptr(s.cast_mut())
|
||||
BigNumRef::from_ptr(s as *mut _)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,12 +15,10 @@
|
||||
//! Err(e) => println!("Parsing Error: {:?}", e),
|
||||
//! }
|
||||
//! ```
|
||||
use libc::{c_char, c_int, c_uint};
|
||||
use openssl_macros::corresponds;
|
||||
use libc::{c_char, c_uint};
|
||||
use std::borrow::Cow;
|
||||
use std::error;
|
||||
use std::ffi::CStr;
|
||||
use std::ffi::CString;
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use std::ptr;
|
||||
@ -28,8 +26,6 @@ use std::str;
|
||||
|
||||
use crate::ffi;
|
||||
|
||||
pub use crate::ffi::ErrLib;
|
||||
|
||||
/// Collection of [`Error`]s from OpenSSL.
|
||||
///
|
||||
/// [`Error`]: struct.Error.html
|
||||
@ -37,12 +33,7 @@ pub use crate::ffi::ErrLib;
|
||||
pub struct ErrorStack(Vec<Error>);
|
||||
|
||||
impl ErrorStack {
|
||||
/// Pops the contents of the OpenSSL error stack, and returns it.
|
||||
///
|
||||
/// This should be used only immediately after calling Boring FFI functions,
|
||||
/// otherwise the stack may be empty or a leftover from unrelated calls.
|
||||
#[corresponds(ERR_get_error_line_data)]
|
||||
#[must_use = "Use ErrorStack::clear() to drop the error stack"]
|
||||
/// Returns the contents of the OpenSSL error stack.
|
||||
pub fn get() -> ErrorStack {
|
||||
let mut vec = vec![];
|
||||
while let Some(err) = Error::get() {
|
||||
@ -52,37 +43,15 @@ impl ErrorStack {
|
||||
}
|
||||
|
||||
/// Pushes the errors back onto the OpenSSL error stack.
|
||||
#[corresponds(ERR_put_error)]
|
||||
pub fn put(&self) {
|
||||
for error in self.errors() {
|
||||
error.put();
|
||||
}
|
||||
}
|
||||
|
||||
/// Used to report errors from the Rust crate
|
||||
#[cold]
|
||||
pub(crate) fn internal_error(err: impl error::Error) -> Self {
|
||||
Self(vec![Error::new_internal(Data::String(err.to_string()))])
|
||||
}
|
||||
|
||||
/// Used to report errors from the Rust crate
|
||||
#[cold]
|
||||
pub(crate) fn internal_error_str(message: &'static str) -> Self {
|
||||
Self(vec![Error::new_internal(Data::Static(message))])
|
||||
}
|
||||
|
||||
/// Empties the current thread's error queue.
|
||||
#[corresponds(ERR_clear_error)]
|
||||
pub(crate) fn clear() {
|
||||
unsafe {
|
||||
ffi::ERR_clear_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ErrorStack {
|
||||
/// Returns the errors in the stack.
|
||||
#[must_use]
|
||||
pub fn errors(&self) -> &[Error] {
|
||||
&self.0
|
||||
}
|
||||
@ -100,13 +69,7 @@ impl fmt::Display for ErrorStack {
|
||||
fmt.write_str(" ")?;
|
||||
}
|
||||
first = false;
|
||||
write!(
|
||||
fmt,
|
||||
"[{}]",
|
||||
err.reason()
|
||||
.or_else(|| err.library())
|
||||
.unwrap_or("unknown reason")
|
||||
)?;
|
||||
write!(fmt, "[{}]", err.reason().unwrap_or("unknown reason"))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -116,7 +79,7 @@ impl error::Error for ErrorStack {}
|
||||
|
||||
impl From<ErrorStack> for io::Error {
|
||||
fn from(e: ErrorStack) -> io::Error {
|
||||
io::Error::other(e)
|
||||
io::Error::new(io::ErrorKind::Other, e)
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,32 +89,20 @@ impl From<ErrorStack> for fmt::Error {
|
||||
}
|
||||
}
|
||||
|
||||
/// A detailed error reported as part of an [`ErrorStack`].
|
||||
/// An error reported from OpenSSL.
|
||||
#[derive(Clone)]
|
||||
pub struct Error {
|
||||
code: c_uint,
|
||||
file: *const c_char,
|
||||
line: c_uint,
|
||||
data: Data,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum Data {
|
||||
None,
|
||||
CString(CString),
|
||||
String(String),
|
||||
Static(&'static str),
|
||||
data: Option<Cow<'static, str>>,
|
||||
}
|
||||
|
||||
unsafe impl Sync for Error {}
|
||||
unsafe impl Send for Error {}
|
||||
|
||||
static BORING_INTERNAL: &CStr = c"boring-rust";
|
||||
|
||||
impl Error {
|
||||
/// Pops the first error off the OpenSSL error stack.
|
||||
#[must_use = "Use ErrorStack::clear() to drop the error stack"]
|
||||
#[corresponds(ERR_get_error_line_data)]
|
||||
/// Returns the first error on the OpenSSL error stack.
|
||||
pub fn get() -> Option<Error> {
|
||||
unsafe {
|
||||
ffi::init();
|
||||
@ -166,9 +117,12 @@ impl Error {
|
||||
// The memory referenced by data is only valid until that slot is overwritten
|
||||
// in the error stack, so we'll need to copy it off if it's dynamic
|
||||
let data = if flags & ffi::ERR_FLAG_STRING != 0 {
|
||||
Data::CString(CStr::from_ptr(data.cast()).to_owned())
|
||||
let bytes = CStr::from_ptr(data as *const _).to_bytes();
|
||||
let data = str::from_utf8(bytes).unwrap();
|
||||
let data = Cow::Owned(data.to_string());
|
||||
Some(data)
|
||||
} else {
|
||||
Data::None
|
||||
None
|
||||
};
|
||||
Some(Error {
|
||||
code,
|
||||
@ -182,7 +136,6 @@ impl Error {
|
||||
}
|
||||
|
||||
/// Pushes the error back onto the OpenSSL error stack.
|
||||
#[corresponds(ERR_put_error)]
|
||||
pub fn put(&self) {
|
||||
unsafe {
|
||||
ffi::ERR_put_error(
|
||||
@ -192,165 +145,103 @@ impl Error {
|
||||
self.file,
|
||||
self.line,
|
||||
);
|
||||
if let Some(cstr) = self.data_cstr() {
|
||||
ffi::ERR_add_error_data(1, cstr.as_ptr().cast_mut());
|
||||
let ptr = match self.data {
|
||||
Some(Cow::Borrowed(data)) => Some(data.as_ptr() as *mut c_char),
|
||||
Some(Cow::Owned(ref data)) => {
|
||||
let ptr = ffi::OPENSSL_malloc((data.len() + 1) as _) as *mut c_char;
|
||||
if ptr.is_null() {
|
||||
None
|
||||
} else {
|
||||
ptr::copy_nonoverlapping(data.as_ptr(), ptr as *mut u8, data.len());
|
||||
*ptr.add(data.len()) = 0;
|
||||
Some(ptr)
|
||||
}
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
if let Some(ptr) = ptr {
|
||||
ffi::ERR_add_error_data(1, ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get `{lib}_R_{reason}` reason code for the given library, or `None` if the error is from a different library.
|
||||
///
|
||||
/// Libraries are identified by [`ERR_LIB_{name}`(ffi::ERR_LIB_SSL) constants.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[track_caller]
|
||||
pub fn library_reason(&self, library_code: ErrLib) -> Option<c_int> {
|
||||
debug_assert!(library_code.0 < ffi::ERR_NUM_LIBS.0);
|
||||
(self.library_code() == library_code.0 as c_int).then_some(self.reason_code())
|
||||
}
|
||||
|
||||
/// Returns a raw OpenSSL **packed** error code for this error, which **can't be reliably compared to any error constant**.
|
||||
///
|
||||
/// Use [`Error::library_code()`] and [`Error::library_reason()`] instead.
|
||||
/// Packed error codes are different than [SSL error codes](crate::ssl::ErrorCode).
|
||||
#[must_use]
|
||||
#[deprecated(note = "use library_reason() to compare error codes")]
|
||||
/// Returns the raw OpenSSL error code for this error.
|
||||
pub fn code(&self) -> c_uint {
|
||||
self.code
|
||||
}
|
||||
|
||||
/// Returns the name of the library reporting the error, if available.
|
||||
#[must_use]
|
||||
pub fn library(&self) -> Option<&'static str> {
|
||||
if self.is_internal() {
|
||||
return None;
|
||||
}
|
||||
unsafe {
|
||||
let cstr = ffi::ERR_lib_error_string(self.code);
|
||||
if cstr.is_null() {
|
||||
return None;
|
||||
}
|
||||
CStr::from_ptr(cstr.cast())
|
||||
.to_str()
|
||||
.ok()
|
||||
.filter(|&msg| msg != "unknown library")
|
||||
let bytes = CStr::from_ptr(cstr as *const _).to_bytes();
|
||||
Some(str::from_utf8(bytes).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the raw OpenSSL error constant for the library reporting the error (`ERR_LIB_{name}`).
|
||||
///
|
||||
/// Error [reason codes](Error::library_reason) are not globally unique, but scoped to each library.
|
||||
#[must_use]
|
||||
pub fn library_code(&self) -> c_int {
|
||||
ffi::ERR_GET_LIB(self.code)
|
||||
}
|
||||
|
||||
/// Returns `None`. Boring doesn't use function codes.
|
||||
/// Returns the name of the function reporting the error.
|
||||
pub fn function(&self) -> Option<&'static str> {
|
||||
None
|
||||
unsafe {
|
||||
let cstr = ffi::ERR_func_error_string(self.code);
|
||||
if cstr.is_null() {
|
||||
return None;
|
||||
}
|
||||
let bytes = CStr::from_ptr(cstr as *const _).to_bytes();
|
||||
Some(str::from_utf8(bytes).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the reason for the error.
|
||||
#[must_use]
|
||||
pub fn reason(&self) -> Option<&str> {
|
||||
if self.is_internal() {
|
||||
return self.data();
|
||||
}
|
||||
pub fn reason(&self) -> Option<&'static str> {
|
||||
unsafe {
|
||||
let cstr = ffi::ERR_reason_error_string(self.code);
|
||||
if cstr.is_null() {
|
||||
return None;
|
||||
}
|
||||
CStr::from_ptr(cstr.cast()).to_str().ok()
|
||||
let bytes = CStr::from_ptr(cstr as *const _).to_bytes();
|
||||
Some(str::from_utf8(bytes).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns [library-specific](Error::library_code) reason code corresponding to some of the `{lib}_R_{reason}` constants.
|
||||
///
|
||||
/// Reason codes are ambiguous, and different libraries reuse the same numeric values for different errors.
|
||||
/// Use [`Error::library_reason`] to compare error codes.
|
||||
///
|
||||
/// For `ERR_LIB_SYS` the reason code is `errno`. `ERR_LIB_USER` can use any values.
|
||||
/// Other libraries may use [`ERR_R_*`](ffi::ERR_R_FATAL) or their own codes.
|
||||
#[must_use]
|
||||
pub fn reason_code(&self) -> c_int {
|
||||
ffi::ERR_GET_REASON(self.code)
|
||||
}
|
||||
|
||||
/// Returns the name of the source file which encountered the error.
|
||||
#[must_use]
|
||||
pub fn file(&self) -> &'static str {
|
||||
unsafe {
|
||||
if self.file.is_null() {
|
||||
return "";
|
||||
}
|
||||
CStr::from_ptr(self.file.cast())
|
||||
.to_str()
|
||||
.unwrap_or_default()
|
||||
assert!(!self.file.is_null());
|
||||
let bytes = CStr::from_ptr(self.file as *const _).to_bytes();
|
||||
str::from_utf8(bytes).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the line in the source file which encountered the error.
|
||||
///
|
||||
/// 0 if unknown
|
||||
#[allow(clippy::unnecessary_cast)]
|
||||
#[must_use]
|
||||
pub fn line(&self) -> u32 {
|
||||
self.line as u32
|
||||
}
|
||||
|
||||
/// Returns additional data describing the error.
|
||||
#[must_use]
|
||||
#[allow(clippy::option_as_ref_deref)]
|
||||
pub fn data(&self) -> Option<&str> {
|
||||
match &self.data {
|
||||
Data::None => None,
|
||||
Data::CString(cstring) => cstring.to_str().ok(),
|
||||
Data::String(s) => Some(s),
|
||||
Data::Static(s) => Some(s),
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
fn data_cstr(&self) -> Option<Cow<'_, CStr>> {
|
||||
let s = match &self.data {
|
||||
Data::None => return None,
|
||||
Data::CString(cstr) => return Some(Cow::Borrowed(cstr)),
|
||||
Data::String(s) => s.as_str(),
|
||||
Data::Static(s) => s,
|
||||
};
|
||||
CString::new(s).ok().map(Cow::Owned)
|
||||
}
|
||||
|
||||
fn new_internal(msg: Data) -> Self {
|
||||
Self {
|
||||
code: ffi::ERR_PACK(ffi::ERR_LIB_NONE.0 as _, 0, 0) as _,
|
||||
file: BORING_INTERNAL.as_ptr(),
|
||||
line: 0,
|
||||
data: msg,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_internal(&self) -> bool {
|
||||
std::ptr::eq(self.file, BORING_INTERNAL.as_ptr())
|
||||
self.data.as_ref().map(|s| &**s)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Error {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
let mut builder = fmt.debug_struct("Error");
|
||||
builder.field("code", &self.code);
|
||||
if !self.is_internal() {
|
||||
if let Some(library) = self.library() {
|
||||
builder.field("library", &library);
|
||||
}
|
||||
builder.field("library_code", &self.library_code());
|
||||
if let Some(reason) = self.reason() {
|
||||
builder.field("reason", &reason);
|
||||
}
|
||||
builder.field("reason_code", &self.reason_code());
|
||||
builder.field("file", &self.file());
|
||||
builder.field("line", &self.line());
|
||||
builder.field("code", &self.code());
|
||||
if let Some(library) = self.library() {
|
||||
builder.field("library", &library);
|
||||
}
|
||||
if let Some(function) = self.function() {
|
||||
builder.field("function", &function);
|
||||
}
|
||||
if let Some(reason) = self.reason() {
|
||||
builder.field("reason", &reason);
|
||||
}
|
||||
builder.field("file", &self.file());
|
||||
builder.field("line", &self.line());
|
||||
if let Some(data) = self.data() {
|
||||
builder.field("data", &data);
|
||||
}
|
||||
@ -364,7 +255,7 @@ impl fmt::Display for Error {
|
||||
fmt,
|
||||
"{}\n\nCode: {:08X}\nLoc: {}:{}",
|
||||
self.reason().unwrap_or("unknown TLS error"),
|
||||
&self.code,
|
||||
self.code(),
|
||||
self.file(),
|
||||
self.line()
|
||||
)
|
||||
@ -372,14 +263,3 @@ impl fmt::Display for Error {
|
||||
}
|
||||
|
||||
impl error::Error for Error {}
|
||||
|
||||
#[test]
|
||||
fn internal_err() {
|
||||
let e = ErrorStack::internal_error(io::Error::other("hello, boring"));
|
||||
assert_eq!(1, e.errors().len());
|
||||
assert!(e.to_string().contains("hello, boring"), "{e} {e:?}");
|
||||
|
||||
e.put();
|
||||
let e = ErrorStack::get();
|
||||
assert!(e.to_string().contains("hello, boring"), "{e} {e:?}");
|
||||
}
|
||||
|
||||
@ -21,13 +21,11 @@ impl<T, U> Index<T, U> {
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that the index correctly maps to a `U` value stored in a `T`.
|
||||
#[must_use]
|
||||
pub unsafe fn from_raw(idx: c_int) -> Index<T, U> {
|
||||
Index(idx, PhantomData)
|
||||
}
|
||||
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
#[must_use]
|
||||
pub fn as_raw(&self) -> c_int {
|
||||
self.0
|
||||
}
|
||||
|
||||
@ -3,12 +3,20 @@
|
||||
//! See [OpenSSL's documentation] for details.
|
||||
//!
|
||||
//! [OpenSSL's documentation]: https://www.openssl.org/docs/fips/UserGuide-2.0.pdf
|
||||
use crate::cvt;
|
||||
use crate::error::ErrorStack;
|
||||
use crate::ffi;
|
||||
use openssl_macros::corresponds;
|
||||
|
||||
/// Moves the library into or out of the FIPS 140-2 mode of operation.
|
||||
///
|
||||
/// This corresponds to `FIPS_mode_set`.
|
||||
pub fn enable(enabled: bool) -> Result<(), ErrorStack> {
|
||||
unsafe { cvt(ffi::FIPS_mode_set(enabled as _)).map(|_| ()) }
|
||||
}
|
||||
|
||||
/// Determines if the library is running in the FIPS 140-2 mode of operation.
|
||||
#[corresponds(FIPS_mode)]
|
||||
#[must_use]
|
||||
///
|
||||
/// This corresponds to `FIPS_mode`.
|
||||
pub fn enabled() -> bool {
|
||||
unsafe { ffi::FIPS_mode() != 0 }
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use openssl_macros::corresponds;
|
||||
use std::ffi::c_uint;
|
||||
use crate::ffi;
|
||||
use std::convert::TryInto;
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use std::io::prelude::*;
|
||||
@ -7,10 +7,8 @@ use std::ops::{Deref, DerefMut};
|
||||
use std::ptr;
|
||||
|
||||
use crate::error::ErrorStack;
|
||||
use crate::ffi;
|
||||
use crate::ffi::{EVP_MD_CTX_free, EVP_MD_CTX_new};
|
||||
use crate::nid::Nid;
|
||||
use crate::try_int;
|
||||
use crate::{cvt, cvt_p};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
@ -22,14 +20,15 @@ impl MessageDigest {
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure the pointer is valid.
|
||||
#[must_use]
|
||||
pub unsafe fn from_ptr(x: *const ffi::EVP_MD) -> Self {
|
||||
MessageDigest(x)
|
||||
}
|
||||
|
||||
/// Returns the `MessageDigest` corresponding to an `Nid`.
|
||||
#[corresponds(EVP_get_digestbynid)]
|
||||
#[must_use]
|
||||
///
|
||||
/// This corresponds to [`EVP_get_digestbynid`].
|
||||
///
|
||||
/// [`EVP_get_digestbynid`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_DigestInit.html
|
||||
pub fn from_nid(type_: Nid) -> Option<MessageDigest> {
|
||||
unsafe {
|
||||
let ptr = ffi::EVP_get_digestbynid(type_.as_raw());
|
||||
@ -41,57 +40,43 @@ impl MessageDigest {
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn md5() -> MessageDigest {
|
||||
unsafe { MessageDigest(ffi::EVP_md5()) }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn sha1() -> MessageDigest {
|
||||
unsafe { MessageDigest(ffi::EVP_sha1()) }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn sha224() -> MessageDigest {
|
||||
unsafe { MessageDigest(ffi::EVP_sha224()) }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn sha256() -> MessageDigest {
|
||||
unsafe { MessageDigest(ffi::EVP_sha256()) }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn sha384() -> MessageDigest {
|
||||
unsafe { MessageDigest(ffi::EVP_sha384()) }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn sha512() -> MessageDigest {
|
||||
unsafe { MessageDigest(ffi::EVP_sha512()) }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn sha512_256() -> MessageDigest {
|
||||
unsafe { MessageDigest(ffi::EVP_sha512_256()) }
|
||||
}
|
||||
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
#[must_use]
|
||||
pub fn as_ptr(&self) -> *const ffi::EVP_MD {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// The size of the digest in bytes.
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
#[must_use]
|
||||
pub fn size(&self) -> usize {
|
||||
unsafe { ffi::EVP_MD_size(self.0) }
|
||||
unsafe { ffi::EVP_MD_size(self.0) as usize }
|
||||
}
|
||||
|
||||
/// The name of the digest.
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
#[must_use]
|
||||
pub fn type_(&self) -> Nid {
|
||||
Nid::from_raw(unsafe { ffi::EVP_MD_type(self.0) })
|
||||
}
|
||||
@ -196,7 +181,7 @@ impl Hasher {
|
||||
unsafe {
|
||||
cvt(ffi::EVP_DigestUpdate(
|
||||
self.ctx,
|
||||
data.as_ptr().cast_mut().cast(),
|
||||
data.as_ptr() as *mut _,
|
||||
data.len(),
|
||||
))?;
|
||||
}
|
||||
@ -210,7 +195,7 @@ impl Hasher {
|
||||
self.init()?;
|
||||
}
|
||||
unsafe {
|
||||
let mut len = try_int(ffi::EVP_MAX_MD_SIZE)?;
|
||||
let mut len = ffi::EVP_MAX_MD_SIZE.try_into().unwrap();
|
||||
let mut buf = [0; ffi::EVP_MAX_MD_SIZE as usize];
|
||||
cvt(ffi::EVP_DigestFinal_ex(
|
||||
self.ctx,
|
||||
@ -220,7 +205,7 @@ impl Hasher {
|
||||
self.state = Finalized;
|
||||
Ok(DigestBytes {
|
||||
buf,
|
||||
len: try_int(len)?,
|
||||
len: len as usize,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -320,7 +305,7 @@ impl DerefMut for DigestBytes {
|
||||
impl AsRef<[u8]> for DigestBytes {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self
|
||||
self.deref()
|
||||
}
|
||||
}
|
||||
|
||||
@ -344,46 +329,6 @@ pub fn hash_xof(t: MessageDigest, data: &[u8], buf: &mut [u8]) -> Result<(), Err
|
||||
h.finish_xof(buf)
|
||||
}
|
||||
|
||||
/// Computes HMAC with SHA-256 digest.
|
||||
pub fn hmac_sha256(key: &[u8], data: &[u8]) -> Result<[u8; 32], ErrorStack> {
|
||||
hmac(MessageDigest::sha256(), key, data)
|
||||
}
|
||||
|
||||
/// Computes HMAC with SHA-512 digest.
|
||||
pub fn hmac_sha512(key: &[u8], data: &[u8]) -> Result<[u8; 64], ErrorStack> {
|
||||
hmac(MessageDigest::sha512(), key, data)
|
||||
}
|
||||
|
||||
/// Computes HMAC with SHA-1 digest.
|
||||
pub fn hmac_sha1(key: &[u8], data: &[u8]) -> Result<[u8; 20], ErrorStack> {
|
||||
hmac(MessageDigest::sha1(), key, data)
|
||||
}
|
||||
|
||||
pub(crate) fn hmac<const N: usize>(
|
||||
digest: MessageDigest,
|
||||
key: &[u8],
|
||||
data: &[u8],
|
||||
) -> Result<[u8; N], ErrorStack> {
|
||||
let mut out = [0u8; N];
|
||||
let mut out_len: c_uint = 0;
|
||||
|
||||
cvt_p(unsafe {
|
||||
ffi::HMAC(
|
||||
digest.as_ptr(),
|
||||
key.as_ptr().cast(),
|
||||
key.len(),
|
||||
data.as_ptr(),
|
||||
data.len(),
|
||||
out.as_mut_ptr(),
|
||||
&mut out_len,
|
||||
)
|
||||
})?;
|
||||
|
||||
assert_eq!(out_len as usize, N);
|
||||
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use hex::{self, FromHex};
|
||||
@ -422,52 +367,9 @@ mod tests {
|
||||
),
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn test_hmac_sha256() {
|
||||
let hmac = hmac_sha256(b"That's a secret".as_slice(), b"Hello world!".as_slice()).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
hmac,
|
||||
[
|
||||
0x50, 0xbb, 0x7d, 0xd2, 0xb8, 0xd2, 0x51, 0x5d, 0xb4, 0x2b, 0x70, 0xc3, 0x0b, 0xfd,
|
||||
0xf5, 0x4c, 0x38, 0xa7, 0xae, 0x99, 0x07, 0xe5, 0x80, 0x0f, 0x8b, 0xe8, 0x34, 0x83,
|
||||
0x55, 0x5f, 0xd0, 0xd4
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hmac_sha512() {
|
||||
let hmac = hmac_sha512(b"That's a secret".as_slice(), b"Hello world!".as_slice()).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
hmac,
|
||||
[
|
||||
0xc2, 0x7a, 0x7f, 0x7c, 0x17, 0x4c, 0x87, 0x70, 0x7f, 0x8c, 0xb7, 0x90, 0x01, 0xba,
|
||||
0x23, 0x0e, 0xb7, 0xd6, 0x1a, 0xfd, 0x50, 0xea, 0x40, 0x43, 0x5f, 0x03, 0x25, 0x5a,
|
||||
0x22, 0xb7, 0x8d, 0x0e, 0xba, 0x0d, 0x47, 0xb8, 0xef, 0xaa, 0xbf, 0xb1, 0xe7, 0xad,
|
||||
0xc5, 0xd1, 0xe5, 0xba, 0x4d, 0xa5, 0xd1, 0xbb, 0x5e, 0xe3, 0xc7, 0x27, 0x0c, 0x57,
|
||||
0x76, 0xd4, 0x2f, 0xb6, 0x5c, 0x21, 0xb7, 0x3a
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hmac_sha1() {
|
||||
let hmac = hmac_sha1(b"That's a secret".as_slice(), b"Hello world!".as_slice()).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
hmac,
|
||||
[
|
||||
0xe1, 0x06, 0x76, 0x46, 0x3b, 0x82, 0x67, 0xa1, 0xae, 0xe5, 0x1c, 0xfa, 0xee, 0x36,
|
||||
0x1d, 0x4b, 0xd4, 0x41, 0x6e, 0x37
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_md5() {
|
||||
for test in &MD5_TESTS {
|
||||
for test in MD5_TESTS.iter() {
|
||||
hash_test(MessageDigest::md5(), test);
|
||||
}
|
||||
}
|
||||
@ -475,7 +377,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_md5_recycle() {
|
||||
let mut h = Hasher::new(MessageDigest::md5()).unwrap();
|
||||
for test in &MD5_TESTS {
|
||||
for test in MD5_TESTS.iter() {
|
||||
hash_recycle_test(&mut h, test);
|
||||
}
|
||||
}
|
||||
@ -526,23 +428,11 @@ mod tests {
|
||||
fn test_sha1() {
|
||||
let tests = [("616263", "a9993e364706816aba3e25717850c26c9cd0d89d")];
|
||||
|
||||
for test in &tests {
|
||||
for test in tests.iter() {
|
||||
hash_test(MessageDigest::sha1(), test);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sha224() {
|
||||
let tests = [(
|
||||
"616263",
|
||||
"23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7",
|
||||
)];
|
||||
|
||||
for test in &tests {
|
||||
hash_test(MessageDigest::sha224(), test);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sha256() {
|
||||
let tests = [(
|
||||
@ -550,36 +440,11 @@ mod tests {
|
||||
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
|
||||
)];
|
||||
|
||||
for test in &tests {
|
||||
for test in tests.iter() {
|
||||
hash_test(MessageDigest::sha256(), test);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sha512() {
|
||||
let tests = [(
|
||||
"616263",
|
||||
"ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2\
|
||||
192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f",
|
||||
)];
|
||||
|
||||
for test in &tests {
|
||||
hash_test(MessageDigest::sha512(), test);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sha512_256() {
|
||||
let tests = [(
|
||||
"616263",
|
||||
"53048e2681941ef99b2e29b76b4c7dabe4c2d0c634fc6d46e0e2f13107e7af23",
|
||||
)];
|
||||
|
||||
for test in &tests {
|
||||
hash_test(MessageDigest::sha512_256(), test);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_nid() {
|
||||
assert_eq!(
|
||||
|
||||
@ -1,130 +0,0 @@
|
||||
use crate::cvt;
|
||||
use crate::error::ErrorStack;
|
||||
use crate::foreign_types::ForeignTypeRef;
|
||||
use crate::hash::MessageDigest;
|
||||
use openssl_macros::corresponds;
|
||||
|
||||
foreign_type_and_impl_send_sync! {
|
||||
type CType = ffi::HMAC_CTX;
|
||||
fn drop = ffi::HMAC_CTX_free;
|
||||
|
||||
pub struct HmacCtx;
|
||||
}
|
||||
|
||||
impl HmacCtxRef {
|
||||
/// Configures HmacCtx to use `md` as the hash function and `key` as the key.
|
||||
///
|
||||
#[corresponds(HMAC_Init_ex)]
|
||||
pub fn init(&mut self, key: &[u8], md: &MessageDigest) -> Result<(), ErrorStack> {
|
||||
ffi::init();
|
||||
|
||||
unsafe {
|
||||
cvt(ffi::HMAC_Init_ex(
|
||||
self.as_ptr(),
|
||||
key.as_ptr().cast(),
|
||||
key.len(),
|
||||
md.as_ptr(),
|
||||
// ENGINE api is deprecated
|
||||
core::ptr::null_mut(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides an init-update-finalize API for HMAC.
|
||||
pub struct Hmac(*mut ffi::HMAC_CTX);
|
||||
|
||||
impl Hmac {
|
||||
/// Creates a new HMAC object with the given key and hash algorithm.
|
||||
pub fn init(key: &[u8], md: &MessageDigest) -> Result<Hmac, ErrorStack> {
|
||||
ffi::init();
|
||||
|
||||
let ctx = unsafe {
|
||||
let ctx = ffi::HMAC_CTX_new();
|
||||
cvt(ffi::HMAC_Init_ex(
|
||||
ctx,
|
||||
key.as_ptr().cast(),
|
||||
key.len(),
|
||||
md.as_ptr(),
|
||||
// ENGINE api is deprecated
|
||||
core::ptr::null_mut(),
|
||||
))?;
|
||||
ctx
|
||||
};
|
||||
|
||||
Ok(Hmac(ctx))
|
||||
}
|
||||
|
||||
/// Updates the HMAC input.
|
||||
pub fn update(&mut self, data: &[u8]) -> Result<(), ErrorStack> {
|
||||
unsafe { cvt(ffi::HMAC_Update(self.0, data.as_ptr().cast(), data.len())) }
|
||||
}
|
||||
|
||||
/// Finalizes the HMAC and returns the output.
|
||||
pub fn finalize(self) -> Result<Vec<u8>, ErrorStack> {
|
||||
let out_len = unsafe { ffi::HMAC_size(self.0) };
|
||||
let mut out = vec![0; out_len];
|
||||
unsafe {
|
||||
cvt(ffi::HMAC_Final(
|
||||
self.0,
|
||||
out.as_mut_ptr().cast(),
|
||||
// ENGINE api is deprecated
|
||||
core::ptr::null_mut(),
|
||||
))?;
|
||||
}
|
||||
Ok(out)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Hmac {
|
||||
fn drop(&mut self) {
|
||||
unsafe { ffi::HMAC_CTX_free(self.0) }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::hash;
|
||||
|
||||
use super::*;
|
||||
|
||||
fn test<const N: usize>(md: MessageDigest) {
|
||||
assert_eq!(N, md.size());
|
||||
let key = vec![0; N];
|
||||
let message_parts = [
|
||||
b"hello".to_vec(),
|
||||
b"world!".to_vec(),
|
||||
b"".to_vec(),
|
||||
vec![0; 23],
|
||||
b"fella guy".to_vec(),
|
||||
];
|
||||
let message = message_parts.concat();
|
||||
|
||||
let mut hmac = Hmac::init(&key, &md).unwrap();
|
||||
for part in &message_parts {
|
||||
hmac.update(part).unwrap();
|
||||
}
|
||||
let res = hmac.finalize().unwrap();
|
||||
assert_eq!(res, hash::hmac::<N>(md, &key, &message).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sha1() {
|
||||
test::<20>(MessageDigest::sha1());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sha256() {
|
||||
test::<32>(MessageDigest::sha256());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sha384() {
|
||||
test::<48>(MessageDigest::sha384());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sha512() {
|
||||
test::<64>(MessageDigest::sha512());
|
||||
}
|
||||
}
|
||||
@ -1,31 +0,0 @@
|
||||
use crate::error::ErrorStack;
|
||||
use crate::{cvt_0i, cvt_p, ffi};
|
||||
|
||||
use foreign_types::ForeignType;
|
||||
|
||||
foreign_type_and_impl_send_sync! {
|
||||
type CType = ffi::EVP_HPKE_KEY;
|
||||
fn drop = ffi::EVP_HPKE_KEY_free;
|
||||
|
||||
pub struct HpkeKey;
|
||||
}
|
||||
|
||||
impl HpkeKey {
|
||||
/// Allocates and initializes a key with the `EVP_HPKE_KEY` type using the
|
||||
/// `EVP_hpke_x25519_hkdf_sha256` KEM algorithm.
|
||||
pub fn dhkem_p256_sha256(pkey: &[u8]) -> Result<HpkeKey, ErrorStack> {
|
||||
unsafe {
|
||||
ffi::init();
|
||||
let hpke = cvt_p(ffi::EVP_HPKE_KEY_new()).map(|p| HpkeKey::from_ptr(p))?;
|
||||
|
||||
cvt_0i(ffi::EVP_HPKE_KEY_init(
|
||||
hpke.as_ptr(),
|
||||
ffi::EVP_hpke_x25519_hkdf_sha256(),
|
||||
pkey.as_ptr(),
|
||||
pkey.len(),
|
||||
))?;
|
||||
|
||||
Ok(hpke)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,117 +1,18 @@
|
||||
//! Bindings to BoringSSL
|
||||
//!
|
||||
//! This crate provides a safe interface to the BoringSSL cryptography library.
|
||||
//!
|
||||
//! # Versioning
|
||||
//!
|
||||
//! ## Crate versioning
|
||||
//!
|
||||
//! The crate and all the related crates (FFI bindings, etc.) are released simultaneously and all
|
||||
//! bumped to the same version disregard whether particular crate has any API changes or not.
|
||||
//! However, semantic versioning guarantees still hold, as all the crate versions will be updated
|
||||
//! based on the crate with most significant changes.
|
||||
//!
|
||||
//! ## BoringSSL version
|
||||
//!
|
||||
//! By default, the crate aims to statically link with the latest BoringSSL master branch.
|
||||
//! *Note*: any BoringSSL revision bumps will be released as a major version update of all crates.
|
||||
//!
|
||||
//! # Compilation and linking options
|
||||
//!
|
||||
//! ## Environment variables
|
||||
//!
|
||||
//! This crate uses various environment variables to tweak how boring is built. The variables
|
||||
//! are all prefixed by `BORING_BSSL_` for non-FIPS builds, and by `BORING_BSSL_FIPS_` for FIPS builds.
|
||||
//!
|
||||
//! ## Support for pre-built binaries or custom source
|
||||
//!
|
||||
//! While this crate can build BoringSSL on its own, you may want to provide pre-built binaries instead.
|
||||
//! To do so, specify the environment variable `BORING_BSSL{,_FIPS}_PATH` with the path to the binaries.
|
||||
//!
|
||||
//! You can also provide specific headers by setting `BORING_BSSL{,_FIPS}_INCLUDE_PATH`.
|
||||
//!
|
||||
//! _Notes_: The crate will look for headers in the`$BORING_BSSL{,_FIPS}_INCLUDE_PATH/openssl/`
|
||||
//! folder, make sure to place your headers there.
|
||||
//!
|
||||
//! In alternative a different path for the BoringSSL source code directory can be specified by setting
|
||||
//! `BORING_BSSL{,_FIPS}_SOURCE_PATH` which will automatically be compiled during the build process.
|
||||
//!
|
||||
//! _Warning_: When providing a different version of BoringSSL make sure to use a compatible one, the
|
||||
//! crate relies on the presence of certain functions.
|
||||
//!
|
||||
//! ## Building with a FIPS-validated module
|
||||
//!
|
||||
//! Only BoringCrypto module version `853ca1ea1168dff08011e5d42d94609cc0ca2e27`, as certified with
|
||||
//! [FIPS 140-2 certificate 4407](https://csrc.nist.gov/projects/cryptographic-module-validation-program/certificate/4407)
|
||||
//! is supported by this crate. Support is enabled by this crate's `fips` feature.
|
||||
//!
|
||||
//! `boring-sys` comes with a test that FIPS is enabled/disabled depending on the feature flag. You can run it as follows:
|
||||
//!
|
||||
//! ```bash
|
||||
//! $ cargo test --features fips fips::is_enabled
|
||||
//! ```
|
||||
//!
|
||||
//! ## Linking current BoringSSL version with precompiled FIPS-validated module (`bcm.o`)
|
||||
//!
|
||||
//! It's possible to link latest supported version of BoringSSL with FIPS-validated crypto module
|
||||
//! (`bcm.o`). To enable this compilation option one should enable `fips-link-precompiled`
|
||||
//! compilation feature and provide a `BORING_BSSL_FIPS_PRECOMPILED_BCM_O` env variable with a path to the
|
||||
//! precompiled FIPS-validated `bcm.o` module.
|
||||
//!
|
||||
//! Note that `BORING_BSSL_PRECOMPILED_BCM_O` is never used, as linking BoringSSL with precompiled non-FIPS
|
||||
//! module is not supported.
|
||||
//!
|
||||
//! ## Linking with a C++ standard library
|
||||
//!
|
||||
//! Recent versions of boringssl require some C++ standard library features, so boring needs to link
|
||||
//! with a STL implementation. This can be controlled using the BORING_BSSL_RUST_CPPLIB variable. If
|
||||
//! no library is specified, libc++ is used on macOS and iOS whereas libstdc++ is used on other Unix
|
||||
//! systems.
|
||||
//!
|
||||
//! # Optional patches
|
||||
//!
|
||||
//! ## Raw Public Key
|
||||
//!
|
||||
//! The crate can be compiled with [RawPublicKey](https://datatracker.ietf.org/doc/html/rfc7250)
|
||||
//! support by turning on `rpk` compilation feature.
|
||||
//!
|
||||
//! ## Experimental post-quantum cryptography
|
||||
//!
|
||||
//! The crate can be compiled with [post-quantum cryptography](https://blog.cloudflare.com/post-quantum-for-all/)
|
||||
//! support by turning on `post-quantum` compilation feature.
|
||||
//!
|
||||
//! Upstream BoringSSL support the post-quantum hybrid key agreement `X25519Kyber768Draft00`. Most
|
||||
//! users should stick to that one for now. Enabling this feature, adds a few other post-quantum key
|
||||
//! agreements:
|
||||
//!
|
||||
//! - `X25519MLKEM768` is the successor of `X25519Kyber768Draft00`. We expect servers to switch
|
||||
//! before the end of 2024.
|
||||
//! - `X25519Kyber768Draft00Old` is the same as `X25519Kyber768Draft00`, but under its old codepoint.
|
||||
//! - `X25519Kyber512Draft00`. Similar to `X25519Kyber768Draft00`, but uses level 1 parameter set for
|
||||
//! Kyber. Not recommended. It's useful to test whether the shorter ClientHello upsets fewer middle
|
||||
//! boxes.
|
||||
//! - `P256Kyber768Draft00`. Similar again to `X25519Kyber768Draft00`, but uses P256 as classical
|
||||
//! part. It uses a non-standard codepoint. Not recommended.
|
||||
//!
|
||||
//! Presently all these key agreements are deployed by Cloudflare, but we do not guarantee continued
|
||||
//! support for them.
|
||||
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
#[macro_use]
|
||||
extern crate foreign_types;
|
||||
extern crate boring_sys as ffi;
|
||||
extern crate libc;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate hex;
|
||||
|
||||
use std::ffi::{c_int, c_long, c_void};
|
||||
use std::num::NonZeroUsize;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use crate::ffi::init;
|
||||
|
||||
use libc::{c_int, size_t};
|
||||
|
||||
use crate::error::ErrorStack;
|
||||
|
||||
#[macro_use]
|
||||
@ -134,11 +35,7 @@ pub mod error;
|
||||
pub mod ex_data;
|
||||
pub mod fips;
|
||||
pub mod hash;
|
||||
pub mod hmac;
|
||||
pub mod hpke;
|
||||
pub mod memcmp;
|
||||
#[cfg(feature = "mlkem")]
|
||||
pub mod mlkem;
|
||||
pub mod nid;
|
||||
pub mod pkcs12;
|
||||
pub mod pkcs5;
|
||||
@ -148,6 +45,7 @@ pub mod rsa;
|
||||
pub mod sha;
|
||||
pub mod sign;
|
||||
pub mod srtp;
|
||||
#[cfg(feature = "ssl")]
|
||||
pub mod ssl;
|
||||
pub mod stack;
|
||||
pub mod string;
|
||||
@ -163,11 +61,11 @@ fn cvt_p<T>(r: *mut T) -> Result<*mut T, ErrorStack> {
|
||||
}
|
||||
}
|
||||
|
||||
fn cvt_0(r: usize) -> Result<(), ErrorStack> {
|
||||
fn cvt_0(r: size_t) -> Result<size_t, ErrorStack> {
|
||||
if r == 0 {
|
||||
Err(ErrorStack::get())
|
||||
} else {
|
||||
Ok(())
|
||||
Ok(r)
|
||||
}
|
||||
}
|
||||
|
||||
@ -179,21 +77,14 @@ fn cvt_0i(r: c_int) -> Result<c_int, ErrorStack> {
|
||||
}
|
||||
}
|
||||
|
||||
fn cvt(r: c_int) -> Result<(), ErrorStack> {
|
||||
fn cvt(r: c_int) -> Result<c_int, ErrorStack> {
|
||||
if r <= 0 {
|
||||
Err(ErrorStack::get())
|
||||
} else {
|
||||
Ok(())
|
||||
Ok(r)
|
||||
}
|
||||
}
|
||||
|
||||
fn cvt_nz(r: c_int) -> Result<NonZeroUsize, ErrorStack> {
|
||||
usize::try_from(r)
|
||||
.ok()
|
||||
.and_then(NonZeroUsize::new)
|
||||
.ok_or_else(ErrorStack::get)
|
||||
}
|
||||
|
||||
fn cvt_n(r: c_int) -> Result<c_int, ErrorStack> {
|
||||
if r < 0 {
|
||||
Err(ErrorStack::get())
|
||||
@ -201,25 +92,3 @@ fn cvt_n(r: c_int) -> Result<c_int, ErrorStack> {
|
||||
Ok(r)
|
||||
}
|
||||
}
|
||||
|
||||
fn try_int<F, T>(from: F) -> Result<T, ErrorStack>
|
||||
where
|
||||
F: TryInto<T> + Send + Sync + Copy + 'static,
|
||||
T: Send + Sync + Copy + 'static,
|
||||
{
|
||||
from.try_into()
|
||||
.map_err(|_| ErrorStack::internal_error_str("int overflow"))
|
||||
}
|
||||
|
||||
unsafe extern "C" fn free_data_box<T>(
|
||||
_parent: *mut c_void,
|
||||
ptr: *mut c_void,
|
||||
_ad: *mut ffi::CRYPTO_EX_DATA,
|
||||
_idx: c_int,
|
||||
_argl: c_long,
|
||||
_argp: *mut c_void,
|
||||
) {
|
||||
if !ptr.is_null() {
|
||||
drop(Box::<T>::from_raw(ptr.cast::<T>()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,8 +7,7 @@ macro_rules! private_key_from_pem {
|
||||
unsafe {
|
||||
ffi::init();
|
||||
let bio = crate::bio::MemBioSlice::new(pem)?;
|
||||
let passphrase = ::std::ffi::CString::new(passphrase)
|
||||
.map_err(crate::error::ErrorStack::internal_error)?;
|
||||
let passphrase = ::std::ffi::CString::new(passphrase).unwrap();
|
||||
cvt_p($f(bio.as_ptr(),
|
||||
ptr::null_mut(),
|
||||
None,
|
||||
@ -28,7 +27,7 @@ macro_rules! private_key_from_pem {
|
||||
cvt_p($f(bio.as_ptr(),
|
||||
ptr::null_mut(),
|
||||
Some(crate::util::invoke_passwd_cb::<F>),
|
||||
ptr::from_mut(&mut cb).cast()))
|
||||
&mut cb as *mut _ as *mut _))
|
||||
.map(|p| ::foreign_types::ForeignType::from_ptr(p))
|
||||
}
|
||||
}
|
||||
@ -60,11 +59,12 @@ macro_rules! private_key_to_pem {
|
||||
) -> Result<Vec<u8>, crate::error::ErrorStack> {
|
||||
unsafe {
|
||||
let bio = crate::bio::MemBio::new()?;
|
||||
assert!(passphrase.len() <= ::libc::c_int::max_value() as usize);
|
||||
cvt($f(bio.as_ptr(),
|
||||
self.as_ptr(),
|
||||
cipher.as_ptr(),
|
||||
passphrase.as_ptr() as *const _ as *mut _,
|
||||
try_int(passphrase.len())?,
|
||||
passphrase.len() as ::libc::c_int,
|
||||
None,
|
||||
ptr::null_mut()))?;
|
||||
Ok(bio.get_buf().to_owned())
|
||||
@ -91,9 +91,9 @@ macro_rules! to_der {
|
||||
$(#[$m])*
|
||||
pub fn $n(&self) -> Result<Vec<u8>, crate::error::ErrorStack> {
|
||||
unsafe {
|
||||
let len = crate::cvt_nz($f(::foreign_types::ForeignTypeRef::as_ptr(self),
|
||||
let len = crate::cvt($f(::foreign_types::ForeignTypeRef::as_ptr(self),
|
||||
ptr::null_mut()))?;
|
||||
let mut buf = vec![0; len.get()];
|
||||
let mut buf = vec![0; len as usize];
|
||||
crate::cvt($f(::foreign_types::ForeignTypeRef::as_ptr(self),
|
||||
&mut buf.as_mut_ptr()))?;
|
||||
Ok(buf)
|
||||
@ -108,7 +108,7 @@ macro_rules! from_der {
|
||||
pub fn $n(der: &[u8]) -> Result<$t, crate::error::ErrorStack> {
|
||||
unsafe {
|
||||
crate::ffi::init();
|
||||
let len = ::std::cmp::min(der.len(), <$len_ty>::MAX as usize) as $len_ty;
|
||||
let len = ::std::cmp::min(der.len(), <$len_ty>::max_value() as usize) as $len_ty;
|
||||
crate::cvt_p($f(::std::ptr::null_mut(), &mut der.as_ptr(), len))
|
||||
.map(|p| ::foreign_types::ForeignType::from_ptr(p))
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! To perform a constant-time comparison of two arrays of the same length but different
|
||||
//! To perform a constant-time comparision of two arrays of the same length but different
|
||||
//! values:
|
||||
//!
|
||||
//! ```
|
||||
@ -30,6 +30,7 @@
|
||||
//! assert!(!eq(&a, &c));
|
||||
//! ```
|
||||
use crate::ffi;
|
||||
use libc::size_t;
|
||||
|
||||
/// Returns `true` iff `a` and `b` contain the same bytes.
|
||||
///
|
||||
@ -43,7 +44,7 @@ use crate::ffi;
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// To perform a constant-time comparison of two arrays of the same length but different
|
||||
/// To perform a constant-time comparision of two arrays of the same length but different
|
||||
/// values:
|
||||
///
|
||||
/// ```
|
||||
@ -60,10 +61,15 @@ use crate::ffi;
|
||||
/// assert!(!eq(&a, &b));
|
||||
/// assert!(!eq(&a, &c));
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn eq(a: &[u8], b: &[u8]) -> bool {
|
||||
assert!(a.len() == b.len());
|
||||
let ret = unsafe { ffi::CRYPTO_memcmp(a.as_ptr().cast(), b.as_ptr().cast(), a.len()) };
|
||||
let ret = unsafe {
|
||||
ffi::CRYPTO_memcmp(
|
||||
a.as_ptr() as *const _,
|
||||
b.as_ptr() as *const _,
|
||||
a.len() as size_t,
|
||||
)
|
||||
};
|
||||
ret == 0
|
||||
}
|
||||
|
||||
@ -81,6 +87,6 @@ mod tests {
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_diff_lens() {
|
||||
let _ = eq(&[], &[1]);
|
||||
eq(&[], &[1]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,743 +0,0 @@
|
||||
//! ML-KEM (FIPS 203) post-quantum key encapsulation.
|
||||
//!
|
||||
//! ML-KEM is a low-level cryptographic primitive. For most applications,
|
||||
//! using higher-level constructions like HPKE is preferred.
|
||||
//! Note that it's also enabled in TLS by default, in the X25519MLKEM768 exchange.
|
||||
//!
|
||||
//! Provides ML-KEM-768 (recommended) and ML-KEM-1024 variants via [`Algorithm`].
|
||||
//!
|
||||
//! ```
|
||||
//! use boring::mlkem::{Algorithm, MlKemPrivateKey};
|
||||
//!
|
||||
//! let (public_key, private_key) = MlKemPrivateKey::generate(Algorithm::MlKem768).unwrap();
|
||||
//! let (ciphertext, shared_secret) = public_key.encapsulate().unwrap();
|
||||
//! let decrypted = private_key.decapsulate(&ciphertext).unwrap();
|
||||
//! assert_eq!(shared_secret, decrypted);
|
||||
//! ```
|
||||
|
||||
use std::fmt;
|
||||
use std::mem::MaybeUninit;
|
||||
|
||||
use crate::cvt;
|
||||
use crate::error::ErrorStack;
|
||||
use crate::ffi;
|
||||
|
||||
// CBS_init is inline in BoringSSL, so bindgen can't generate bindings for it.
|
||||
#[inline]
|
||||
fn cbs_init(data: &[u8]) -> ffi::CBS {
|
||||
ffi::CBS {
|
||||
data: data.as_ptr(),
|
||||
len: data.len(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Private key seed size (64 bytes).
|
||||
pub const PRIVATE_KEY_SEED_BYTES: usize = ffi::MLKEM_SEED_BYTES as usize;
|
||||
|
||||
/// Shared secret size (32 bytes).
|
||||
pub const SHARED_SECRET_BYTES: usize = ffi::MLKEM_SHARED_SECRET_BYTES as usize;
|
||||
|
||||
/// Raw bytes of the private key seed ([`PRIVATE_KEY_SEED_BYTES`] long)
|
||||
pub type MlKemPrivateKeySeed = [u8; PRIVATE_KEY_SEED_BYTES];
|
||||
|
||||
/// Raw bytes of the shared secret ([`SHARED_SECRET_BYTES`] long)
|
||||
pub type MlKemSharedSecret = [u8; SHARED_SECRET_BYTES];
|
||||
|
||||
/// ML-KEM runtime algorithm selection.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Algorithm {
|
||||
/// Recommended. AES-192 equivalent security.
|
||||
MlKem768,
|
||||
/// AES-256 equivalent security.
|
||||
MlKem1024,
|
||||
}
|
||||
|
||||
impl Algorithm {
|
||||
/// Returns 1184 for ML-KEM-768, 1568 for ML-KEM-1024.
|
||||
#[must_use]
|
||||
pub const fn public_key_bytes(&self) -> usize {
|
||||
match self {
|
||||
Self::MlKem768 => MlKem768PublicKey::PUBLIC_KEY_BYTES,
|
||||
Self::MlKem1024 => MlKem1024PublicKey::PUBLIC_KEY_BYTES,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns 1088 for ML-KEM-768, 1568 for ML-KEM-1024.
|
||||
#[must_use]
|
||||
pub const fn ciphertext_bytes(&self) -> usize {
|
||||
match self {
|
||||
Self::MlKem768 => MlKem768PrivateKey::CIPHERTEXT_BYTES,
|
||||
Self::MlKem1024 => MlKem1024PrivateKey::CIPHERTEXT_BYTES,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MlKemPublicKey(Either<Box<MlKem768PublicKey>, Box<MlKem1024PublicKey>>);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MlKemPrivateKey(Either<Box<MlKem768PrivateKey>, Box<MlKem1024PrivateKey>>);
|
||||
|
||||
#[derive(Clone)]
|
||||
enum Either<T768, T1024> {
|
||||
MlKem768(T768),
|
||||
MlKem1024(T1024),
|
||||
}
|
||||
|
||||
impl MlKemPrivateKey {
|
||||
/// Generates a new key pair, returning `(public_key, private_key)`.
|
||||
///
|
||||
/// The private key is a 64-byte seed. Keep it secret.
|
||||
pub fn generate(algorithm: Algorithm) -> Result<(MlKemPublicKey, MlKemPrivateKey), ErrorStack> {
|
||||
match algorithm {
|
||||
Algorithm::MlKem768 => {
|
||||
let (pk, sk) = MlKem768PrivateKey::generate()?;
|
||||
Ok((
|
||||
MlKemPublicKey(Either::MlKem768(pk)),
|
||||
MlKemPrivateKey(Either::MlKem768(sk)),
|
||||
))
|
||||
}
|
||||
Algorithm::MlKem1024 => {
|
||||
let (pk, sk) = MlKem1024PrivateKey::generate()?;
|
||||
Ok((
|
||||
MlKemPublicKey(Either::MlKem1024(pk)),
|
||||
MlKemPrivateKey(Either::MlKem1024(sk)),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MlKemPublicKey {
|
||||
pub fn from_slice(algorithm: Algorithm, public_key: &[u8]) -> Result<Self, ErrorStack> {
|
||||
match algorithm {
|
||||
Algorithm::MlKem768 => Ok(Self(Either::MlKem768(Box::new(
|
||||
MlKem768PublicKey::from_slice(public_key)?,
|
||||
)))),
|
||||
Algorithm::MlKem1024 => Ok(Self(Either::MlKem1024(Box::new(
|
||||
MlKem1024PublicKey::from_slice(public_key)?,
|
||||
)))),
|
||||
}
|
||||
}
|
||||
|
||||
/// Serialized bytes of the public key
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
match &self.0 {
|
||||
Either::MlKem768(pk) => &pk.bytes,
|
||||
Either::MlKem1024(pk) => &pk.bytes,
|
||||
}
|
||||
}
|
||||
|
||||
/// Encapsulates a shared secret to the given public key, returning
|
||||
/// `(ciphertext, shared_secret)`.
|
||||
pub fn encapsulate(&self) -> Result<(Vec<u8>, MlKemSharedSecret), ErrorStack> {
|
||||
match &self.0 {
|
||||
Either::MlKem768(pk) => {
|
||||
let (ct, ss) = pk.encapsulate();
|
||||
Ok((ct.to_vec(), ss))
|
||||
}
|
||||
Either::MlKem1024(pk) => {
|
||||
let (ct, ss) = pk.encapsulate();
|
||||
Ok((ct.to_vec(), ss))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Query public key and ciphertext length
|
||||
pub fn algorithm(&self) -> Algorithm {
|
||||
match self.0 {
|
||||
Either::MlKem768(_) => Algorithm::MlKem768,
|
||||
Either::MlKem1024(_) => Algorithm::MlKem1024,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MlKemPrivateKey {
|
||||
/// Expand private key from the seed bytes
|
||||
pub fn from_seed(
|
||||
algorithm: Algorithm,
|
||||
private_seed: &MlKemPrivateKeySeed,
|
||||
) -> Result<Self, ErrorStack> {
|
||||
match algorithm {
|
||||
Algorithm::MlKem768 => Ok(Self(Either::MlKem768(Box::new(
|
||||
MlKem768PrivateKey::from_seed(private_seed)?,
|
||||
)))),
|
||||
Algorithm::MlKem1024 => Ok(Self(Either::MlKem1024(Box::new(
|
||||
MlKem1024PrivateKey::from_seed(private_seed)?,
|
||||
)))),
|
||||
}
|
||||
}
|
||||
|
||||
/// Secret seed bytes of this private key
|
||||
pub fn seed_bytes(&self) -> &MlKemPrivateKeySeed {
|
||||
match &self.0 {
|
||||
Either::MlKem768(sk) => &sk.seed,
|
||||
Either::MlKem1024(sk) => &sk.seed,
|
||||
}
|
||||
}
|
||||
|
||||
/// Decapsulates a shared secret from a ciphertext using the private key.
|
||||
pub fn decapsulate(&self, ciphertext: &[u8]) -> Result<MlKemSharedSecret, ErrorStack> {
|
||||
match &self.0 {
|
||||
Either::MlKem768(sk) => {
|
||||
let ct: &[u8; MlKem768PrivateKey::CIPHERTEXT_BYTES] = ciphertext
|
||||
.try_into()
|
||||
.map_err(|_| ErrorStack::internal_error_str("invalid ciphertext length"))?;
|
||||
Ok(sk.decapsulate(ct))
|
||||
}
|
||||
Either::MlKem1024(sk) => {
|
||||
let ct: &[u8; MlKem1024PrivateKey::CIPHERTEXT_BYTES] = ciphertext
|
||||
.try_into()
|
||||
.map_err(|_| ErrorStack::internal_error_str("invalid ciphertext length"))?;
|
||||
Ok(sk.decapsulate(ct))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Query public key and ciphertext length
|
||||
pub fn algorithm(&self) -> Algorithm {
|
||||
match self.0 {
|
||||
Either::MlKem768(_) => Algorithm::MlKem768,
|
||||
Either::MlKem1024(_) => Algorithm::MlKem1024,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// ML-KEM-768 private key.
|
||||
///
|
||||
/// Caches the expanded key for fast decapsulation.
|
||||
struct MlKem768PrivateKey {
|
||||
seed: MlKemPrivateKeySeed,
|
||||
expanded: ffi::MLKEM768_private_key,
|
||||
}
|
||||
|
||||
impl Clone for MlKem768PrivateKey {
|
||||
fn clone(&self) -> Self {
|
||||
// unwrap is safe: cloning a valid key with a valid seed always succeeds
|
||||
Self::from_seed(&self.seed).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl MlKem768PrivateKey {
|
||||
pub const CIPHERTEXT_BYTES: usize = ffi::MLKEM768_CIPHERTEXT_BYTES as usize;
|
||||
|
||||
/// Generate a new key pair.
|
||||
fn generate() -> Result<(Box<MlKem768PublicKey>, Box<MlKem768PrivateKey>), ErrorStack> {
|
||||
// SAFETY: all buffers are out parameters, correctly sized
|
||||
unsafe {
|
||||
ffi::init();
|
||||
let mut bytes = [0; MlKem768PublicKey::PUBLIC_KEY_BYTES];
|
||||
let mut seed = [0; PRIVATE_KEY_SEED_BYTES];
|
||||
let mut expanded: MaybeUninit<ffi::MLKEM768_private_key> = MaybeUninit::uninit();
|
||||
|
||||
ffi::MLKEM768_generate_key(
|
||||
bytes.as_mut_ptr().cast(),
|
||||
seed.as_mut_ptr(),
|
||||
expanded.as_mut_ptr(),
|
||||
);
|
||||
|
||||
Ok((
|
||||
Box::new(MlKem768PublicKey::from_slice(&bytes)?),
|
||||
Box::new(MlKem768PrivateKey {
|
||||
seed,
|
||||
expanded: expanded.assume_init(),
|
||||
}),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Restore private key from seed.
|
||||
fn from_seed(seed: &MlKemPrivateKeySeed) -> Result<Self, ErrorStack> {
|
||||
// SAFETY: seed is 64 bytes, out parameter correctly sized
|
||||
unsafe {
|
||||
ffi::init();
|
||||
let mut expanded: MaybeUninit<ffi::MLKEM768_private_key> = MaybeUninit::uninit();
|
||||
cvt(ffi::MLKEM768_private_key_from_seed(
|
||||
expanded.as_mut_ptr(),
|
||||
seed.as_ptr(),
|
||||
seed.len(),
|
||||
))?;
|
||||
Ok(Self {
|
||||
seed: *seed,
|
||||
expanded: expanded.assume_init(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Derive the public key.
|
||||
#[cfg(test)]
|
||||
fn public_key(&self) -> Result<MlKem768PublicKey, ErrorStack> {
|
||||
// SAFETY: expanded key is valid, buffers correctly sized
|
||||
unsafe {
|
||||
ffi::init();
|
||||
let mut parsed: MaybeUninit<ffi::MLKEM768_public_key> = MaybeUninit::uninit();
|
||||
ffi::MLKEM768_public_from_private(parsed.as_mut_ptr(), &self.expanded);
|
||||
|
||||
let mut bytes = [0u8; MlKem768PublicKey::PUBLIC_KEY_BYTES];
|
||||
let mut cbb: MaybeUninit<ffi::CBB> = MaybeUninit::uninit();
|
||||
cvt(ffi::CBB_init_fixed(
|
||||
cbb.as_mut_ptr(),
|
||||
bytes.as_mut_ptr(),
|
||||
bytes.len(),
|
||||
))?;
|
||||
cvt(ffi::MLKEM768_marshal_public_key(
|
||||
cbb.as_mut_ptr(),
|
||||
parsed.as_ptr(),
|
||||
))?;
|
||||
|
||||
Ok(MlKem768PublicKey {
|
||||
bytes,
|
||||
parsed: parsed.assume_init(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Decapsulate to get the shared secret.
|
||||
fn decapsulate(&self, ciphertext: &[u8; Self::CIPHERTEXT_BYTES]) -> MlKemSharedSecret {
|
||||
// SAFETY: expanded key is valid, ciphertext is correctly sized
|
||||
unsafe {
|
||||
ffi::init();
|
||||
let mut shared_secret = [0u8; SHARED_SECRET_BYTES];
|
||||
|
||||
ffi::MLKEM768_decap(
|
||||
shared_secret.as_mut_ptr(),
|
||||
ciphertext.as_ptr(),
|
||||
ciphertext.len(),
|
||||
&self.expanded,
|
||||
);
|
||||
|
||||
shared_secret
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for MlKem768PrivateKey {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("MlKem768PrivateKey")
|
||||
.field("key", &"[redacted]")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for MlKem768PrivateKey {
|
||||
fn drop(&mut self) {
|
||||
// SAFETY: pointers and lengths are valid
|
||||
unsafe {
|
||||
ffi::OPENSSL_cleanse(self.seed.as_mut_ptr().cast(), self.seed.len());
|
||||
ffi::OPENSSL_cleanse(
|
||||
self.expanded.opaque.bytes.as_mut_ptr().cast(),
|
||||
self.expanded.opaque.bytes.len(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// ML-KEM-768 public key.
|
||||
#[derive(Clone)]
|
||||
struct MlKem768PublicKey {
|
||||
bytes: [u8; Self::PUBLIC_KEY_BYTES],
|
||||
parsed: ffi::MLKEM768_public_key,
|
||||
}
|
||||
|
||||
impl MlKem768PublicKey {
|
||||
pub const PUBLIC_KEY_BYTES: usize = ffi::MLKEM768_PUBLIC_KEY_BYTES as usize;
|
||||
|
||||
/// Parse and validate a public key.
|
||||
///
|
||||
/// The slice must be [`Self::PUBLIC_KEY_BYTES`] long.
|
||||
fn from_slice(slice: &[u8]) -> Result<Self, ErrorStack> {
|
||||
if slice.len() != Self::PUBLIC_KEY_BYTES {
|
||||
return Err(ErrorStack::internal_error_str("invalid public key length"));
|
||||
}
|
||||
|
||||
// SAFETY: CBS correctly initialized, length already checked
|
||||
unsafe {
|
||||
ffi::init();
|
||||
let mut cbs = cbs_init(slice);
|
||||
let mut parsed: MaybeUninit<ffi::MLKEM768_public_key> = MaybeUninit::uninit();
|
||||
|
||||
cvt(ffi::MLKEM768_parse_public_key(
|
||||
parsed.as_mut_ptr(),
|
||||
&mut cbs,
|
||||
))?;
|
||||
if cbs.len != 0 {
|
||||
return Err(ErrorStack::internal_error_str(
|
||||
"trailing bytes after public key",
|
||||
));
|
||||
}
|
||||
|
||||
let mut bytes = [0u8; Self::PUBLIC_KEY_BYTES];
|
||||
bytes.copy_from_slice(slice);
|
||||
Ok(Self {
|
||||
bytes,
|
||||
parsed: parsed.assume_init(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Encapsulate: returns (ciphertext, shared_secret).
|
||||
fn encapsulate(
|
||||
&self,
|
||||
) -> (
|
||||
[u8; MlKem768PrivateKey::CIPHERTEXT_BYTES],
|
||||
MlKemSharedSecret,
|
||||
) {
|
||||
// SAFETY: buffers correctly sized, parsed key is valid
|
||||
unsafe {
|
||||
ffi::init();
|
||||
let mut ciphertext = [0u8; MlKem768PrivateKey::CIPHERTEXT_BYTES];
|
||||
let mut shared_secret = [0u8; SHARED_SECRET_BYTES];
|
||||
|
||||
ffi::MLKEM768_encap(
|
||||
ciphertext.as_mut_ptr(),
|
||||
shared_secret.as_mut_ptr(),
|
||||
&self.parsed,
|
||||
);
|
||||
|
||||
(ciphertext, shared_secret)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for MlKem768PublicKey {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("MlKem768PublicKey")
|
||||
.field("bytes", &format_args!("[{}]", self.bytes.len()))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// ML-KEM-1024 private key.
|
||||
///
|
||||
/// Prefer ML-KEM-768 unless you need AES-256 equivalent security.
|
||||
/// Caches the expanded key for fast decapsulation.
|
||||
struct MlKem1024PrivateKey {
|
||||
seed: MlKemPrivateKeySeed,
|
||||
expanded: ffi::MLKEM1024_private_key,
|
||||
}
|
||||
|
||||
impl Clone for MlKem1024PrivateKey {
|
||||
fn clone(&self) -> Self {
|
||||
// unwrap is safe: cloning a valid key with a valid seed always succeeds
|
||||
Self::from_seed(&self.seed).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl MlKem1024PrivateKey {
|
||||
pub const CIPHERTEXT_BYTES: usize = ffi::MLKEM1024_CIPHERTEXT_BYTES as usize;
|
||||
|
||||
/// Generate a new key pair.
|
||||
fn generate() -> Result<(Box<MlKem1024PublicKey>, Box<MlKem1024PrivateKey>), ErrorStack> {
|
||||
// SAFETY: all buffers are out parameters, correctly sized
|
||||
unsafe {
|
||||
ffi::init();
|
||||
let mut bytes = [0; MlKem1024PublicKey::PUBLIC_KEY_BYTES];
|
||||
let mut seed = [0; PRIVATE_KEY_SEED_BYTES];
|
||||
let mut expanded: MaybeUninit<ffi::MLKEM1024_private_key> = MaybeUninit::uninit();
|
||||
|
||||
ffi::MLKEM1024_generate_key(
|
||||
bytes.as_mut_ptr().cast(),
|
||||
seed.as_mut_ptr(),
|
||||
expanded.as_mut_ptr(),
|
||||
);
|
||||
|
||||
Ok((
|
||||
Box::new(MlKem1024PublicKey::from_slice(&bytes)?),
|
||||
Box::new(MlKem1024PrivateKey {
|
||||
seed,
|
||||
expanded: expanded.assume_init(),
|
||||
}),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Restore private key from seed.
|
||||
fn from_seed(seed: &MlKemPrivateKeySeed) -> Result<Self, ErrorStack> {
|
||||
// SAFETY: seed is 64 bytes, out parameter correctly sized
|
||||
unsafe {
|
||||
ffi::init();
|
||||
let mut expanded: MaybeUninit<ffi::MLKEM1024_private_key> = MaybeUninit::uninit();
|
||||
cvt(ffi::MLKEM1024_private_key_from_seed(
|
||||
expanded.as_mut_ptr(),
|
||||
seed.as_ptr(),
|
||||
seed.len(),
|
||||
))?;
|
||||
Ok(Self {
|
||||
seed: *seed,
|
||||
expanded: expanded.assume_init(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Derive the public key.
|
||||
#[cfg(test)]
|
||||
fn public_key(&self) -> Result<MlKem1024PublicKey, ErrorStack> {
|
||||
// SAFETY: expanded key is valid, buffers correctly sized
|
||||
unsafe {
|
||||
ffi::init();
|
||||
let mut parsed: MaybeUninit<ffi::MLKEM1024_public_key> = MaybeUninit::uninit();
|
||||
ffi::MLKEM1024_public_from_private(parsed.as_mut_ptr(), &self.expanded);
|
||||
|
||||
let mut bytes = [0u8; MlKem1024PublicKey::PUBLIC_KEY_BYTES];
|
||||
let mut cbb: MaybeUninit<ffi::CBB> = MaybeUninit::uninit();
|
||||
cvt(ffi::CBB_init_fixed(
|
||||
cbb.as_mut_ptr(),
|
||||
bytes.as_mut_ptr(),
|
||||
bytes.len(),
|
||||
))?;
|
||||
cvt(ffi::MLKEM1024_marshal_public_key(
|
||||
cbb.as_mut_ptr(),
|
||||
parsed.as_ptr(),
|
||||
))?;
|
||||
|
||||
Ok(MlKem1024PublicKey {
|
||||
bytes,
|
||||
parsed: parsed.assume_init(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Decapsulate to get the shared secret.
|
||||
fn decapsulate(&self, ciphertext: &[u8; Self::CIPHERTEXT_BYTES]) -> MlKemSharedSecret {
|
||||
// SAFETY: expanded key is valid, ciphertext is correctly sized
|
||||
unsafe {
|
||||
ffi::init();
|
||||
let mut shared_secret = [0u8; SHARED_SECRET_BYTES];
|
||||
|
||||
ffi::MLKEM1024_decap(
|
||||
shared_secret.as_mut_ptr(),
|
||||
ciphertext.as_ptr(),
|
||||
ciphertext.len(),
|
||||
&self.expanded,
|
||||
);
|
||||
|
||||
shared_secret
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for MlKem1024PrivateKey {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("MlKem1024PrivateKey")
|
||||
.field("key", &"[redacted]")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for MlKem1024PrivateKey {
|
||||
fn drop(&mut self) {
|
||||
// SAFETY: pointers and lengths are valid
|
||||
unsafe {
|
||||
ffi::OPENSSL_cleanse(self.seed.as_mut_ptr().cast(), self.seed.len());
|
||||
ffi::OPENSSL_cleanse(
|
||||
self.expanded.opaque.bytes.as_mut_ptr().cast(),
|
||||
self.expanded.opaque.bytes.len(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// ML-KEM-1024 public key.
|
||||
///
|
||||
/// Prefer ML-KEM-768 unless you need AES-256 equivalent security.
|
||||
#[derive(Clone)]
|
||||
struct MlKem1024PublicKey {
|
||||
bytes: [u8; Self::PUBLIC_KEY_BYTES],
|
||||
parsed: ffi::MLKEM1024_public_key,
|
||||
}
|
||||
|
||||
impl MlKem1024PublicKey {
|
||||
pub const PUBLIC_KEY_BYTES: usize = ffi::MLKEM1024_PUBLIC_KEY_BYTES as usize;
|
||||
|
||||
/// Parse and validate a serialized public key.
|
||||
///
|
||||
/// The slice must be [`Self::PUBLIC_KEY_BYTES`] long.
|
||||
fn from_slice(slice: &[u8]) -> Result<Self, ErrorStack> {
|
||||
if slice.len() != Self::PUBLIC_KEY_BYTES {
|
||||
return Err(ErrorStack::internal_error_str("invalid public key length"));
|
||||
}
|
||||
|
||||
// SAFETY: CBS correctly initialized, length already checked
|
||||
unsafe {
|
||||
ffi::init();
|
||||
let mut cbs = cbs_init(slice);
|
||||
let mut parsed: MaybeUninit<ffi::MLKEM1024_public_key> = MaybeUninit::uninit();
|
||||
|
||||
cvt(ffi::MLKEM1024_parse_public_key(
|
||||
parsed.as_mut_ptr(),
|
||||
&mut cbs,
|
||||
))?;
|
||||
if cbs.len != 0 {
|
||||
return Err(ErrorStack::internal_error_str(
|
||||
"trailing bytes after public key",
|
||||
));
|
||||
}
|
||||
|
||||
let mut bytes = [0u8; Self::PUBLIC_KEY_BYTES];
|
||||
bytes.copy_from_slice(slice);
|
||||
Ok(Self {
|
||||
bytes,
|
||||
parsed: parsed.assume_init(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Encapsulate: returns (ciphertext, shared_secret).
|
||||
fn encapsulate(
|
||||
&self,
|
||||
) -> (
|
||||
[u8; MlKem1024PrivateKey::CIPHERTEXT_BYTES],
|
||||
[u8; SHARED_SECRET_BYTES],
|
||||
) {
|
||||
// SAFETY: buffers correctly sized, parsed key is valid
|
||||
unsafe {
|
||||
ffi::init();
|
||||
let mut ciphertext = [0u8; MlKem1024PrivateKey::CIPHERTEXT_BYTES];
|
||||
let mut shared_secret = [0u8; SHARED_SECRET_BYTES];
|
||||
|
||||
ffi::MLKEM1024_encap(
|
||||
ciphertext.as_mut_ptr(),
|
||||
shared_secret.as_mut_ptr(),
|
||||
&self.parsed,
|
||||
);
|
||||
|
||||
(ciphertext, shared_secret)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for MlKem1024PublicKey {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("MlKem1024PublicKey")
|
||||
.field("bytes", &format_args!("[{}]", self.bytes.len()))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
macro_rules! mlkem_tests {
|
||||
($name:ident, $priv:ty, $pub:ty, $ct_len:expr) => {
|
||||
mod $name {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn roundtrip() {
|
||||
let (pk, sk) = <$priv>::generate().unwrap();
|
||||
let (ct, ss1) = pk.encapsulate();
|
||||
let ss2 = sk.decapsulate(&ct);
|
||||
assert_eq!(ss1, ss2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seed_roundtrip() {
|
||||
let (pk, sk) = <$priv>::generate().unwrap();
|
||||
let sk2 = <$priv>::from_seed(&sk.seed).unwrap();
|
||||
let (ct, ss1) = pk.encapsulate();
|
||||
let ss2 = sk2.decapsulate(&ct);
|
||||
assert_eq!(ss1, ss2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn derive_pubkey() {
|
||||
let (pk, sk) = <$priv>::generate().unwrap();
|
||||
assert_eq!(pk.bytes, sk.public_key().unwrap().bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_slice_rejects_bad_len() {
|
||||
assert!(<$pub>::from_slice(&[0u8; 100]).is_err());
|
||||
assert!(<$pub>::from_slice(&[]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_slice_roundtrip() {
|
||||
let (pk, _) = <$priv>::generate().unwrap();
|
||||
let pk2 = <$pub>::from_slice(&pk.bytes).unwrap();
|
||||
assert_eq!(pk.bytes, pk2.bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn implicit_rejection() {
|
||||
let (_, sk) = <$priv>::generate().unwrap();
|
||||
let bad_ct = [0x42u8; $ct_len];
|
||||
// bad ciphertext still "works", just returns deterministic garbage
|
||||
let ss1 = sk.decapsulate(&bad_ct);
|
||||
let ss2 = sk.decapsulate(&bad_ct);
|
||||
assert_eq!(ss1, ss2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn debug_redacts_seed() {
|
||||
let (_, sk) = <$priv>::generate().unwrap();
|
||||
let dbg = format!("{:?}", sk);
|
||||
assert!(dbg.contains("redacted"));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
mlkem_tests!(mlkem768, MlKem768PrivateKey, MlKem768PublicKey, 1088);
|
||||
mlkem_tests!(mlkem1024, MlKem1024PrivateKey, MlKem1024PublicKey, 1568);
|
||||
|
||||
// Tests for unified API (MlKem struct)
|
||||
mod unified_api {
|
||||
use super::*;
|
||||
|
||||
macro_rules! unified_tests {
|
||||
($name:ident, $algorithm:expr, $pk_len:expr, $ct_len:expr) => {
|
||||
mod $name {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn roundtrip() {
|
||||
let (pk, sk) = MlKemPrivateKey::generate($algorithm).unwrap();
|
||||
let (ct, ss1) = pk.encapsulate().unwrap();
|
||||
let ss2 = sk.decapsulate(&ct).unwrap();
|
||||
assert_eq!(ss1, ss2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn key_sizes() {
|
||||
assert_eq!($algorithm.public_key_bytes(), $pk_len);
|
||||
assert_eq!($algorithm.ciphertext_bytes(), $ct_len);
|
||||
|
||||
let (pk, private_key) = MlKemPrivateKey::generate($algorithm).unwrap();
|
||||
assert_eq!(pk.as_bytes().len(), $pk_len);
|
||||
assert_eq!(private_key.seed_bytes().len(), PRIVATE_KEY_SEED_BYTES);
|
||||
|
||||
let (ct, ss) = pk.encapsulate().unwrap();
|
||||
assert_eq!(ct.len(), $ct_len);
|
||||
assert_eq!(ss.len(), SHARED_SECRET_BYTES);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_public_key_length() {
|
||||
let result = MlKemPublicKey::from_slice($algorithm, &[0u8; 100]);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_ciphertext_length() {
|
||||
let (_, sk) = MlKemPrivateKey::generate($algorithm).unwrap();
|
||||
let result = sk.decapsulate(&[0u8; 100]);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
unified_tests!(mlkem768, Algorithm::MlKem768, 1184, 1088);
|
||||
unified_tests!(mlkem1024, Algorithm::MlKem1024, 1568, 1568);
|
||||
|
||||
#[test]
|
||||
fn params_constants() {
|
||||
assert_eq!(Algorithm::MlKem768.public_key_bytes(), 1184);
|
||||
assert_eq!(Algorithm::MlKem768.ciphertext_bytes(), 1088);
|
||||
assert_eq!(Algorithm::MlKem1024.public_key_bytes(), 1568);
|
||||
assert_eq!(Algorithm::MlKem1024.ciphertext_bytes(), 1568);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,6 @@
|
||||
//! A collection of numerical identifiers for OpenSSL objects.
|
||||
use crate::ffi;
|
||||
use libc::c_int;
|
||||
use openssl_macros::corresponds;
|
||||
use libc::{c_char, c_int};
|
||||
|
||||
use std::ffi::CStr;
|
||||
use std::str;
|
||||
@ -51,22 +50,20 @@ pub struct Nid(c_int);
|
||||
#[allow(non_snake_case)]
|
||||
impl Nid {
|
||||
/// Create a `Nid` from an integer representation.
|
||||
#[must_use]
|
||||
pub fn from_raw(raw: c_int) -> Nid {
|
||||
Nid(raw)
|
||||
}
|
||||
|
||||
/// Return the integer representation of a `Nid`.
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
#[must_use]
|
||||
pub fn as_raw(&self) -> c_int {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Returns the `Nid`s of the digest and public key algorithms associated with a signature ID.
|
||||
#[corresponds(OBJ_find_sigid_algs)]
|
||||
///
|
||||
/// This corresponds to `OBJ_find_sigid_algs`.
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
#[must_use]
|
||||
pub fn signature_algorithms(&self) -> Option<SignatureAlgorithms> {
|
||||
unsafe {
|
||||
let mut digest = 0;
|
||||
@ -83,26 +80,26 @@ impl Nid {
|
||||
}
|
||||
|
||||
/// Return the string representation of a `Nid` (long)
|
||||
#[corresponds(OBJ_nid2ln)]
|
||||
/// This corresponds to [`OBJ_nid2ln`]
|
||||
///
|
||||
/// [`OBJ_nid2ln`]: https://www.openssl.org/docs/man1.1.0/crypto/OBJ_nid2ln.html
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
pub fn long_name(&self) -> Result<&'static str, ErrorStack> {
|
||||
unsafe {
|
||||
let nameptr = cvt_p(ffi::OBJ_nid2ln(self.0).cast_mut())?;
|
||||
CStr::from_ptr(nameptr)
|
||||
.to_str()
|
||||
.map_err(ErrorStack::internal_error)
|
||||
cvt_p(ffi::OBJ_nid2ln(self.0) as *mut c_char)
|
||||
.map(|nameptr| str::from_utf8(CStr::from_ptr(nameptr).to_bytes()).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the string representation of a `Nid` (short)
|
||||
#[corresponds(OBJ_nid2sn)]
|
||||
/// This corresponds to [`OBJ_nid2sn`]
|
||||
///
|
||||
/// [`OBJ_nid2sn`]: https://www.openssl.org/docs/man1.1.0/crypto/OBJ_nid2sn.html
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
pub fn short_name(&self) -> Result<&'static str, ErrorStack> {
|
||||
unsafe {
|
||||
let nameptr = cvt_p(ffi::OBJ_nid2sn(self.0).cast_mut())?;
|
||||
CStr::from_ptr(nameptr)
|
||||
.to_str()
|
||||
.map_err(ErrorStack::internal_error)
|
||||
cvt_p(ffi::OBJ_nid2sn(self.0) as *mut c_char)
|
||||
.map(|nameptr| str::from_utf8(CStr::from_ptr(nameptr).to_bytes()).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
@ -731,7 +728,6 @@ impl Nid {
|
||||
pub const SHA256: Nid = Nid(ffi::NID_sha256);
|
||||
pub const SHA384: Nid = Nid(ffi::NID_sha384);
|
||||
pub const SHA512: Nid = Nid(ffi::NID_sha512);
|
||||
pub const SHA512_256: Nid = Nid(ffi::NID_sha512_256);
|
||||
pub const SHA224: Nid = Nid(ffi::NID_sha224);
|
||||
pub const DSA_WITH_SHA224: Nid = Nid(ffi::NID_dsa_with_SHA224);
|
||||
pub const DSA_WITH_SHA256: Nid = Nid(ffi::NID_dsa_with_SHA256);
|
||||
@ -1053,10 +1049,6 @@ impl Nid {
|
||||
pub const AES_128_CBC_HMAC_SHA1: Nid = Nid(ffi::NID_aes_128_cbc_hmac_sha1);
|
||||
pub const AES_192_CBC_HMAC_SHA1: Nid = Nid(ffi::NID_aes_192_cbc_hmac_sha1);
|
||||
pub const AES_256_CBC_HMAC_SHA1: Nid = Nid(ffi::NID_aes_256_cbc_hmac_sha1);
|
||||
pub const AUTH_RSA: Nid = Nid(ffi::NID_auth_rsa);
|
||||
pub const AUTH_ECDSA: Nid = Nid(ffi::NID_auth_ecdsa);
|
||||
pub const AUTH_PSK: Nid = Nid(ffi::NID_auth_psk);
|
||||
pub const AUTH_ANY: Nid = Nid(ffi::NID_auth_any);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@ -3,14 +3,13 @@
|
||||
use crate::ffi;
|
||||
use foreign_types::{ForeignType, ForeignTypeRef};
|
||||
use libc::c_int;
|
||||
use openssl_macros::corresponds;
|
||||
use std::ffi::CString;
|
||||
use std::ptr;
|
||||
|
||||
use crate::error::ErrorStack;
|
||||
use crate::nid::Nid;
|
||||
use crate::pkey::{HasPrivate, PKey, PKeyRef, Private};
|
||||
use crate::stack::{Stack, StackRef};
|
||||
use crate::stack::Stack;
|
||||
use crate::x509::{X509Ref, X509};
|
||||
use crate::{cvt_0i, cvt_p};
|
||||
|
||||
@ -26,48 +25,41 @@ foreign_type_and_impl_send_sync! {
|
||||
impl Pkcs12Ref {
|
||||
to_der! {
|
||||
/// Serializes the `Pkcs12` to its standard DER encoding.
|
||||
#[corresponds(i2d_PKCS12)]
|
||||
///
|
||||
/// This corresponds to [`i2d_PKCS12`].
|
||||
///
|
||||
/// [`i2d_PKCS12`]: https://www.openssl.org/docs/manmaster/man3/i2d_PKCS12.html
|
||||
to_der,
|
||||
ffi::i2d_PKCS12
|
||||
}
|
||||
|
||||
/// Extracts the contents of the `Pkcs12` with `pkey` and `cert` required.
|
||||
/// Extracts the contents of the `Pkcs12`.
|
||||
pub fn parse(&self, pass: &str) -> Result<ParsedPkcs12, ErrorStack> {
|
||||
let p2 = self.parse2(pass)?;
|
||||
Ok(ParsedPkcs12 {
|
||||
pkey: p2
|
||||
.pkey
|
||||
.ok_or_else(|| ErrorStack::internal_error_str("missing pkey"))?,
|
||||
cert: p2
|
||||
.cert
|
||||
.ok_or_else(|| ErrorStack::internal_error_str("missing cert"))?,
|
||||
chain: p2.ca,
|
||||
})
|
||||
}
|
||||
|
||||
/// Extracts the contents of the `Pkcs12` with `pkey` and `cert` optional.
|
||||
#[corresponds(PKCS12_parse)]
|
||||
pub fn parse2(&self, pass: &str) -> Result<ParsedPkcs12_2, ErrorStack> {
|
||||
unsafe {
|
||||
let pass = CString::new(pass.as_bytes()).map_err(ErrorStack::internal_error)?;
|
||||
let pass = CString::new(pass.as_bytes()).unwrap();
|
||||
|
||||
let mut pkey = ptr::null_mut();
|
||||
let mut cert = ptr::null_mut();
|
||||
let mut ca = ptr::null_mut();
|
||||
let mut chain = ptr::null_mut();
|
||||
|
||||
cvt_0i(ffi::PKCS12_parse(
|
||||
self.as_ptr(),
|
||||
pass.as_ptr(),
|
||||
&mut pkey,
|
||||
&mut cert,
|
||||
&mut ca,
|
||||
&mut chain,
|
||||
))?;
|
||||
|
||||
let pkey = (!pkey.is_null()).then(|| PKey::from_ptr(pkey));
|
||||
let cert = (!cert.is_null()).then(|| X509::from_ptr(cert));
|
||||
let ca = (!ca.is_null()).then(|| Stack::from_ptr(ca));
|
||||
let pkey = PKey::from_ptr(pkey);
|
||||
let cert = X509::from_ptr(cert);
|
||||
|
||||
Ok(ParsedPkcs12_2 { pkey, cert, ca })
|
||||
let chain = if chain.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(Stack::from_ptr(chain))
|
||||
};
|
||||
|
||||
Ok(ParsedPkcs12 { pkey, cert, chain })
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -75,7 +67,10 @@ impl Pkcs12Ref {
|
||||
impl Pkcs12 {
|
||||
from_der! {
|
||||
/// Deserializes a DER-encoded PKCS#12 archive.
|
||||
#[corresponds(d2i_PKCS12)]
|
||||
///
|
||||
/// This corresponds to [`d2i_PKCS12`].
|
||||
///
|
||||
/// [`d2i_PKCS12`]: https://www.openssl.org/docs/man1.1.0/crypto/d2i_PKCS12.html
|
||||
from_der,
|
||||
Pkcs12,
|
||||
ffi::d2i_PKCS12,
|
||||
@ -90,7 +85,6 @@ impl Pkcs12 {
|
||||
/// * `nid_cert` - `nid::PBE_WITHSHA1AND40BITRC2_CBC`
|
||||
/// * `iter` - `2048`
|
||||
/// * `mac_iter` - `2048`
|
||||
#[must_use]
|
||||
pub fn builder() -> Pkcs12Builder {
|
||||
ffi::init();
|
||||
|
||||
@ -110,19 +104,6 @@ pub struct ParsedPkcs12 {
|
||||
pub chain: Option<Stack<X509>>,
|
||||
}
|
||||
|
||||
/// [`ParsedPkcs12`] with optional fields
|
||||
pub struct ParsedPkcs12_2 {
|
||||
pub pkey: Option<PKey<Private>>,
|
||||
pub cert: Option<X509>,
|
||||
pub ca: Option<Stack<X509>>,
|
||||
}
|
||||
|
||||
impl ParsedPkcs12_2 {
|
||||
pub fn chain(&self) -> Option<&StackRef<X509>> {
|
||||
self.ca.as_deref()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Pkcs12Builder {
|
||||
nid_key: Nid,
|
||||
nid_cert: Nid,
|
||||
@ -185,8 +166,8 @@ impl Pkcs12Builder {
|
||||
T: HasPrivate,
|
||||
{
|
||||
unsafe {
|
||||
let pass = CString::new(password).map_err(ErrorStack::internal_error)?;
|
||||
let friendly_name = CString::new(friendly_name).map_err(ErrorStack::internal_error)?;
|
||||
let pass = CString::new(password).unwrap();
|
||||
let friendly_name = CString::new(friendly_name).unwrap();
|
||||
let pkey = pkey.as_ptr();
|
||||
let cert = cert.as_ptr();
|
||||
let ca = self
|
||||
@ -203,8 +184,8 @@ impl Pkcs12Builder {
|
||||
let keytype = 0;
|
||||
|
||||
cvt_p(ffi::PKCS12_create(
|
||||
pass.as_ptr(),
|
||||
friendly_name.as_ptr(),
|
||||
pass.as_ptr() as *const _ as *mut _,
|
||||
friendly_name.as_ptr() as *const _ as *mut _,
|
||||
pkey,
|
||||
cert,
|
||||
ca,
|
||||
@ -283,7 +264,7 @@ mod test {
|
||||
.unwrap();
|
||||
builder.set_subject_name(&name).unwrap();
|
||||
builder.set_issuer_name(&name).unwrap();
|
||||
builder.append_extension(&key_usage).unwrap();
|
||||
builder.append_extension(key_usage).unwrap();
|
||||
builder.set_pubkey(&pkey).unwrap();
|
||||
builder.sign(&pkey, MessageDigest::sha256()).unwrap();
|
||||
let cert = builder.build();
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
use crate::ffi;
|
||||
use std::ffi::c_int;
|
||||
use libc::{c_int, c_uint};
|
||||
use std::ptr;
|
||||
|
||||
use crate::cvt;
|
||||
use crate::error::ErrorStack;
|
||||
use crate::hash::MessageDigest;
|
||||
use crate::symm::Cipher;
|
||||
use crate::{cvt, cvt_nz, try_int};
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub struct KeyIvPair {
|
||||
@ -32,7 +32,7 @@ pub fn bytes_to_key(
|
||||
count: u32,
|
||||
) -> Result<KeyIvPair, ErrorStack> {
|
||||
unsafe {
|
||||
assert!(data.len() <= c_int::MAX as usize);
|
||||
assert!(data.len() <= c_int::max_value() as usize);
|
||||
let salt_ptr = match salt {
|
||||
Some(salt) => {
|
||||
pub const PKCS5_SALT_LEN: c_int = 8;
|
||||
@ -49,7 +49,7 @@ pub fn bytes_to_key(
|
||||
let cipher = cipher.as_ptr();
|
||||
let digest = digest.as_ptr();
|
||||
|
||||
let len = cvt_nz(ffi::EVP_BytesToKey(
|
||||
let len = cvt(ffi::EVP_BytesToKey(
|
||||
cipher,
|
||||
digest,
|
||||
salt_ptr,
|
||||
@ -60,7 +60,7 @@ pub fn bytes_to_key(
|
||||
ptr::null_mut(),
|
||||
))?;
|
||||
|
||||
let mut key = vec![0; len.get()];
|
||||
let mut key = vec![0; len as usize];
|
||||
let iv_ptr = iv
|
||||
.as_mut()
|
||||
.map(|v| v.as_mut_ptr())
|
||||
@ -90,18 +90,22 @@ pub fn pbkdf2_hmac(
|
||||
key: &mut [u8],
|
||||
) -> Result<(), ErrorStack> {
|
||||
unsafe {
|
||||
ffi::init();
|
||||
assert!(pass.len() <= c_int::max_value() as usize);
|
||||
assert!(salt.len() <= c_int::max_value() as usize);
|
||||
assert!(key.len() <= c_int::max_value() as usize);
|
||||
|
||||
ffi::init();
|
||||
cvt(ffi::PKCS5_PBKDF2_HMAC(
|
||||
pass.as_ptr().cast(),
|
||||
pass.as_ptr() as *const _,
|
||||
pass.len(),
|
||||
salt.as_ptr(),
|
||||
salt.len(),
|
||||
try_int(iter)?,
|
||||
iter as c_uint,
|
||||
hash.as_ptr(),
|
||||
key.len(),
|
||||
key.as_mut_ptr(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
@ -118,17 +122,18 @@ pub fn scrypt(
|
||||
unsafe {
|
||||
ffi::init();
|
||||
cvt(ffi::EVP_PBE_scrypt(
|
||||
pass.as_ptr().cast(),
|
||||
pass.as_ptr() as *const _,
|
||||
pass.len(),
|
||||
salt.as_ptr().cast(),
|
||||
salt.as_ptr() as *const _,
|
||||
salt.len(),
|
||||
n,
|
||||
r,
|
||||
p,
|
||||
maxmem,
|
||||
key.as_mut_ptr(),
|
||||
key.as_mut_ptr() as *mut _,
|
||||
key.len(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -40,9 +40,9 @@
|
||||
//! println!("{:?}", str::from_utf8(pub_key.as_slice()).unwrap());
|
||||
//! ```
|
||||
|
||||
use crate::ffi;
|
||||
use foreign_types::{ForeignType, ForeignTypeRef};
|
||||
use libc::{c_int, c_long};
|
||||
use openssl_macros::corresponds;
|
||||
use std::ffi::CString;
|
||||
use std::fmt;
|
||||
use std::mem;
|
||||
@ -53,11 +53,9 @@ use crate::dh::Dh;
|
||||
use crate::dsa::Dsa;
|
||||
use crate::ec::EcKey;
|
||||
use crate::error::ErrorStack;
|
||||
use crate::ffi;
|
||||
use crate::rsa::Rsa;
|
||||
use crate::try_int;
|
||||
use crate::util::{invoke_passwd_cb, CallbackState};
|
||||
use crate::{cvt, cvt_0i, cvt_p};
|
||||
use crate::{cvt, cvt_p};
|
||||
|
||||
/// A tag type indicating that a key only has parameters.
|
||||
pub enum Params {}
|
||||
@ -74,24 +72,23 @@ pub struct Id(c_int);
|
||||
|
||||
impl Id {
|
||||
pub const RSA: Id = Id(ffi::EVP_PKEY_RSA);
|
||||
pub const RSAPSS: Id = Id(ffi::EVP_PKEY_RSA_PSS);
|
||||
pub const DSA: Id = Id(ffi::EVP_PKEY_DSA);
|
||||
pub const DH: Id = Id(ffi::EVP_PKEY_DH);
|
||||
pub const EC: Id = Id(ffi::EVP_PKEY_EC);
|
||||
pub const ED25519: Id = Id(ffi::EVP_PKEY_ED25519);
|
||||
#[cfg(not(feature = "fips"))]
|
||||
pub const ED448: Id = Id(ffi::EVP_PKEY_ED448);
|
||||
pub const X25519: Id = Id(ffi::EVP_PKEY_X25519);
|
||||
#[cfg(not(feature = "fips"))]
|
||||
pub const X448: Id = Id(ffi::EVP_PKEY_X448);
|
||||
|
||||
/// Creates a `Id` from an integer representation.
|
||||
#[must_use]
|
||||
pub fn from_raw(value: c_int) -> Id {
|
||||
Id(value)
|
||||
}
|
||||
|
||||
/// Returns the integer representation of the `Id`.
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
#[must_use]
|
||||
pub fn as_raw(&self) -> c_int {
|
||||
self.0
|
||||
}
|
||||
@ -125,7 +122,7 @@ generic_foreign_type_and_impl_send_sync! {
|
||||
|
||||
/// A public or private key.
|
||||
pub struct PKey<T>;
|
||||
/// Reference to [`PKey`].
|
||||
/// Reference to `PKey`.
|
||||
pub struct PKeyRef<T>;
|
||||
}
|
||||
|
||||
@ -142,7 +139,10 @@ impl<T> ToOwned for PKeyRef<T> {
|
||||
|
||||
impl<T> PKeyRef<T> {
|
||||
/// Returns a copy of the internal RSA key.
|
||||
#[corresponds(EVP_PKEY_get1_RSA)]
|
||||
///
|
||||
/// This corresponds to [`EVP_PKEY_get1_RSA`].
|
||||
///
|
||||
/// [`EVP_PKEY_get1_RSA`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_get1_RSA.html
|
||||
pub fn rsa(&self) -> Result<Rsa<T>, ErrorStack> {
|
||||
unsafe {
|
||||
let rsa = cvt_p(ffi::EVP_PKEY_get1_RSA(self.as_ptr()))?;
|
||||
@ -151,7 +151,10 @@ impl<T> PKeyRef<T> {
|
||||
}
|
||||
|
||||
/// Returns a copy of the internal DSA key.
|
||||
#[corresponds(EVP_PKEY_get1_DSA)]
|
||||
///
|
||||
/// This corresponds to [`EVP_PKEY_get1_DSA`].
|
||||
///
|
||||
/// [`EVP_PKEY_get1_DSA`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_get1_DSA.html
|
||||
pub fn dsa(&self) -> Result<Dsa<T>, ErrorStack> {
|
||||
unsafe {
|
||||
let dsa = cvt_p(ffi::EVP_PKEY_get1_DSA(self.as_ptr()))?;
|
||||
@ -160,7 +163,10 @@ impl<T> PKeyRef<T> {
|
||||
}
|
||||
|
||||
/// Returns a copy of the internal DH key.
|
||||
#[corresponds(EVP_PKEY_get1_DH)]
|
||||
///
|
||||
/// This corresponds to [`EVP_PKEY_get1_DH`].
|
||||
///
|
||||
/// [`EVP_PKEY_get1_DH`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_get1_DH.html
|
||||
pub fn dh(&self) -> Result<Dh<T>, ErrorStack> {
|
||||
unsafe {
|
||||
let dh = cvt_p(ffi::EVP_PKEY_get1_DH(self.as_ptr()))?;
|
||||
@ -169,7 +175,10 @@ impl<T> PKeyRef<T> {
|
||||
}
|
||||
|
||||
/// Returns a copy of the internal elliptic curve key.
|
||||
#[corresponds(EVP_PKEY_get1_EC_KEY)]
|
||||
///
|
||||
/// This corresponds to [`EVP_PKEY_get1_EC_KEY`].
|
||||
///
|
||||
/// [`EVP_PKEY_get1_EC_KEY`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_get1_EC_KEY.html
|
||||
pub fn ec_key(&self) -> Result<EcKey<T>, ErrorStack> {
|
||||
unsafe {
|
||||
let ec_key = cvt_p(ffi::EVP_PKEY_get1_EC_KEY(self.as_ptr()))?;
|
||||
@ -178,15 +187,19 @@ impl<T> PKeyRef<T> {
|
||||
}
|
||||
|
||||
/// Returns the `Id` that represents the type of this key.
|
||||
#[corresponds(EVP_PKEY_id)]
|
||||
#[must_use]
|
||||
///
|
||||
/// This corresponds to [`EVP_PKEY_id`].
|
||||
///
|
||||
/// [`EVP_PKEY_id`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_id.html
|
||||
pub fn id(&self) -> Id {
|
||||
unsafe { Id::from_raw(ffi::EVP_PKEY_id(self.as_ptr())) }
|
||||
}
|
||||
|
||||
/// Returns the maximum size of a signature in bytes.
|
||||
#[corresponds(EVP_PKEY_size)]
|
||||
#[must_use]
|
||||
///
|
||||
/// This corresponds to [`EVP_PKEY_size`].
|
||||
///
|
||||
/// [`EVP_PKEY_size`]: https://www.openssl.org/docs/man1.1.1/man3/EVP_PKEY_size.html
|
||||
pub fn size(&self) -> usize {
|
||||
unsafe { ffi::EVP_PKEY_size(self.as_ptr()) as usize }
|
||||
}
|
||||
@ -200,14 +213,20 @@ where
|
||||
/// Serializes the public key into a PEM-encoded SubjectPublicKeyInfo structure.
|
||||
///
|
||||
/// The output will have a header of `-----BEGIN PUBLIC KEY-----`.
|
||||
#[corresponds(PEM_write_bio_PUBKEY)]
|
||||
///
|
||||
/// This corresponds to [`PEM_write_bio_PUBKEY`].
|
||||
///
|
||||
/// [`PEM_write_bio_PUBKEY`]: https://www.openssl.org/docs/man1.1.0/crypto/PEM_write_bio_PUBKEY.html
|
||||
public_key_to_pem,
|
||||
ffi::PEM_write_bio_PUBKEY
|
||||
}
|
||||
|
||||
to_der! {
|
||||
/// Serializes the public key into a DER-encoded SubjectPublicKeyInfo structure.
|
||||
#[corresponds(i2d_PUBKEY)]
|
||||
///
|
||||
/// This corresponds to [`i2d_PUBKEY`].
|
||||
///
|
||||
/// [`i2d_PUBKEY`]: https://www.openssl.org/docs/man1.1.0/crypto/i2d_PUBKEY.html
|
||||
public_key_to_der,
|
||||
ffi::i2d_PUBKEY
|
||||
}
|
||||
@ -216,49 +235,17 @@ where
|
||||
///
|
||||
/// This corresponds to the bit length of the modulus of an RSA key, and the bit length of the
|
||||
/// group order for an elliptic curve key, for example.
|
||||
#[must_use]
|
||||
pub fn bits(&self) -> u32 {
|
||||
unsafe { ffi::EVP_PKEY_bits(self.as_ptr()) as u32 }
|
||||
}
|
||||
|
||||
/// Compares the public component of this key with another.
|
||||
#[must_use]
|
||||
pub fn public_eq<U>(&self, other: &PKeyRef<U>) -> bool
|
||||
where
|
||||
U: HasPublic,
|
||||
{
|
||||
unsafe { ffi::EVP_PKEY_cmp(self.as_ptr(), other.as_ptr()) == 1 }
|
||||
}
|
||||
|
||||
/// Returns the length of the "raw" form of the public key. Only supported for certain key types.
|
||||
#[corresponds(EVP_PKEY_get_raw_public_key)]
|
||||
pub fn raw_public_key_len(&self) -> Result<usize, ErrorStack> {
|
||||
unsafe {
|
||||
let mut size = 0;
|
||||
_ = cvt_0i(ffi::EVP_PKEY_get_raw_public_key(
|
||||
self.as_ptr(),
|
||||
std::ptr::null_mut(),
|
||||
&mut size,
|
||||
))?;
|
||||
Ok(size)
|
||||
}
|
||||
}
|
||||
|
||||
/// Outputs a copy of the "raw" form of the public key. Only supported for certain key types.
|
||||
///
|
||||
/// Returns the used portion of `out`.
|
||||
#[corresponds(EVP_PKEY_get_raw_public_key)]
|
||||
pub fn raw_public_key<'a>(&self, out: &'a mut [u8]) -> Result<&'a [u8], ErrorStack> {
|
||||
unsafe {
|
||||
let mut size = out.len();
|
||||
_ = cvt_0i(ffi::EVP_PKEY_get_raw_public_key(
|
||||
self.as_ptr(),
|
||||
out.as_mut_ptr(),
|
||||
&mut size,
|
||||
))?;
|
||||
Ok(&out[..size])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PKeyRef<T>
|
||||
@ -269,75 +256,42 @@ where
|
||||
/// Serializes the private key to a PEM-encoded PKCS#8 PrivateKeyInfo structure.
|
||||
///
|
||||
/// The output will have a header of `-----BEGIN PRIVATE KEY-----`.
|
||||
#[corresponds(PEM_write_bio_PKCS8PrivateKey)]
|
||||
///
|
||||
/// This corresponds to [`PEM_write_bio_PKCS8PrivateKey`].
|
||||
///
|
||||
/// [`PEM_write_bio_PKCS8PrivateKey`]: https://www.openssl.org/docs/man1.0.2/crypto/PEM_write_bio_PKCS8PrivateKey.html
|
||||
private_key_to_pem_pkcs8,
|
||||
/// Serializes the private key to a PEM-encoded PKCS#8 EncryptedPrivateKeyInfo structure.
|
||||
///
|
||||
/// The output will have a header of `-----BEGIN ENCRYPTED PRIVATE KEY-----`.
|
||||
#[corresponds(PEM_write_bio_PKCS8PrivateKey)]
|
||||
///
|
||||
/// This corresponds to [`PEM_write_bio_PKCS8PrivateKey`].
|
||||
///
|
||||
/// [`PEM_write_bio_PKCS8PrivateKey`]: https://www.openssl.org/docs/man1.0.2/crypto/PEM_write_bio_PKCS8PrivateKey.html
|
||||
private_key_to_pem_pkcs8_passphrase,
|
||||
ffi::PEM_write_bio_PKCS8PrivateKey
|
||||
}
|
||||
|
||||
to_der! {
|
||||
/// Serializes the private key to a DER-encoded key type specific format.
|
||||
#[corresponds(i2d_PrivateKey)]
|
||||
///
|
||||
/// This corresponds to [`i2d_PrivateKey`].
|
||||
///
|
||||
/// [`i2d_PrivateKey`]: https://www.openssl.org/docs/man1.0.2/crypto/i2d_PrivateKey.html
|
||||
private_key_to_der,
|
||||
ffi::i2d_PrivateKey
|
||||
}
|
||||
|
||||
// This isn't actually PEM output, but `i2d_PKCS8PrivateKey_bio` is documented to be
|
||||
// "identical to the corresponding PEM function", and it's declared in pem.h.
|
||||
private_key_to_pem! {
|
||||
/// Serializes the private key to a DER-encoded PKCS#8 PrivateKeyInfo structure.
|
||||
#[corresponds(i2d_PKCS8PrivateKey_bio)]
|
||||
private_key_to_der_pkcs8,
|
||||
/// Serializes the private key to a DER-encoded PKCS#8 EncryptedPrivateKeyInfo structure.
|
||||
#[corresponds(i2d_PKCS8PrivateKey_bio)]
|
||||
private_key_to_der_pkcs8_passphrase,
|
||||
ffi::i2d_PKCS8PrivateKey_bio
|
||||
}
|
||||
|
||||
/// Returns the length of the "raw" form of the private key. Only supported for certain key types.
|
||||
#[corresponds(EVP_PKEY_get_raw_private_key)]
|
||||
pub fn raw_private_key_len(&self) -> Result<usize, ErrorStack> {
|
||||
unsafe {
|
||||
let mut size = 0;
|
||||
_ = cvt_0i(ffi::EVP_PKEY_get_raw_private_key(
|
||||
self.as_ptr(),
|
||||
std::ptr::null_mut(),
|
||||
&mut size,
|
||||
))?;
|
||||
Ok(size)
|
||||
}
|
||||
}
|
||||
|
||||
/// Outputs a copy of the "raw" form of the private key. Only supported for certain key types.
|
||||
///
|
||||
/// Returns the used portion of `out`.
|
||||
#[corresponds(EVP_PKEY_get_raw_private_key)]
|
||||
pub fn raw_private_key<'a>(&self, out: &'a mut [u8]) -> Result<&'a [u8], ErrorStack> {
|
||||
unsafe {
|
||||
let mut size = out.len();
|
||||
_ = cvt_0i(ffi::EVP_PKEY_get_raw_private_key(
|
||||
self.as_ptr(),
|
||||
out.as_mut_ptr(),
|
||||
&mut size,
|
||||
))?;
|
||||
Ok(&out[..size])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for PKey<T> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
let alg = match self.id() {
|
||||
Id::RSA => "RSA",
|
||||
Id::RSAPSS => "RSAPSS",
|
||||
Id::DSA => "DSA",
|
||||
Id::DH => "DH",
|
||||
Id::EC => "EC",
|
||||
Id::ED25519 => "Ed25519",
|
||||
#[cfg(not(feature = "fips"))]
|
||||
Id::ED448 => "Ed448",
|
||||
_ => "unknown",
|
||||
};
|
||||
@ -354,7 +308,10 @@ impl<T> Clone for PKey<T> {
|
||||
|
||||
impl<T> PKey<T> {
|
||||
/// Creates a new `PKey` containing an RSA key.
|
||||
#[corresponds(EVP_PKEY_assign_RSA)]
|
||||
///
|
||||
/// This corresponds to [`EVP_PKEY_assign_RSA`].
|
||||
///
|
||||
/// [`EVP_PKEY_assign_RSA`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_assign_RSA.html
|
||||
pub fn from_rsa(rsa: Rsa<T>) -> Result<PKey<T>, ErrorStack> {
|
||||
unsafe {
|
||||
let evp = cvt_p(ffi::EVP_PKEY_new())?;
|
||||
@ -362,7 +319,7 @@ impl<T> PKey<T> {
|
||||
cvt(ffi::EVP_PKEY_assign(
|
||||
pkey.0,
|
||||
ffi::EVP_PKEY_RSA,
|
||||
rsa.as_ptr().cast(),
|
||||
rsa.as_ptr() as *mut _,
|
||||
))?;
|
||||
mem::forget(rsa);
|
||||
Ok(pkey)
|
||||
@ -370,7 +327,10 @@ impl<T> PKey<T> {
|
||||
}
|
||||
|
||||
/// Creates a new `PKey` containing an elliptic curve key.
|
||||
#[corresponds(EVP_PKEY_assign_EC_KEY)]
|
||||
///
|
||||
/// This corresponds to [`EVP_PKEY_assign_EC_KEY`].
|
||||
///
|
||||
/// [`EVP_PKEY_assign_EC_KEY`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_assign_EC_KEY.html
|
||||
pub fn from_ec_key(ec_key: EcKey<T>) -> Result<PKey<T>, ErrorStack> {
|
||||
unsafe {
|
||||
let evp = cvt_p(ffi::EVP_PKEY_new())?;
|
||||
@ -378,7 +338,7 @@ impl<T> PKey<T> {
|
||||
cvt(ffi::EVP_PKEY_assign(
|
||||
pkey.0,
|
||||
ffi::EVP_PKEY_EC,
|
||||
ec_key.as_ptr().cast(),
|
||||
ec_key.as_ptr() as *mut _,
|
||||
))?;
|
||||
mem::forget(ec_key);
|
||||
Ok(pkey)
|
||||
@ -389,17 +349,26 @@ impl<T> PKey<T> {
|
||||
impl PKey<Private> {
|
||||
private_key_from_pem! {
|
||||
/// Deserializes a private key from a PEM-encoded key type specific format.
|
||||
#[corresponds(PEM_read_bio_PrivateKey)]
|
||||
///
|
||||
/// This corresponds to [`PEM_read_bio_PrivateKey`].
|
||||
///
|
||||
/// [`PEM_read_bio_PrivateKey`]: https://www.openssl.org/docs/man1.1.0/crypto/PEM_read_bio_PrivateKey.html
|
||||
private_key_from_pem,
|
||||
|
||||
/// Deserializes a private key from a PEM-encoded encrypted key type specific format.
|
||||
#[corresponds(PEM_read_bio_PrivateKey)]
|
||||
///
|
||||
/// This corresponds to [`PEM_read_bio_PrivateKey`].
|
||||
///
|
||||
/// [`PEM_read_bio_PrivateKey`]: https://www.openssl.org/docs/man1.1.0/crypto/PEM_read_bio_PrivateKey.html
|
||||
private_key_from_pem_passphrase,
|
||||
|
||||
/// Deserializes a private key from a PEM-encoded encrypted key type specific format.
|
||||
///
|
||||
/// The callback should fill the password into the provided buffer and return its length.
|
||||
#[corresponds(PEM_read_bio_PrivateKey)]
|
||||
///
|
||||
/// This corresponds to [`PEM_read_bio_PrivateKey`].
|
||||
///
|
||||
/// [`PEM_read_bio_PrivateKey`]: https://www.openssl.org/docs/man1.1.0/crypto/PEM_read_bio_PrivateKey.html
|
||||
private_key_from_pem_callback,
|
||||
PKey<Private>,
|
||||
ffi::PEM_read_bio_PrivateKey
|
||||
@ -411,7 +380,10 @@ impl PKey<Private> {
|
||||
/// This function will automatically attempt to detect the underlying key format, and
|
||||
/// supports the unencrypted PKCS#8 PrivateKeyInfo structures as well as key type specific
|
||||
/// formats.
|
||||
#[corresponds(d2i_AutoPrivateKey)]
|
||||
///
|
||||
/// This corresponds to [`d2i_AutoPrivateKey`].
|
||||
///
|
||||
/// [`d2i_AutoPrivateKey`]: https://www.openssl.org/docs/man1.0.2/crypto/d2i_AutoPrivateKey.html
|
||||
private_key_from_der,
|
||||
PKey<Private>,
|
||||
ffi::d2i_AutoPrivateKey,
|
||||
@ -424,7 +396,7 @@ impl PKey<Private> {
|
||||
pub fn private_key_from_pkcs8(der: &[u8]) -> Result<PKey<Private>, ErrorStack> {
|
||||
unsafe {
|
||||
ffi::init();
|
||||
let len = der.len().min(c_long::MAX as usize) as c_long;
|
||||
let len = der.len().min(c_long::max_value() as usize) as c_long;
|
||||
let p8inf = cvt_p(ffi::d2i_PKCS8_PRIV_KEY_INFO(
|
||||
ptr::null_mut(),
|
||||
&mut der.as_ptr(),
|
||||
@ -437,7 +409,7 @@ impl PKey<Private> {
|
||||
}
|
||||
|
||||
/// Deserializes a DER-formatted PKCS#8 private key, using a callback to retrieve the password
|
||||
/// if the key is encrypted.
|
||||
/// if the key is encrpyted.
|
||||
///
|
||||
/// The callback should copy the password into the provided buffer and return the number of
|
||||
/// bytes written.
|
||||
@ -456,7 +428,7 @@ impl PKey<Private> {
|
||||
bio.as_ptr(),
|
||||
ptr::null_mut(),
|
||||
Some(invoke_passwd_cb::<F>),
|
||||
std::ptr::addr_of_mut!(cb).cast(),
|
||||
&mut cb as *mut _ as *mut _,
|
||||
))
|
||||
.map(|p| PKey::from_ptr(p))
|
||||
}
|
||||
@ -475,12 +447,12 @@ impl PKey<Private> {
|
||||
unsafe {
|
||||
ffi::init();
|
||||
let bio = MemBioSlice::new(der)?;
|
||||
let passphrase = CString::new(passphrase).map_err(ErrorStack::internal_error)?;
|
||||
let passphrase = CString::new(passphrase).unwrap();
|
||||
cvt_p(ffi::d2i_PKCS8PrivateKey_bio(
|
||||
bio.as_ptr(),
|
||||
ptr::null_mut(),
|
||||
None,
|
||||
passphrase.as_ptr().cast_mut().cast(),
|
||||
passphrase.as_ptr() as *const _ as *mut _,
|
||||
))
|
||||
.map(|p| PKey::from_ptr(p))
|
||||
}
|
||||
@ -492,7 +464,10 @@ impl PKey<Public> {
|
||||
/// Decodes a PEM-encoded SubjectPublicKeyInfo structure.
|
||||
///
|
||||
/// The input should have a header of `-----BEGIN PUBLIC KEY-----`.
|
||||
#[corresponds(PEM_read_bio_PUBKEY)]
|
||||
///
|
||||
/// This corresponds to [`PEM_read_bio_PUBKEY`].
|
||||
///
|
||||
/// [`PEM_read_bio_PUBKEY`]: https://www.openssl.org/docs/man1.0.2/crypto/PEM_read_bio_PUBKEY.html
|
||||
public_key_from_pem,
|
||||
PKey<Public>,
|
||||
ffi::PEM_read_bio_PUBKEY
|
||||
@ -500,7 +475,10 @@ impl PKey<Public> {
|
||||
|
||||
from_der! {
|
||||
/// Decodes a DER-encoded SubjectPublicKeyInfo structure.
|
||||
#[corresponds(d2i_PUBKEY)]
|
||||
///
|
||||
/// This corresponds to [`d2i_PUBKEY`].
|
||||
///
|
||||
/// [`d2i_PUBKEY`]: https://www.openssl.org/docs/man1.1.0/crypto/d2i_PUBKEY.html
|
||||
public_key_from_der,
|
||||
PKey<Public>,
|
||||
ffi::d2i_PUBKEY,
|
||||
@ -512,8 +490,6 @@ use crate::ffi::EVP_PKEY_up_ref;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use hex::FromHex as _;
|
||||
|
||||
use crate::ec::EcKey;
|
||||
use crate::nid::Nid;
|
||||
use crate::rsa::Rsa;
|
||||
@ -595,18 +571,6 @@ mod tests {
|
||||
assert!(pub_key.windows(10).any(|s| s == b"PUBLIC KEY"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_der_pkcs8() {
|
||||
let key = include_bytes!("../test/key.der");
|
||||
let key = PKey::private_key_from_der(key).unwrap();
|
||||
|
||||
let priv_key = key.private_key_to_der_pkcs8().unwrap();
|
||||
|
||||
// Check that this has the correct PKCS#8 version number and algorithm.
|
||||
assert_eq!(hex::encode(&priv_key[4..=6]), "020100"); // Version 0
|
||||
assert_eq!(hex::encode(&priv_key[9..=19]), "06092a864886f70d010101"); // Algorithm RSA/PKCS#1
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rsa_accessor() {
|
||||
let rsa = Rsa::generate(2048).unwrap();
|
||||
@ -624,34 +588,4 @@ mod tests {
|
||||
assert_eq!(pkey.id(), Id::EC);
|
||||
assert!(pkey.rsa().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_raw_accessors() {
|
||||
const ED25519_PRIVATE_KEY_DER: &str = concat!(
|
||||
"302e020100300506032b6570042204207c8c6497f9960d5595d7815f550569e5",
|
||||
"f77764ac97e63e339aaa68cc1512b683"
|
||||
);
|
||||
let pkey =
|
||||
PKey::private_key_from_der(&Vec::from_hex(ED25519_PRIVATE_KEY_DER).unwrap()).unwrap();
|
||||
assert_eq!(pkey.id(), Id::ED25519);
|
||||
|
||||
let priv_len = pkey.raw_private_key_len().unwrap();
|
||||
assert_eq!(priv_len, 32);
|
||||
let mut raw_private_key_buf = [0; 40];
|
||||
let raw_private_key = pkey.raw_private_key(&mut raw_private_key_buf).unwrap();
|
||||
assert_eq!(raw_private_key.len(), 32);
|
||||
assert_ne!(raw_private_key, [0; 32]);
|
||||
pkey.raw_private_key(&mut [0; 5])
|
||||
.expect_err("buffer too small");
|
||||
|
||||
let pub_len = pkey.raw_public_key_len().unwrap();
|
||||
assert_eq!(pub_len, 32);
|
||||
let mut raw_public_key_buf = [0; 40];
|
||||
let raw_public_key = pkey.raw_public_key(&mut raw_public_key_buf).unwrap();
|
||||
assert_eq!(raw_public_key.len(), 32);
|
||||
assert_ne!(raw_public_key, [0; 32]);
|
||||
assert_ne!(raw_public_key, raw_private_key);
|
||||
pkey.raw_public_key(&mut [0; 5])
|
||||
.expect_err("buffer too small");
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,8 +35,8 @@ use crate::error::ErrorStack;
|
||||
pub fn rand_bytes(buf: &mut [u8]) -> Result<(), ErrorStack> {
|
||||
unsafe {
|
||||
ffi::init();
|
||||
assert!(buf.len() <= c_int::MAX as usize);
|
||||
cvt(ffi::RAND_bytes(buf.as_mut_ptr(), buf.len()))
|
||||
assert!(buf.len() <= c_int::max_value() as usize);
|
||||
cvt(ffi::RAND_bytes(buf.as_mut_ptr(), buf.len())).map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -23,18 +23,16 @@
|
||||
//! let mut buf = vec![0; rsa.size() as usize];
|
||||
//! let encrypted_len = rsa.public_encrypt(data, &mut buf, Padding::PKCS1).unwrap();
|
||||
//! ```
|
||||
use crate::ffi;
|
||||
use foreign_types::{ForeignType, ForeignTypeRef};
|
||||
use libc::c_int;
|
||||
use openssl_macros::corresponds;
|
||||
use std::fmt;
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
|
||||
use crate::bn::{BigNum, BigNumRef};
|
||||
use crate::error::ErrorStack;
|
||||
use crate::ffi;
|
||||
use crate::pkey::{HasPrivate, HasPublic, Private, Public};
|
||||
use crate::try_int;
|
||||
use crate::{cvt, cvt_n, cvt_p};
|
||||
|
||||
pub const EVP_PKEY_OP_SIGN: c_int = 1 << 3;
|
||||
@ -68,14 +66,12 @@ impl Padding {
|
||||
pub const PKCS1_PSS: Padding = Padding(ffi::RSA_PKCS1_PSS_PADDING);
|
||||
|
||||
/// Creates a `Padding` from an integer representation.
|
||||
#[must_use]
|
||||
pub fn from_raw(value: c_int) -> Padding {
|
||||
Padding(value)
|
||||
}
|
||||
|
||||
/// Returns the integer representation of `Padding`.
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
#[must_use]
|
||||
pub fn as_raw(&self) -> c_int {
|
||||
self.0
|
||||
}
|
||||
@ -117,19 +113,28 @@ where
|
||||
/// Serializes the private key to a PEM-encoded PKCS#1 RSAPrivateKey structure.
|
||||
///
|
||||
/// The output will have a header of `-----BEGIN RSA PRIVATE KEY-----`.
|
||||
#[corresponds(PEM_write_bio_RSAPrivateKey)]
|
||||
///
|
||||
/// This corresponds to [`PEM_write_bio_RSAPrivateKey`].
|
||||
///
|
||||
/// [`PEM_write_bio_RSAPrivateKey`]: https://www.openssl.org/docs/man1.1.0/crypto/PEM_write_bio_RSAPrivateKey.html
|
||||
private_key_to_pem,
|
||||
/// Serializes the private key to a PEM-encoded encrypted PKCS#1 RSAPrivateKey structure.
|
||||
///
|
||||
/// The output will have a header of `-----BEGIN RSA PRIVATE KEY-----`.
|
||||
#[corresponds(PEM_write_bio_RSAPrivateKey)]
|
||||
///
|
||||
/// This corresponds to [`PEM_write_bio_RSAPrivateKey`].
|
||||
///
|
||||
/// [`PEM_write_bio_RSAPrivateKey`]: https://www.openssl.org/docs/man1.1.0/crypto/PEM_write_bio_RSAPrivateKey.html
|
||||
private_key_to_pem_passphrase,
|
||||
ffi::PEM_write_bio_RSAPrivateKey
|
||||
}
|
||||
|
||||
to_der! {
|
||||
/// Serializes the private key to a DER-encoded PKCS#1 RSAPrivateKey structure.
|
||||
#[corresponds(i2d_RSAPrivateKey)]
|
||||
///
|
||||
/// This corresponds to [`i2d_RSAPrivateKey`].
|
||||
///
|
||||
/// [`i2d_RSAPrivateKey`]: https://www.openssl.org/docs/man1.0.2/crypto/i2d_RSAPrivateKey.html
|
||||
private_key_to_der,
|
||||
ffi::i2d_RSAPrivateKey
|
||||
}
|
||||
@ -146,7 +151,7 @@ where
|
||||
to: &mut [u8],
|
||||
padding: Padding,
|
||||
) -> Result<usize, ErrorStack> {
|
||||
assert!(i32::try_from(from.len()).is_ok());
|
||||
assert!(from.len() <= i32::max_value() as usize);
|
||||
assert!(to.len() >= self.size() as usize);
|
||||
|
||||
unsafe {
|
||||
@ -173,7 +178,7 @@ where
|
||||
to: &mut [u8],
|
||||
padding: Padding,
|
||||
) -> Result<usize, ErrorStack> {
|
||||
assert!(i32::try_from(from.len()).is_ok());
|
||||
assert!(from.len() <= i32::max_value() as usize);
|
||||
assert!(to.len() >= self.size() as usize);
|
||||
|
||||
unsafe {
|
||||
@ -189,19 +194,23 @@ where
|
||||
}
|
||||
|
||||
/// Returns a reference to the private exponent of the key.
|
||||
#[corresponds(RSA_get0_key)]
|
||||
#[must_use]
|
||||
///
|
||||
/// This corresponds to [`RSA_get0_key`].
|
||||
///
|
||||
/// [`RSA_get0_key`]: https://www.openssl.org/docs/man1.1.0/crypto/RSA_get0_key.html
|
||||
pub fn d(&self) -> &BigNumRef {
|
||||
unsafe {
|
||||
let mut d = ptr::null();
|
||||
RSA_get0_key(self.as_ptr(), ptr::null_mut(), ptr::null_mut(), &mut d);
|
||||
BigNumRef::from_ptr(d.cast_mut())
|
||||
BigNumRef::from_ptr(d as *mut _)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the first factor of the exponent of the key.
|
||||
#[corresponds(RSA_get0_factors)]
|
||||
#[must_use]
|
||||
///
|
||||
/// This corresponds to [`RSA_get0_factors`].
|
||||
///
|
||||
/// [`RSA_get0_factors`]: https://www.openssl.org/docs/man1.1.0/crypto/RSA_get0_key.html
|
||||
pub fn p(&self) -> Option<&BigNumRef> {
|
||||
unsafe {
|
||||
let mut p = ptr::null();
|
||||
@ -209,14 +218,16 @@ where
|
||||
if p.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(BigNumRef::from_ptr(p.cast_mut()))
|
||||
Some(BigNumRef::from_ptr(p as *mut _))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the second factor of the exponent of the key.
|
||||
#[corresponds(RSA_get0_factors)]
|
||||
#[must_use]
|
||||
///
|
||||
/// This corresponds to [`RSA_get0_factors`].
|
||||
///
|
||||
/// [`RSA_get0_factors`]: https://www.openssl.org/docs/man1.1.0/crypto/RSA_get0_key.html
|
||||
pub fn q(&self) -> Option<&BigNumRef> {
|
||||
unsafe {
|
||||
let mut q = ptr::null();
|
||||
@ -224,14 +235,16 @@ where
|
||||
if q.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(BigNumRef::from_ptr(q.cast_mut()))
|
||||
Some(BigNumRef::from_ptr(q as *mut _))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the first exponent used for CRT calculations.
|
||||
#[corresponds(RSA_get0_crt_params)]
|
||||
#[must_use]
|
||||
///
|
||||
/// This corresponds to [`RSA_get0_crt_params`].
|
||||
///
|
||||
/// [`RSA_get0_crt_params`]: https://www.openssl.org/docs/man1.1.0/crypto/RSA_get0_key.html
|
||||
pub fn dmp1(&self) -> Option<&BigNumRef> {
|
||||
unsafe {
|
||||
let mut dp = ptr::null();
|
||||
@ -239,14 +252,16 @@ where
|
||||
if dp.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(BigNumRef::from_ptr(dp.cast_mut()))
|
||||
Some(BigNumRef::from_ptr(dp as *mut _))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the second exponent used for CRT calculations.
|
||||
#[corresponds(RSA_get0_crt_params)]
|
||||
#[must_use]
|
||||
///
|
||||
/// This corresponds to [`RSA_get0_crt_params`].
|
||||
///
|
||||
/// [`RSA_get0_crt_params`]: https://www.openssl.org/docs/man1.1.0/crypto/RSA_get0_key.html
|
||||
pub fn dmq1(&self) -> Option<&BigNumRef> {
|
||||
unsafe {
|
||||
let mut dq = ptr::null();
|
||||
@ -254,14 +269,16 @@ where
|
||||
if dq.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(BigNumRef::from_ptr(dq.cast_mut()))
|
||||
Some(BigNumRef::from_ptr(dq as *mut _))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the coefficient used for CRT calculations.
|
||||
#[corresponds(RSA_get0_crt_params)]
|
||||
#[must_use]
|
||||
///
|
||||
/// This corresponds to [`RSA_get0_crt_params`].
|
||||
///
|
||||
/// [`RSA_get0_crt_params`]: https://www.openssl.org/docs/man1.1.0/crypto/RSA_get0_key.html
|
||||
pub fn iqmp(&self) -> Option<&BigNumRef> {
|
||||
unsafe {
|
||||
let mut qi = ptr::null();
|
||||
@ -269,14 +286,16 @@ where
|
||||
if qi.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(BigNumRef::from_ptr(qi.cast_mut()))
|
||||
Some(BigNumRef::from_ptr(qi as *mut _))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Validates RSA parameters for correctness
|
||||
#[corresponds(RSA_check_key)]
|
||||
#[allow(clippy::unnecessary_cast)]
|
||||
///
|
||||
/// This corresponds to [`RSA_check_key`].
|
||||
///
|
||||
/// [`RSA_check_key`]: https://www.openssl.org/docs/man1.1.0/crypto/RSA_check_key.html
|
||||
pub fn check_key(&self) -> Result<bool, ErrorStack> {
|
||||
unsafe {
|
||||
let result = ffi::RSA_check_key(self.as_ptr()) as i32;
|
||||
@ -297,14 +316,20 @@ where
|
||||
/// Serializes the public key into a PEM-encoded SubjectPublicKeyInfo structure.
|
||||
///
|
||||
/// The output will have a header of `-----BEGIN PUBLIC KEY-----`.
|
||||
#[corresponds(PEM_write_bio_RSA_PUBKEY)]
|
||||
///
|
||||
/// This corresponds to [`PEM_write_bio_RSA_PUBKEY`].
|
||||
///
|
||||
/// [`PEM_write_bio_RSA_PUBKEY`]: https://www.openssl.org/docs/man1.0.2/crypto/pem.html
|
||||
public_key_to_pem,
|
||||
ffi::PEM_write_bio_RSA_PUBKEY
|
||||
}
|
||||
|
||||
to_der! {
|
||||
/// Serializes the public key into a DER-encoded SubjectPublicKeyInfo structure.
|
||||
#[corresponds(i2d_RSA_PUBKEY)]
|
||||
///
|
||||
/// This corresponds to [`i2d_RSA_PUBKEY`].
|
||||
///
|
||||
/// [`i2d_RSA_PUBKEY`]: https://www.openssl.org/docs/man1.1.0/crypto/i2d_RSA_PUBKEY.html
|
||||
public_key_to_der,
|
||||
ffi::i2d_RSA_PUBKEY
|
||||
}
|
||||
@ -313,22 +338,29 @@ where
|
||||
/// Serializes the public key into a PEM-encoded PKCS#1 RSAPublicKey structure.
|
||||
///
|
||||
/// The output will have a header of `-----BEGIN RSA PUBLIC KEY-----`.
|
||||
#[corresponds(PEM_write_bio_RSAPublicKey)]
|
||||
///
|
||||
/// This corresponds to [`PEM_write_bio_RSAPublicKey`].
|
||||
///
|
||||
/// [`PEM_write_bio_RSAPublicKey`]: https://www.openssl.org/docs/man1.0.2/crypto/pem.html
|
||||
public_key_to_pem_pkcs1,
|
||||
ffi::PEM_write_bio_RSAPublicKey
|
||||
}
|
||||
|
||||
to_der! {
|
||||
/// Serializes the public key into a DER-encoded PKCS#1 RSAPublicKey structure.
|
||||
#[corresponds(i2d_RSAPublicKey)]
|
||||
///
|
||||
/// This corresponds to [`i2d_RSAPublicKey`].
|
||||
///
|
||||
/// [`i2d_RSAPublicKey`]: https://www.openssl.org/docs/man1.0.2/crypto/i2d_RSAPublicKey.html
|
||||
public_key_to_der_pkcs1,
|
||||
ffi::i2d_RSAPublicKey
|
||||
}
|
||||
|
||||
/// Returns the size of the modulus in bytes.
|
||||
#[corresponds(RSA_size)]
|
||||
#[allow(clippy::unnecessary_cast)]
|
||||
#[must_use]
|
||||
///
|
||||
/// This corresponds to [`RSA_size`].
|
||||
///
|
||||
/// [`RSA_size`]: https://www.openssl.org/docs/man1.1.0/crypto/RSA_size.html
|
||||
pub fn size(&self) -> u32 {
|
||||
unsafe { ffi::RSA_size(self.as_ptr()) as u32 }
|
||||
}
|
||||
@ -344,7 +376,7 @@ where
|
||||
to: &mut [u8],
|
||||
padding: Padding,
|
||||
) -> Result<usize, ErrorStack> {
|
||||
assert!(i32::try_from(from.len()).is_ok());
|
||||
assert!(from.len() <= i32::max_value() as usize);
|
||||
assert!(to.len() >= self.size() as usize);
|
||||
|
||||
unsafe {
|
||||
@ -370,7 +402,7 @@ where
|
||||
to: &mut [u8],
|
||||
padding: Padding,
|
||||
) -> Result<usize, ErrorStack> {
|
||||
assert!(i32::try_from(from.len()).is_ok());
|
||||
assert!(from.len() <= i32::max_value() as usize);
|
||||
assert!(to.len() >= self.size() as usize);
|
||||
|
||||
unsafe {
|
||||
@ -386,24 +418,28 @@ where
|
||||
}
|
||||
|
||||
/// Returns a reference to the modulus of the key.
|
||||
#[corresponds(RSA_get0_key)]
|
||||
#[must_use]
|
||||
///
|
||||
/// This corresponds to [`RSA_get0_key`].
|
||||
///
|
||||
/// [`RSA_get0_key`]: https://www.openssl.org/docs/man1.1.0/crypto/RSA_get0_key.html
|
||||
pub fn n(&self) -> &BigNumRef {
|
||||
unsafe {
|
||||
let mut n = ptr::null();
|
||||
RSA_get0_key(self.as_ptr(), &mut n, ptr::null_mut(), ptr::null_mut());
|
||||
BigNumRef::from_ptr(n.cast_mut())
|
||||
BigNumRef::from_ptr(n as *mut _)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the public exponent of the key.
|
||||
#[corresponds(RSA_get0_key)]
|
||||
#[must_use]
|
||||
///
|
||||
/// This corresponds to [`RSA_get0_key`].
|
||||
///
|
||||
/// [`RSA_get0_key`]: https://www.openssl.org/docs/man1.1.0/crypto/RSA_get0_key.html
|
||||
pub fn e(&self) -> &BigNumRef {
|
||||
unsafe {
|
||||
let mut e = ptr::null();
|
||||
RSA_get0_key(self.as_ptr(), ptr::null_mut(), &mut e, ptr::null_mut());
|
||||
BigNumRef::from_ptr(e.cast_mut())
|
||||
BigNumRef::from_ptr(e as *mut _)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -413,11 +449,15 @@ impl Rsa<Public> {
|
||||
///
|
||||
/// `n` is the modulus common to both public and private key.
|
||||
/// `e` is the public exponent.
|
||||
#[corresponds(RSA_new)]
|
||||
///
|
||||
/// This corresponds to [`RSA_new`] and uses [`RSA_set0_key`].
|
||||
///
|
||||
/// [`RSA_new`]: https://www.openssl.org/docs/man1.1.0/crypto/RSA_new.html
|
||||
/// [`RSA_set0_key`]: https://www.openssl.org/docs/man1.1.0/crypto/RSA_set0_key.html
|
||||
pub fn from_public_components(n: BigNum, e: BigNum) -> Result<Rsa<Public>, ErrorStack> {
|
||||
unsafe {
|
||||
let rsa = cvt_p(ffi::RSA_new())?;
|
||||
cvt(RSA_set0_key(rsa, n.as_ptr(), e.as_ptr(), ptr::null_mut()))?;
|
||||
RSA_set0_key(rsa, n.as_ptr(), e.as_ptr(), ptr::null_mut());
|
||||
mem::forget((n, e));
|
||||
Ok(Rsa::from_ptr(rsa))
|
||||
}
|
||||
@ -427,7 +467,10 @@ impl Rsa<Public> {
|
||||
/// Decodes a PEM-encoded SubjectPublicKeyInfo structure containing an RSA key.
|
||||
///
|
||||
/// The input should have a header of `-----BEGIN PUBLIC KEY-----`.
|
||||
#[corresponds(PEM_read_bio_RSA_PUBKEY)]
|
||||
///
|
||||
/// This corresponds to [`PEM_read_bio_RSA_PUBKEY`].
|
||||
///
|
||||
/// [`PEM_read_bio_RSA_PUBKEY`]: https://www.openssl.org/docs/man1.0.2/crypto/PEM_read_bio_RSA_PUBKEY.html
|
||||
public_key_from_pem,
|
||||
Rsa<Public>,
|
||||
ffi::PEM_read_bio_RSA_PUBKEY
|
||||
@ -437,7 +480,10 @@ impl Rsa<Public> {
|
||||
/// Decodes a PEM-encoded PKCS#1 RSAPublicKey structure.
|
||||
///
|
||||
/// The input should have a header of `-----BEGIN RSA PUBLIC KEY-----`.
|
||||
#[corresponds(PEM_read_bio_RSAPublicKey)]
|
||||
///
|
||||
/// This corresponds to [`PEM_read_bio_RSAPublicKey`].
|
||||
///
|
||||
/// [`PEM_read_bio_RSAPublicKey`]: https://www.openssl.org/docs/man1.0.2/crypto/PEM_read_bio_RSAPublicKey.html
|
||||
public_key_from_pem_pkcs1,
|
||||
Rsa<Public>,
|
||||
ffi::PEM_read_bio_RSAPublicKey
|
||||
@ -445,7 +491,10 @@ impl Rsa<Public> {
|
||||
|
||||
from_der! {
|
||||
/// Decodes a DER-encoded SubjectPublicKeyInfo structure containing an RSA key.
|
||||
#[corresponds(d2i_RSA_PUBKEY)]
|
||||
///
|
||||
/// This corresponds to [`d2i_RSA_PUBKEY`].
|
||||
///
|
||||
/// [`d2i_RSA_PUBKEY`]: https://www.openssl.org/docs/man1.0.2/crypto/d2i_RSA_PUBKEY.html
|
||||
public_key_from_der,
|
||||
Rsa<Public>,
|
||||
ffi::d2i_RSA_PUBKEY,
|
||||
@ -454,7 +503,10 @@ impl Rsa<Public> {
|
||||
|
||||
from_der! {
|
||||
/// Decodes a DER-encoded PKCS#1 RSAPublicKey structure.
|
||||
#[corresponds(d2i_RSAPublicKey)]
|
||||
///
|
||||
/// This corresponds to [`d2i_RSAPublicKey`].
|
||||
///
|
||||
/// [`d2i_RSAPublicKey`]: https://www.openssl.org/docs/man1.0.2/crypto/d2i_RSA_PUBKEY.html
|
||||
public_key_from_der_pkcs1,
|
||||
Rsa<Public>,
|
||||
ffi::d2i_RSAPublicKey,
|
||||
@ -471,11 +523,15 @@ impl RsaPrivateKeyBuilder {
|
||||
///
|
||||
/// `n` is the modulus common to both public and private key.
|
||||
/// `e` is the public exponent and `d` is the private exponent.
|
||||
#[corresponds(RSA_new)]
|
||||
///
|
||||
/// This corresponds to [`RSA_new`] and uses [`RSA_set0_key`].
|
||||
///
|
||||
/// [`RSA_new`]: https://www.openssl.org/docs/man1.1.0/crypto/RSA_new.html
|
||||
/// [`RSA_set0_key`]: https://www.openssl.org/docs/man1.1.0/crypto/RSA_set0_key.html
|
||||
pub fn new(n: BigNum, e: BigNum, d: BigNum) -> Result<RsaPrivateKeyBuilder, ErrorStack> {
|
||||
unsafe {
|
||||
let rsa = cvt_p(ffi::RSA_new())?;
|
||||
cvt(RSA_set0_key(rsa, n.as_ptr(), e.as_ptr(), d.as_ptr()))?;
|
||||
RSA_set0_key(rsa, n.as_ptr(), e.as_ptr(), d.as_ptr());
|
||||
mem::forget((n, e, d));
|
||||
Ok(RsaPrivateKeyBuilder {
|
||||
rsa: Rsa::from_ptr(rsa),
|
||||
@ -486,10 +542,14 @@ impl RsaPrivateKeyBuilder {
|
||||
/// Sets the factors of the Rsa key.
|
||||
///
|
||||
/// `p` and `q` are the first and second factors of `n`.
|
||||
#[corresponds(RSA_set0_factors)]
|
||||
///
|
||||
/// This correspond to [`RSA_set0_factors`].
|
||||
///
|
||||
/// [`RSA_set0_factors`]: https://www.openssl.org/docs/man1.1.0/crypto/RSA_set0_factors.html
|
||||
// FIXME should be infallible
|
||||
pub fn set_factors(self, p: BigNum, q: BigNum) -> Result<RsaPrivateKeyBuilder, ErrorStack> {
|
||||
unsafe {
|
||||
cvt(RSA_set0_factors(self.rsa.as_ptr(), p.as_ptr(), q.as_ptr()))?;
|
||||
RSA_set0_factors(self.rsa.as_ptr(), p.as_ptr(), q.as_ptr());
|
||||
mem::forget((p, q));
|
||||
}
|
||||
Ok(self)
|
||||
@ -499,7 +559,11 @@ impl RsaPrivateKeyBuilder {
|
||||
///
|
||||
/// `dmp1`, `dmq1`, and `iqmp` are the exponents and coefficient for
|
||||
/// CRT calculations which is used to speed up RSA operations.
|
||||
#[corresponds(RSA_set0_crt_params)]
|
||||
///
|
||||
/// This correspond to [`RSA_set0_crt_params`].
|
||||
///
|
||||
/// [`RSA_set0_crt_params`]: https://www.openssl.org/docs/man1.1.0/crypto/RSA_set0_crt_params.html
|
||||
// FIXME should be infallible
|
||||
pub fn set_crt_params(
|
||||
self,
|
||||
dmp1: BigNum,
|
||||
@ -507,19 +571,18 @@ impl RsaPrivateKeyBuilder {
|
||||
iqmp: BigNum,
|
||||
) -> Result<RsaPrivateKeyBuilder, ErrorStack> {
|
||||
unsafe {
|
||||
cvt(RSA_set0_crt_params(
|
||||
RSA_set0_crt_params(
|
||||
self.rsa.as_ptr(),
|
||||
dmp1.as_ptr(),
|
||||
dmq1.as_ptr(),
|
||||
iqmp.as_ptr(),
|
||||
))?;
|
||||
);
|
||||
mem::forget((dmp1, dmq1, iqmp));
|
||||
}
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Returns the Rsa key.
|
||||
#[must_use]
|
||||
pub fn build(self) -> Rsa<Private> {
|
||||
self.rsa
|
||||
}
|
||||
@ -550,7 +613,10 @@ impl Rsa<Private> {
|
||||
/// Generates a public/private key pair with the specified size.
|
||||
///
|
||||
/// The public exponent will be 65537.
|
||||
#[corresponds(RSA_generate_key_ex)]
|
||||
///
|
||||
/// This corresponds to [`RSA_generate_key_ex`].
|
||||
///
|
||||
/// [`RSA_generate_key_ex`]: https://www.openssl.org/docs/man1.1.0/crypto/RSA_generate_key_ex.html
|
||||
pub fn generate(bits: u32) -> Result<Rsa<Private>, ErrorStack> {
|
||||
let e = BigNum::from_u32(ffi::RSA_F4 as u32)?;
|
||||
Rsa::generate_with_e(bits, &e)
|
||||
@ -559,7 +625,10 @@ impl Rsa<Private> {
|
||||
/// Generates a public/private key pair with the specified size and a custom exponent.
|
||||
///
|
||||
/// Unless you have specific needs and know what you're doing, use `Rsa::generate` instead.
|
||||
#[corresponds(RSA_generate_key_ex)]
|
||||
///
|
||||
/// This corresponds to [`RSA_generate_key_ex`].
|
||||
///
|
||||
/// [`RSA_generate_key_ex`]: https://www.openssl.org/docs/man1.1.0/crypto/RSA_generate_key_ex.html
|
||||
pub fn generate_with_e(bits: u32, e: &BigNumRef) -> Result<Rsa<Private>, ErrorStack> {
|
||||
unsafe {
|
||||
let rsa = Rsa::from_ptr(cvt_p(ffi::RSA_new())?);
|
||||
@ -576,17 +645,26 @@ impl Rsa<Private> {
|
||||
// FIXME these need to identify input formats
|
||||
private_key_from_pem! {
|
||||
/// Deserializes a private key from a PEM-encoded PKCS#1 RSAPrivateKey structure.
|
||||
#[corresponds(PEM_read_bio_RSAPrivateKey)]
|
||||
///
|
||||
/// This corresponds to [`PEM_read_bio_RSAPrivateKey`].
|
||||
///
|
||||
/// [`PEM_read_bio_RSAPrivateKey`]: https://www.openssl.org/docs/man1.1.0/crypto/PEM_read_bio_RSAPrivateKey.html
|
||||
private_key_from_pem,
|
||||
|
||||
/// Deserializes a private key from a PEM-encoded encrypted PKCS#1 RSAPrivateKey structure.
|
||||
#[corresponds(PEM_read_bio_RSAPrivateKey)]
|
||||
///
|
||||
/// This corresponds to [`PEM_read_bio_RSAPrivateKey`].
|
||||
///
|
||||
/// [`PEM_read_bio_RSAPrivateKey`]: https://www.openssl.org/docs/man1.1.0/crypto/PEM_read_bio_RSAPrivateKey.html
|
||||
private_key_from_pem_passphrase,
|
||||
|
||||
/// Deserializes a private key from a PEM-encoded encrypted PKCS#1 RSAPrivateKey structure.
|
||||
///
|
||||
/// The callback should fill the password into the provided buffer and return its length.
|
||||
#[corresponds(PEM_read_bio_RSAPrivateKey)]
|
||||
///
|
||||
/// This corresponds to [`PEM_read_bio_RSAPrivateKey`].
|
||||
///
|
||||
/// [`PEM_read_bio_RSAPrivateKey`]: https://www.openssl.org/docs/man1.1.0/crypto/PEM_read_bio_RSAPrivateKey.html
|
||||
private_key_from_pem_callback,
|
||||
Rsa<Private>,
|
||||
ffi::PEM_read_bio_RSAPrivateKey
|
||||
@ -594,7 +672,10 @@ impl Rsa<Private> {
|
||||
|
||||
from_der! {
|
||||
/// Decodes a DER-encoded PKCS#1 RSAPrivateKey structure.
|
||||
#[corresponds(d2i_RSAPrivateKey)]
|
||||
///
|
||||
/// This corresponds to [`d2i_RSAPrivateKey`].
|
||||
///
|
||||
/// [`d2i_RSAPrivateKey`]: https://www.openssl.org/docs/man1.0.2/crypto/d2i_RSA_PUBKEY.html
|
||||
private_key_from_der,
|
||||
Rsa<Private>,
|
||||
ffi::d2i_RSAPrivateKey,
|
||||
|
||||
@ -45,7 +45,7 @@
|
||||
//! ```
|
||||
use crate::ffi;
|
||||
use libc::c_void;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::mem;
|
||||
|
||||
/// Computes the SHA1 hash of some data.
|
||||
///
|
||||
@ -54,67 +54,56 @@ use std::mem::MaybeUninit;
|
||||
/// SHA1 is known to be insecure - it should not be used unless required for
|
||||
/// compatibility with existing systems.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[allow(deprecated)] // https://github.com/rust-lang/rust/issues/63566
|
||||
pub fn sha1(data: &[u8]) -> [u8; 20] {
|
||||
unsafe {
|
||||
let mut hash: MaybeUninit<[u8; 20]> = MaybeUninit::uninit();
|
||||
ffi::SHA1(data.as_ptr(), data.len(), hash.as_mut_ptr().cast());
|
||||
hash.assume_init()
|
||||
let mut hash: [u8; 20] = mem::uninitialized();
|
||||
ffi::SHA1(data.as_ptr(), data.len(), hash.as_mut_ptr());
|
||||
hash
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes the SHA224 hash of some data.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[allow(deprecated)] // https://github.com/rust-lang/rust/issues/63566
|
||||
pub fn sha224(data: &[u8]) -> [u8; 28] {
|
||||
unsafe {
|
||||
let mut hash: MaybeUninit<[u8; 28]> = MaybeUninit::uninit();
|
||||
ffi::SHA224(data.as_ptr(), data.len(), hash.as_mut_ptr().cast());
|
||||
hash.assume_init()
|
||||
let mut hash: [u8; 28] = mem::uninitialized();
|
||||
ffi::SHA224(data.as_ptr(), data.len(), hash.as_mut_ptr());
|
||||
hash
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes the SHA256 hash of some data.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[allow(deprecated)] // https://github.com/rust-lang/rust/issues/63566
|
||||
pub fn sha256(data: &[u8]) -> [u8; 32] {
|
||||
unsafe {
|
||||
let mut hash: MaybeUninit<[u8; 32]> = MaybeUninit::uninit();
|
||||
ffi::SHA256(data.as_ptr(), data.len(), hash.as_mut_ptr().cast());
|
||||
hash.assume_init()
|
||||
let mut hash: [u8; 32] = mem::uninitialized();
|
||||
ffi::SHA256(data.as_ptr(), data.len(), hash.as_mut_ptr());
|
||||
hash
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes the SHA384 hash of some data.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[allow(deprecated)] // https://github.com/rust-lang/rust/issues/63566
|
||||
pub fn sha384(data: &[u8]) -> [u8; 48] {
|
||||
unsafe {
|
||||
let mut hash: MaybeUninit<[u8; 48]> = MaybeUninit::uninit();
|
||||
ffi::SHA384(data.as_ptr(), data.len(), hash.as_mut_ptr().cast());
|
||||
hash.assume_init()
|
||||
let mut hash: [u8; 48] = mem::uninitialized();
|
||||
ffi::SHA384(data.as_ptr(), data.len(), hash.as_mut_ptr());
|
||||
hash
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes the SHA512 hash of some data.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[allow(deprecated)] // https://github.com/rust-lang/rust/issues/63566
|
||||
pub fn sha512(data: &[u8]) -> [u8; 64] {
|
||||
unsafe {
|
||||
let mut hash: MaybeUninit<[u8; 64]> = MaybeUninit::uninit();
|
||||
ffi::SHA512(data.as_ptr(), data.len(), hash.as_mut_ptr().cast());
|
||||
hash.assume_init()
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes the SHA512-256 hash of some data.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn sha512_256(data: &[u8]) -> [u8; 32] {
|
||||
unsafe {
|
||||
let mut hash: MaybeUninit<[u8; 32]> = MaybeUninit::uninit();
|
||||
ffi::SHA512_256(data.as_ptr(), data.len(), hash.as_mut_ptr().cast());
|
||||
hash.assume_init()
|
||||
let mut hash: [u8; 64] = mem::uninitialized();
|
||||
ffi::SHA512(data.as_ptr(), data.len(), hash.as_mut_ptr());
|
||||
hash
|
||||
}
|
||||
}
|
||||
|
||||
@ -137,12 +126,12 @@ impl Default for Sha1 {
|
||||
impl Sha1 {
|
||||
/// Creates a new hasher.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[allow(deprecated)] // https://github.com/rust-lang/rust/issues/63566
|
||||
pub fn new() -> Sha1 {
|
||||
unsafe {
|
||||
let mut ctx = MaybeUninit::uninit();
|
||||
ffi::SHA1_Init(ctx.as_mut_ptr());
|
||||
Sha1(ctx.assume_init())
|
||||
let mut ctx = mem::uninitialized();
|
||||
ffi::SHA1_Init(&mut ctx);
|
||||
Sha1(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
@ -152,18 +141,18 @@ impl Sha1 {
|
||||
#[inline]
|
||||
pub fn update(&mut self, buf: &[u8]) {
|
||||
unsafe {
|
||||
ffi::SHA1_Update(&mut self.0, buf.as_ptr().cast::<c_void>(), buf.len());
|
||||
ffi::SHA1_Update(&mut self.0, buf.as_ptr() as *const c_void, buf.len());
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the hash of the data.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[allow(deprecated)] // https://github.com/rust-lang/rust/issues/63566
|
||||
pub fn finish(mut self) -> [u8; 20] {
|
||||
unsafe {
|
||||
let mut hash: MaybeUninit<[u8; 20]> = MaybeUninit::uninit();
|
||||
ffi::SHA1_Final(hash.as_mut_ptr().cast(), &mut self.0);
|
||||
hash.assume_init()
|
||||
let mut hash: [u8; 20] = mem::uninitialized();
|
||||
ffi::SHA1_Final(hash.as_mut_ptr(), &mut self.0);
|
||||
hash
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -182,12 +171,12 @@ impl Default for Sha224 {
|
||||
impl Sha224 {
|
||||
/// Creates a new hasher.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[allow(deprecated)] // https://github.com/rust-lang/rust/issues/63566
|
||||
pub fn new() -> Sha224 {
|
||||
unsafe {
|
||||
let mut ctx = MaybeUninit::uninit();
|
||||
ffi::SHA224_Init(ctx.as_mut_ptr());
|
||||
Sha224(ctx.assume_init())
|
||||
let mut ctx = mem::uninitialized();
|
||||
ffi::SHA224_Init(&mut ctx);
|
||||
Sha224(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
@ -197,18 +186,18 @@ impl Sha224 {
|
||||
#[inline]
|
||||
pub fn update(&mut self, buf: &[u8]) {
|
||||
unsafe {
|
||||
ffi::SHA224_Update(&mut self.0, buf.as_ptr().cast::<c_void>(), buf.len());
|
||||
ffi::SHA224_Update(&mut self.0, buf.as_ptr() as *const c_void, buf.len());
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the hash of the data.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[allow(deprecated)] // https://github.com/rust-lang/rust/issues/63566
|
||||
pub fn finish(mut self) -> [u8; 28] {
|
||||
unsafe {
|
||||
let mut hash: MaybeUninit<[u8; 28]> = MaybeUninit::uninit();
|
||||
ffi::SHA224_Final(hash.as_mut_ptr().cast(), &mut self.0);
|
||||
hash.assume_init()
|
||||
let mut hash: [u8; 28] = mem::uninitialized();
|
||||
ffi::SHA224_Final(hash.as_mut_ptr(), &mut self.0);
|
||||
hash
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -227,12 +216,12 @@ impl Default for Sha256 {
|
||||
impl Sha256 {
|
||||
/// Creates a new hasher.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[allow(deprecated)] // https://github.com/rust-lang/rust/issues/63566
|
||||
pub fn new() -> Sha256 {
|
||||
unsafe {
|
||||
let mut ctx = MaybeUninit::uninit();
|
||||
ffi::SHA256_Init(ctx.as_mut_ptr());
|
||||
Sha256(ctx.assume_init())
|
||||
let mut ctx = mem::uninitialized();
|
||||
ffi::SHA256_Init(&mut ctx);
|
||||
Sha256(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
@ -242,18 +231,18 @@ impl Sha256 {
|
||||
#[inline]
|
||||
pub fn update(&mut self, buf: &[u8]) {
|
||||
unsafe {
|
||||
ffi::SHA256_Update(&mut self.0, buf.as_ptr().cast::<c_void>(), buf.len());
|
||||
ffi::SHA256_Update(&mut self.0, buf.as_ptr() as *const c_void, buf.len());
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the hash of the data.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[allow(deprecated)] // https://github.com/rust-lang/rust/issues/63566
|
||||
pub fn finish(mut self) -> [u8; 32] {
|
||||
unsafe {
|
||||
let mut hash: MaybeUninit<[u8; 32]> = MaybeUninit::uninit();
|
||||
ffi::SHA256_Final(hash.as_mut_ptr().cast(), &mut self.0);
|
||||
hash.assume_init()
|
||||
let mut hash: [u8; 32] = mem::uninitialized();
|
||||
ffi::SHA256_Final(hash.as_mut_ptr(), &mut self.0);
|
||||
hash
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -272,12 +261,12 @@ impl Default for Sha384 {
|
||||
impl Sha384 {
|
||||
/// Creates a new hasher.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[allow(deprecated)] // https://github.com/rust-lang/rust/issues/63566
|
||||
pub fn new() -> Sha384 {
|
||||
unsafe {
|
||||
let mut ctx = MaybeUninit::uninit();
|
||||
ffi::SHA384_Init(ctx.as_mut_ptr());
|
||||
Sha384(ctx.assume_init())
|
||||
let mut ctx = mem::uninitialized();
|
||||
ffi::SHA384_Init(&mut ctx);
|
||||
Sha384(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
@ -287,18 +276,18 @@ impl Sha384 {
|
||||
#[inline]
|
||||
pub fn update(&mut self, buf: &[u8]) {
|
||||
unsafe {
|
||||
ffi::SHA384_Update(&mut self.0, buf.as_ptr().cast::<c_void>(), buf.len());
|
||||
ffi::SHA384_Update(&mut self.0, buf.as_ptr() as *const c_void, buf.len());
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the hash of the data.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[allow(deprecated)] // https://github.com/rust-lang/rust/issues/63566
|
||||
pub fn finish(mut self) -> [u8; 48] {
|
||||
unsafe {
|
||||
let mut hash: MaybeUninit<[u8; 48]> = MaybeUninit::uninit();
|
||||
ffi::SHA384_Final(hash.as_mut_ptr().cast(), &mut self.0);
|
||||
hash.assume_init()
|
||||
let mut hash: [u8; 48] = mem::uninitialized();
|
||||
ffi::SHA384_Final(hash.as_mut_ptr(), &mut self.0);
|
||||
hash
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -317,12 +306,12 @@ impl Default for Sha512 {
|
||||
impl Sha512 {
|
||||
/// Creates a new hasher.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[allow(deprecated)] // https://github.com/rust-lang/rust/issues/63566
|
||||
pub fn new() -> Sha512 {
|
||||
unsafe {
|
||||
let mut ctx = MaybeUninit::uninit();
|
||||
ffi::SHA512_Init(ctx.as_mut_ptr());
|
||||
Sha512(ctx.assume_init())
|
||||
let mut ctx = mem::uninitialized();
|
||||
ffi::SHA512_Init(&mut ctx);
|
||||
Sha512(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
@ -332,63 +321,18 @@ impl Sha512 {
|
||||
#[inline]
|
||||
pub fn update(&mut self, buf: &[u8]) {
|
||||
unsafe {
|
||||
ffi::SHA512_Update(&mut self.0, buf.as_ptr().cast::<c_void>(), buf.len());
|
||||
ffi::SHA512_Update(&mut self.0, buf.as_ptr() as *const c_void, buf.len());
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the hash of the data.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[allow(deprecated)] // https://github.com/rust-lang/rust/issues/63566
|
||||
pub fn finish(mut self) -> [u8; 64] {
|
||||
unsafe {
|
||||
let mut hash: MaybeUninit<[u8; 64]> = MaybeUninit::uninit();
|
||||
ffi::SHA512_Final(hash.as_mut_ptr().cast(), &mut self.0);
|
||||
hash.assume_init()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An object which calculates a SHA512-256 hash of some data.
|
||||
#[derive(Clone)]
|
||||
pub struct Sha512_256(ffi::SHA512_CTX);
|
||||
|
||||
impl Default for Sha512_256 {
|
||||
#[inline]
|
||||
fn default() -> Sha512_256 {
|
||||
Sha512_256::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Sha512_256 {
|
||||
/// Creates a new hasher.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn new() -> Sha512_256 {
|
||||
unsafe {
|
||||
let mut ctx = MaybeUninit::uninit();
|
||||
ffi::SHA512_256_Init(ctx.as_mut_ptr());
|
||||
Sha512_256(ctx.assume_init())
|
||||
}
|
||||
}
|
||||
|
||||
/// Feeds some data into the hasher.
|
||||
///
|
||||
/// This can be called multiple times.
|
||||
#[inline]
|
||||
pub fn update(&mut self, buf: &[u8]) {
|
||||
unsafe {
|
||||
ffi::SHA512_256_Update(&mut self.0, buf.as_ptr().cast::<c_void>(), buf.len());
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the hash of the data.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn finish(mut self) -> [u8; 32] {
|
||||
unsafe {
|
||||
let mut hash: MaybeUninit<[u8; 32]> = MaybeUninit::uninit();
|
||||
ffi::SHA512_256_Final(hash.as_mut_ptr().cast(), &mut self.0);
|
||||
hash.assume_init()
|
||||
let mut hash: [u8; 64] = mem::uninitialized();
|
||||
ffi::SHA512_Final(hash.as_mut_ptr(), &mut self.0);
|
||||
hash
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -510,22 +454,4 @@ mod test {
|
||||
hasher.update(b"bc");
|
||||
assert_eq!(hex::encode(&hasher.finish()[..]), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn standalone_512_256() {
|
||||
let data = b"abc";
|
||||
let expected = "53048e2681941ef99b2e29b76b4c7dabe4c2d0c634fc6d46e0e2f13107e7af23";
|
||||
|
||||
assert_eq!(hex::encode(&sha512_256(data)[..]), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn struct_512_256() {
|
||||
let expected = "53048e2681941ef99b2e29b76b4c7dabe4c2d0c634fc6d46e0e2f13107e7af23";
|
||||
|
||||
let mut hasher = Sha512_256::new();
|
||||
hasher.update(b"a");
|
||||
hasher.update(b"bc");
|
||||
assert_eq!(hex::encode(&hasher.finish()[..]), expected);
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,7 +37,6 @@
|
||||
use crate::ffi;
|
||||
use foreign_types::ForeignTypeRef;
|
||||
use libc::c_int;
|
||||
use openssl_macros::corresponds;
|
||||
use std::io::{self, Write};
|
||||
use std::marker::PhantomData;
|
||||
use std::ptr;
|
||||
@ -60,7 +59,6 @@ impl RsaPssSaltlen {
|
||||
}
|
||||
|
||||
/// Sets the salt length to the given value.
|
||||
#[must_use]
|
||||
pub fn custom(val: c_int) -> RsaPssSaltlen {
|
||||
RsaPssSaltlen(val)
|
||||
}
|
||||
@ -80,10 +78,10 @@ pub struct Signer<'a> {
|
||||
_p: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
unsafe impl Sync for Signer<'_> {}
|
||||
unsafe impl Send for Signer<'_> {}
|
||||
unsafe impl<'a> Sync for Signer<'a> {}
|
||||
unsafe impl<'a> Send for Signer<'a> {}
|
||||
|
||||
impl Drop for Signer<'_> {
|
||||
impl<'a> Drop for Signer<'a> {
|
||||
fn drop(&mut self) {
|
||||
// pkey_ctx is owned by the md_ctx, so no need to explicitly free it.
|
||||
unsafe {
|
||||
@ -98,7 +96,10 @@ impl<'a> Signer<'a> {
|
||||
///
|
||||
/// This cannot be used with Ed25519 or Ed448 keys. Please refer to
|
||||
/// `new_without_digest`.
|
||||
#[corresponds(EVP_DigestSignInit)]
|
||||
///
|
||||
/// OpenSSL documentation at [`EVP_DigestSignInit`].
|
||||
///
|
||||
/// [`EVP_DigestSignInit`]: https://www.openssl.org/docs/manmaster/man3/EVP_DigestSignInit.html
|
||||
pub fn new<T>(type_: MessageDigest, pkey: &'a PKeyRef<T>) -> Result<Signer<'a>, ErrorStack>
|
||||
where
|
||||
T: HasPrivate,
|
||||
@ -110,7 +111,10 @@ impl<'a> Signer<'a> {
|
||||
///
|
||||
/// This is the only way to create a `Verifier` for Ed25519 or Ed448 keys.
|
||||
/// It can also be used to create a CMAC.
|
||||
#[corresponds(EVP_DigestSignInit)]
|
||||
///
|
||||
/// OpenSSL documentation at [`EVP_DigestSignInit`].
|
||||
///
|
||||
/// [`EVP_DigestSignInit`]: https://www.openssl.org/docs/manmaster/man3/EVP_DigestSignInit.html
|
||||
pub fn new_without_digest<T>(pkey: &'a PKeyRef<T>) -> Result<Signer<'a>, ErrorStack>
|
||||
where
|
||||
T: HasPrivate,
|
||||
@ -155,7 +159,8 @@ impl<'a> Signer<'a> {
|
||||
/// Returns the RSA padding mode in use.
|
||||
///
|
||||
/// This is only useful for RSA keys.
|
||||
#[corresponds(EVP_PKEY_CTX_get_rsa_padding)]
|
||||
///
|
||||
/// This corresponds to `EVP_PKEY_CTX_get_rsa_padding`.
|
||||
pub fn rsa_padding(&self) -> Result<Padding, ErrorStack> {
|
||||
unsafe {
|
||||
let mut pad = 0;
|
||||
@ -167,39 +172,51 @@ impl<'a> Signer<'a> {
|
||||
/// Sets the RSA padding mode.
|
||||
///
|
||||
/// This is only useful for RSA keys.
|
||||
#[corresponds(EVP_PKEY_CTX_set_rsa_padding)]
|
||||
///
|
||||
/// This corresponds to [`EVP_PKEY_CTX_set_rsa_padding`].
|
||||
///
|
||||
/// [`EVP_PKEY_CTX_set_rsa_padding`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_CTX_set_rsa_padding.html
|
||||
pub fn set_rsa_padding(&mut self, padding: Padding) -> Result<(), ErrorStack> {
|
||||
unsafe {
|
||||
cvt(ffi::EVP_PKEY_CTX_set_rsa_padding(
|
||||
self.pctx,
|
||||
padding.as_raw(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the RSA PSS salt length.
|
||||
///
|
||||
/// This is only useful for RSA keys.
|
||||
#[corresponds(EVP_PKEY_CTX_set_rsa_pss_saltlen)]
|
||||
///
|
||||
/// This corresponds to [`EVP_PKEY_CTX_set_rsa_pss_saltlen`].
|
||||
///
|
||||
/// [`EVP_PKEY_CTX_set_rsa_pss_saltlen`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_CTX_set_rsa_pss_saltlen.html
|
||||
pub fn set_rsa_pss_saltlen(&mut self, len: RsaPssSaltlen) -> Result<(), ErrorStack> {
|
||||
unsafe {
|
||||
cvt(ffi::EVP_PKEY_CTX_set_rsa_pss_saltlen(
|
||||
self.pctx,
|
||||
len.as_raw(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the RSA MGF1 algorithm.
|
||||
///
|
||||
/// This is only useful for RSA keys.
|
||||
#[corresponds(EVP_PKEY_CTX_set_rsa_mgf1_md)]
|
||||
///
|
||||
/// This corresponds to [`EVP_PKEY_CTX_set_rsa_mgf1_md`].
|
||||
///
|
||||
/// [`EVP_PKEY_CTX_set_rsa_mgf1_md`]: https://www.openssl.org/docs/manmaster/man7/RSA-PSS.html
|
||||
pub fn set_rsa_mgf1_md(&mut self, md: MessageDigest) -> Result<(), ErrorStack> {
|
||||
unsafe {
|
||||
cvt(ffi::EVP_PKEY_CTX_set_rsa_mgf1_md(
|
||||
self.pctx,
|
||||
md.as_ptr().cast_mut(),
|
||||
md.as_ptr() as *mut _,
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
@ -207,14 +224,18 @@ impl<'a> Signer<'a> {
|
||||
///
|
||||
/// Please note that PureEdDSA (Ed25519 and Ed448 keys) do not support streaming.
|
||||
/// Use `sign_oneshot` instead.
|
||||
#[corresponds(EVP_DigestUpdate)]
|
||||
///
|
||||
/// OpenSSL documentation at [`EVP_DigestUpdate`].
|
||||
///
|
||||
/// [`EVP_DigestUpdate`]: https://www.openssl.org/docs/manmaster/man3/EVP_DigestInit.html
|
||||
pub fn update(&mut self, buf: &[u8]) -> Result<(), ErrorStack> {
|
||||
unsafe {
|
||||
cvt(ffi::EVP_DigestUpdate(
|
||||
self.md_ctx,
|
||||
buf.as_ptr().cast(),
|
||||
buf.as_ptr() as *const _,
|
||||
buf.len(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
@ -222,7 +243,10 @@ impl<'a> Signer<'a> {
|
||||
///
|
||||
/// The actual signature may be shorter than this value. Check the return value of
|
||||
/// `sign` to get the exact length.
|
||||
#[corresponds(EVP_DigestSignFinal)]
|
||||
///
|
||||
/// OpenSSL documentation at [`EVP_DigestSignFinal`].
|
||||
///
|
||||
/// [`EVP_DigestSignFinal`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_DigestSignFinal.html
|
||||
pub fn len(&self) -> Result<usize, ErrorStack> {
|
||||
self.len_intern()
|
||||
}
|
||||
@ -245,13 +269,16 @@ impl<'a> Signer<'a> {
|
||||
///
|
||||
/// This method will fail if the buffer is not large enough for the signature. Use the `len`
|
||||
/// method to get an upper bound on the required size.
|
||||
#[corresponds(EVP_DigestSignFinal)]
|
||||
///
|
||||
/// OpenSSL documentation at [`EVP_DigestSignFinal`].
|
||||
///
|
||||
/// [`EVP_DigestSignFinal`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_DigestSignFinal.html
|
||||
pub fn sign(&self, buf: &mut [u8]) -> Result<usize, ErrorStack> {
|
||||
unsafe {
|
||||
let mut len = buf.len();
|
||||
cvt(ffi::EVP_DigestSignFinal(
|
||||
self.md_ctx,
|
||||
buf.as_mut_ptr().cast(),
|
||||
buf.as_mut_ptr() as *mut _,
|
||||
&mut len,
|
||||
))?;
|
||||
Ok(len)
|
||||
@ -276,7 +303,10 @@ impl<'a> Signer<'a> {
|
||||
///
|
||||
/// This method will fail if the buffer is not large enough for the signature. Use the `len`
|
||||
/// method to get an upper bound on the required size.
|
||||
#[corresponds(EVP_DigestSign)]
|
||||
///
|
||||
/// OpenSSL documentation at [`EVP_DigestSign`].
|
||||
///
|
||||
/// [`EVP_DigestSign`]: https://www.openssl.org/docs/man1.1.1/man3/EVP_DigestSign.html
|
||||
pub fn sign_oneshot(
|
||||
&mut self,
|
||||
sig_buf: &mut [u8],
|
||||
@ -286,9 +316,9 @@ impl<'a> Signer<'a> {
|
||||
let mut sig_len = sig_buf.len();
|
||||
cvt(ffi::EVP_DigestSign(
|
||||
self.md_ctx,
|
||||
sig_buf.as_mut_ptr(),
|
||||
sig_buf.as_mut_ptr() as *mut _,
|
||||
&mut sig_len,
|
||||
data_buf.as_ptr(),
|
||||
data_buf.as_ptr() as *const _,
|
||||
data_buf.len(),
|
||||
))?;
|
||||
Ok(sig_len)
|
||||
@ -307,7 +337,7 @@ impl<'a> Signer<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Signer<'_> {
|
||||
impl<'a> Write for Signer<'a> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.update(buf)?;
|
||||
Ok(buf.len())
|
||||
@ -324,10 +354,10 @@ pub struct Verifier<'a> {
|
||||
pkey_pd: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
unsafe impl Sync for Verifier<'_> {}
|
||||
unsafe impl Send for Verifier<'_> {}
|
||||
unsafe impl<'a> Sync for Verifier<'a> {}
|
||||
unsafe impl<'a> Send for Verifier<'a> {}
|
||||
|
||||
impl Drop for Verifier<'_> {
|
||||
impl<'a> Drop for Verifier<'a> {
|
||||
fn drop(&mut self) {
|
||||
// pkey_ctx is owned by the md_ctx, so no need to explicitly free it.
|
||||
unsafe {
|
||||
@ -342,7 +372,10 @@ impl<'a> Verifier<'a> {
|
||||
///
|
||||
/// This cannot be used with Ed25519 or Ed448 keys. Please refer to
|
||||
/// `new_without_digest`.
|
||||
#[corresponds(EVP_DigestVerifyInit)]
|
||||
///
|
||||
/// OpenSSL documentation at [`EVP_DigestVerifyInit`].
|
||||
///
|
||||
/// [`EVP_DigestVerifyInit`]: https://www.openssl.org/docs/manmaster/man3/EVP_DigestVerifyInit.html
|
||||
pub fn new<T>(type_: MessageDigest, pkey: &'a PKeyRef<T>) -> Result<Verifier<'a>, ErrorStack>
|
||||
where
|
||||
T: HasPublic,
|
||||
@ -353,7 +386,10 @@ impl<'a> Verifier<'a> {
|
||||
/// Creates a new `Verifier` without a digest.
|
||||
///
|
||||
/// This is the only way to create a `Verifier` for Ed25519 or Ed448 keys.
|
||||
#[corresponds(EVP_DigestVerifyInit)]
|
||||
///
|
||||
/// OpenSSL documentation at [`EVP_DigestVerifyInit`].
|
||||
///
|
||||
/// [`EVP_DigestVerifyInit`]: https://www.openssl.org/docs/manmaster/man3/EVP_DigestVerifyInit.html
|
||||
pub fn new_without_digest<T>(pkey: &'a PKeyRef<T>) -> Result<Verifier<'a>, ErrorStack>
|
||||
where
|
||||
T: HasPublic,
|
||||
@ -398,7 +434,8 @@ impl<'a> Verifier<'a> {
|
||||
/// Returns the RSA padding mode in use.
|
||||
///
|
||||
/// This is only useful for RSA keys.
|
||||
#[corresponds(EVP_PKEY_CTX_get_rsa_padding)]
|
||||
///
|
||||
/// This corresponds to `EVP_PKEY_CTX_get_rsa_padding`.
|
||||
pub fn rsa_padding(&self) -> Result<Padding, ErrorStack> {
|
||||
unsafe {
|
||||
let mut pad = 0;
|
||||
@ -410,39 +447,51 @@ impl<'a> Verifier<'a> {
|
||||
/// Sets the RSA padding mode.
|
||||
///
|
||||
/// This is only useful for RSA keys.
|
||||
#[corresponds(EVP_PKEY_CTX_set_rsa_padding)]
|
||||
///
|
||||
/// This corresponds to [`EVP_PKEY_CTX_set_rsa_padding`].
|
||||
///
|
||||
/// [`EVP_PKEY_CTX_set_rsa_padding`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_CTX_set_rsa_padding.html
|
||||
pub fn set_rsa_padding(&mut self, padding: Padding) -> Result<(), ErrorStack> {
|
||||
unsafe {
|
||||
cvt(ffi::EVP_PKEY_CTX_set_rsa_padding(
|
||||
self.pctx,
|
||||
padding.as_raw(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the RSA PSS salt length.
|
||||
///
|
||||
/// This is only useful for RSA keys.
|
||||
#[corresponds(EVP_PKEY_CTX_set_rsa_pss_saltlen)]
|
||||
///
|
||||
/// This corresponds to [`EVP_PKEY_CTX_set_rsa_pss_saltlen`].
|
||||
///
|
||||
/// [`EVP_PKEY_CTX_set_rsa_pss_saltlen`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_CTX_set_rsa_pss_saltlen.html
|
||||
pub fn set_rsa_pss_saltlen(&mut self, len: RsaPssSaltlen) -> Result<(), ErrorStack> {
|
||||
unsafe {
|
||||
cvt(ffi::EVP_PKEY_CTX_set_rsa_pss_saltlen(
|
||||
self.pctx,
|
||||
len.as_raw(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the RSA MGF1 algorithm.
|
||||
///
|
||||
/// This is only useful for RSA keys.
|
||||
#[corresponds(EVP_PKEY_CTX_set_rsa_mgf1_md)]
|
||||
///
|
||||
/// This corresponds to [`EVP_PKEY_CTX_set_rsa_mgf1_md`].
|
||||
///
|
||||
/// [`EVP_PKEY_CTX_set_rsa_mgf1_md`]: https://www.openssl.org/docs/manmaster/man7/RSA-PSS.html
|
||||
pub fn set_rsa_mgf1_md(&mut self, md: MessageDigest) -> Result<(), ErrorStack> {
|
||||
unsafe {
|
||||
cvt(ffi::EVP_PKEY_CTX_set_rsa_mgf1_md(
|
||||
self.pctx,
|
||||
md.as_ptr().cast_mut(),
|
||||
md.as_ptr() as *mut _,
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
@ -450,27 +499,34 @@ impl<'a> Verifier<'a> {
|
||||
///
|
||||
/// Please note that PureEdDSA (Ed25519 and Ed448 keys) do not support streaming.
|
||||
/// Use `verify_oneshot` instead.
|
||||
#[corresponds(EVP_DigestUpdate)]
|
||||
///
|
||||
/// OpenSSL documentation at [`EVP_DigestUpdate`].
|
||||
///
|
||||
/// [`EVP_DigestUpdate`]: https://www.openssl.org/docs/manmaster/man3/EVP_DigestInit.html
|
||||
pub fn update(&mut self, buf: &[u8]) -> Result<(), ErrorStack> {
|
||||
unsafe {
|
||||
cvt(ffi::EVP_DigestUpdate(
|
||||
self.md_ctx,
|
||||
buf.as_ptr().cast(),
|
||||
buf.as_ptr() as *const _,
|
||||
buf.len(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Determines if the data fed into the `Verifier` matches the provided signature.
|
||||
#[corresponds(EVP_DigestVerifyFinal)]
|
||||
///
|
||||
/// OpenSSL documentation at [`EVP_DigestVerifyFinal`].
|
||||
///
|
||||
/// [`EVP_DigestVerifyFinal`]: https://www.openssl.org/docs/manmaster/man3/EVP_DigestVerifyFinal.html
|
||||
pub fn verify(&self, signature: &[u8]) -> Result<bool, ErrorStack> {
|
||||
unsafe {
|
||||
let r =
|
||||
EVP_DigestVerifyFinal(self.md_ctx, signature.as_ptr().cast_mut(), signature.len());
|
||||
EVP_DigestVerifyFinal(self.md_ctx, signature.as_ptr() as *mut _, signature.len());
|
||||
match r {
|
||||
1 => Ok(true),
|
||||
0 => {
|
||||
ErrorStack::clear(); // discard error stack
|
||||
ErrorStack::get(); // discard error stack
|
||||
Ok(false)
|
||||
}
|
||||
_ => Err(ErrorStack::get()),
|
||||
@ -479,20 +535,23 @@ impl<'a> Verifier<'a> {
|
||||
}
|
||||
|
||||
/// Determines if the data given in buf matches the provided signature.
|
||||
#[corresponds(EVP_DigestVerify)]
|
||||
///
|
||||
/// OpenSSL documentation at [`EVP_DigestVerify`].
|
||||
///
|
||||
/// [`EVP_DigestVerify`]: https://www.openssl.org/docs/man1.1.1/man3/EVP_DigestVerify.html
|
||||
pub fn verify_oneshot(&mut self, signature: &[u8], buf: &[u8]) -> Result<bool, ErrorStack> {
|
||||
unsafe {
|
||||
let r = ffi::EVP_DigestVerify(
|
||||
self.md_ctx,
|
||||
signature.as_ptr().cast(),
|
||||
signature.as_ptr() as *const _,
|
||||
signature.len(),
|
||||
buf.as_ptr().cast(),
|
||||
buf.as_ptr() as *const _,
|
||||
buf.len(),
|
||||
);
|
||||
match r {
|
||||
1 => Ok(true),
|
||||
0 => {
|
||||
ErrorStack::clear();
|
||||
ErrorStack::get();
|
||||
Ok(false)
|
||||
}
|
||||
_ => Err(ErrorStack::get()),
|
||||
@ -501,7 +560,7 @@ impl<'a> Verifier<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Verifier<'_> {
|
||||
impl<'a> Write for Verifier<'a> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.update(buf)?;
|
||||
Ok(buf.len())
|
||||
|
||||
@ -20,14 +20,11 @@ impl Stackable for SrtpProtectionProfile {
|
||||
}
|
||||
|
||||
impl SrtpProtectionProfileRef {
|
||||
#[must_use]
|
||||
pub fn id(&self) -> SrtpProfileId {
|
||||
SrtpProfileId::from_raw(unsafe { (*self.as_ptr()).id })
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn name(&self) -> &'static str {
|
||||
unsafe { CStr::from_ptr((*self.as_ptr()).name.cast()) }
|
||||
unsafe { CStr::from_ptr((*self.as_ptr()).name as *const _) }
|
||||
.to_str()
|
||||
.expect("should be UTF-8")
|
||||
}
|
||||
@ -50,14 +47,12 @@ impl SrtpProfileId {
|
||||
pub const SRTP_NULL_SHA1_32: SrtpProfileId = SrtpProfileId(ffi::SRTP_NULL_SHA1_32 as _);
|
||||
|
||||
/// Creates a `SrtpProfileId` from an integer representation.
|
||||
#[must_use]
|
||||
pub fn from_raw(value: c_ulong) -> SrtpProfileId {
|
||||
SrtpProfileId(value)
|
||||
}
|
||||
|
||||
/// Returns the integer representation of `SrtpProfileId`.
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
#[must_use]
|
||||
pub fn as_raw(&self) -> c_ulong {
|
||||
self.0
|
||||
}
|
||||
|
||||
@ -1,392 +0,0 @@
|
||||
use super::mut_only::MutOnly;
|
||||
use super::{
|
||||
ClientHello, GetSessionPendingError, PrivateKeyMethod, PrivateKeyMethodError, SelectCertError,
|
||||
Ssl, SslAlert, SslContextBuilder, SslRef, SslSession, SslSignatureAlgorithm, SslVerifyError,
|
||||
SslVerifyMode,
|
||||
};
|
||||
#[cfg(feature = "credential")]
|
||||
use crate::error::ErrorStack;
|
||||
use crate::ex_data::Index;
|
||||
#[cfg(feature = "credential")]
|
||||
use crate::ssl::SslCredentialBuilder;
|
||||
use std::convert::identity;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::sync::LazyLock;
|
||||
use std::task::{ready, Context, Poll, Waker};
|
||||
|
||||
/// The type of futures to pass to [`SslContextBuilder::set_async_select_certificate_callback`].
|
||||
pub type BoxSelectCertFuture = ExDataFuture<Result<BoxSelectCertFinish, AsyncSelectCertError>>;
|
||||
|
||||
/// The type of callbacks returned by [`BoxSelectCertFuture`] methods.
|
||||
pub type BoxSelectCertFinish = Box<dyn FnOnce(ClientHello<'_>) -> Result<(), AsyncSelectCertError>>;
|
||||
|
||||
/// The type of futures returned by [`AsyncPrivateKeyMethod`] methods.
|
||||
pub type BoxPrivateKeyMethodFuture =
|
||||
ExDataFuture<Result<BoxPrivateKeyMethodFinish, AsyncPrivateKeyMethodError>>;
|
||||
|
||||
/// The type of callbacks returned by [`BoxPrivateKeyMethodFuture`].
|
||||
pub type BoxPrivateKeyMethodFinish =
|
||||
Box<dyn FnOnce(&mut SslRef, &mut [u8]) -> Result<usize, AsyncPrivateKeyMethodError>>;
|
||||
|
||||
/// The type of futures to pass to [`SslContextBuilder::set_async_get_session_callback`].
|
||||
pub type BoxGetSessionFuture = ExDataFuture<Option<BoxGetSessionFinish>>;
|
||||
|
||||
/// The type of callbacks returned by [`BoxSelectCertFuture`] methods.
|
||||
pub type BoxGetSessionFinish = Box<dyn FnOnce(&mut SslRef, &[u8]) -> Option<SslSession>>;
|
||||
|
||||
/// The type of futures to pass to [`SslContextBuilder::set_async_custom_verify_callback`].
|
||||
pub type BoxCustomVerifyFuture = ExDataFuture<Result<BoxCustomVerifyFinish, SslAlert>>;
|
||||
|
||||
/// The type of callbacks returned by [`BoxCustomVerifyFuture`] methods.
|
||||
pub type BoxCustomVerifyFinish = Box<dyn FnOnce(&mut SslRef) -> Result<(), SslAlert>>;
|
||||
|
||||
/// Convenience alias for futures stored in [`Ssl`] ex data by [`SslContextBuilder`] methods.
|
||||
///
|
||||
/// Public for documentation purposes.
|
||||
pub type ExDataFuture<T> = Pin<Box<dyn Future<Output = T> + Send>>;
|
||||
|
||||
pub(crate) static TASK_WAKER_INDEX: LazyLock<Index<Ssl, Option<Waker>>> =
|
||||
LazyLock::new(|| Ssl::new_ex_index().unwrap());
|
||||
pub(crate) static SELECT_CERT_FUTURE_INDEX: LazyLock<
|
||||
Index<Ssl, MutOnly<Option<BoxSelectCertFuture>>>,
|
||||
> = LazyLock::new(|| Ssl::new_ex_index().unwrap());
|
||||
pub(crate) static SELECT_PRIVATE_KEY_METHOD_FUTURE_INDEX: LazyLock<
|
||||
Index<Ssl, MutOnly<Option<BoxPrivateKeyMethodFuture>>>,
|
||||
> = LazyLock::new(|| Ssl::new_ex_index().unwrap());
|
||||
pub(crate) static SELECT_GET_SESSION_FUTURE_INDEX: LazyLock<
|
||||
Index<Ssl, MutOnly<Option<BoxGetSessionFuture>>>,
|
||||
> = LazyLock::new(|| Ssl::new_ex_index().unwrap());
|
||||
pub(crate) static SELECT_CUSTOM_VERIFY_FUTURE_INDEX: LazyLock<
|
||||
Index<Ssl, MutOnly<Option<BoxCustomVerifyFuture>>>,
|
||||
> = LazyLock::new(|| Ssl::new_ex_index().unwrap());
|
||||
|
||||
impl SslContextBuilder {
|
||||
/// Sets a callback that is called before most [`ClientHello`] processing
|
||||
/// and before the decision whether to resume a session is made. The
|
||||
/// callback may inspect the [`ClientHello`] and configure the connection.
|
||||
///
|
||||
/// This method uses a function that returns a future whose output is
|
||||
/// itself a closure that will be passed [`ClientHello`] to configure
|
||||
/// the connection based on the computations done in the future.
|
||||
///
|
||||
/// A task waker must be set on `Ssl` values associated with the resulting
|
||||
/// `SslContext` with [`SslRef::set_task_waker`].
|
||||
///
|
||||
/// See [`SslContextBuilder::set_select_certificate_callback`] for the sync
|
||||
/// setter of this callback.
|
||||
pub fn set_async_select_certificate_callback<F>(&mut self, callback: F)
|
||||
where
|
||||
F: Fn(&mut ClientHello<'_>) -> Result<BoxSelectCertFuture, AsyncSelectCertError>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
{
|
||||
self.set_select_certificate_callback(move |mut client_hello| {
|
||||
let fut_poll_result = with_ex_data_future(
|
||||
&mut client_hello,
|
||||
*SELECT_CERT_FUTURE_INDEX,
|
||||
ClientHello::ssl_mut,
|
||||
&callback,
|
||||
identity,
|
||||
);
|
||||
|
||||
let fut_result = match fut_poll_result {
|
||||
Poll::Ready(fut_result) => fut_result,
|
||||
Poll::Pending => return Err(SelectCertError::RETRY),
|
||||
};
|
||||
|
||||
let finish = fut_result.or(Err(SelectCertError::ERROR))?;
|
||||
|
||||
finish(client_hello).or(Err(SelectCertError::ERROR))
|
||||
});
|
||||
}
|
||||
|
||||
/// Configures a custom private key method on the context.
|
||||
///
|
||||
/// A task waker must be set on `Ssl` values associated with the resulting
|
||||
/// `SslContext` with [`SslRef::set_task_waker`].
|
||||
///
|
||||
/// See [`AsyncPrivateKeyMethod`] for more details.
|
||||
pub fn set_async_private_key_method(&mut self, method: impl AsyncPrivateKeyMethod) {
|
||||
self.set_private_key_method(AsyncPrivateKeyMethodBridge(Box::new(method)));
|
||||
}
|
||||
|
||||
/// Sets a callback that is called when a client proposed to resume a session
|
||||
/// but it was not found in the internal cache.
|
||||
///
|
||||
/// The callback is passed a reference to the session ID provided by the client.
|
||||
/// It should return the session corresponding to that ID if available. This is
|
||||
/// only used for servers, not clients.
|
||||
///
|
||||
/// A task waker must be set on `Ssl` values associated with the resulting
|
||||
/// `SslContext` with [`SslRef::set_task_waker`].
|
||||
///
|
||||
/// See [`SslContextBuilder::set_get_session_callback`] for the sync setter
|
||||
/// of this callback.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The returned [`SslSession`] must not be associated with a different [`SslContextBuilder`].
|
||||
pub unsafe fn set_async_get_session_callback<F>(&mut self, callback: F)
|
||||
where
|
||||
F: Fn(&mut SslRef, &[u8]) -> Option<BoxGetSessionFuture> + Send + Sync + 'static,
|
||||
{
|
||||
let async_callback = move |ssl: &mut SslRef, id: &[u8]| {
|
||||
let fut_poll_result = with_ex_data_future(
|
||||
&mut *ssl,
|
||||
*SELECT_GET_SESSION_FUTURE_INDEX,
|
||||
|ssl| ssl,
|
||||
|ssl| callback(ssl, id).ok_or(()),
|
||||
|option| option.ok_or(()),
|
||||
);
|
||||
|
||||
match fut_poll_result {
|
||||
Poll::Ready(Err(())) => Ok(None),
|
||||
Poll::Ready(Ok(finish)) => Ok(finish(ssl, id)),
|
||||
Poll::Pending => Err(GetSessionPendingError),
|
||||
}
|
||||
};
|
||||
|
||||
self.set_get_session_callback(async_callback);
|
||||
}
|
||||
|
||||
/// Configures certificate verification.
|
||||
///
|
||||
/// The callback should return `Ok(())` if the certificate is valid.
|
||||
/// If the certificate is invalid, the callback should return `SslVerifyError::Invalid(alert)`.
|
||||
/// Some useful alerts include [`SslAlert::CERTIFICATE_EXPIRED`], [`SslAlert::CERTIFICATE_REVOKED`],
|
||||
/// [`SslAlert::UNKNOWN_CA`], [`SslAlert::BAD_CERTIFICATE`], [`SslAlert::CERTIFICATE_UNKNOWN`],
|
||||
/// and [`SslAlert::INTERNAL_ERROR`]. See RFC 5246 section 7.2.2 for their precise meanings.
|
||||
///
|
||||
/// A task waker must be set on `Ssl` values associated with the resulting
|
||||
/// `SslContext` with [`SslRef::set_task_waker`].
|
||||
///
|
||||
/// See [`SslContextBuilder::set_custom_verify_callback`] for the sync version of this method.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This method panics if this `Ssl` is associated with a RPK context.
|
||||
pub fn set_async_custom_verify_callback<F>(&mut self, mode: SslVerifyMode, callback: F)
|
||||
where
|
||||
F: Fn(&mut SslRef) -> Result<BoxCustomVerifyFuture, SslAlert> + Send + Sync + 'static,
|
||||
{
|
||||
self.set_custom_verify_callback(mode, async_custom_verify_callback(callback));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "credential")]
|
||||
impl SslCredentialBuilder {
|
||||
/// Configures a custom private key method on the context.
|
||||
///
|
||||
/// A task waker must be set on `Ssl` values associated with the resulting
|
||||
/// `SslContext` with [`SslRef::set_task_waker`].
|
||||
///
|
||||
/// See [`AsyncPrivateKeyMethod`] for more details.
|
||||
pub fn set_async_private_key_method(
|
||||
&mut self,
|
||||
method: impl AsyncPrivateKeyMethod,
|
||||
) -> Result<(), ErrorStack> {
|
||||
self.set_private_key_method(AsyncPrivateKeyMethodBridge(Box::new(method)))
|
||||
}
|
||||
}
|
||||
|
||||
impl SslRef {
|
||||
pub fn set_async_custom_verify_callback<F>(&mut self, mode: SslVerifyMode, callback: F)
|
||||
where
|
||||
F: Fn(&mut SslRef) -> Result<BoxCustomVerifyFuture, SslAlert> + Send + Sync + 'static,
|
||||
{
|
||||
self.set_custom_verify_callback(mode, async_custom_verify_callback(callback));
|
||||
}
|
||||
|
||||
/// Sets the task waker to be used in async callbacks installed on this `Ssl`.
|
||||
pub fn set_task_waker(&mut self, waker: Option<Waker>) {
|
||||
self.replace_ex_data(*TASK_WAKER_INDEX, waker);
|
||||
}
|
||||
}
|
||||
|
||||
fn async_custom_verify_callback<F>(
|
||||
callback: F,
|
||||
) -> impl Fn(&mut SslRef) -> Result<(), SslVerifyError>
|
||||
where
|
||||
F: Fn(&mut SslRef) -> Result<BoxCustomVerifyFuture, SslAlert> + Send + Sync + 'static,
|
||||
{
|
||||
move |ssl| {
|
||||
let fut_poll_result = with_ex_data_future(
|
||||
&mut *ssl,
|
||||
*SELECT_CUSTOM_VERIFY_FUTURE_INDEX,
|
||||
|ssl| ssl,
|
||||
&callback,
|
||||
identity,
|
||||
);
|
||||
|
||||
match fut_poll_result {
|
||||
Poll::Ready(Err(alert)) => Err(SslVerifyError::Invalid(alert)),
|
||||
Poll::Ready(Ok(finish)) => Ok(finish(ssl).map_err(SslVerifyError::Invalid)?),
|
||||
Poll::Pending => Err(SslVerifyError::Retry),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A fatal error to be returned from async select certificate callbacks.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct AsyncSelectCertError;
|
||||
|
||||
/// Describes async private key hooks. This is used to off-load signing
|
||||
/// operations to a custom, potentially asynchronous, backend. Metadata about the
|
||||
/// key such as the type and size are parsed out of the certificate.
|
||||
///
|
||||
/// See [`PrivateKeyMethod`] for the sync version of those hooks.
|
||||
///
|
||||
/// [`ssl_private_key_method_st`]: https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#ssl_private_key_method_st
|
||||
pub trait AsyncPrivateKeyMethod: Send + Sync + 'static {
|
||||
/// Signs the message `input` using the specified signature algorithm.
|
||||
///
|
||||
/// This method uses a function that returns a future whose output is
|
||||
/// itself a closure that will be passed `ssl` and `output`
|
||||
/// to finish writing the signature.
|
||||
///
|
||||
/// See [`PrivateKeyMethod::sign`] for the sync version of this method.
|
||||
fn sign(
|
||||
&self,
|
||||
ssl: &mut SslRef,
|
||||
input: &[u8],
|
||||
signature_algorithm: SslSignatureAlgorithm,
|
||||
output: &mut [u8],
|
||||
) -> Result<BoxPrivateKeyMethodFuture, AsyncPrivateKeyMethodError>;
|
||||
|
||||
/// Decrypts `input`.
|
||||
///
|
||||
/// This method uses a function that returns a future whose output is
|
||||
/// itself a closure that will be passed `ssl` and `output`
|
||||
/// to finish decrypting the input.
|
||||
///
|
||||
/// See [`PrivateKeyMethod::decrypt`] for the sync version of this method.
|
||||
fn decrypt(
|
||||
&self,
|
||||
ssl: &mut SslRef,
|
||||
input: &[u8],
|
||||
output: &mut [u8],
|
||||
) -> Result<BoxPrivateKeyMethodFuture, AsyncPrivateKeyMethodError>;
|
||||
}
|
||||
|
||||
/// A fatal error to be returned from async private key methods.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct AsyncPrivateKeyMethodError;
|
||||
|
||||
struct AsyncPrivateKeyMethodBridge(Box<dyn AsyncPrivateKeyMethod>);
|
||||
|
||||
impl PrivateKeyMethod for AsyncPrivateKeyMethodBridge {
|
||||
fn sign(
|
||||
&self,
|
||||
ssl: &mut SslRef,
|
||||
input: &[u8],
|
||||
signature_algorithm: SslSignatureAlgorithm,
|
||||
output: &mut [u8],
|
||||
) -> Result<usize, PrivateKeyMethodError> {
|
||||
with_private_key_method(ssl, output, |ssl, output| {
|
||||
<dyn AsyncPrivateKeyMethod>::sign(&*self.0, ssl, input, signature_algorithm, output)
|
||||
})
|
||||
}
|
||||
|
||||
fn decrypt(
|
||||
&self,
|
||||
ssl: &mut SslRef,
|
||||
input: &[u8],
|
||||
output: &mut [u8],
|
||||
) -> Result<usize, PrivateKeyMethodError> {
|
||||
with_private_key_method(ssl, output, |ssl, output| {
|
||||
<dyn AsyncPrivateKeyMethod>::decrypt(&*self.0, ssl, input, output)
|
||||
})
|
||||
}
|
||||
|
||||
fn complete(
|
||||
&self,
|
||||
ssl: &mut SslRef,
|
||||
output: &mut [u8],
|
||||
) -> Result<usize, PrivateKeyMethodError> {
|
||||
with_private_key_method(ssl, output, |_, _| {
|
||||
// This should never be reached, if it does, that's a bug on boring's side,
|
||||
// which called `complete` without having been returned to with a pending
|
||||
// future from `sign` or `decrypt`.
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
panic!("BUG: boring called complete without a pending operation");
|
||||
}
|
||||
|
||||
Err(AsyncPrivateKeyMethodError)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates and drives a private key method future.
|
||||
///
|
||||
/// This is a convenience function for the three methods of impl `PrivateKeyMethod``
|
||||
/// for `dyn AsyncPrivateKeyMethod`. It relies on [`with_ex_data_future`] to
|
||||
/// drive the future and then immediately calls the final [`BoxPrivateKeyMethodFinish`]
|
||||
/// when the future is ready.
|
||||
fn with_private_key_method(
|
||||
ssl: &mut SslRef,
|
||||
output: &mut [u8],
|
||||
create_fut: impl FnOnce(
|
||||
&mut SslRef,
|
||||
&mut [u8],
|
||||
) -> Result<BoxPrivateKeyMethodFuture, AsyncPrivateKeyMethodError>,
|
||||
) -> Result<usize, PrivateKeyMethodError> {
|
||||
let fut_poll_result = with_ex_data_future(
|
||||
ssl,
|
||||
*SELECT_PRIVATE_KEY_METHOD_FUTURE_INDEX,
|
||||
|ssl| ssl,
|
||||
|ssl| create_fut(ssl, output),
|
||||
identity,
|
||||
);
|
||||
|
||||
let fut_result = match fut_poll_result {
|
||||
Poll::Ready(fut_result) => fut_result,
|
||||
Poll::Pending => return Err(PrivateKeyMethodError::RETRY),
|
||||
};
|
||||
|
||||
let finish = fut_result.or(Err(PrivateKeyMethodError::FAILURE))?;
|
||||
|
||||
finish(ssl, output).or(Err(PrivateKeyMethodError::FAILURE))
|
||||
}
|
||||
|
||||
/// Creates and drives a future stored in `ssl_handle`'s `Ssl` at ex data index `index`.
|
||||
///
|
||||
/// This function won't even bother storing the future in `index` if the future
|
||||
/// created by `create_fut` returns `Poll::Ready(_)` on the first poll call.
|
||||
fn with_ex_data_future<H, R, T, E>(
|
||||
ssl_handle: &mut H,
|
||||
index: Index<Ssl, MutOnly<Option<ExDataFuture<R>>>>,
|
||||
get_ssl_mut: impl Fn(&mut H) -> &mut SslRef,
|
||||
create_fut: impl FnOnce(&mut H) -> Result<ExDataFuture<R>, E>,
|
||||
into_result: impl Fn(R) -> Result<T, E>,
|
||||
) -> Poll<Result<T, E>> {
|
||||
let ssl = get_ssl_mut(ssl_handle);
|
||||
let waker = ssl
|
||||
.ex_data(*TASK_WAKER_INDEX)
|
||||
.cloned()
|
||||
.flatten()
|
||||
.expect("task waker should be set");
|
||||
|
||||
let mut ctx = Context::from_waker(&waker);
|
||||
|
||||
if let Some(data @ Some(_)) = ssl.ex_data_mut(index).map(MutOnly::get_mut) {
|
||||
let fut_result = into_result(ready!(data.as_mut().unwrap().as_mut().poll(&mut ctx)));
|
||||
|
||||
*data = None;
|
||||
|
||||
Poll::Ready(fut_result)
|
||||
} else {
|
||||
let mut fut = create_fut(ssl_handle)?;
|
||||
|
||||
match fut.as_mut().poll(&mut ctx) {
|
||||
Poll::Ready(fut_result) => Poll::Ready(into_result(fut_result)),
|
||||
Poll::Pending => {
|
||||
get_ssl_mut(ssl_handle).replace_ex_data(index, MutOnly::new(Some(fut)));
|
||||
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -44,7 +44,7 @@ pub fn new<S: Read + Write>(stream: S) -> Result<(*mut BIO, BioMethod), ErrorSta
|
||||
|
||||
unsafe {
|
||||
let bio = cvt_p(BIO_new(method.0.get()))?;
|
||||
BIO_set_data(bio, Box::into_raw(state).cast());
|
||||
BIO_set_data(bio, Box::into_raw(state) as *mut _);
|
||||
BIO_set_init(bio, 1);
|
||||
|
||||
Ok((bio, method))
|
||||
@ -76,7 +76,7 @@ pub unsafe extern "C" fn take_stream<S>(bio: *mut BIO) -> S {
|
||||
|
||||
assert!(!data.is_null());
|
||||
|
||||
let state = Box::<StreamState<S>>::from_raw(data.cast());
|
||||
let state = Box::<StreamState<S>>::from_raw(data as *mut _);
|
||||
|
||||
BIO_set_data(bio, ptr::null_mut());
|
||||
|
||||
@ -84,14 +84,17 @@ pub unsafe extern "C" fn take_stream<S>(bio: *mut BIO) -> S {
|
||||
}
|
||||
|
||||
pub unsafe fn set_dtls_mtu_size<S>(bio: *mut BIO, mtu_size: usize) {
|
||||
if mtu_size as u64 > c_long::MAX as u64 {
|
||||
panic!("Given MTU size {mtu_size} can't be represented in a positive `c_long` range")
|
||||
if mtu_size as u64 > c_long::max_value() as u64 {
|
||||
panic!(
|
||||
"Given MTU size {} can't be represented in a positive `c_long` range",
|
||||
mtu_size
|
||||
)
|
||||
}
|
||||
state::<S>(bio).dtls_mtu_size = mtu_size as c_long;
|
||||
}
|
||||
|
||||
unsafe fn state<'a, S: 'a>(bio: *mut BIO) -> &'a mut StreamState<S> {
|
||||
let data = BIO_get_data(bio).cast::<StreamState<S>>();
|
||||
let data = BIO_get_data(bio) as *mut StreamState<S>;
|
||||
|
||||
assert!(!data.is_null());
|
||||
|
||||
@ -101,12 +104,8 @@ unsafe fn state<'a, S: 'a>(bio: *mut BIO) -> &'a mut StreamState<S> {
|
||||
unsafe extern "C" fn bwrite<S: Write>(bio: *mut BIO, buf: *const c_char, len: c_int) -> c_int {
|
||||
BIO_clear_retry_flags(bio);
|
||||
|
||||
let Ok(len) = usize::try_from(len) else {
|
||||
return -1;
|
||||
};
|
||||
|
||||
let state = state::<S>(bio);
|
||||
let buf = slice::from_raw_parts(buf.cast(), len);
|
||||
let buf = slice::from_raw_parts(buf as *const _, len as usize);
|
||||
|
||||
match catch_unwind(AssertUnwindSafe(|| state.stream.write(buf))) {
|
||||
Ok(Ok(len)) => len as c_int,
|
||||
@ -127,12 +126,8 @@ unsafe extern "C" fn bwrite<S: Write>(bio: *mut BIO, buf: *const c_char, len: c_
|
||||
unsafe extern "C" fn bread<S: Read>(bio: *mut BIO, buf: *mut c_char, len: c_int) -> c_int {
|
||||
BIO_clear_retry_flags(bio);
|
||||
|
||||
let Ok(len) = usize::try_from(len) else {
|
||||
return -1;
|
||||
};
|
||||
|
||||
let state = state::<S>(bio);
|
||||
let buf = slice::from_raw_parts_mut(buf.cast(), len);
|
||||
let buf = slice::from_raw_parts_mut(buf as *mut _, len as usize);
|
||||
|
||||
match catch_unwind(AssertUnwindSafe(|| state.stream.read(buf))) {
|
||||
Ok(Ok(len)) => len as c_int,
|
||||
@ -171,13 +166,9 @@ unsafe extern "C" fn ctrl<S: Write>(
|
||||
let state = state::<S>(bio);
|
||||
|
||||
if cmd == BIO_CTRL_FLUSH {
|
||||
BIO_clear_retry_flags(bio);
|
||||
match catch_unwind(AssertUnwindSafe(|| state.stream.flush())) {
|
||||
Ok(Ok(())) => 1,
|
||||
Ok(Err(err)) => {
|
||||
if retriable_error(&err) {
|
||||
BIO_set_retry_write(bio);
|
||||
}
|
||||
state.error = Some(err);
|
||||
0
|
||||
}
|
||||
@ -209,7 +200,7 @@ unsafe extern "C" fn destroy<S>(bio: *mut BIO) -> c_int {
|
||||
let data = BIO_get_data(bio);
|
||||
|
||||
if !data.is_null() {
|
||||
drop(Box::<StreamState<S>>::from_raw(data.cast()));
|
||||
Box::<StreamState<S>>::from_raw(data as *mut _);
|
||||
BIO_set_data(bio, ptr::null_mut());
|
||||
}
|
||||
|
||||
@ -228,7 +219,7 @@ struct BIO_METHOD(*mut ffi::BIO_METHOD);
|
||||
impl BIO_METHOD {
|
||||
fn new<S: Read + Write>() -> BIO_METHOD {
|
||||
unsafe {
|
||||
let ptr = ffi::BIO_meth_new(ffi::BIO_TYPE_NONE, c"rust".as_ptr().cast());
|
||||
let ptr = ffi::BIO_meth_new(ffi::BIO_TYPE_NONE, b"rust\0".as_ptr() as *const _);
|
||||
assert!(!ptr.is_null());
|
||||
let ret = BIO_METHOD(ptr);
|
||||
assert!(ffi::BIO_meth_set_write(ptr, Some(bwrite::<S>)) != 0);
|
||||
|
||||
@ -1,141 +1,49 @@
|
||||
#![forbid(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
use super::{
|
||||
AlpnError, CertificateCompressor, ClientHello, GetSessionPendingError, PrivateKeyMethod,
|
||||
PrivateKeyMethodError, SelectCertError, SniError, Ssl, SslAlert, SslContext, SslContextRef,
|
||||
SslInfoCallbackAlert, SslInfoCallbackMode, SslInfoCallbackValue, SslRef, SslSession,
|
||||
SslSessionRef, SslSignatureAlgorithm, SslVerifyError, SESSION_CTX_INDEX,
|
||||
};
|
||||
use crate::error::ErrorStack;
|
||||
use crate::ffi;
|
||||
use crate::hmac::HmacCtxRef;
|
||||
use crate::ssl::TicketKeyCallbackResult;
|
||||
use crate::symm::CipherCtxRef;
|
||||
use crate::x509::{X509StoreContext, X509StoreContextRef};
|
||||
use foreign_types::ForeignType;
|
||||
use foreign_types::ForeignTypeRef;
|
||||
use libc::{c_char, c_int, c_uchar, c_uint, c_void};
|
||||
use libc::c_char;
|
||||
use libc::{c_int, c_uchar, c_uint, c_void};
|
||||
use std::ffi::CStr;
|
||||
use std::mem::{self, MaybeUninit};
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
use std::slice;
|
||||
use std::str;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::error::ErrorStack;
|
||||
use crate::ssl::AlpnError;
|
||||
use crate::ssl::{ClientHello, SelectCertError};
|
||||
use crate::ssl::{
|
||||
SniError, Ssl, SslAlert, SslContext, SslContextRef, SslRef, SslSession, SslSessionRef,
|
||||
SESSION_CTX_INDEX,
|
||||
};
|
||||
use crate::x509::{X509StoreContext, X509StoreContextRef};
|
||||
|
||||
pub extern "C" fn raw_verify<F>(preverify_ok: c_int, x509_ctx: *mut ffi::X509_STORE_CTX) -> c_int
|
||||
where
|
||||
F: Fn(bool, &mut X509StoreContextRef) -> bool + 'static + Sync + Send,
|
||||
{
|
||||
// SAFETY: boring provides valid inputs.
|
||||
let ctx = unsafe { X509StoreContextRef::from_ptr_mut(x509_ctx) };
|
||||
unsafe {
|
||||
let ctx = X509StoreContextRef::from_ptr_mut(x509_ctx);
|
||||
let ssl_idx = X509StoreContext::ssl_idx().expect("BUG: store context ssl index missing");
|
||||
let verify_idx = SslContext::cached_ex_index::<F>();
|
||||
|
||||
let ssl_idx = X509StoreContext::ssl_idx().expect("BUG: store context ssl index missing");
|
||||
let verify_idx = SslContext::cached_ex_index::<F>();
|
||||
// raw pointer shenanigans to break the borrow of ctx
|
||||
// the callback can't mess with its own ex_data slot so this is safe
|
||||
let verify = ctx
|
||||
.ex_data(ssl_idx)
|
||||
.expect("BUG: store context missing ssl")
|
||||
.ssl_context()
|
||||
.ex_data(verify_idx)
|
||||
.expect("BUG: verify callback missing") as *const F;
|
||||
|
||||
let verify = ctx
|
||||
.ex_data(ssl_idx)
|
||||
.expect("BUG: store context missing ssl")
|
||||
.ssl_context()
|
||||
.ex_data(verify_idx)
|
||||
.expect("BUG: verify callback missing");
|
||||
|
||||
// SAFETY: The callback won't outlive the context it's associated with
|
||||
// because there is no `X509StoreContextRef::ssl_mut(&mut self)` method.
|
||||
let verify = unsafe { &*std::ptr::from_ref::<F>(verify) };
|
||||
|
||||
c_int::from(verify(preverify_ok != 0, ctx))
|
||||
}
|
||||
|
||||
pub(super) unsafe extern "C" fn raw_custom_verify<F>(
|
||||
ssl: *mut ffi::SSL,
|
||||
out_alert: *mut u8,
|
||||
) -> ffi::ssl_verify_result_t
|
||||
where
|
||||
F: Fn(&mut SslRef) -> Result<(), SslVerifyError> + 'static + Sync + Send,
|
||||
{
|
||||
let callback = |ssl: &mut SslRef| {
|
||||
let custom_verify_idx = SslContext::cached_ex_index::<F>();
|
||||
|
||||
let ssl_context = ssl.ssl_context().to_owned();
|
||||
let callback = ssl_context
|
||||
.ex_data(custom_verify_idx)
|
||||
.expect("BUG: custom verify callback missing");
|
||||
|
||||
callback(ssl)
|
||||
};
|
||||
|
||||
unsafe { raw_custom_verify_callback(ssl, out_alert, callback) }
|
||||
}
|
||||
|
||||
pub(super) unsafe extern "C" fn raw_cert_verify<F>(
|
||||
x509_ctx: *mut ffi::X509_STORE_CTX,
|
||||
_arg: *mut c_void,
|
||||
) -> c_int
|
||||
where
|
||||
F: Fn(&mut X509StoreContextRef) -> bool + 'static + Sync + Send,
|
||||
{
|
||||
// SAFETY: boring provides valid inputs.
|
||||
let ctx = unsafe { X509StoreContextRef::from_ptr_mut(x509_ctx) };
|
||||
|
||||
let ssl_idx = X509StoreContext::ssl_idx().expect("BUG: store context ssl index missing");
|
||||
let verify_idx = SslContext::cached_ex_index::<F>();
|
||||
|
||||
let verify = ctx
|
||||
.ex_data(ssl_idx)
|
||||
.expect("BUG: store context missing ssl")
|
||||
.ssl_context()
|
||||
.ex_data(verify_idx)
|
||||
.expect("BUG: verify callback missing");
|
||||
|
||||
// SAFETY: The callback won't outlive the context it's associated with
|
||||
// because there is no way to get a mutable reference to the `SslContext`,
|
||||
// so the callback can't replace itself.
|
||||
let verify = unsafe { &*std::ptr::from_ref::<F>(verify) };
|
||||
|
||||
c_int::from(verify(ctx))
|
||||
}
|
||||
|
||||
pub(super) unsafe extern "C" fn ssl_raw_custom_verify<F>(
|
||||
ssl: *mut ffi::SSL,
|
||||
out_alert: *mut u8,
|
||||
) -> ffi::ssl_verify_result_t
|
||||
where
|
||||
F: Fn(&mut SslRef) -> Result<(), SslVerifyError> + 'static + Sync + Send,
|
||||
{
|
||||
let callback = |ssl: &mut SslRef| {
|
||||
let callback = ssl
|
||||
.ex_data(Ssl::cached_ex_index::<Arc<F>>())
|
||||
.expect("BUG: ssl verify callback missing")
|
||||
.clone();
|
||||
|
||||
callback(ssl)
|
||||
};
|
||||
|
||||
unsafe { raw_custom_verify_callback(ssl, out_alert, callback) }
|
||||
}
|
||||
|
||||
unsafe fn raw_custom_verify_callback(
|
||||
ssl: *mut ffi::SSL,
|
||||
out_alert: *mut u8,
|
||||
callback: impl FnOnce(&mut SslRef) -> Result<(), SslVerifyError>,
|
||||
) -> ffi::ssl_verify_result_t {
|
||||
// SAFETY: boring provides valid inputs.
|
||||
let ssl = unsafe { SslRef::from_ptr_mut(ssl) };
|
||||
let out_alert = unsafe { &mut *out_alert };
|
||||
|
||||
match callback(ssl) {
|
||||
Ok(()) => ffi::ssl_verify_result_t::ssl_verify_ok,
|
||||
Err(SslVerifyError::Invalid(alert)) => {
|
||||
*out_alert = alert.0 as u8;
|
||||
|
||||
ffi::ssl_verify_result_t::ssl_verify_invalid
|
||||
}
|
||||
Err(SslVerifyError::Retry) => ffi::ssl_verify_result_t::ssl_verify_retry,
|
||||
(*verify)(preverify_ok != 0, ctx) as c_int
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) unsafe extern "C" fn raw_client_psk<F>(
|
||||
ssl_ptr: *mut ffi::SSL,
|
||||
#[cfg(not(osslconf = "OPENSSL_NO_PSK"))]
|
||||
pub extern "C" fn raw_client_psk<F>(
|
||||
ssl: *mut ffi::SSL,
|
||||
hint: *const c_char,
|
||||
identity: *mut c_char,
|
||||
max_identity_len: c_uint,
|
||||
@ -148,36 +56,34 @@ where
|
||||
+ Sync
|
||||
+ Send,
|
||||
{
|
||||
// SAFETY: boring provides valid inputs.
|
||||
unsafe {
|
||||
let ssl = SslRef::from_ptr_mut(ssl);
|
||||
let callback_idx = SslContext::cached_ex_index::<F>();
|
||||
|
||||
let ssl = unsafe { SslRef::from_ptr_mut(ssl_ptr) };
|
||||
|
||||
let hint = if hint.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(unsafe { CStr::from_ptr(hint) }.to_bytes())
|
||||
};
|
||||
|
||||
// Give the callback mutable slices into which it can write the identity and psk.
|
||||
let identity_sl =
|
||||
unsafe { slice::from_raw_parts_mut(identity.cast::<u8>(), max_identity_len as usize) };
|
||||
let psk_sl = unsafe { slice::from_raw_parts_mut(psk, max_psk_len as usize) };
|
||||
|
||||
let ssl_context = ssl.ssl_context().to_owned();
|
||||
let callback = ssl_context
|
||||
.ex_data(SslContext::cached_ex_index::<F>())
|
||||
.expect("BUG: psk callback missing");
|
||||
|
||||
match callback(ssl, hint, identity_sl, psk_sl) {
|
||||
Ok(psk_len) => psk_len as u32,
|
||||
Err(e) => {
|
||||
e.put();
|
||||
0
|
||||
let callback = ssl
|
||||
.ssl_context()
|
||||
.ex_data(callback_idx)
|
||||
.expect("BUG: psk callback missing") as *const F;
|
||||
let hint = if !hint.is_null() {
|
||||
Some(CStr::from_ptr(hint).to_bytes())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
// Give the callback mutable slices into which it can write the identity and psk.
|
||||
let identity_sl = slice::from_raw_parts_mut(identity as *mut u8, max_identity_len as usize);
|
||||
let psk_sl = slice::from_raw_parts_mut(psk as *mut u8, max_psk_len as usize);
|
||||
match (*callback)(ssl, hint, identity_sl, psk_sl) {
|
||||
Ok(psk_len) => psk_len as u32,
|
||||
Err(e) => {
|
||||
e.put();
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) unsafe extern "C" fn raw_server_psk<F>(
|
||||
#[cfg(not(osslconf = "OPENSSL_NO_PSK"))]
|
||||
pub extern "C" fn raw_server_psk<F>(
|
||||
ssl: *mut ffi::SSL,
|
||||
identity: *const c_char,
|
||||
psk: *mut c_uchar,
|
||||
@ -189,152 +95,73 @@ where
|
||||
+ Sync
|
||||
+ Send,
|
||||
{
|
||||
// SAFETY: boring provides valid inputs.
|
||||
unsafe {
|
||||
let ssl = SslRef::from_ptr_mut(ssl);
|
||||
let callback_idx = SslContext::cached_ex_index::<F>();
|
||||
|
||||
let ssl = unsafe { SslRef::from_ptr_mut(ssl) };
|
||||
|
||||
let identity = if identity.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(unsafe { CStr::from_ptr(identity) }.to_bytes())
|
||||
};
|
||||
|
||||
// Give the callback mutable slices into which it can write the psk.
|
||||
let psk_sl = unsafe { slice::from_raw_parts_mut(psk, max_psk_len as usize) };
|
||||
|
||||
let ssl_context = ssl.ssl_context().to_owned();
|
||||
let callback = ssl_context
|
||||
.ex_data(SslContext::cached_ex_index::<F>())
|
||||
.expect("BUG: psk callback missing");
|
||||
|
||||
match callback(ssl, identity, psk_sl) {
|
||||
Ok(psk_len) => psk_len as u32,
|
||||
Err(e) => {
|
||||
e.put();
|
||||
0
|
||||
let callback = ssl
|
||||
.ssl_context()
|
||||
.ex_data(callback_idx)
|
||||
.expect("BUG: psk callback missing") as *const F;
|
||||
let identity = if identity.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(CStr::from_ptr(identity).to_bytes())
|
||||
};
|
||||
// Give the callback mutable slices into which it can write the psk.
|
||||
let psk_sl = slice::from_raw_parts_mut(psk as *mut u8, max_psk_len as usize);
|
||||
match (*callback)(ssl, identity, psk_sl) {
|
||||
Ok(psk_len) => psk_len as u32,
|
||||
Err(e) => {
|
||||
e.put();
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) unsafe extern "C" fn ssl_raw_verify<F>(
|
||||
pub extern "C" fn ssl_raw_verify<F>(
|
||||
preverify_ok: c_int,
|
||||
x509_ctx: *mut ffi::X509_STORE_CTX,
|
||||
) -> c_int
|
||||
where
|
||||
F: Fn(bool, &mut X509StoreContextRef) -> bool + 'static + Sync + Send,
|
||||
{
|
||||
// SAFETY: boring provides valid inputs.
|
||||
let ctx = unsafe { X509StoreContextRef::from_ptr_mut(x509_ctx) };
|
||||
unsafe {
|
||||
let ctx = X509StoreContextRef::from_ptr_mut(x509_ctx);
|
||||
let ssl_idx = X509StoreContext::ssl_idx().expect("BUG: store context ssl index missing");
|
||||
let callback_idx = Ssl::cached_ex_index::<Arc<F>>();
|
||||
|
||||
let ssl_idx = X509StoreContext::ssl_idx().expect("BUG: store context ssl index missing");
|
||||
let callback = ctx
|
||||
.ex_data(ssl_idx)
|
||||
.expect("BUG: store context missing ssl")
|
||||
.ex_data(callback_idx)
|
||||
.expect("BUG: ssl verify callback missing")
|
||||
.clone();
|
||||
|
||||
// NOTE(nox): I'm pretty sure this Arc<F> is unnecessary here as there is
|
||||
// no way to get a `&mut SslRef` from a `&mut X509StoreContextRef`, and I
|
||||
// don't understand how this callback is different from `raw_verify` above.
|
||||
let callback = ctx
|
||||
.ex_data(ssl_idx)
|
||||
.expect("BUG: store context missing ssl")
|
||||
.ex_data(Ssl::cached_ex_index::<Arc<F>>())
|
||||
.expect("BUG: ssl verify callback missing")
|
||||
.clone();
|
||||
|
||||
c_int::from(callback(preverify_ok != 0, ctx))
|
||||
callback(preverify_ok != 0, ctx) as c_int
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) unsafe extern "C" fn raw_sni<F>(
|
||||
ssl: *mut ffi::SSL,
|
||||
al: *mut c_int,
|
||||
arg: *mut c_void,
|
||||
) -> c_int
|
||||
pub extern "C" fn raw_sni<F>(ssl: *mut ffi::SSL, al: *mut c_int, arg: *mut c_void) -> c_int
|
||||
where
|
||||
F: Fn(&mut SslRef, &mut SslAlert) -> Result<(), SniError> + 'static + Sync + Send,
|
||||
{
|
||||
// SAFETY: boring provides valid inputs.
|
||||
let ssl = unsafe { SslRef::from_ptr_mut(ssl) };
|
||||
let al = unsafe { &mut *al };
|
||||
unsafe {
|
||||
let ssl = SslRef::from_ptr_mut(ssl);
|
||||
let callback = arg as *const F;
|
||||
let mut alert = SslAlert(*al);
|
||||
|
||||
// SAFETY: We can make `callback` outlive `ssl` because it is a callback
|
||||
// stored in the original context with which `ssl` was built. That
|
||||
// original context is always stored as the session context in
|
||||
// `Ssl::new` so it is always guaranteed to outlive the lifetime of
|
||||
// this function's scope.
|
||||
let callback = unsafe { &*(arg as *const F) };
|
||||
|
||||
let mut alert = SslAlert(*al);
|
||||
|
||||
let r = callback(ssl, &mut alert);
|
||||
|
||||
*al = alert.0;
|
||||
|
||||
match r {
|
||||
Ok(()) => ffi::SSL_TLSEXT_ERR_OK,
|
||||
Err(e) => e.0,
|
||||
let r = (*callback)(ssl, &mut alert);
|
||||
*al = alert.0;
|
||||
match r {
|
||||
Ok(()) => ffi::SSL_TLSEXT_ERR_OK,
|
||||
Err(e) => e.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn to_uninit<'a, T: 'a>(ptr: *mut T) -> &'a mut MaybeUninit<T> {
|
||||
assert!(!ptr.is_null());
|
||||
unsafe { &mut *ptr.cast::<MaybeUninit<T>>() }
|
||||
}
|
||||
|
||||
pub(super) unsafe extern "C" fn raw_ticket_key<F>(
|
||||
ssl: *mut ffi::SSL,
|
||||
key_name: *mut u8,
|
||||
iv: *mut u8,
|
||||
evp_ctx: *mut ffi::EVP_CIPHER_CTX,
|
||||
hmac_ctx: *mut ffi::HMAC_CTX,
|
||||
encrypt: c_int,
|
||||
) -> c_int
|
||||
where
|
||||
F: Fn(
|
||||
&SslRef,
|
||||
&mut [u8; 16],
|
||||
&mut [u8; ffi::EVP_MAX_IV_LENGTH as usize],
|
||||
&mut CipherCtxRef,
|
||||
&mut HmacCtxRef,
|
||||
bool,
|
||||
) -> TicketKeyCallbackResult
|
||||
+ 'static
|
||||
+ Sync
|
||||
+ Send,
|
||||
{
|
||||
// SAFETY: boring provides valid inputs.
|
||||
let ssl = unsafe { SslRef::from_ptr_mut(ssl) };
|
||||
|
||||
let ssl_context = ssl.ssl_context().to_owned();
|
||||
let callback = ssl_context
|
||||
.ex_data::<F>(SslContext::cached_ex_index::<F>())
|
||||
.expect("expected session resumption callback");
|
||||
|
||||
// SAFETY: the callback guarantees that key_name is 16 bytes
|
||||
let key_name =
|
||||
unsafe { to_uninit(key_name.cast::<[u8; ffi::SSL_TICKET_KEY_NAME_LEN as usize]>()) };
|
||||
|
||||
// SAFETY: the callback provides 16 bytes iv
|
||||
//
|
||||
// https://github.com/google/boringssl/blob/main/ssl/ssl_session.cc#L331
|
||||
let iv = unsafe { to_uninit(iv.cast::<[u8; ffi::EVP_MAX_IV_LENGTH as usize]>()) };
|
||||
|
||||
// When encrypting a new ticket, encrypt will be one.
|
||||
let encrypt = encrypt == 1;
|
||||
|
||||
// Zero-initialize the key_name and iv, since the application is expected to populate these
|
||||
// fields in the encrypt mode.
|
||||
if encrypt {
|
||||
*key_name = MaybeUninit::zeroed();
|
||||
*iv = MaybeUninit::zeroed();
|
||||
}
|
||||
let key_name = unsafe { key_name.assume_init_mut() };
|
||||
let iv = unsafe { iv.assume_init_mut() };
|
||||
|
||||
// The EVP_CIPHER_CTX and HMAC_CTX are owned by boringSSL.
|
||||
let evp_ctx = unsafe { CipherCtxRef::from_ptr_mut(evp_ctx) };
|
||||
let hmac_ctx = unsafe { HmacCtxRef::from_ptr_mut(hmac_ctx) };
|
||||
|
||||
callback(ssl, key_name, iv, evp_ctx, hmac_ctx, encrypt).into()
|
||||
}
|
||||
|
||||
pub(super) unsafe extern "C" fn raw_alpn_select<F>(
|
||||
pub extern "C" fn raw_alpn_select<F>(
|
||||
ssl: *mut ffi::SSL,
|
||||
out: *mut *const c_uchar,
|
||||
outlen: *mut c_uchar,
|
||||
@ -345,61 +172,54 @@ pub(super) unsafe extern "C" fn raw_alpn_select<F>(
|
||||
where
|
||||
F: for<'a> Fn(&mut SslRef, &'a [u8]) -> Result<&'a [u8], AlpnError> + 'static + Sync + Send,
|
||||
{
|
||||
// SAFETY: boring provides valid inputs.
|
||||
let ssl = unsafe { SslRef::from_ptr_mut(ssl) };
|
||||
let protos = unsafe { slice::from_raw_parts(inbuf, inlen as usize) };
|
||||
let out = unsafe { &mut *out };
|
||||
let outlen = unsafe { &mut *outlen };
|
||||
unsafe {
|
||||
let ssl = SslRef::from_ptr_mut(ssl);
|
||||
let callback = ssl
|
||||
.ssl_context()
|
||||
.ex_data(SslContext::cached_ex_index::<F>())
|
||||
.expect("BUG: alpn callback missing") as *const F;
|
||||
let protos = slice::from_raw_parts(inbuf as *const u8, inlen as usize);
|
||||
|
||||
let ssl_context = ssl.ssl_context().to_owned();
|
||||
let callback = ssl_context
|
||||
.ex_data(SslContext::cached_ex_index::<F>())
|
||||
.expect("BUG: alpn callback missing");
|
||||
|
||||
match callback(ssl, protos) {
|
||||
Ok(proto) => {
|
||||
*out = proto.as_ptr();
|
||||
*outlen = proto.len() as c_uchar;
|
||||
|
||||
ffi::SSL_TLSEXT_ERR_OK
|
||||
match (*callback)(ssl, protos) {
|
||||
Ok(proto) => {
|
||||
*out = proto.as_ptr() as *const c_uchar;
|
||||
*outlen = proto.len() as c_uchar;
|
||||
ffi::SSL_TLSEXT_ERR_OK
|
||||
}
|
||||
Err(e) => e.0,
|
||||
}
|
||||
Err(e) => e.0,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) unsafe extern "C" fn raw_select_cert<F>(
|
||||
pub unsafe extern "C" fn raw_select_cert<F>(
|
||||
client_hello: *const ffi::SSL_CLIENT_HELLO,
|
||||
) -> ffi::ssl_select_cert_result_t
|
||||
where
|
||||
F: Fn(ClientHello<'_>) -> Result<(), SelectCertError> + Sync + Send + 'static,
|
||||
F: Fn(&ClientHello) -> Result<(), SelectCertError> + Sync + Send + 'static,
|
||||
{
|
||||
// SAFETY: boring provides valid inputs.
|
||||
let client_hello = ClientHello(unsafe { &*client_hello });
|
||||
|
||||
let ssl_context = client_hello.ssl().ssl_context().to_owned();
|
||||
let callback = ssl_context
|
||||
let ssl = SslRef::from_ptr_mut((*client_hello).ssl);
|
||||
let client_hello = &*(client_hello as *const ClientHello);
|
||||
let callback = ssl
|
||||
.ssl_context()
|
||||
.ex_data(SslContext::cached_ex_index::<F>())
|
||||
.expect("BUG: select cert callback missing");
|
||||
.expect("BUG: select cert callback missing") as *const F;
|
||||
|
||||
match callback(client_hello) {
|
||||
match (*callback)(client_hello) {
|
||||
Ok(()) => ffi::ssl_select_cert_result_t::ssl_select_cert_success,
|
||||
Err(e) => e.0,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) unsafe extern "C" fn raw_tlsext_status<F>(ssl: *mut ffi::SSL, _: *mut c_void) -> c_int
|
||||
pub unsafe extern "C" fn raw_tlsext_status<F>(ssl: *mut ffi::SSL, _: *mut c_void) -> c_int
|
||||
where
|
||||
F: Fn(&mut SslRef) -> Result<bool, ErrorStack> + 'static + Sync + Send,
|
||||
{
|
||||
// SAFETY: boring provides valid inputs.
|
||||
let ssl = unsafe { SslRef::from_ptr_mut(ssl) };
|
||||
|
||||
let ssl_context = ssl.ssl_context().to_owned();
|
||||
let callback = ssl_context
|
||||
let ssl = SslRef::from_ptr_mut(ssl);
|
||||
let callback = ssl
|
||||
.ssl_context()
|
||||
.ex_data(SslContext::cached_ex_index::<F>())
|
||||
.expect("BUG: ocsp callback missing");
|
||||
|
||||
let ret = callback(ssl);
|
||||
.expect("BUG: ocsp callback missing") as *const F;
|
||||
let ret = (*callback)(ssl);
|
||||
|
||||
if ssl.is_server() {
|
||||
match ret {
|
||||
@ -422,364 +242,83 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) unsafe extern "C" fn raw_new_session<F>(
|
||||
pub unsafe extern "C" fn raw_new_session<F>(
|
||||
ssl: *mut ffi::SSL,
|
||||
session: *mut ffi::SSL_SESSION,
|
||||
) -> c_int
|
||||
where
|
||||
F: Fn(&mut SslRef, SslSession) + 'static + Sync + Send,
|
||||
{
|
||||
// SAFETY: boring provides valid inputs.
|
||||
let ssl = unsafe { SslRef::from_ptr_mut(ssl) };
|
||||
let session = unsafe { SslSession::from_ptr(session) };
|
||||
|
||||
let ssl = SslRef::from_ptr_mut(ssl);
|
||||
let callback = ssl
|
||||
.ex_data(*SESSION_CTX_INDEX)
|
||||
.expect("BUG: session context missing")
|
||||
.ex_data(SslContext::cached_ex_index::<F>())
|
||||
.expect("BUG: new session callback missing");
|
||||
.expect("BUG: new session callback missing") as *const F;
|
||||
let session = SslSession::from_ptr(session);
|
||||
|
||||
// SAFETY: We can make `callback` outlive `ssl` because it is a callback
|
||||
// stored in the session context set in `Ssl::new` so it is always
|
||||
// guaranteed to outlive the lifetime of this function's scope.
|
||||
let callback = unsafe { &*std::ptr::from_ref::<F>(callback) };
|
||||
|
||||
callback(ssl, session);
|
||||
(*callback)(ssl, session);
|
||||
|
||||
// the return code doesn't indicate error vs success, but whether or not we consumed the session
|
||||
1
|
||||
}
|
||||
|
||||
pub(super) unsafe extern "C" fn raw_remove_session<F>(
|
||||
pub unsafe extern "C" fn raw_remove_session<F>(
|
||||
ctx: *mut ffi::SSL_CTX,
|
||||
session: *mut ffi::SSL_SESSION,
|
||||
) where
|
||||
F: Fn(&SslContextRef, &SslSessionRef) + 'static + Sync + Send,
|
||||
{
|
||||
// SAFETY: boring provides valid inputs.
|
||||
let ctx = unsafe { SslContextRef::from_ptr(ctx) };
|
||||
let session = unsafe { SslSessionRef::from_ptr(session) };
|
||||
|
||||
let ctx = SslContextRef::from_ptr(ctx);
|
||||
let callback = ctx
|
||||
.ex_data(SslContext::cached_ex_index::<F>())
|
||||
.expect("BUG: remove session callback missing");
|
||||
let session = SslSessionRef::from_ptr(session);
|
||||
|
||||
callback(ctx, session);
|
||||
callback(ctx, session)
|
||||
}
|
||||
|
||||
type DataPtr = *const c_uchar;
|
||||
|
||||
pub(super) unsafe extern "C" fn raw_get_session<F>(
|
||||
pub unsafe extern "C" fn raw_get_session<F>(
|
||||
ssl: *mut ffi::SSL,
|
||||
data: DataPtr,
|
||||
len: c_int,
|
||||
copy: *mut c_int,
|
||||
) -> *mut ffi::SSL_SESSION
|
||||
where
|
||||
F: Fn(&mut SslRef, &[u8]) -> Result<Option<SslSession>, GetSessionPendingError>
|
||||
+ 'static
|
||||
+ Sync
|
||||
+ Send,
|
||||
F: Fn(&mut SslRef, &[u8]) -> Option<SslSession> + 'static + Sync + Send,
|
||||
{
|
||||
// SAFETY: boring provides valid inputs.
|
||||
let ssl = unsafe { SslRef::from_ptr_mut(ssl) };
|
||||
let data = unsafe { slice::from_raw_parts(data, len as usize) };
|
||||
let copy = unsafe { &mut *copy };
|
||||
|
||||
let ssl = SslRef::from_ptr_mut(ssl);
|
||||
let callback = ssl
|
||||
.ex_data(*SESSION_CTX_INDEX)
|
||||
.expect("BUG: session context missing")
|
||||
.ex_data(SslContext::cached_ex_index::<F>())
|
||||
.expect("BUG: get session callback missing");
|
||||
|
||||
// SAFETY: We can make `callback` outlive `ssl` because it is a callback
|
||||
// stored in the session context set in `Ssl::new` so it is always
|
||||
// guaranteed to outlive the lifetime of this function's scope.
|
||||
let callback = unsafe { &*std::ptr::from_ref::<F>(callback) };
|
||||
|
||||
match callback(ssl, data) {
|
||||
Ok(Some(session)) => {
|
||||
let p = session.into_ptr();
|
||||
.expect("BUG: get session callback missing") as *const F;
|
||||
let data = slice::from_raw_parts(data as *const u8, len as usize);
|
||||
|
||||
match (*callback)(ssl, data) {
|
||||
Some(session) => {
|
||||
let p = session.as_ptr();
|
||||
mem::forget(session);
|
||||
*copy = 0;
|
||||
|
||||
p
|
||||
}
|
||||
Ok(None) => ptr::null_mut(),
|
||||
Err(GetSessionPendingError) => unsafe { ffi::SSL_magic_pending_session_ptr() },
|
||||
None => ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) unsafe extern "C" fn raw_keylog<F>(ssl: *const ffi::SSL, line: *const c_char)
|
||||
pub unsafe extern "C" fn raw_keylog<F>(ssl: *const ffi::SSL, line: *const c_char)
|
||||
where
|
||||
F: Fn(&SslRef, &str) + 'static + Sync + Send,
|
||||
{
|
||||
// SAFETY: boring provides valid inputs.
|
||||
let ssl = unsafe { SslRef::from_ptr(ssl.cast_mut()) };
|
||||
let line = unsafe { CStr::from_ptr(line).to_string_lossy() };
|
||||
|
||||
let ssl = SslRef::from_ptr(ssl as *mut _);
|
||||
let callback = ssl
|
||||
.ssl_context()
|
||||
.ex_data(SslContext::cached_ex_index::<F>())
|
||||
.expect("BUG: get session callback missing");
|
||||
let line = CStr::from_ptr(line).to_bytes();
|
||||
let line = str::from_utf8_unchecked(line);
|
||||
|
||||
callback(ssl, &line);
|
||||
}
|
||||
|
||||
pub(super) unsafe extern "C" fn raw_sign<M>(
|
||||
ssl: *mut ffi::SSL,
|
||||
out: *mut u8,
|
||||
out_len: *mut usize,
|
||||
max_out: usize,
|
||||
signature_algorithm: u16,
|
||||
in_: *const u8,
|
||||
in_len: usize,
|
||||
) -> ffi::ssl_private_key_result_t
|
||||
where
|
||||
M: PrivateKeyMethod,
|
||||
{
|
||||
// SAFETY: boring provides valid inputs.
|
||||
let input = unsafe { slice::from_raw_parts(in_, in_len) };
|
||||
|
||||
let signature_algorithm = SslSignatureAlgorithm(signature_algorithm);
|
||||
|
||||
let callback = |method: &M, ssl: &mut _, output: &mut _| {
|
||||
method.sign(ssl, input, signature_algorithm, output)
|
||||
};
|
||||
|
||||
// SAFETY: boring provides valid inputs.
|
||||
unsafe { raw_private_key_callback(ssl, out, out_len, max_out, callback) }
|
||||
}
|
||||
|
||||
pub(super) unsafe extern "C" fn raw_decrypt<M>(
|
||||
ssl: *mut ffi::SSL,
|
||||
out: *mut u8,
|
||||
out_len: *mut usize,
|
||||
max_out: usize,
|
||||
in_: *const u8,
|
||||
in_len: usize,
|
||||
) -> ffi::ssl_private_key_result_t
|
||||
where
|
||||
M: PrivateKeyMethod,
|
||||
{
|
||||
// SAFETY: boring provides valid inputs.
|
||||
let input = unsafe { slice::from_raw_parts(in_, in_len) };
|
||||
|
||||
let callback = |method: &M, ssl: &mut _, output: &mut _| method.decrypt(ssl, input, output);
|
||||
|
||||
// SAFETY: boring provides valid inputs.
|
||||
unsafe { raw_private_key_callback(ssl, out, out_len, max_out, callback) }
|
||||
}
|
||||
|
||||
pub(super) unsafe extern "C" fn raw_complete<M>(
|
||||
ssl: *mut ffi::SSL,
|
||||
out: *mut u8,
|
||||
out_len: *mut usize,
|
||||
max_out: usize,
|
||||
) -> ffi::ssl_private_key_result_t
|
||||
where
|
||||
M: PrivateKeyMethod,
|
||||
{
|
||||
// SAFETY: boring provides valid inputs.
|
||||
unsafe { raw_private_key_callback::<M>(ssl, out, out_len, max_out, M::complete) }
|
||||
}
|
||||
|
||||
unsafe fn raw_private_key_callback<M>(
|
||||
ssl: *mut ffi::SSL,
|
||||
out: *mut u8,
|
||||
out_len: *mut usize,
|
||||
max_out: usize,
|
||||
callback: impl FnOnce(&M, &mut SslRef, &mut [u8]) -> Result<usize, PrivateKeyMethodError>,
|
||||
) -> ffi::ssl_private_key_result_t
|
||||
where
|
||||
M: PrivateKeyMethod,
|
||||
{
|
||||
// SAFETY: boring provides valid inputs.
|
||||
let ssl = unsafe { SslRef::from_ptr_mut(ssl) };
|
||||
let output = unsafe { slice::from_raw_parts_mut(out, max_out) };
|
||||
let out_len = unsafe { &mut *out_len };
|
||||
|
||||
let ssl_context = ssl.ssl_context().to_owned();
|
||||
let method = ssl_context
|
||||
.ex_data(SslContext::cached_ex_index::<M>())
|
||||
.expect("BUG: private key method missing");
|
||||
|
||||
match callback(method, ssl, output) {
|
||||
Ok(written) => {
|
||||
assert!(written <= max_out);
|
||||
|
||||
*out_len = written;
|
||||
|
||||
ffi::ssl_private_key_result_t::ssl_private_key_success
|
||||
}
|
||||
Err(err) => err.0,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) unsafe extern "C" fn raw_info_callback<F>(
|
||||
ssl: *const ffi::SSL,
|
||||
mode: c_int,
|
||||
value: c_int,
|
||||
) where
|
||||
F: Fn(&SslRef, SslInfoCallbackMode, SslInfoCallbackValue) + Send + Sync + 'static,
|
||||
{
|
||||
// Due to FFI signature requirements we have to pass a *const SSL into this function, but
|
||||
// foreign-types requires a *mut SSL to get the Rust SslRef
|
||||
let mut_ref = ssl.cast_mut();
|
||||
|
||||
// SAFETY: boring provides valid inputs.
|
||||
let ssl = unsafe { SslRef::from_ptr(mut_ref) };
|
||||
let ssl_context = ssl.ssl_context();
|
||||
|
||||
let callback = ssl_context
|
||||
.ex_data(SslContext::cached_ex_index::<F>())
|
||||
.expect("BUG: info callback missing");
|
||||
|
||||
let value = match mode {
|
||||
ffi::SSL_CB_READ_ALERT | ffi::SSL_CB_WRITE_ALERT => {
|
||||
SslInfoCallbackValue::Alert(SslInfoCallbackAlert(value))
|
||||
}
|
||||
_ => SslInfoCallbackValue::Unit,
|
||||
};
|
||||
|
||||
callback(ssl, SslInfoCallbackMode(mode), value);
|
||||
}
|
||||
|
||||
pub(super) unsafe extern "C" fn raw_ssl_cert_compress<C>(
|
||||
ssl: *mut ffi::SSL,
|
||||
out: *mut ffi::CBB,
|
||||
input: *const u8,
|
||||
input_len: usize,
|
||||
) -> ::std::os::raw::c_int
|
||||
where
|
||||
C: CertificateCompressor,
|
||||
{
|
||||
const {
|
||||
assert!(C::CAN_COMPRESS);
|
||||
}
|
||||
|
||||
// SAFETY: boring provides valid inputs.
|
||||
let ssl = unsafe { SslRef::from_ptr_mut(ssl) };
|
||||
|
||||
let ssl_context = ssl.ssl_context();
|
||||
let compressor = ssl_context
|
||||
.ex_data(SslContext::cached_ex_index::<C>())
|
||||
.expect("BUG: certificate compression missed");
|
||||
|
||||
let input_slice = unsafe { std::slice::from_raw_parts(input, input_len) };
|
||||
let mut writer = CryptoByteBuilder::from_ptr(out);
|
||||
if compressor.compress(input_slice, &mut writer).is_err() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
1
|
||||
}
|
||||
|
||||
pub(super) unsafe extern "C" fn raw_ssl_cert_decompress<C>(
|
||||
ssl: *mut ffi::SSL,
|
||||
out: *mut *mut ffi::CRYPTO_BUFFER,
|
||||
uncompressed_len: usize,
|
||||
input: *const u8,
|
||||
input_len: usize,
|
||||
) -> ::std::os::raw::c_int
|
||||
where
|
||||
C: CertificateCompressor,
|
||||
{
|
||||
const {
|
||||
assert!(C::CAN_DECOMPRESS);
|
||||
}
|
||||
|
||||
// SAFETY: boring provides valid inputs.
|
||||
let ssl = unsafe { SslRef::from_ptr_mut(ssl) };
|
||||
|
||||
let ssl_context = ssl.ssl_context();
|
||||
let compressor = ssl_context
|
||||
.ex_data(SslContext::cached_ex_index::<C>())
|
||||
.expect("BUG: certificate compression missed");
|
||||
|
||||
let Ok(mut decompression_buffer) = CryptoBufferBuilder::with_capacity(uncompressed_len) else {
|
||||
return 0;
|
||||
};
|
||||
|
||||
let input_slice = unsafe { std::slice::from_raw_parts(input, input_len) };
|
||||
|
||||
if compressor
|
||||
.decompress(input_slice, decompression_buffer.as_writer())
|
||||
.is_err()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
let Ok(crypto_buffer) = decompression_buffer.build() else {
|
||||
return 0;
|
||||
};
|
||||
|
||||
unsafe { *out = crypto_buffer };
|
||||
1
|
||||
}
|
||||
|
||||
struct CryptoByteBuilder<'a>(*mut ffi::CBB, std::marker::PhantomData<&'a [u8]>);
|
||||
|
||||
impl CryptoByteBuilder<'_> {
|
||||
fn from_ptr(ptr: *mut ffi::CBB) -> Self {
|
||||
Self(ptr, Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::io::Write for CryptoByteBuilder<'_> {
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
let success = unsafe { ffi::CBB_add_bytes(self.0, buf.as_ptr(), buf.len()) == 1 };
|
||||
if !success {
|
||||
return Err(std::io::Error::other("CBB_add_bytes failed"));
|
||||
}
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> std::io::Result<()> {
|
||||
let success = unsafe { ffi::CBB_flush(self.0) == 1 };
|
||||
if !success {
|
||||
return Err(std::io::Error::other("CBB_flush failed"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct CryptoBufferBuilder<'a> {
|
||||
buffer: *mut ffi::CRYPTO_BUFFER,
|
||||
cursor: std::io::Cursor<&'a mut [u8]>,
|
||||
}
|
||||
|
||||
impl<'a> CryptoBufferBuilder<'a> {
|
||||
fn with_capacity(capacity: usize) -> Result<CryptoBufferBuilder<'a>, ErrorStack> {
|
||||
let mut data: *mut u8 = std::ptr::null_mut();
|
||||
let buffer = unsafe { crate::cvt_p(ffi::CRYPTO_BUFFER_alloc(&mut data, capacity))? };
|
||||
Ok(CryptoBufferBuilder {
|
||||
buffer,
|
||||
cursor: std::io::Cursor::new(unsafe { std::slice::from_raw_parts_mut(data, capacity) }),
|
||||
})
|
||||
}
|
||||
|
||||
fn as_writer(&mut self) -> &mut (impl std::io::Write + 'a) {
|
||||
&mut self.cursor
|
||||
}
|
||||
|
||||
fn build(mut self) -> Result<*mut ffi::CRYPTO_BUFFER, ErrorStack> {
|
||||
let buffer_capacity = unsafe { ffi::CRYPTO_BUFFER_len(self.buffer) };
|
||||
if self.cursor.position() != buffer_capacity as u64 {
|
||||
// Make sure all bytes in buffer initialized as required by Boring SSL.
|
||||
return Err(ErrorStack::internal_error_str("invalid len"));
|
||||
}
|
||||
// Drop is no-op if the buffer is null
|
||||
Ok(mem::replace(&mut self.buffer, ptr::null_mut()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for CryptoBufferBuilder<'_> {
|
||||
fn drop(&mut self) {
|
||||
if !self.buffer.is_null() {
|
||||
unsafe {
|
||||
boring_sys::CRYPTO_BUFFER_free(self.buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
callback(ssl, line);
|
||||
}
|
||||
|
||||
@ -8,9 +8,6 @@ use crate::ssl::{
|
||||
SslOptions, SslRef, SslStream, SslVerifyMode,
|
||||
};
|
||||
use crate::version;
|
||||
use std::net::IpAddr;
|
||||
|
||||
use super::MidHandshakeSslStream;
|
||||
|
||||
const FFDHE_2048: &str = "
|
||||
-----BEGIN DH PARAMETERS-----
|
||||
@ -80,30 +77,11 @@ impl SslConnector {
|
||||
/// Initiates a client-side TLS session on a stream.
|
||||
///
|
||||
/// The domain is used for SNI and hostname verification.
|
||||
pub fn setup_connect<S>(
|
||||
&self,
|
||||
domain: &str,
|
||||
stream: S,
|
||||
) -> Result<MidHandshakeSslStream<S>, ErrorStack>
|
||||
where
|
||||
S: Read + Write,
|
||||
{
|
||||
self.configure()?.setup_connect(domain, stream)
|
||||
}
|
||||
|
||||
/// Attempts a client-side TLS session on a stream.
|
||||
///
|
||||
/// The domain is used for SNI (if it is not an IP address) and hostname verification if enabled.
|
||||
///
|
||||
/// This is a convenience method which combines [`Self::setup_connect`] and
|
||||
/// [`MidHandshakeSslStream::handshake`].
|
||||
pub fn connect<S>(&self, domain: &str, stream: S) -> Result<SslStream<S>, HandshakeError<S>>
|
||||
where
|
||||
S: Read + Write,
|
||||
{
|
||||
self.setup_connect(domain, stream)
|
||||
.map_err(HandshakeError::SetupFailure)?
|
||||
.handshake()
|
||||
self.configure()?.connect(domain, stream)
|
||||
}
|
||||
|
||||
/// Returns a structure allowing for configuration of a single TLS session before connection.
|
||||
@ -116,15 +94,13 @@ impl SslConnector {
|
||||
}
|
||||
|
||||
/// Consumes the `SslConnector`, returning the inner raw `SslContext`.
|
||||
#[must_use]
|
||||
pub fn into_context(self) -> SslContext {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Returns a shared reference to the inner raw `SslContext`.
|
||||
#[must_use]
|
||||
pub fn context(&self) -> &SslContextRef {
|
||||
&self.0
|
||||
&*self.0
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,7 +109,6 @@ pub struct SslConnectorBuilder(SslContextBuilder);
|
||||
|
||||
impl SslConnectorBuilder {
|
||||
/// Consumes the builder, returning an `SslConnector`.
|
||||
#[must_use]
|
||||
pub fn build(self) -> SslConnector {
|
||||
SslConnector(self.0.build())
|
||||
}
|
||||
@ -162,7 +137,6 @@ pub struct ConnectConfiguration {
|
||||
|
||||
impl ConnectConfiguration {
|
||||
/// A builder-style version of `set_use_server_name_indication`.
|
||||
#[must_use]
|
||||
pub fn use_server_name_indication(mut self, use_sni: bool) -> ConnectConfiguration {
|
||||
self.set_use_server_name_indication(use_sni);
|
||||
self
|
||||
@ -176,7 +150,6 @@ impl ConnectConfiguration {
|
||||
}
|
||||
|
||||
/// A builder-style version of `set_verify_hostname`.
|
||||
#[must_use]
|
||||
pub fn verify_hostname(mut self, verify_hostname: bool) -> ConnectConfiguration {
|
||||
self.set_verify_hostname(verify_hostname);
|
||||
self
|
||||
@ -195,11 +168,14 @@ impl ConnectConfiguration {
|
||||
self.verify_hostname = verify_hostname;
|
||||
}
|
||||
|
||||
/// Returns an [`Ssl`] configured to connect to the provided domain.
|
||||
/// Initiates a client-side TLS session on a stream.
|
||||
///
|
||||
/// The domain is used for SNI (if it is not an IP address) and hostname verification if enabled.
|
||||
pub fn into_ssl(mut self, domain: &str) -> Result<Ssl, ErrorStack> {
|
||||
if self.sni && domain.parse::<IpAddr>().is_err() {
|
||||
/// The domain is used for SNI and hostname verification if enabled.
|
||||
pub fn connect<S>(mut self, domain: &str, stream: S) -> Result<SslStream<S>, HandshakeError<S>>
|
||||
where
|
||||
S: Read + Write,
|
||||
{
|
||||
if self.sni {
|
||||
self.ssl.set_hostname(domain)?;
|
||||
}
|
||||
|
||||
@ -207,39 +183,7 @@ impl ConnectConfiguration {
|
||||
setup_verify_hostname(&mut self.ssl, domain)?;
|
||||
}
|
||||
|
||||
Ok(self.ssl)
|
||||
}
|
||||
|
||||
/// Initiates a client-side TLS session on a stream.
|
||||
///
|
||||
/// The domain is used for SNI (if it is not an IP address) and hostname verification if enabled.
|
||||
///
|
||||
/// This is a convenience method which combines [`Self::into_ssl`] and
|
||||
/// [`Ssl::setup_connect`].
|
||||
pub fn setup_connect<S>(
|
||||
self,
|
||||
domain: &str,
|
||||
stream: S,
|
||||
) -> Result<MidHandshakeSslStream<S>, ErrorStack>
|
||||
where
|
||||
S: Read + Write,
|
||||
{
|
||||
Ok(self.into_ssl(domain)?.setup_connect(stream))
|
||||
}
|
||||
|
||||
/// Attempts a client-side TLS session on a stream.
|
||||
///
|
||||
/// The domain is used for SNI (if it is not an IP address) and hostname verification if enabled.
|
||||
///
|
||||
/// This is a convenience method which combines [`Self::setup_connect`] and
|
||||
/// [`MidHandshakeSslStream::handshake`].
|
||||
pub fn connect<S>(self, domain: &str, stream: S) -> Result<SslStream<S>, HandshakeError<S>>
|
||||
where
|
||||
S: Read + Write,
|
||||
{
|
||||
self.setup_connect(domain, stream)
|
||||
.map_err(HandshakeError::SetupFailure)?
|
||||
.handshake()
|
||||
self.ssl.connect(stream)
|
||||
}
|
||||
}
|
||||
|
||||
@ -333,41 +277,23 @@ impl SslAcceptor {
|
||||
Ok(SslAcceptorBuilder(ctx))
|
||||
}
|
||||
|
||||
/// Initiates a server-side TLS handshake on a stream.
|
||||
///
|
||||
/// See [`Ssl::setup_accept`] for more details.
|
||||
pub fn setup_accept<S>(&self, stream: S) -> Result<MidHandshakeSslStream<S>, ErrorStack>
|
||||
where
|
||||
S: Read + Write,
|
||||
{
|
||||
let ssl = Ssl::new(&self.0)?;
|
||||
|
||||
Ok(ssl.setup_accept(stream))
|
||||
}
|
||||
|
||||
/// Attempts a server-side TLS handshake on a stream.
|
||||
///
|
||||
/// This is a convenience method which combines [`Self::setup_accept`] and
|
||||
/// [`MidHandshakeSslStream::handshake`].
|
||||
/// Initiates a server-side TLS session on a stream.
|
||||
pub fn accept<S>(&self, stream: S) -> Result<SslStream<S>, HandshakeError<S>>
|
||||
where
|
||||
S: Read + Write,
|
||||
{
|
||||
self.setup_accept(stream)
|
||||
.map_err(HandshakeError::SetupFailure)?
|
||||
.handshake()
|
||||
let ssl = Ssl::new(&self.0)?;
|
||||
ssl.accept(stream)
|
||||
}
|
||||
|
||||
/// Consumes the `SslAcceptor`, returning the inner raw `SslContext`.
|
||||
#[must_use]
|
||||
pub fn into_context(self) -> SslContext {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Returns a shared reference to the inner raw `SslContext`.
|
||||
#[must_use]
|
||||
pub fn context(&self) -> &SslContextRef {
|
||||
&self.0
|
||||
&*self.0
|
||||
}
|
||||
}
|
||||
|
||||
@ -376,7 +302,6 @@ pub struct SslAcceptorBuilder(SslContextBuilder);
|
||||
|
||||
impl SslAcceptorBuilder {
|
||||
/// Consumes the builder, returning a `SslAcceptor`.
|
||||
#[must_use]
|
||||
pub fn build(self) -> SslAcceptor {
|
||||
SslAcceptor(self.0.build())
|
||||
}
|
||||
|
||||
@ -1,211 +0,0 @@
|
||||
#[cfg(feature = "rpk")]
|
||||
use crate::cvt_p;
|
||||
use crate::error::ErrorStack;
|
||||
use crate::ex_data::Index;
|
||||
use crate::pkey::{PKeyRef, Private};
|
||||
use crate::ssl::callbacks;
|
||||
use crate::ssl::PrivateKeyMethod;
|
||||
use crate::{cvt_0i, cvt_n};
|
||||
use crate::{ffi, free_data_box};
|
||||
use foreign_types::{ForeignType, ForeignTypeRef};
|
||||
use openssl_macros::corresponds;
|
||||
use std::any::TypeId;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::{c_int, c_void};
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
use std::sync::{LazyLock, Mutex};
|
||||
|
||||
static SSL_CREDENTIAL_INDEXES: LazyLock<Mutex<HashMap<TypeId, c_int>>> =
|
||||
LazyLock::new(|| Mutex::new(HashMap::new()));
|
||||
|
||||
foreign_type_and_impl_send_sync! {
|
||||
type CType = ffi::SSL_CREDENTIAL;
|
||||
fn drop = ffi::SSL_CREDENTIAL_free;
|
||||
|
||||
/// A credential.
|
||||
pub struct SslCredential;
|
||||
}
|
||||
|
||||
impl SslCredential {
|
||||
/// Create a credential suitable for a handshake using a raw public key.
|
||||
#[corresponds(SSL_CREDENTIAL_new_raw_public_key)]
|
||||
#[cfg(feature = "rpk")]
|
||||
pub fn new_raw_public_key() -> Result<SslCredentialBuilder, ErrorStack> {
|
||||
unsafe {
|
||||
Ok(SslCredentialBuilder(Self::from_ptr(cvt_p(
|
||||
ffi::SSL_CREDENTIAL_new_raw_public_key(),
|
||||
)?)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a new extra data index.
|
||||
///
|
||||
/// Each invocation of this function is guaranteed to return a distinct index. These can be used
|
||||
/// to store data in the context that can be retrieved later by callbacks, for example.
|
||||
#[corresponds(SSL_C_get_ex_new_index)]
|
||||
pub fn new_ex_index<T>() -> Result<Index<Self, T>, ErrorStack>
|
||||
where
|
||||
T: 'static + Sync + Send,
|
||||
{
|
||||
unsafe {
|
||||
ffi::init();
|
||||
let idx = cvt_n(get_new_ssl_credential_idx(Some(free_data_box::<T>)))?;
|
||||
Ok(Index::from_raw(idx))
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME should return a result?
|
||||
pub(crate) fn cached_ex_index<T>() -> Index<Self, T>
|
||||
where
|
||||
T: 'static + Sync + Send,
|
||||
{
|
||||
unsafe {
|
||||
let idx = *SSL_CREDENTIAL_INDEXES
|
||||
.lock()
|
||||
.unwrap_or_else(|e| e.into_inner())
|
||||
.entry(TypeId::of::<T>())
|
||||
.or_insert_with(|| Self::new_ex_index::<T>().unwrap().as_raw());
|
||||
Index::from_raw(idx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SslCredentialRef {
|
||||
/// Returns a reference to the extra data at the specified index.
|
||||
#[corresponds(SSL_CREDENTIAL_get_ex_data)]
|
||||
#[must_use]
|
||||
pub fn ex_data<T>(&self, index: Index<SslCredential, T>) -> Option<&T> {
|
||||
unsafe {
|
||||
let data = ffi::SSL_CREDENTIAL_get_ex_data(self.as_ptr(), index.as_raw());
|
||||
if data.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(&*(data as *const T))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unsafe because SSL contexts are not guaranteed to be unique, we call
|
||||
// this only from SslCredentialBuilder.
|
||||
#[corresponds(SSL_CREDENTIAL_get_ex_data)]
|
||||
pub(crate) unsafe fn ex_data_mut<T>(
|
||||
&mut self,
|
||||
index: Index<SslCredential, T>,
|
||||
) -> Option<&mut T> {
|
||||
let data = ffi::SSL_CREDENTIAL_get_ex_data(self.as_ptr(), index.as_raw());
|
||||
if data.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(&mut *(data as *mut T))
|
||||
}
|
||||
}
|
||||
|
||||
// Unsafe because SSL contexts are not guaranteed to be unique, we call
|
||||
// this only from SslCredentialBuilder.
|
||||
#[corresponds(SSL_CREDENTIAL_set_ex_data)]
|
||||
pub(crate) unsafe fn replace_ex_data<T>(
|
||||
&mut self,
|
||||
index: Index<SslCredential, T>,
|
||||
data: T,
|
||||
) -> Option<T> {
|
||||
if let Some(old) = self.ex_data_mut(index) {
|
||||
return Some(mem::replace(old, data));
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let data = Box::into_raw(Box::new(data)) as *mut c_void;
|
||||
ffi::SSL_CREDENTIAL_set_ex_data(self.as_ptr(), index.as_raw(), data);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// A builder for [`SslCredential`]
|
||||
pub struct SslCredentialBuilder(SslCredential);
|
||||
|
||||
impl SslCredentialBuilder {
|
||||
/// Sets or overwrites the extra data at the specified index.
|
||||
///
|
||||
/// This can be used to provide data to callbacks registered with the context. Use the
|
||||
/// `SslCredential::new_ex_index` method to create an `Index`.
|
||||
///
|
||||
/// Any previous value will be returned and replaced by the new one.
|
||||
#[corresponds(SSL_CREDENTIAL_set_ex_data)]
|
||||
pub fn replace_ex_data<T>(&mut self, index: Index<SslCredential, T>, data: T) -> Option<T> {
|
||||
unsafe { self.0.replace_ex_data(index, data) }
|
||||
}
|
||||
|
||||
// Sets the private key of the credential.
|
||||
#[corresponds(SSL_CREDENTIAL_set1_private_key)]
|
||||
pub fn set_private_key(&mut self, private_key: &PKeyRef<Private>) -> Result<(), ErrorStack> {
|
||||
unsafe {
|
||||
cvt_0i(ffi::SSL_CREDENTIAL_set1_private_key(
|
||||
self.0.as_ptr(),
|
||||
private_key.as_ptr(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Configures a custom private key method on the credential.
|
||||
///
|
||||
/// See [`PrivateKeyMethod`] for more details.
|
||||
#[corresponds(SSL_CREDENTIAL_set_private_key_method)]
|
||||
pub fn set_private_key_method<M>(&mut self, method: M) -> Result<(), ErrorStack>
|
||||
where
|
||||
M: PrivateKeyMethod,
|
||||
{
|
||||
unsafe {
|
||||
self.replace_ex_data(SslCredential::cached_ex_index::<M>(), method);
|
||||
|
||||
cvt_0i(ffi::SSL_CREDENTIAL_set_private_key_method(
|
||||
self.0.as_ptr(),
|
||||
&ffi::SSL_PRIVATE_KEY_METHOD {
|
||||
sign: Some(callbacks::raw_sign::<M>),
|
||||
decrypt: Some(callbacks::raw_decrypt::<M>),
|
||||
complete: Some(callbacks::raw_complete::<M>),
|
||||
},
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
// Sets the SPKI of the raw public key credential.
|
||||
//
|
||||
// If `spki` is `None`, the SPKI is extracted from the credential's private key.
|
||||
#[corresponds(SSL_CREDENTIAL_set1_spki)]
|
||||
#[cfg(feature = "rpk")]
|
||||
pub fn set_spki_bytes(&mut self, spki: Option<&[u8]>) -> Result<(), ErrorStack> {
|
||||
unsafe {
|
||||
let spki = spki
|
||||
.map(|spki| {
|
||||
cvt_p(ffi::CRYPTO_BUFFER_new(
|
||||
spki.as_ptr(),
|
||||
spki.len(),
|
||||
ptr::null_mut(),
|
||||
))
|
||||
})
|
||||
.transpose()?
|
||||
.unwrap_or(ptr::null_mut());
|
||||
|
||||
let ret = cvt_0i(ffi::SSL_CREDENTIAL_set1_spki(self.0.as_ptr(), spki)).map(|_| ());
|
||||
|
||||
if !spki.is_null() {
|
||||
ffi::CRYPTO_BUFFER_free(spki);
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn build(self) -> SslCredential {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn get_new_ssl_credential_idx(f: ffi::CRYPTO_EX_free) -> c_int {
|
||||
ffi::SSL_CREDENTIAL_get_ex_new_index(0, ptr::null_mut(), ptr::null_mut(), None, f)
|
||||
}
|
||||
@ -1,63 +0,0 @@
|
||||
use crate::ffi;
|
||||
use foreign_types::ForeignType;
|
||||
use libc::c_int;
|
||||
|
||||
use crate::error::ErrorStack;
|
||||
use crate::hpke::HpkeKey;
|
||||
use crate::{cvt_0i, cvt_p};
|
||||
|
||||
pub struct SslEchKeysBuilder {
|
||||
keys: SslEchKeys,
|
||||
}
|
||||
|
||||
impl SslEchKeysBuilder {
|
||||
pub fn new() -> Result<SslEchKeysBuilder, ErrorStack> {
|
||||
unsafe {
|
||||
ffi::init();
|
||||
let keys = cvt_p(ffi::SSL_ECH_KEYS_new())?;
|
||||
|
||||
Ok(SslEchKeysBuilder::from_ptr(keys))
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn from_ptr(keys: *mut ffi::SSL_ECH_KEYS) -> Self {
|
||||
Self {
|
||||
keys: SslEchKeys::from_ptr(keys),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_key(
|
||||
&mut self,
|
||||
is_retry_config: bool,
|
||||
ech_config: &[u8],
|
||||
key: HpkeKey,
|
||||
) -> Result<(), ErrorStack> {
|
||||
unsafe {
|
||||
cvt_0i(ffi::SSL_ECH_KEYS_add(
|
||||
self.keys.as_ptr(),
|
||||
c_int::from(is_retry_config),
|
||||
ech_config.as_ptr(),
|
||||
ech_config.len(),
|
||||
key.as_ptr(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build(self) -> SslEchKeys {
|
||||
self.keys
|
||||
}
|
||||
}
|
||||
|
||||
foreign_type_and_impl_send_sync! {
|
||||
type CType = ffi::SSL_ECH_KEYS;
|
||||
fn drop = ffi::SSL_ECH_KEYS_free;
|
||||
|
||||
pub struct SslEchKeys;
|
||||
}
|
||||
|
||||
impl SslEchKeys {
|
||||
pub fn builder() -> Result<SslEchKeysBuilder, ErrorStack> {
|
||||
SslEchKeysBuilder::new()
|
||||
}
|
||||
}
|
||||
@ -1,26 +1,19 @@
|
||||
use crate::ffi;
|
||||
use crate::x509::X509VerifyError;
|
||||
use libc::c_int;
|
||||
use openssl_macros::corresponds;
|
||||
use std::error;
|
||||
use std::error::Error as StdError;
|
||||
use std::ffi::CStr;
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
|
||||
use crate::error::ErrorStack;
|
||||
use crate::ssl::MidHandshakeSslStream;
|
||||
use crate::x509::X509VerifyResult;
|
||||
|
||||
/// `SSL_ERROR_*` error code returned from SSL functions.
|
||||
///
|
||||
/// This is different than [packed error codes](crate::error::Error).
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
/// An error code returned from SSL functions.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct ErrorCode(c_int);
|
||||
|
||||
impl ErrorCode {
|
||||
/// No error.
|
||||
pub const NONE: ErrorCode = ErrorCode(ffi::SSL_ERROR_NONE);
|
||||
|
||||
/// The SSL session has been closed.
|
||||
pub const ZERO_RETURN: ErrorCode = ErrorCode(ffi::SSL_ERROR_ZERO_RETURN);
|
||||
|
||||
@ -34,73 +27,20 @@ impl ErrorCode {
|
||||
/// Wait for write readiness and retry the operation.
|
||||
pub const WANT_WRITE: ErrorCode = ErrorCode(ffi::SSL_ERROR_WANT_WRITE);
|
||||
|
||||
pub const WANT_X509_LOOKUP: ErrorCode = ErrorCode(ffi::SSL_ERROR_WANT_X509_LOOKUP);
|
||||
|
||||
pub const PENDING_SESSION: ErrorCode = ErrorCode(ffi::SSL_ERROR_PENDING_SESSION);
|
||||
|
||||
pub const PENDING_CERTIFICATE: ErrorCode = ErrorCode(ffi::SSL_ERROR_PENDING_CERTIFICATE);
|
||||
|
||||
pub const WANT_CERTIFICATE_VERIFY: ErrorCode =
|
||||
ErrorCode(ffi::SSL_ERROR_WANT_CERTIFICATE_VERIFY);
|
||||
|
||||
pub const WANT_PRIVATE_KEY_OPERATION: ErrorCode =
|
||||
ErrorCode(ffi::SSL_ERROR_WANT_PRIVATE_KEY_OPERATION);
|
||||
|
||||
pub const PENDING_TICKET: ErrorCode = ErrorCode(ffi::SSL_ERROR_PENDING_TICKET);
|
||||
|
||||
/// A non-recoverable IO error occurred.
|
||||
pub const SYSCALL: ErrorCode = ErrorCode(ffi::SSL_ERROR_SYSCALL);
|
||||
|
||||
/// An error occurred in the SSL library.
|
||||
pub const SSL: ErrorCode = ErrorCode(ffi::SSL_ERROR_SSL);
|
||||
|
||||
/// Wrap an `SSL_ERROR_*` error code.
|
||||
///
|
||||
/// This is different than [packed error codes](crate::error::Error).
|
||||
#[must_use]
|
||||
#[inline]
|
||||
#[cfg_attr(debug_assertions, track_caller)]
|
||||
pub fn from_raw(raw: c_int) -> ErrorCode {
|
||||
let code = ErrorCode(raw);
|
||||
debug_assert!(
|
||||
raw < 64 || code.description().is_some(),
|
||||
"{raw} is not an SSL_ERROR_* code"
|
||||
);
|
||||
code
|
||||
ErrorCode(raw)
|
||||
}
|
||||
|
||||
/// An `SSL_ERROR_*` error code.
|
||||
///
|
||||
/// This is different than [packed error codes](crate::error::Error).
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
#[must_use]
|
||||
pub fn as_raw(&self) -> c_int {
|
||||
self.0
|
||||
}
|
||||
|
||||
#[corresponds(SSL_error_description)]
|
||||
#[must_use]
|
||||
pub fn description(self) -> Option<&'static str> {
|
||||
unsafe {
|
||||
let msg = ffi::SSL_error_description(self.0);
|
||||
if msg.is_null() {
|
||||
return None;
|
||||
}
|
||||
CStr::from_ptr(msg).to_str().ok()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ErrorCode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{} ({})", self.description().unwrap_or("error"), self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ErrorCode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -109,7 +49,7 @@ pub(crate) enum InnerError {
|
||||
Ssl(ErrorStack),
|
||||
}
|
||||
|
||||
/// A general SSL error, based on [`SSL_ERROR_*` error codes](ErrorCode).
|
||||
/// An SSL error.
|
||||
#[derive(Debug)]
|
||||
pub struct Error {
|
||||
pub(crate) code: ErrorCode,
|
||||
@ -117,13 +57,10 @@ pub struct Error {
|
||||
}
|
||||
|
||||
impl Error {
|
||||
/// An `SSL_ERROR_*` error code.
|
||||
#[must_use]
|
||||
pub fn code(&self) -> ErrorCode {
|
||||
self.code
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn io_error(&self) -> Option<&io::Error> {
|
||||
match self.cause {
|
||||
Some(InnerError::Io(ref e)) => Some(e),
|
||||
@ -138,29 +75,12 @@ impl Error {
|
||||
}
|
||||
}
|
||||
|
||||
/// Stack of [library-specific errors](crate::error::Error), if available.
|
||||
#[must_use]
|
||||
pub fn ssl_error(&self) -> Option<&ErrorStack> {
|
||||
match self.cause {
|
||||
Some(InnerError::Ssl(ref e)) => Some(e),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn would_block(&self) -> bool {
|
||||
matches!(
|
||||
self.code,
|
||||
ErrorCode::WANT_READ
|
||||
| ErrorCode::WANT_WRITE
|
||||
| ErrorCode::WANT_X509_LOOKUP
|
||||
| ErrorCode::PENDING_SESSION
|
||||
| ErrorCode::PENDING_CERTIFICATE
|
||||
| ErrorCode::WANT_PRIVATE_KEY_OPERATION
|
||||
| ErrorCode::WANT_CERTIFICATE_VERIFY
|
||||
| ErrorCode::PENDING_TICKET
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ErrorStack> for Error {
|
||||
@ -174,27 +94,26 @@ impl From<ErrorStack> for Error {
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
let msg = match self.code {
|
||||
ErrorCode::ZERO_RETURN => "the SSL session has been shut down",
|
||||
match self.code {
|
||||
ErrorCode::ZERO_RETURN => fmt.write_str("the SSL session has been shut down"),
|
||||
ErrorCode::WANT_READ => match self.io_error() {
|
||||
Some(_) => "a nonblocking read call would have blocked",
|
||||
None => "the operation should be retried",
|
||||
Some(_) => fmt.write_str("a nonblocking read call would have blocked"),
|
||||
None => fmt.write_str("the operation should be retried"),
|
||||
},
|
||||
ErrorCode::WANT_WRITE => match self.io_error() {
|
||||
Some(_) => "a nonblocking write call would have blocked",
|
||||
None => "the operation should be retried",
|
||||
Some(_) => fmt.write_str("a nonblocking write call would have blocked"),
|
||||
None => fmt.write_str("the operation should be retried"),
|
||||
},
|
||||
ErrorCode::SYSCALL => match self.io_error() {
|
||||
Some(err) => return err.fmt(fmt),
|
||||
None => "unexpected EOF",
|
||||
Some(err) => write!(fmt, "{}", err),
|
||||
None => fmt.write_str("unexpected EOF"),
|
||||
},
|
||||
ErrorCode::SSL => match self.ssl_error() {
|
||||
Some(err) => return err.fmt(fmt),
|
||||
None => "unknown BoringSSL error",
|
||||
Some(e) => write!(fmt, "{}", e),
|
||||
None => fmt.write_str("unknown BoringSSL error"),
|
||||
},
|
||||
ErrorCode(code) => return code.fmt(fmt),
|
||||
};
|
||||
fmt.write_str(msg)
|
||||
ErrorCode(code) => write!(fmt, "unknown error code {}", code),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -235,7 +154,7 @@ impl<S> fmt::Display for HandshakeError<S> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
HandshakeError::SetupFailure(ref e) => {
|
||||
write!(f, "TLS stream setup failed {e}")
|
||||
write!(f, "TLS stream setup failed {}", e)
|
||||
}
|
||||
HandshakeError::Failure(ref s) => fmt_mid_handshake_error(s, f, "TLS handshake failed"),
|
||||
HandshakeError::WouldBlock(ref s) => {
|
||||
@ -250,16 +169,9 @@ fn fmt_mid_handshake_error(
|
||||
f: &mut fmt::Formatter,
|
||||
prefix: &str,
|
||||
) -> fmt::Result {
|
||||
if !s.ssl().ssl_context().has_x509_support() {
|
||||
write!(f, "{}", prefix)?;
|
||||
return write!(f, " {}", s.error());
|
||||
}
|
||||
|
||||
match s.ssl().verify_result() {
|
||||
// INVALID_CALL is returned if no verification took place,
|
||||
// such as before a cert is sent.
|
||||
Ok(()) | Err(X509VerifyError::INVALID_CALL) => write!(f, "{prefix}")?,
|
||||
Err(verify) => write!(f, "{prefix}: cert verification failed - {verify}")?,
|
||||
X509VerifyResult::OK => write!(f, "{}", prefix)?,
|
||||
verify => write!(f, "{}: cert verification failed - {}", prefix, verify)?,
|
||||
}
|
||||
|
||||
write!(f, " {}", s.error())
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,14 +0,0 @@
|
||||
pub(crate) struct MutOnly<T>(T);
|
||||
|
||||
impl<T> MutOnly<T> {
|
||||
pub(crate) fn new(value: T) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
|
||||
pub(crate) fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// SAFETY: The type does not let anyone get a &T so Sync is irrelevant.
|
||||
unsafe impl<T> Sync for MutOnly<T> {}
|
||||
@ -1,102 +0,0 @@
|
||||
use std::io::Write as _;
|
||||
|
||||
use super::server::Server;
|
||||
use crate::ssl::CertificateCompressor;
|
||||
use crate::x509::store::X509StoreBuilder;
|
||||
use crate::x509::X509;
|
||||
|
||||
struct BrotliCompressor {
|
||||
q: u32,
|
||||
lgwin: u32,
|
||||
}
|
||||
|
||||
impl Default for BrotliCompressor {
|
||||
fn default() -> Self {
|
||||
Self { q: 11, lgwin: 32 }
|
||||
}
|
||||
}
|
||||
|
||||
impl CertificateCompressor for BrotliCompressor {
|
||||
const ALGORITHM: crate::ssl::CertificateCompressionAlgorithm =
|
||||
crate::ssl::CertificateCompressionAlgorithm(1234);
|
||||
|
||||
const CAN_COMPRESS: bool = true;
|
||||
|
||||
const CAN_DECOMPRESS: bool = true;
|
||||
|
||||
fn compress<W>(&self, input: &[u8], output: &mut W) -> std::io::Result<()>
|
||||
where
|
||||
W: std::io::Write,
|
||||
{
|
||||
let mut writer = brotli::CompressorWriter::new(output, 1024, self.q, self.lgwin);
|
||||
writer.write_all(input)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn decompress<W>(&self, input: &[u8], output: &mut W) -> std::io::Result<()>
|
||||
where
|
||||
W: std::io::Write,
|
||||
{
|
||||
brotli::BrotliDecompress(&mut std::io::Cursor::new(input), output)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn server_only_cert_compression() {
|
||||
let mut server_builder = Server::builder();
|
||||
server_builder
|
||||
.ctx()
|
||||
.add_certificate_compression_algorithm(BrotliCompressor::default())
|
||||
.unwrap();
|
||||
|
||||
let server = server_builder.build();
|
||||
|
||||
let mut store = X509StoreBuilder::new().unwrap();
|
||||
let x509 = X509::from_pem(super::ROOT_CERT).unwrap();
|
||||
store.add_cert(&x509).unwrap();
|
||||
|
||||
let client = server.client();
|
||||
|
||||
client.connect();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn client_only_cert_compression() {
|
||||
let server_builder = Server::builder().build();
|
||||
|
||||
let mut store = X509StoreBuilder::new().unwrap();
|
||||
let x509 = X509::from_pem(super::ROOT_CERT).unwrap();
|
||||
store.add_cert(&x509).unwrap();
|
||||
|
||||
let mut client = server_builder.client();
|
||||
client
|
||||
.ctx()
|
||||
.add_certificate_compression_algorithm(BrotliCompressor::default())
|
||||
.unwrap();
|
||||
|
||||
client.connect();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn client_and_server_cert_compression() {
|
||||
let mut server = Server::builder();
|
||||
server
|
||||
.ctx()
|
||||
.add_certificate_compression_algorithm(BrotliCompressor::default())
|
||||
.unwrap();
|
||||
|
||||
let server = server.build();
|
||||
|
||||
let mut store = X509StoreBuilder::new().unwrap();
|
||||
let x509 = X509::from_pem(super::ROOT_CERT).unwrap();
|
||||
store.add_cert(&x509).unwrap();
|
||||
|
||||
let mut client = server.client();
|
||||
client
|
||||
.ctx()
|
||||
.add_certificate_compression_algorithm(BrotliCompressor::default())
|
||||
.unwrap();
|
||||
|
||||
client.connect();
|
||||
}
|
||||
@ -1,132 +0,0 @@
|
||||
use crate::hash::MessageDigest;
|
||||
use crate::ssl::test::Server;
|
||||
use crate::ssl::SslVerifyMode;
|
||||
|
||||
#[test]
|
||||
fn error_when_trusted_but_callback_returns_false() {
|
||||
let mut server = Server::builder();
|
||||
server.should_error();
|
||||
let server = server.build();
|
||||
let mut client = server.client_with_root_ca();
|
||||
client.ctx().set_verify(SslVerifyMode::PEER);
|
||||
client.ctx().set_cert_verify_callback(|x509| {
|
||||
// The cert is OK
|
||||
assert!(x509.verify_cert().unwrap());
|
||||
assert!(x509.current_cert().is_some());
|
||||
assert!(x509.verify_result().is_ok());
|
||||
// But we return false
|
||||
false
|
||||
});
|
||||
|
||||
client.connect_err();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_error_when_untrusted_but_callback_returns_true() {
|
||||
let server = Server::builder().build();
|
||||
let mut client = server.client();
|
||||
client.ctx().set_verify(SslVerifyMode::PEER);
|
||||
client.ctx().set_cert_verify_callback(|x509| {
|
||||
// The cert is not OK
|
||||
assert!(!x509.verify_cert().unwrap());
|
||||
assert!(x509.current_cert().is_some());
|
||||
assert!(x509.verify_result().is_err());
|
||||
// But we return true
|
||||
true
|
||||
});
|
||||
|
||||
client.connect();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_error_when_trusted_and_callback_returns_true() {
|
||||
let server = Server::builder().build();
|
||||
let mut client = server.client_with_root_ca();
|
||||
client.ctx().set_verify(SslVerifyMode::PEER);
|
||||
client.ctx().set_cert_verify_callback(|x509| {
|
||||
// The cert is OK
|
||||
assert!(x509.verify_cert().unwrap());
|
||||
assert!(x509.current_cert().is_some());
|
||||
assert!(x509.verify_result().is_ok());
|
||||
// And we return true
|
||||
true
|
||||
});
|
||||
client.connect();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn callback_receives_correct_certificate() {
|
||||
// Server sends the full chain (leaf + root)...
|
||||
let server = Server::builder_full_chain().build();
|
||||
// but client doesn't load the root as trusted.
|
||||
// So we expect an error.
|
||||
let mut client = server.client();
|
||||
let leaf_sha1 = "59172d9313e84459bcff27f967e79e6e9217e584";
|
||||
let root_sha1 = "c0cbdf7cdd03c9773e5468e1f6d2da7d5cbb1875";
|
||||
client.ctx().set_verify(SslVerifyMode::PEER);
|
||||
client.ctx().set_cert_verify_callback(move |x509| {
|
||||
assert!(!x509.verify_cert().unwrap());
|
||||
// This is set to the root, since that's the problematic cert.
|
||||
assert!(x509.current_cert().is_some());
|
||||
// This is set to the leaf, since that's the cert we're verifying.
|
||||
assert!(x509.cert().is_some());
|
||||
assert!(x509.verify_result().is_err());
|
||||
|
||||
let root = x509
|
||||
.current_cert()
|
||||
.unwrap()
|
||||
.digest(MessageDigest::sha1())
|
||||
.unwrap();
|
||||
assert_eq!(hex::encode(root), root_sha1);
|
||||
|
||||
let leaf = x509.cert().unwrap().digest(MessageDigest::sha1()).unwrap();
|
||||
assert_eq!(hex::encode(leaf), leaf_sha1);
|
||||
|
||||
// Test that `untrusted` is set to the original chain.
|
||||
assert_eq!(x509.untrusted().unwrap().len(), 2);
|
||||
let leaf = x509
|
||||
.untrusted()
|
||||
.unwrap()
|
||||
.get(0)
|
||||
.unwrap()
|
||||
.digest(MessageDigest::sha1())
|
||||
.unwrap();
|
||||
assert_eq!(hex::encode(leaf), leaf_sha1);
|
||||
let root = x509
|
||||
.untrusted()
|
||||
.unwrap()
|
||||
.get(1)
|
||||
.unwrap()
|
||||
.digest(MessageDigest::sha1())
|
||||
.unwrap();
|
||||
assert_eq!(hex::encode(root), root_sha1);
|
||||
true
|
||||
});
|
||||
|
||||
client.connect();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn callback_receives_correct_chain() {
|
||||
let server = Server::builder().build();
|
||||
let mut client = server.client_with_root_ca();
|
||||
let leaf_sha1 = "59172d9313e84459bcff27f967e79e6e9217e584";
|
||||
let root_sha1 = "c0cbdf7cdd03c9773e5468e1f6d2da7d5cbb1875";
|
||||
client.ctx().set_verify(SslVerifyMode::PEER);
|
||||
client.ctx().set_cert_verify_callback(move |x509| {
|
||||
assert!(x509.verify_cert().unwrap());
|
||||
assert!(x509.current_cert().is_some());
|
||||
assert!(x509.verify_result().is_ok());
|
||||
let chain = x509.chain().unwrap();
|
||||
assert!(chain.len() == 2);
|
||||
let leaf_cert = chain.get(0).unwrap();
|
||||
let leaf_digest = leaf_cert.digest(MessageDigest::sha1()).unwrap();
|
||||
assert_eq!(hex::encode(leaf_digest), leaf_sha1);
|
||||
let root_cert = chain.get(1).unwrap();
|
||||
let root_digest = root_cert.digest(MessageDigest::sha1()).unwrap();
|
||||
assert_eq!(hex::encode(root_digest), root_sha1);
|
||||
true
|
||||
});
|
||||
|
||||
client.connect();
|
||||
}
|
||||
@ -1,278 +0,0 @@
|
||||
use super::server::Server;
|
||||
use crate::ssl::{ErrorCode, HandshakeError, SslAlert, SslVerifyMode};
|
||||
use crate::x509::X509StoreContext;
|
||||
use crate::{hash::MessageDigest, ssl::SslVerifyError};
|
||||
use hex;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
#[test]
|
||||
fn untrusted_callback_override_bad() {
|
||||
let mut server = Server::builder();
|
||||
|
||||
server.err_cb(|err| {
|
||||
let HandshakeError::Failure(handshake) = err else {
|
||||
panic!("expected failure error");
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
handshake.error().to_string(),
|
||||
"[SSLV3_ALERT_CERTIFICATE_REVOKED]"
|
||||
);
|
||||
});
|
||||
|
||||
let server = server.build();
|
||||
|
||||
let mut client = server.client();
|
||||
client
|
||||
.ctx()
|
||||
.set_custom_verify_callback(SslVerifyMode::PEER, |_| {
|
||||
Err(SslVerifyError::Invalid(SslAlert::CERTIFICATE_REVOKED))
|
||||
});
|
||||
|
||||
client.connect_err();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn untrusted_callback_override_ok() {
|
||||
let server = Server::builder().build();
|
||||
let mut client = server.client();
|
||||
|
||||
client
|
||||
.ctx()
|
||||
.set_custom_verify_callback(SslVerifyMode::PEER, |ssl| {
|
||||
assert!(ssl.peer_cert_chain().is_some());
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
client.connect();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn untrusted_with_set_cert() {
|
||||
let mut server = Server::builder();
|
||||
|
||||
server.should_error();
|
||||
|
||||
let server = server.build();
|
||||
let mut client = server.client();
|
||||
|
||||
client
|
||||
.ctx()
|
||||
.set_custom_verify_callback(SslVerifyMode::PEER, move |ssl| {
|
||||
let store = ssl.ssl_context().cert_store();
|
||||
let cert = ssl.peer_certificate().unwrap();
|
||||
let cert_chain = ssl.peer_cert_chain().unwrap();
|
||||
|
||||
assert_eq!(store.objects_len(), 0);
|
||||
|
||||
X509StoreContext::new()
|
||||
.unwrap()
|
||||
.init(store, &cert, cert_chain, |store_ctx| {
|
||||
assert!(!store_ctx.verify_cert().unwrap());
|
||||
assert!(store_ctx.verify_result().is_err());
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
Err(SslVerifyError::Invalid(SslAlert::CERTIFICATE_UNKNOWN))
|
||||
});
|
||||
|
||||
client.connect_err();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trusted_with_set_cert() {
|
||||
let server = Server::builder().build();
|
||||
let mut client = server.client_with_root_ca();
|
||||
|
||||
client
|
||||
.ctx()
|
||||
.set_custom_verify_callback(SslVerifyMode::PEER, move |ssl| {
|
||||
let store = ssl.ssl_context().cert_store();
|
||||
let cert = ssl.peer_certificate().unwrap();
|
||||
let cert_chain = ssl.peer_cert_chain().unwrap();
|
||||
|
||||
assert_eq!(store.objects_len(), 1);
|
||||
|
||||
X509StoreContext::new()
|
||||
.unwrap()
|
||||
.init(store, &cert, cert_chain, |store_ctx| {
|
||||
assert!(store_ctx.verify_cert().unwrap());
|
||||
assert_eq!(store_ctx.verify_result(), Ok(()));
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
client.connect();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trusted_callback_override_ok() {
|
||||
let server = Server::builder().build();
|
||||
let mut client = server.client_with_root_ca();
|
||||
|
||||
client
|
||||
.ctx()
|
||||
.set_custom_verify_callback(SslVerifyMode::PEER, |ssl| {
|
||||
assert!(ssl.peer_certificate().is_some());
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
client.connect();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trusted_callback_override_bad() {
|
||||
let mut server = Server::builder();
|
||||
|
||||
server.should_error();
|
||||
|
||||
let server = server.build();
|
||||
let mut client = server.client_with_root_ca();
|
||||
|
||||
client
|
||||
.ctx()
|
||||
.set_custom_verify_callback(SslVerifyMode::PEER, |_| {
|
||||
Err(SslVerifyError::Invalid(SslAlert::CERTIFICATE_UNKNOWN))
|
||||
});
|
||||
|
||||
client.connect_err();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn callback() {
|
||||
static CALLED_BACK: AtomicBool = AtomicBool::new(false);
|
||||
let server = Server::builder().build();
|
||||
let mut client = server.client();
|
||||
let expected = "59172d9313e84459bcff27f967e79e6e9217e584";
|
||||
|
||||
client
|
||||
.ctx()
|
||||
.set_verify_callback(SslVerifyMode::PEER, |_, _| {
|
||||
panic!("verify callback should not be called");
|
||||
});
|
||||
|
||||
client
|
||||
.ctx()
|
||||
.set_custom_verify_callback(SslVerifyMode::PEER, move |ssl| {
|
||||
CALLED_BACK.store(true, Ordering::SeqCst);
|
||||
|
||||
let cert = ssl.peer_certificate().unwrap();
|
||||
let digest = cert.digest(MessageDigest::sha1()).unwrap();
|
||||
|
||||
assert_eq!(hex::encode(digest), expected);
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
client.connect();
|
||||
assert!(CALLED_BACK.load(Ordering::SeqCst));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ssl_callback() {
|
||||
static CALLED_BACK: AtomicBool = AtomicBool::new(false);
|
||||
let server = Server::builder().build();
|
||||
let mut client = server.client().build().builder();
|
||||
let expected = "59172d9313e84459bcff27f967e79e6e9217e584";
|
||||
|
||||
client
|
||||
.ssl()
|
||||
.set_verify_callback(SslVerifyMode::PEER, |_, _| {
|
||||
panic!("verify callback should not be called");
|
||||
});
|
||||
|
||||
client
|
||||
.ssl()
|
||||
.set_custom_verify_callback(SslVerifyMode::PEER, move |ssl| {
|
||||
CALLED_BACK.store(true, Ordering::SeqCst);
|
||||
|
||||
let cert = ssl.peer_certificate().unwrap();
|
||||
let digest = cert.digest(MessageDigest::sha1()).unwrap();
|
||||
|
||||
assert_eq!(hex::encode(digest), expected);
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
client.connect();
|
||||
assert!(CALLED_BACK.load(Ordering::SeqCst));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn both_callback() {
|
||||
static CALLED_BACK: AtomicBool = AtomicBool::new(false);
|
||||
let server = Server::builder().build();
|
||||
let mut client = server.client();
|
||||
|
||||
client
|
||||
.ctx()
|
||||
.set_custom_verify_callback(SslVerifyMode::PEER, |_| {
|
||||
panic!("verify callback should not be called");
|
||||
});
|
||||
|
||||
let mut client = client.build().builder();
|
||||
let expected = "59172d9313e84459bcff27f967e79e6e9217e584";
|
||||
|
||||
client
|
||||
.ssl()
|
||||
.set_custom_verify_callback(SslVerifyMode::PEER, move |ssl| {
|
||||
CALLED_BACK.store(true, Ordering::SeqCst);
|
||||
|
||||
let cert = ssl.peer_certificate().unwrap();
|
||||
let digest = cert.digest(MessageDigest::sha1()).unwrap();
|
||||
|
||||
assert_eq!(hex::encode(digest), expected);
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
client.connect();
|
||||
assert!(CALLED_BACK.load(Ordering::SeqCst));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn retry() {
|
||||
let mut server = Server::builder();
|
||||
|
||||
server.err_cb(|err| {
|
||||
let HandshakeError::Failure(handshake) = err else {
|
||||
panic!("expected failure error");
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
handshake.error().to_string(),
|
||||
"[SSLV3_ALERT_CERTIFICATE_REVOKED]"
|
||||
);
|
||||
});
|
||||
|
||||
let server = server.build();
|
||||
let mut client = server.client();
|
||||
static CALLED_BACK: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
client
|
||||
.ctx()
|
||||
.set_custom_verify_callback(SslVerifyMode::PEER, move |_| {
|
||||
if !CALLED_BACK.swap(true, Ordering::SeqCst) {
|
||||
return Err(SslVerifyError::Retry);
|
||||
}
|
||||
|
||||
Err(SslVerifyError::Invalid(SslAlert::CERTIFICATE_REVOKED))
|
||||
});
|
||||
|
||||
let HandshakeError::WouldBlock(handshake) = client.connect_err() else {
|
||||
panic!("should be WouldBlock");
|
||||
};
|
||||
|
||||
assert!(CALLED_BACK.load(Ordering::SeqCst));
|
||||
assert!(handshake.error().would_block());
|
||||
assert_eq!(handshake.error().code(), ErrorCode::WANT_CERTIFICATE_VERIFY);
|
||||
handshake.handshake().unwrap_err();
|
||||
}
|
||||
@ -1,73 +0,0 @@
|
||||
use crate::hpke::HpkeKey;
|
||||
use crate::ssl::ech::SslEchKeys;
|
||||
use crate::ssl::test::server::{ClientSslBuilder, Server};
|
||||
use crate::ssl::HandshakeError;
|
||||
|
||||
// For future reference, these configs are generated by building the bssl tool (the binary is built
|
||||
// alongside boringssl) and running the following command:
|
||||
//
|
||||
// ./bssl generate-ech -out-ech-config-list ./list -out-ech-config ./config -out-private-key ./key
|
||||
// -public-name ech.com -config-id 1
|
||||
static ECH_CONFIG_LIST: &[u8] = include_bytes!("../../../test/echconfiglist");
|
||||
static ECH_CONFIG: &[u8] = include_bytes!("../../../test/echconfig");
|
||||
static ECH_KEY: &[u8] = include_bytes!("../../../test/echkey");
|
||||
|
||||
static ECH_CONFIG_2: &[u8] = include_bytes!("../../../test/echconfig-2");
|
||||
static ECH_KEY_2: &[u8] = include_bytes!("../../../test/echkey-2");
|
||||
|
||||
fn bootstrap_ech(config: &[u8], key: &[u8], list: &[u8]) -> (Server, ClientSslBuilder) {
|
||||
let server = {
|
||||
let key = HpkeKey::dhkem_p256_sha256(key).unwrap();
|
||||
let mut ech_keys_builder = SslEchKeys::builder().unwrap();
|
||||
ech_keys_builder.add_key(true, config, key).unwrap();
|
||||
let ech_keys = ech_keys_builder.build();
|
||||
|
||||
let mut builder = Server::builder();
|
||||
builder.ctx().set_ech_keys(&ech_keys).unwrap();
|
||||
|
||||
builder.build()
|
||||
};
|
||||
|
||||
let mut client = server.client_with_root_ca().build().builder();
|
||||
client.ssl().set_ech_config_list(list).unwrap();
|
||||
client.ssl().set_hostname("foobar.com").unwrap();
|
||||
|
||||
(server, client)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ech() {
|
||||
let (_server, client) = bootstrap_ech(ECH_CONFIG, ECH_KEY, ECH_CONFIG_LIST);
|
||||
|
||||
let ssl_stream = client.connect();
|
||||
assert!(ssl_stream.ssl().ech_accepted());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ech_rejection() {
|
||||
// Server is initialized using `ECH_CONFIG_2`, so using `ECH_CONFIG_LIST` instead of
|
||||
// `ECH_CONFIG_LIST_2` should trigger rejection.
|
||||
let (_server, client) = bootstrap_ech(ECH_CONFIG_2, ECH_KEY_2, ECH_CONFIG_LIST);
|
||||
|
||||
let HandshakeError::Failure(failed_ssl_stream) = client.connect_err() else {
|
||||
panic!("wrong HandshakeError failure variant!");
|
||||
};
|
||||
assert_eq!(
|
||||
failed_ssl_stream.ssl().get_ech_name_override(),
|
||||
Some(b"ech.com".to_vec().as_ref())
|
||||
);
|
||||
assert!(failed_ssl_stream.ssl().get_ech_retry_configs().is_some());
|
||||
assert!(!failed_ssl_stream.ssl().ech_accepted());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ech_grease() {
|
||||
let server = Server::builder().build();
|
||||
|
||||
let mut client = server.client_with_root_ca().build().builder();
|
||||
// Verified with a pcap locally that the ECH extension gets sent due to GREASE
|
||||
client.ssl().set_enable_ech_grease(true);
|
||||
|
||||
let ssl_stream = client.connect();
|
||||
assert!(!ssl_stream.ssl().ech_accepted());
|
||||
}
|
||||
@ -1,46 +1,238 @@
|
||||
use foreign_types::{ForeignType, ForeignTypeRef};
|
||||
use std::io;
|
||||
#![allow(unused_imports)]
|
||||
|
||||
use hex;
|
||||
use std::cell::Cell;
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
use std::io::{self, BufReader};
|
||||
use std::iter;
|
||||
use std::mem;
|
||||
use std::net::{TcpListener, TcpStream};
|
||||
use std::net::UdpSocket;
|
||||
use std::net::{SocketAddr, TcpListener, TcpStream};
|
||||
use std::path::Path;
|
||||
use std::process::{Child, ChildStdin, Command, Stdio};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::dh::Dh;
|
||||
use crate::error::ErrorStack;
|
||||
use crate::hash::MessageDigest;
|
||||
use crate::pkey::PKey;
|
||||
use crate::srtp::SrtpProfileId;
|
||||
use crate::ssl;
|
||||
use crate::ssl::test::server::Server;
|
||||
use crate::ssl::SslVersion;
|
||||
use crate::ssl::{
|
||||
self, ExtensionType, ShutdownResult, ShutdownState, Ssl, SslAcceptor, SslAcceptorBuilder,
|
||||
SslConnector, SslContext, SslFiletype, SslMethod, SslOptions, SslStream, SslVerifyMode,
|
||||
Error, ExtensionType, HandshakeError, MidHandshakeSslStream, ShutdownResult, ShutdownState,
|
||||
Ssl, SslAcceptor, SslAcceptorBuilder, SslConnector, SslContext, SslContextBuilder, SslFiletype,
|
||||
SslMethod, SslOptions, SslSessionCacheMode, SslStream, SslStreamBuilder, SslVerifyMode,
|
||||
StatusType,
|
||||
};
|
||||
use crate::ssl::{HandshakeError, SslVersion};
|
||||
use crate::x509::store::X509StoreBuilder;
|
||||
use crate::x509::verify::X509CheckFlags;
|
||||
use crate::x509::{X509Name, X509};
|
||||
use crate::x509::{X509Name, X509StoreContext, X509VerifyResult, X509};
|
||||
|
||||
use super::CompliancePolicy;
|
||||
|
||||
mod cert_compressor;
|
||||
mod cert_verify;
|
||||
mod custom_verify;
|
||||
mod ech;
|
||||
mod private_key_method;
|
||||
mod server;
|
||||
mod session;
|
||||
mod session_resumption;
|
||||
mod verify;
|
||||
|
||||
static ROOT_CERT: &[u8] = include_bytes!("../../../test/root-ca.pem");
|
||||
static CERT: &[u8] = include_bytes!("../../../test/cert.pem");
|
||||
static KEY: &[u8] = include_bytes!("../../../test/key.pem");
|
||||
|
||||
#[test]
|
||||
fn verify_untrusted() {
|
||||
let mut server = Server::builder();
|
||||
server.should_error();
|
||||
let server = server.build();
|
||||
|
||||
let mut client = server.client();
|
||||
client.ctx().set_verify(SslVerifyMode::PEER);
|
||||
|
||||
client.connect_err();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_trusted() {
|
||||
let server = Server::builder().build();
|
||||
|
||||
let mut client = server.client();
|
||||
client.ctx().set_ca_file("test/root-ca.pem").unwrap();
|
||||
|
||||
client.connect();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_trusted_with_set_cert() {
|
||||
let server = Server::builder().build();
|
||||
|
||||
let mut store = X509StoreBuilder::new().unwrap();
|
||||
let x509 = X509::from_pem(ROOT_CERT).unwrap();
|
||||
store.add_cert(x509).unwrap();
|
||||
|
||||
let mut client = server.client();
|
||||
client.ctx().set_verify(SslVerifyMode::PEER);
|
||||
client.ctx().set_verify_cert_store(store.build()).unwrap();
|
||||
|
||||
client.connect();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_untrusted_callback_override_ok() {
|
||||
let server = Server::builder().build();
|
||||
|
||||
let mut client = server.client();
|
||||
client
|
||||
.ctx()
|
||||
.set_verify_callback(SslVerifyMode::PEER, |_, x509| {
|
||||
assert!(x509.current_cert().is_some());
|
||||
true
|
||||
});
|
||||
|
||||
client.connect();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_untrusted_callback_override_bad() {
|
||||
let mut server = Server::builder();
|
||||
server.should_error();
|
||||
let server = server.build();
|
||||
|
||||
let mut client = server.client();
|
||||
client
|
||||
.ctx()
|
||||
.set_verify_callback(SslVerifyMode::PEER, |_, _| false);
|
||||
|
||||
client.connect_err();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_trusted_callback_override_ok() {
|
||||
let server = Server::builder().build();
|
||||
|
||||
let mut client = server.client();
|
||||
client.ctx().set_ca_file("test/root-ca.pem").unwrap();
|
||||
client
|
||||
.ctx()
|
||||
.set_verify_callback(SslVerifyMode::PEER, |_, x509| {
|
||||
assert!(x509.current_cert().is_some());
|
||||
true
|
||||
});
|
||||
|
||||
client.connect();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_trusted_callback_override_bad() {
|
||||
let mut server = Server::builder();
|
||||
server.should_error();
|
||||
let server = server.build();
|
||||
|
||||
let mut client = server.client();
|
||||
client.ctx().set_ca_file("test/root-ca.pem").unwrap();
|
||||
client
|
||||
.ctx()
|
||||
.set_verify_callback(SslVerifyMode::PEER, |_, _| false);
|
||||
|
||||
client.connect_err();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_callback_load_certs() {
|
||||
let server = Server::builder().build();
|
||||
|
||||
let mut client = server.client();
|
||||
client
|
||||
.ctx()
|
||||
.set_verify_callback(SslVerifyMode::PEER, |_, x509| {
|
||||
assert!(x509.current_cert().is_some());
|
||||
true
|
||||
});
|
||||
|
||||
client.connect();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_trusted_get_error_ok() {
|
||||
let server = Server::builder().build();
|
||||
|
||||
let mut client = server.client();
|
||||
client.ctx().set_ca_file("test/root-ca.pem").unwrap();
|
||||
client
|
||||
.ctx()
|
||||
.set_verify_callback(SslVerifyMode::PEER, |_, x509| {
|
||||
assert_eq!(x509.error(), X509VerifyResult::OK);
|
||||
true
|
||||
});
|
||||
|
||||
client.connect();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_trusted_get_error_err() {
|
||||
let mut server = Server::builder();
|
||||
server.should_error();
|
||||
let server = server.build();
|
||||
|
||||
let mut client = server.client();
|
||||
client
|
||||
.ctx()
|
||||
.set_verify_callback(SslVerifyMode::PEER, |_, x509| {
|
||||
assert_ne!(x509.error(), X509VerifyResult::OK);
|
||||
false
|
||||
});
|
||||
|
||||
client.connect_err();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_callback() {
|
||||
static CALLED_BACK: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
let server = Server::builder().build();
|
||||
|
||||
let mut client = server.client();
|
||||
let expected = "59172d9313e84459bcff27f967e79e6e9217e584";
|
||||
client
|
||||
.ctx()
|
||||
.set_verify_callback(SslVerifyMode::PEER, move |_, x509| {
|
||||
CALLED_BACK.store(true, Ordering::SeqCst);
|
||||
let cert = x509.current_cert().unwrap();
|
||||
let digest = cert.digest(MessageDigest::sha1()).unwrap();
|
||||
assert_eq!(hex::encode(&digest), expected);
|
||||
true
|
||||
});
|
||||
|
||||
client.connect();
|
||||
assert!(CALLED_BACK.load(Ordering::SeqCst));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ssl_verify_callback() {
|
||||
static CALLED_BACK: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
let server = Server::builder().build();
|
||||
|
||||
let mut client = server.client().build().builder();
|
||||
let expected = "59172d9313e84459bcff27f967e79e6e9217e584";
|
||||
client
|
||||
.ssl()
|
||||
.set_verify_callback(SslVerifyMode::PEER, move |_, x509| {
|
||||
CALLED_BACK.store(true, Ordering::SeqCst);
|
||||
let cert = x509.current_cert().unwrap();
|
||||
let digest = cert.digest(MessageDigest::sha1()).unwrap();
|
||||
assert_eq!(hex::encode(&digest), expected);
|
||||
true
|
||||
});
|
||||
|
||||
client.connect();
|
||||
assert!(CALLED_BACK.load(Ordering::SeqCst));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_ctx_options() {
|
||||
let ctx = SslContext::builder(SslMethod::tls()).unwrap();
|
||||
let _ = ctx.options();
|
||||
ctx.options();
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -119,9 +311,9 @@ fn test_connect_with_srtp_ctx() {
|
||||
let mut ctx = SslContext::builder(SslMethod::dtls()).unwrap();
|
||||
ctx.set_tlsext_use_srtp("SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32")
|
||||
.unwrap();
|
||||
ctx.set_certificate_file(Path::new("test/cert.pem"), SslFiletype::PEM)
|
||||
ctx.set_certificate_file(&Path::new("test/cert.pem"), SslFiletype::PEM)
|
||||
.unwrap();
|
||||
ctx.set_private_key_file(Path::new("test/key.pem"), SslFiletype::PEM)
|
||||
ctx.set_private_key_file(&Path::new("test/key.pem"), SslFiletype::PEM)
|
||||
.unwrap();
|
||||
let mut ssl = Ssl::new(&ctx.build()).unwrap();
|
||||
ssl.set_mtu(1500).unwrap();
|
||||
@ -175,9 +367,9 @@ fn test_connect_with_srtp_ssl() {
|
||||
let guard = thread::spawn(move || {
|
||||
let stream = listener.accept().unwrap().0;
|
||||
let mut ctx = SslContext::builder(SslMethod::dtls()).unwrap();
|
||||
ctx.set_certificate_file(Path::new("test/cert.pem"), SslFiletype::PEM)
|
||||
ctx.set_certificate_file(&Path::new("test/cert.pem"), SslFiletype::PEM)
|
||||
.unwrap();
|
||||
ctx.set_private_key_file(Path::new("test/key.pem"), SslFiletype::PEM)
|
||||
ctx.set_private_key_file(&Path::new("test/key.pem"), SslFiletype::PEM)
|
||||
.unwrap();
|
||||
let mut ssl = Ssl::new(&ctx.build()).unwrap();
|
||||
ssl.set_tlsext_use_srtp("SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32")
|
||||
@ -256,12 +448,19 @@ fn test_alpn_server_select_none_fatal() {
|
||||
ssl::select_next_proto(b"\x08http/1.1\x08spdy/3.1", client)
|
||||
.ok_or(ssl::AlpnError::ALERT_FATAL)
|
||||
});
|
||||
#[cfg(not(feature = "fips"))]
|
||||
server.should_error();
|
||||
let server = server.build();
|
||||
|
||||
let mut client = server.client();
|
||||
client.ctx().set_alpn_protos(b"\x06http/2").unwrap();
|
||||
client.connect_err();
|
||||
|
||||
if cfg!(feature = "fips") {
|
||||
let s = client.connect();
|
||||
assert_eq!(None, s.ssl().selected_alpn_protocol());
|
||||
} else {
|
||||
client.connect_err();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -278,13 +477,6 @@ fn test_alpn_server_select_none() {
|
||||
assert_eq!(None, s.ssl().selected_alpn_protocol());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_alpn() {
|
||||
assert_eq!(ssl::select_next_proto(b"", b""), None);
|
||||
assert_eq!(ssl::select_next_proto(b"", b"\x08http/1.1"), None);
|
||||
assert_eq!(ssl::select_next_proto(b"\x08http/1.1", b""), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_alpn_server_unilateral() {
|
||||
let server = Server::builder().build();
|
||||
@ -307,52 +499,6 @@ fn test_select_cert_ok() {
|
||||
client.connect();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mutable_store() {
|
||||
#![allow(deprecated)]
|
||||
|
||||
let cert = include_bytes!("../../../test/cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
let cert2 = include_bytes!("../../../test/root-ca.pem");
|
||||
let cert2 = X509::from_pem(cert2).unwrap();
|
||||
|
||||
let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
|
||||
ctx.cert_store_mut().add_cert(cert.clone()).unwrap();
|
||||
assert_eq!(1, ctx.cert_store().objects_len());
|
||||
|
||||
ctx.set_cert_store_builder(X509StoreBuilder::new().unwrap());
|
||||
assert_eq!(0, ctx.cert_store().objects_len());
|
||||
|
||||
ctx.cert_store_mut().add_cert(cert.clone()).unwrap();
|
||||
assert_eq!(1, ctx.cert_store().objects_len());
|
||||
|
||||
let mut new_store = X509StoreBuilder::new().unwrap();
|
||||
new_store.add_cert(&cert).unwrap();
|
||||
new_store.add_cert(&cert2).unwrap();
|
||||
let new_store = new_store.build();
|
||||
assert_eq!(2, new_store.objects_len());
|
||||
|
||||
ctx.set_cert_store_ref(&new_store);
|
||||
assert_eq!(2, ctx.cert_store().objects_len());
|
||||
assert!(std::ptr::eq(new_store.as_ptr(), ctx.cert_store().as_ptr()));
|
||||
|
||||
let ctx = ctx.build();
|
||||
assert!(std::ptr::eq(new_store.as_ptr(), ctx.cert_store().as_ptr()));
|
||||
|
||||
drop(new_store);
|
||||
assert_eq!(2, ctx.cert_store().objects_len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "mutated")]
|
||||
fn shared_store_must_not_be_mutated() {
|
||||
let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
|
||||
|
||||
let shared = X509StoreBuilder::new().unwrap().build();
|
||||
ctx.set_cert_store_ref(&shared);
|
||||
ctx.cert_store_mut();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_select_cert_error() {
|
||||
let mut server = Server::builder();
|
||||
@ -417,68 +563,6 @@ fn test_select_cert_alpn_extension() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_io_retry() {
|
||||
#[derive(Debug)]
|
||||
struct RetryStream {
|
||||
inner: TcpStream,
|
||||
first_read: bool,
|
||||
first_write: bool,
|
||||
first_flush: bool,
|
||||
}
|
||||
|
||||
impl Read for RetryStream {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
if mem::replace(&mut self.first_read, false) {
|
||||
Err(io::Error::new(io::ErrorKind::WouldBlock, "first read"))
|
||||
} else {
|
||||
self.inner.read(buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for RetryStream {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
if mem::replace(&mut self.first_write, false) {
|
||||
Err(io::Error::new(io::ErrorKind::WouldBlock, "first write"))
|
||||
} else {
|
||||
self.inner.write(buf)
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
if mem::replace(&mut self.first_flush, false) {
|
||||
Err(io::Error::new(io::ErrorKind::WouldBlock, "first flush"))
|
||||
} else {
|
||||
self.inner.flush()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let server = Server::builder().build();
|
||||
|
||||
let stream = RetryStream {
|
||||
inner: server.connect_tcp(),
|
||||
first_read: true,
|
||||
first_write: true,
|
||||
first_flush: true,
|
||||
};
|
||||
|
||||
let ctx = SslContext::builder(SslMethod::tls()).unwrap();
|
||||
let mut s = match Ssl::new(&ctx.build()).unwrap().connect(stream) {
|
||||
Ok(mut s) => return s.read_exact(&mut [0]).unwrap(),
|
||||
Err(HandshakeError::WouldBlock(s)) => s,
|
||||
Err(_) => panic!("should not fail on setup"),
|
||||
};
|
||||
loop {
|
||||
match s.handshake() {
|
||||
Ok(mut s) => return s.read_exact(&mut [0]).unwrap(),
|
||||
Err(HandshakeError::WouldBlock(mid_s)) => s = mid_s,
|
||||
Err(_) => panic!("should not fail on handshake"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "blammo")]
|
||||
fn write_panic() {
|
||||
@ -587,7 +671,7 @@ fn refcount_ssl_context() {
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "windows", ignore)]
|
||||
#[cfg_attr(all(target_os = "macos"), ignore)]
|
||||
#[cfg_attr(all(target_os = "macos", feature = "vendored"), ignore)]
|
||||
fn default_verify_paths() {
|
||||
let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
|
||||
ctx.set_default_verify_paths().unwrap();
|
||||
@ -620,97 +704,28 @@ fn add_extra_chain_cert() {
|
||||
#[test]
|
||||
fn verify_valid_hostname() {
|
||||
let server = Server::builder().build();
|
||||
let mut client = server.client_with_root_ca();
|
||||
|
||||
let mut client = server.client();
|
||||
client.ctx().set_ca_file("test/root-ca.pem").unwrap();
|
||||
client.ctx().set_verify(SslVerifyMode::PEER);
|
||||
|
||||
let mut client = client.build().builder();
|
||||
|
||||
client
|
||||
.ssl()
|
||||
.param_mut()
|
||||
.set_hostflags(X509CheckFlags::NO_PARTIAL_WILDCARDS);
|
||||
client.ssl().param_mut().set_host("foobar.com").unwrap();
|
||||
client.connect();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_valid_hostname_with_wildcard() {
|
||||
let mut server = Server::builder();
|
||||
|
||||
server
|
||||
.ctx()
|
||||
.set_certificate_chain_file("test/cert-wildcard.pem")
|
||||
.unwrap();
|
||||
|
||||
let server = server.build();
|
||||
let mut client = server.client_with_root_ca();
|
||||
|
||||
client.ctx().set_verify(SslVerifyMode::PEER);
|
||||
|
||||
let mut client = client.build().builder();
|
||||
client.ssl().param_mut().set_host("yes.foobar.com").unwrap();
|
||||
client.connect();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_reject_underscore_hostname_with_wildcard() {
|
||||
let mut server = Server::builder();
|
||||
|
||||
server.should_error();
|
||||
server
|
||||
.ctx()
|
||||
.set_certificate_chain_file("test/cert-wildcard.pem")
|
||||
.unwrap();
|
||||
|
||||
let server = server.build();
|
||||
let mut client = server.client_with_root_ca();
|
||||
|
||||
client.ctx().set_verify(SslVerifyMode::PEER);
|
||||
|
||||
let mut client = client.build().builder();
|
||||
client
|
||||
.ssl()
|
||||
.param_mut()
|
||||
.set_host("not_allowed.foobar.com")
|
||||
.unwrap();
|
||||
client.connect_err();
|
||||
}
|
||||
|
||||
#[cfg(feature = "underscore-wildcards")]
|
||||
#[test]
|
||||
fn verify_allow_underscore_hostname_with_wildcard() {
|
||||
let mut server = Server::builder();
|
||||
|
||||
server
|
||||
.ctx()
|
||||
.set_certificate_chain_file("test/cert-wildcard.pem")
|
||||
.unwrap();
|
||||
|
||||
let server = server.build();
|
||||
let mut client = server.client_with_root_ca();
|
||||
|
||||
client.ctx().set_verify(SslVerifyMode::PEER);
|
||||
|
||||
let mut client = client.build().builder();
|
||||
|
||||
client
|
||||
.ssl()
|
||||
.param_mut()
|
||||
.set_hostflags(X509CheckFlags::UNDERSCORE_WILDCARDS);
|
||||
client
|
||||
.ssl()
|
||||
.param_mut()
|
||||
.set_host("now_allowed.foobar.com")
|
||||
.unwrap();
|
||||
client.connect();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_invalid_hostname() {
|
||||
let mut server = Server::builder();
|
||||
|
||||
server.should_error();
|
||||
|
||||
let server = server.build();
|
||||
let mut client = server.client_with_root_ca();
|
||||
|
||||
let mut client = server.client();
|
||||
client.ctx().set_ca_file("test/root-ca.pem").unwrap();
|
||||
client.ctx().set_verify(SslVerifyMode::PEER);
|
||||
|
||||
let mut client = client.build().builder();
|
||||
@ -876,6 +891,92 @@ fn client_ca_list() {
|
||||
ctx.set_client_ca_list(names);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cert_store() {
|
||||
let server = Server::builder().build();
|
||||
|
||||
let mut client = server.client();
|
||||
let cert = X509::from_pem(ROOT_CERT).unwrap();
|
||||
client.ctx().cert_store_mut().add_cert(cert).unwrap();
|
||||
client.ctx().set_verify(SslVerifyMode::PEER);
|
||||
|
||||
client.connect();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn idle_session() {
|
||||
let ctx = SslContext::builder(SslMethod::tls()).unwrap().build();
|
||||
let ssl = Ssl::new(&ctx).unwrap();
|
||||
assert!(ssl.session().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn active_session() {
|
||||
let server = Server::builder().build();
|
||||
|
||||
let s = server.client().connect();
|
||||
|
||||
let session = s.ssl().session().unwrap();
|
||||
let len = session.master_key_len();
|
||||
let mut buf = vec![0; len - 1];
|
||||
let copied = session.master_key(&mut buf);
|
||||
assert_eq!(copied, buf.len());
|
||||
let mut buf = vec![0; len + 1];
|
||||
let copied = session.master_key(&mut buf);
|
||||
assert_eq!(copied, len);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_session_callback() {
|
||||
static CALLED_BACK: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
let mut server = Server::builder();
|
||||
server.ctx().set_session_id_context(b"foo").unwrap();
|
||||
|
||||
let server = server.build();
|
||||
|
||||
let mut client = server.client();
|
||||
|
||||
client
|
||||
.ctx()
|
||||
.set_session_cache_mode(SslSessionCacheMode::CLIENT | SslSessionCacheMode::NO_INTERNAL);
|
||||
client
|
||||
.ctx()
|
||||
.set_new_session_callback(|_, _| CALLED_BACK.store(true, Ordering::SeqCst));
|
||||
|
||||
client.connect();
|
||||
|
||||
assert!(CALLED_BACK.load(Ordering::SeqCst));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_session_callback_swapped_ctx() {
|
||||
static CALLED_BACK: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
let mut server = Server::builder();
|
||||
server.ctx().set_session_id_context(b"foo").unwrap();
|
||||
|
||||
let server = server.build();
|
||||
|
||||
let mut client = server.client();
|
||||
|
||||
client
|
||||
.ctx()
|
||||
.set_session_cache_mode(SslSessionCacheMode::CLIENT | SslSessionCacheMode::NO_INTERNAL);
|
||||
client
|
||||
.ctx()
|
||||
.set_new_session_callback(|_, _| CALLED_BACK.store(true, Ordering::SeqCst));
|
||||
|
||||
let mut client = client.build().builder();
|
||||
|
||||
let ctx = SslContextBuilder::new(SslMethod::tls()).unwrap().build();
|
||||
client.ssl().set_ssl_context(&ctx).unwrap();
|
||||
|
||||
client.connect();
|
||||
|
||||
assert!(CALLED_BACK.load(Ordering::SeqCst));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn keying_export() {
|
||||
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
|
||||
@ -887,9 +988,9 @@ fn keying_export() {
|
||||
let guard = thread::spawn(move || {
|
||||
let stream = listener.accept().unwrap().0;
|
||||
let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
|
||||
ctx.set_certificate_file(Path::new("test/cert.pem"), SslFiletype::PEM)
|
||||
ctx.set_certificate_file(&Path::new("test/cert.pem"), SslFiletype::PEM)
|
||||
.unwrap();
|
||||
ctx.set_private_key_file(Path::new("test/key.pem"), SslFiletype::PEM)
|
||||
ctx.set_private_key_file(&Path::new("test/key.pem"), SslFiletype::PEM)
|
||||
.unwrap();
|
||||
let ssl = Ssl::new(&ctx.build()).unwrap();
|
||||
let mut stream = ssl.accept(stream).unwrap();
|
||||
@ -954,6 +1055,7 @@ fn _check_kinds() {
|
||||
is_sync::<SslStream<TcpStream>>();
|
||||
}
|
||||
|
||||
#[cfg(not(osslconf = "OPENSSL_NO_PSK"))]
|
||||
#[test]
|
||||
fn psk_ciphers() {
|
||||
const CIPHER: &str = "PSK-AES128-CBC-SHA";
|
||||
@ -1015,211 +1117,9 @@ fn sni_callback_swapped_ctx() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_curve() {
|
||||
let server = Server::builder().build();
|
||||
let client = server.client_with_root_ca();
|
||||
let client_stream = client.connect();
|
||||
let curve = client_stream.ssl().curve();
|
||||
assert!(curve.is_some());
|
||||
let curve_name = client_stream.ssl().curve_name();
|
||||
assert!(curve_name.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_ciphers() {
|
||||
let ctx_builder = SslContext::builder(SslMethod::tls()).unwrap();
|
||||
let ctx_builder_ciphers: Vec<&str> = ctx_builder
|
||||
.ciphers()
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.map(|v| v.name())
|
||||
.collect();
|
||||
assert!(!(ctx_builder_ciphers.is_empty()));
|
||||
|
||||
let ctx = ctx_builder.build();
|
||||
let ctx_ciphers: Vec<&str> = ctx
|
||||
.ciphers()
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.map(|v| v.name())
|
||||
.collect();
|
||||
assert!(!(ctx_ciphers.is_empty()));
|
||||
|
||||
assert_eq!(ctx_builder_ciphers.len(), ctx_ciphers.len());
|
||||
|
||||
for (ctx_builder_cipher, ctx_cipher) in ctx_builder_ciphers.into_iter().zip(ctx_ciphers) {
|
||||
assert_eq!(ctx_builder_cipher, ctx_cipher);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_compliance() {
|
||||
fn session_cache_size() {
|
||||
let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
|
||||
ctx.set_compliance_policy(CompliancePolicy::FIPS_202205)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(ctx.max_proto_version().unwrap(), SslVersion::TLS1_3);
|
||||
assert_eq!(ctx.min_proto_version().unwrap(), SslVersion::TLS1_2);
|
||||
|
||||
const FIPS_CIPHERS: [&str; 4] = [
|
||||
"ECDHE-ECDSA-AES128-GCM-SHA256",
|
||||
"ECDHE-RSA-AES128-GCM-SHA256",
|
||||
"ECDHE-ECDSA-AES256-GCM-SHA384",
|
||||
"ECDHE-RSA-AES256-GCM-SHA384",
|
||||
];
|
||||
|
||||
let ciphers = ctx.ciphers().unwrap();
|
||||
assert_eq!(ciphers.len(), FIPS_CIPHERS.len());
|
||||
|
||||
for cipher in ciphers.into_iter().zip(FIPS_CIPHERS) {
|
||||
assert_eq!(cipher.0.name(), cipher.1);
|
||||
}
|
||||
|
||||
let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
|
||||
ctx.set_compliance_policy(CompliancePolicy::WPA3_192_202304)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(ctx.max_proto_version().unwrap(), SslVersion::TLS1_3);
|
||||
assert_eq!(ctx.min_proto_version().unwrap(), SslVersion::TLS1_2);
|
||||
|
||||
const WPA3_192_CIPHERS: [&str; 2] = [
|
||||
"ECDHE-ECDSA-AES256-GCM-SHA384",
|
||||
"ECDHE-RSA-AES256-GCM-SHA384",
|
||||
];
|
||||
|
||||
let ciphers = ctx.ciphers().unwrap();
|
||||
assert_eq!(ciphers.len(), WPA3_192_CIPHERS.len());
|
||||
|
||||
for cipher in ciphers.into_iter().zip(WPA3_192_CIPHERS) {
|
||||
assert_eq!(cipher.0.name(), cipher.1);
|
||||
}
|
||||
|
||||
ctx.set_compliance_policy(CompliancePolicy::NONE)
|
||||
.expect_err("Testing expect err if set compliance policy to NONE");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drop_ex_data_in_context() {
|
||||
let index = SslContext::new_ex_index::<&'static str>().unwrap();
|
||||
let mut ctx = SslContext::builder(SslMethod::dtls()).unwrap();
|
||||
|
||||
assert_eq!(ctx.replace_ex_data(index, "comté"), None);
|
||||
assert_eq!(ctx.replace_ex_data(index, "camembert"), Some("comté"));
|
||||
assert_eq!(ctx.replace_ex_data(index, "raclette"), Some("camembert"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drop_ex_data_in_ssl() {
|
||||
let index = Ssl::new_ex_index::<&'static str>().unwrap();
|
||||
let ctx = SslContext::builder(SslMethod::dtls()).unwrap().build();
|
||||
let mut ssl = Ssl::new(&ctx).unwrap();
|
||||
|
||||
assert_eq!(ssl.replace_ex_data(index, "comté"), None);
|
||||
assert_eq!(ssl.replace_ex_data(index, "camembert"), Some("comté"));
|
||||
assert_eq!(ssl.replace_ex_data(index, "raclette"), Some("camembert"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_info_callback() {
|
||||
static CALLED_BACK: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
let server = Server::builder().build();
|
||||
let mut client = server.client();
|
||||
client.ctx().set_info_callback(move |_, _, _| {
|
||||
CALLED_BACK.store(true, Ordering::Relaxed);
|
||||
});
|
||||
|
||||
client.connect();
|
||||
assert!(CALLED_BACK.load(Ordering::Relaxed));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ssl_set_compliance() {
|
||||
let ctx = SslContext::builder(SslMethod::tls()).unwrap().build();
|
||||
let mut ssl = Ssl::new(&ctx).unwrap();
|
||||
ssl.set_compliance_policy(CompliancePolicy::FIPS_202205)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(ssl.max_proto_version().unwrap(), SslVersion::TLS1_3);
|
||||
assert_eq!(ssl.min_proto_version().unwrap(), SslVersion::TLS1_2);
|
||||
|
||||
const FIPS_CIPHERS: [&str; 4] = [
|
||||
"ECDHE-ECDSA-AES128-GCM-SHA256",
|
||||
"ECDHE-RSA-AES128-GCM-SHA256",
|
||||
"ECDHE-ECDSA-AES256-GCM-SHA384",
|
||||
"ECDHE-RSA-AES256-GCM-SHA384",
|
||||
];
|
||||
|
||||
let ciphers = ssl.ciphers();
|
||||
assert_eq!(ciphers.len(), FIPS_CIPHERS.len());
|
||||
|
||||
for cipher in ciphers.into_iter().zip(FIPS_CIPHERS) {
|
||||
assert_eq!(cipher.0.name(), cipher.1);
|
||||
}
|
||||
|
||||
let ctx = SslContext::builder(SslMethod::tls()).unwrap().build();
|
||||
let mut ssl = Ssl::new(&ctx).unwrap();
|
||||
ssl.set_compliance_policy(CompliancePolicy::WPA3_192_202304)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(ssl.max_proto_version().unwrap(), SslVersion::TLS1_3);
|
||||
assert_eq!(ssl.min_proto_version().unwrap(), SslVersion::TLS1_2);
|
||||
|
||||
const WPA3_192_CIPHERS: [&str; 2] = [
|
||||
"ECDHE-ECDSA-AES256-GCM-SHA384",
|
||||
"ECDHE-RSA-AES256-GCM-SHA384",
|
||||
];
|
||||
|
||||
let ciphers = ssl.ciphers();
|
||||
assert_eq!(ciphers.len(), WPA3_192_CIPHERS.len());
|
||||
|
||||
for cipher in ciphers.into_iter().zip(WPA3_192_CIPHERS) {
|
||||
assert_eq!(cipher.0.name(), cipher.1);
|
||||
}
|
||||
|
||||
ssl.set_compliance_policy(CompliancePolicy::NONE)
|
||||
.expect_err("Testing expect err if set compliance policy to NONE");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ex_data_drop() {
|
||||
use crate::ssl::SslContextBuilder;
|
||||
use std::sync::atomic::AtomicU32;
|
||||
use std::sync::atomic::Ordering::Relaxed;
|
||||
use std::sync::Arc;
|
||||
|
||||
struct TrackDrop(Arc<AtomicU32>);
|
||||
impl Drop for TrackDrop {
|
||||
fn drop(&mut self) {
|
||||
self.0.fetch_add(1, Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
let mut ctx = SslContextBuilder::new(SslMethod::tls()).unwrap();
|
||||
let index = SslContext::new_ex_index().unwrap();
|
||||
let d1 = Arc::new(AtomicU32::new(100));
|
||||
let d2 = Arc::new(AtomicU32::new(200));
|
||||
let d3 = Arc::new(AtomicU32::new(300));
|
||||
ctx.set_ex_data(index, TrackDrop(d1.clone()));
|
||||
assert_eq!(100, d1.load(Relaxed));
|
||||
assert_eq!(200, d2.load(Relaxed));
|
||||
ctx.replace_ex_data(index, TrackDrop(d2.clone()));
|
||||
assert_eq!(101, d1.load(Relaxed));
|
||||
assert_eq!(200, d2.load(Relaxed));
|
||||
ctx.replace_ex_data(index, TrackDrop(d3.clone()));
|
||||
assert_eq!(101, d1.load(Relaxed));
|
||||
assert_eq!(201, d2.load(Relaxed));
|
||||
assert_eq!(300, d3.load(Relaxed));
|
||||
drop(ctx);
|
||||
assert_eq!(101, d1.load(Relaxed));
|
||||
assert_eq!(201, d2.load(Relaxed));
|
||||
assert_eq!(301, d3.load(Relaxed));
|
||||
|
||||
let mut ctx2 = SslContextBuilder::new(SslMethod::tls()).unwrap();
|
||||
|
||||
ctx2.set_ex_data(index, TrackDrop(d1.clone()));
|
||||
ctx2.set_ex_data(index, TrackDrop(d2.clone()));
|
||||
drop(ctx2);
|
||||
assert_eq!(102, d1.load(Relaxed));
|
||||
assert_eq!(202, d2.load(Relaxed));
|
||||
ctx.set_session_cache_size(1234);
|
||||
let ctx = ctx.build();
|
||||
assert_eq!(ctx.session_cache_size(), 1234);
|
||||
}
|
||||
|
||||
@ -1,279 +0,0 @@
|
||||
use super::server::{Builder, Server};
|
||||
use super::KEY;
|
||||
use crate::hash::MessageDigest;
|
||||
use crate::pkey::PKey;
|
||||
use crate::rsa::Padding;
|
||||
use crate::sign::{RsaPssSaltlen, Signer};
|
||||
use crate::ssl::{
|
||||
ErrorCode, HandshakeError, PrivateKeyMethod, PrivateKeyMethodError, SslRef,
|
||||
SslSignatureAlgorithm,
|
||||
};
|
||||
use std::io::Write;
|
||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||
use std::sync::{Arc, OnceLock};
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub(super) struct Method {
|
||||
sign: Box<
|
||||
dyn Fn(
|
||||
&mut SslRef,
|
||||
&[u8],
|
||||
SslSignatureAlgorithm,
|
||||
&mut [u8],
|
||||
) -> Result<usize, PrivateKeyMethodError>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
>,
|
||||
decrypt: Box<
|
||||
dyn Fn(&mut SslRef, &[u8], &mut [u8]) -> Result<usize, PrivateKeyMethodError>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
>,
|
||||
complete: Box<
|
||||
dyn Fn(&mut SslRef, &mut [u8]) -> Result<usize, PrivateKeyMethodError>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
>,
|
||||
}
|
||||
|
||||
impl Method {
|
||||
pub(super) fn new() -> Self {
|
||||
Self {
|
||||
sign: Box::new(|_, _, _, _| unreachable!("called sign")),
|
||||
decrypt: Box::new(|_, _, _| unreachable!("called decrypt")),
|
||||
complete: Box::new(|_, _| unreachable!("called complete")),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn sign(
|
||||
mut self,
|
||||
sign: impl Fn(
|
||||
&mut SslRef,
|
||||
&[u8],
|
||||
SslSignatureAlgorithm,
|
||||
&mut [u8],
|
||||
) -> Result<usize, PrivateKeyMethodError>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
) -> Self {
|
||||
self.sign = Box::new(sign);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(super) fn decrypt(
|
||||
mut self,
|
||||
decrypt: impl Fn(&mut SslRef, &[u8], &mut [u8]) -> Result<usize, PrivateKeyMethodError>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
) -> Self {
|
||||
self.decrypt = Box::new(decrypt);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub(super) fn complete(
|
||||
mut self,
|
||||
complete: impl Fn(&mut SslRef, &mut [u8]) -> Result<usize, PrivateKeyMethodError>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
) -> Self {
|
||||
self.complete = Box::new(complete);
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl PrivateKeyMethod for Method {
|
||||
fn sign(
|
||||
&self,
|
||||
ssl: &mut SslRef,
|
||||
input: &[u8],
|
||||
signature_algorithm: SslSignatureAlgorithm,
|
||||
output: &mut [u8],
|
||||
) -> Result<usize, PrivateKeyMethodError> {
|
||||
(self.sign)(ssl, input, signature_algorithm, output)
|
||||
}
|
||||
|
||||
fn decrypt(
|
||||
&self,
|
||||
ssl: &mut SslRef,
|
||||
input: &[u8],
|
||||
output: &mut [u8],
|
||||
) -> Result<usize, PrivateKeyMethodError> {
|
||||
(self.decrypt)(ssl, input, output)
|
||||
}
|
||||
|
||||
fn complete(
|
||||
&self,
|
||||
ssl: &mut SslRef,
|
||||
output: &mut [u8],
|
||||
) -> Result<usize, PrivateKeyMethodError> {
|
||||
(self.complete)(ssl, output)
|
||||
}
|
||||
}
|
||||
|
||||
fn builder_with_private_key_method(method: Method) -> Builder {
|
||||
let mut builder = Server::builder();
|
||||
|
||||
builder.ctx().set_private_key_method(method);
|
||||
|
||||
builder
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sign_failure() {
|
||||
let called_sign = Arc::new(AtomicBool::new(false));
|
||||
let called_sign_clone = called_sign.clone();
|
||||
|
||||
let mut builder = builder_with_private_key_method(Method::new().sign(move |_, _, _, _| {
|
||||
called_sign_clone.store(true, Ordering::SeqCst);
|
||||
|
||||
Err(PrivateKeyMethodError::FAILURE)
|
||||
}));
|
||||
|
||||
builder.err_cb(|error| {
|
||||
let HandshakeError::Failure(mid_handshake) = error else {
|
||||
panic!("should be Failure");
|
||||
};
|
||||
|
||||
assert_eq!(mid_handshake.error().code(), ErrorCode::SSL);
|
||||
});
|
||||
|
||||
let server = builder.build();
|
||||
let client = server.client_with_root_ca();
|
||||
|
||||
client.connect_err();
|
||||
|
||||
assert!(called_sign.load(Ordering::SeqCst));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sign_retry_complete_failure() {
|
||||
let called_complete = Arc::new(AtomicUsize::new(0));
|
||||
let called_complete_clone = called_complete.clone();
|
||||
|
||||
let mut builder = builder_with_private_key_method(
|
||||
Method::new()
|
||||
.sign(|_, _, _, _| Err(PrivateKeyMethodError::RETRY))
|
||||
.complete(move |_, _| {
|
||||
let old = called_complete_clone.fetch_add(1, Ordering::SeqCst);
|
||||
|
||||
Err(if old == 0 {
|
||||
PrivateKeyMethodError::RETRY
|
||||
} else {
|
||||
PrivateKeyMethodError::FAILURE
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
builder.err_cb(|error| {
|
||||
let HandshakeError::WouldBlock(mid_handshake) = error else {
|
||||
panic!("should be WouldBlock");
|
||||
};
|
||||
|
||||
assert!(mid_handshake.error().would_block());
|
||||
assert_eq!(
|
||||
mid_handshake.error().code(),
|
||||
ErrorCode::WANT_PRIVATE_KEY_OPERATION
|
||||
);
|
||||
|
||||
let HandshakeError::WouldBlock(mid_handshake) = mid_handshake.handshake().unwrap_err()
|
||||
else {
|
||||
panic!("should be WouldBlock");
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
mid_handshake.error().code(),
|
||||
ErrorCode::WANT_PRIVATE_KEY_OPERATION
|
||||
);
|
||||
|
||||
let HandshakeError::Failure(mid_handshake) = mid_handshake.handshake().unwrap_err() else {
|
||||
panic!("should be Failure");
|
||||
};
|
||||
|
||||
assert_eq!(mid_handshake.error().code(), ErrorCode::SSL);
|
||||
});
|
||||
|
||||
let server = builder.build();
|
||||
let client = server.client_with_root_ca();
|
||||
|
||||
client.connect_err();
|
||||
|
||||
assert_eq!(called_complete.load(Ordering::SeqCst), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sign_ok() {
|
||||
let server = builder_with_private_key_method(Method::new().sign(
|
||||
|_, input, signature_algorithm, output| {
|
||||
assert_eq!(
|
||||
signature_algorithm,
|
||||
SslSignatureAlgorithm::RSA_PSS_RSAE_SHA256,
|
||||
);
|
||||
|
||||
Ok(sign_with_default_config(input, output))
|
||||
},
|
||||
))
|
||||
.build();
|
||||
|
||||
let client = server.client_with_root_ca();
|
||||
|
||||
client.connect();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sign_retry_complete_ok() {
|
||||
let input_cell = Arc::new(OnceLock::new());
|
||||
let input_cell_clone = input_cell.clone();
|
||||
|
||||
let mut builder = builder_with_private_key_method(
|
||||
Method::new()
|
||||
.sign(move |_, input, _, _| {
|
||||
input_cell.set(input.to_owned()).unwrap();
|
||||
|
||||
Err(PrivateKeyMethodError::RETRY)
|
||||
})
|
||||
.complete(move |_, output| {
|
||||
let input = input_cell_clone.get().unwrap();
|
||||
|
||||
Ok(sign_with_default_config(input, output))
|
||||
}),
|
||||
);
|
||||
|
||||
builder.err_cb(|error| {
|
||||
let HandshakeError::WouldBlock(mid_handshake) = error else {
|
||||
panic!("should be WouldBlock");
|
||||
};
|
||||
|
||||
let mut socket = mid_handshake.handshake().unwrap();
|
||||
|
||||
socket.write_all(&[0]).unwrap();
|
||||
});
|
||||
|
||||
let server = builder.build();
|
||||
let client = server.client_with_root_ca();
|
||||
|
||||
client.connect();
|
||||
}
|
||||
|
||||
fn sign_with_default_config(input: &[u8], output: &mut [u8]) -> usize {
|
||||
let pkey = PKey::private_key_from_pem(KEY).unwrap();
|
||||
let mut signer = Signer::new(MessageDigest::sha256(), &pkey).unwrap();
|
||||
|
||||
signer.set_rsa_padding(Padding::PKCS1_PSS).unwrap();
|
||||
signer
|
||||
.set_rsa_pss_saltlen(RsaPssSaltlen::DIGEST_LENGTH)
|
||||
.unwrap();
|
||||
|
||||
signer.update(input).unwrap();
|
||||
|
||||
signer.sign(output).unwrap()
|
||||
}
|
||||
@ -2,9 +2,7 @@ use std::io::{Read, Write};
|
||||
use std::net::{SocketAddr, TcpListener, TcpStream};
|
||||
use std::thread::{self, JoinHandle};
|
||||
|
||||
use crate::ssl::{
|
||||
HandshakeError, Ssl, SslContext, SslContextBuilder, SslFiletype, SslMethod, SslRef, SslStream,
|
||||
};
|
||||
use crate::ssl::{Ssl, SslContext, SslContextBuilder, SslFiletype, SslMethod, SslRef, SslStream};
|
||||
|
||||
pub struct Server {
|
||||
handle: Option<JoinHandle<()>>,
|
||||
@ -30,27 +28,7 @@ impl Server {
|
||||
ctx,
|
||||
ssl_cb: Box::new(|_| {}),
|
||||
io_cb: Box::new(|_| {}),
|
||||
err_cb: Box::new(|_| {}),
|
||||
should_error: false,
|
||||
expected_connections_count: 1,
|
||||
}
|
||||
}
|
||||
|
||||
/// Serves the leaf and the root together.
|
||||
pub fn builder_full_chain() -> Builder {
|
||||
let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
|
||||
// Uses certs.pem instead of cert.pem.
|
||||
ctx.set_certificate_chain_file("test/certs.pem").unwrap();
|
||||
ctx.set_private_key_file("test/key.pem", SslFiletype::PEM)
|
||||
.unwrap();
|
||||
|
||||
Builder {
|
||||
ctx,
|
||||
ssl_cb: Box::new(|_| {}),
|
||||
io_cb: Box::new(|_| {}),
|
||||
err_cb: Box::new(|_| {}),
|
||||
should_error: false,
|
||||
expected_connections_count: 1,
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,14 +39,6 @@ impl Server {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn client_with_root_ca(&self) -> ClientBuilder {
|
||||
let mut client = self.client();
|
||||
|
||||
client.ctx().set_ca_file("test/root-ca.pem").unwrap();
|
||||
|
||||
client
|
||||
}
|
||||
|
||||
pub fn connect_tcp(&self) -> TcpStream {
|
||||
TcpStream::connect(self.addr).unwrap()
|
||||
}
|
||||
@ -78,9 +48,7 @@ pub struct Builder {
|
||||
ctx: SslContextBuilder,
|
||||
ssl_cb: Box<dyn FnMut(&mut SslRef) + Send>,
|
||||
io_cb: Box<dyn FnMut(SslStream<TcpStream>) + Send>,
|
||||
err_cb: Box<dyn FnMut(HandshakeError<TcpStream>) + Send>,
|
||||
should_error: bool,
|
||||
expected_connections_count: usize,
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
@ -102,49 +70,29 @@ impl Builder {
|
||||
self.io_cb = Box::new(cb);
|
||||
}
|
||||
|
||||
pub fn err_cb(&mut self, cb: impl FnMut(HandshakeError<TcpStream>) + Send + 'static) {
|
||||
self.should_error();
|
||||
|
||||
self.err_cb = Box::new(cb);
|
||||
}
|
||||
|
||||
pub fn should_error(&mut self) {
|
||||
self.should_error = true;
|
||||
}
|
||||
|
||||
pub fn expected_connections_count(&mut self, count: usize) {
|
||||
self.expected_connections_count = count;
|
||||
}
|
||||
|
||||
pub fn build(self) -> Server {
|
||||
let ctx = self.ctx.build();
|
||||
let socket = TcpListener::bind("127.0.0.1:0").unwrap();
|
||||
let addr = socket.local_addr().unwrap();
|
||||
let mut ssl_cb = self.ssl_cb;
|
||||
let mut io_cb = self.io_cb;
|
||||
let mut err_cb = self.err_cb;
|
||||
let should_error = self.should_error;
|
||||
let mut count = self.expected_connections_count;
|
||||
|
||||
let handle = thread::spawn(move || {
|
||||
while count > 0 {
|
||||
let socket = socket.accept().unwrap().0;
|
||||
let mut ssl = Ssl::new(&ctx).unwrap();
|
||||
|
||||
ssl_cb(&mut ssl);
|
||||
|
||||
let r = ssl.accept(socket);
|
||||
|
||||
if should_error {
|
||||
err_cb(r.unwrap_err());
|
||||
} else {
|
||||
let mut socket = r.unwrap();
|
||||
|
||||
socket.write_all(&[0]).unwrap();
|
||||
io_cb(socket);
|
||||
}
|
||||
|
||||
count -= 1;
|
||||
let socket = socket.accept().unwrap().0;
|
||||
let mut ssl = Ssl::new(&ctx).unwrap();
|
||||
ssl_cb(&mut ssl);
|
||||
let r = ssl.accept(socket);
|
||||
if should_error {
|
||||
r.unwrap_err();
|
||||
} else {
|
||||
let mut socket = r.unwrap();
|
||||
socket.write_all(&[0]).unwrap();
|
||||
io_cb(socket);
|
||||
}
|
||||
});
|
||||
|
||||
@ -176,8 +124,8 @@ impl ClientBuilder {
|
||||
self.build().builder().connect()
|
||||
}
|
||||
|
||||
pub fn connect_err(self) -> HandshakeError<TcpStream> {
|
||||
self.build().builder().connect_err()
|
||||
pub fn connect_err(self) {
|
||||
self.build().builder().connect_err();
|
||||
}
|
||||
}
|
||||
|
||||
@ -212,9 +160,8 @@ impl ClientSslBuilder {
|
||||
s
|
||||
}
|
||||
|
||||
pub fn connect_err(self) -> HandshakeError<TcpStream> {
|
||||
pub fn connect_err(self) {
|
||||
let socket = TcpStream::connect(self.addr).unwrap();
|
||||
|
||||
self.ssl.setup_connect(socket).handshake().unwrap_err()
|
||||
self.ssl.connect(socket).unwrap_err();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,186 +0,0 @@
|
||||
use std::io::Write;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use crate::ssl::test::server::Server;
|
||||
use crate::ssl::{
|
||||
ErrorCode, GetSessionPendingError, HandshakeError, Ssl, SslContext, SslContextBuilder,
|
||||
SslMethod, SslOptions, SslSession, SslSessionCacheMode, SslVersion,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn idle_session() {
|
||||
let ctx = SslContext::builder(SslMethod::tls()).unwrap().build();
|
||||
let ssl = Ssl::new(&ctx).unwrap();
|
||||
assert!(ssl.session().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn active_session() {
|
||||
let server = Server::builder().build();
|
||||
|
||||
let s = server.client().connect();
|
||||
|
||||
let session = s.ssl().session().unwrap();
|
||||
let len = session.master_key_len();
|
||||
let mut buf = vec![0; len - 1];
|
||||
let copied = session.master_key(&mut buf);
|
||||
assert_eq!(copied, buf.len());
|
||||
let mut buf = vec![0; len + 1];
|
||||
let copied = session.master_key(&mut buf);
|
||||
assert_eq!(copied, len);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_get_session_callback() {
|
||||
static FOUND_SESSION: AtomicBool = AtomicBool::new(false);
|
||||
static SERVER_SESSION_DER: OnceLock<Vec<u8>> = OnceLock::new();
|
||||
static CLIENT_SESSION_DER: OnceLock<Vec<u8>> = OnceLock::new();
|
||||
|
||||
let mut server = Server::builder();
|
||||
|
||||
server.expected_connections_count(2);
|
||||
server
|
||||
.ctx()
|
||||
.set_max_proto_version(Some(SslVersion::TLS1_2))
|
||||
.unwrap();
|
||||
server.ctx().set_options(SslOptions::NO_TICKET);
|
||||
server
|
||||
.ctx()
|
||||
.set_session_cache_mode(SslSessionCacheMode::SERVER | SslSessionCacheMode::NO_INTERNAL);
|
||||
server.ctx().set_new_session_callback(|_, session| {
|
||||
SERVER_SESSION_DER.set(session.to_der().unwrap()).unwrap();
|
||||
});
|
||||
unsafe {
|
||||
server.ctx().set_get_session_callback(|_, id| {
|
||||
let Some(der) = SERVER_SESSION_DER.get() else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let session = SslSession::from_der(der).unwrap();
|
||||
|
||||
FOUND_SESSION.store(true, Ordering::SeqCst);
|
||||
|
||||
assert_eq!(id, session.id());
|
||||
|
||||
Ok(Some(session))
|
||||
});
|
||||
}
|
||||
server.ctx().set_session_id_context(b"foo").unwrap();
|
||||
|
||||
let server = server.build();
|
||||
|
||||
let mut client = server.client();
|
||||
|
||||
client
|
||||
.ctx()
|
||||
.set_session_cache_mode(SslSessionCacheMode::CLIENT);
|
||||
client.ctx().set_new_session_callback(|_, session| {
|
||||
CLIENT_SESSION_DER.set(session.to_der().unwrap()).unwrap();
|
||||
});
|
||||
|
||||
let client = client.build();
|
||||
|
||||
client.builder().connect();
|
||||
|
||||
assert!(CLIENT_SESSION_DER.get().is_some());
|
||||
assert!(SERVER_SESSION_DER.get().is_some());
|
||||
assert!(!FOUND_SESSION.load(Ordering::SeqCst));
|
||||
|
||||
let mut ssl_builder = client.builder();
|
||||
|
||||
unsafe {
|
||||
ssl_builder
|
||||
.ssl()
|
||||
.set_session(&SslSession::from_der(CLIENT_SESSION_DER.get().unwrap()).unwrap())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
ssl_builder.connect();
|
||||
|
||||
assert!(FOUND_SESSION.load(Ordering::SeqCst));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_get_session_callback_pending() {
|
||||
static CALLED_SERVER_CALLBACK: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
let mut server = Server::builder();
|
||||
|
||||
server
|
||||
.ctx()
|
||||
.set_max_proto_version(Some(SslVersion::TLS1_2))
|
||||
.unwrap();
|
||||
server.ctx().set_options(SslOptions::NO_TICKET);
|
||||
server
|
||||
.ctx()
|
||||
.set_session_cache_mode(SslSessionCacheMode::SERVER | SslSessionCacheMode::NO_INTERNAL);
|
||||
unsafe {
|
||||
server.ctx().set_get_session_callback(|_, _| {
|
||||
if !CALLED_SERVER_CALLBACK.swap(true, Ordering::SeqCst) {
|
||||
return Err(GetSessionPendingError);
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
});
|
||||
}
|
||||
server.ctx().set_session_id_context(b"foo").unwrap();
|
||||
server.err_cb(|error| {
|
||||
let HandshakeError::WouldBlock(mid_handshake) = error else {
|
||||
panic!("should be WouldBlock");
|
||||
};
|
||||
|
||||
assert!(mid_handshake.error().would_block());
|
||||
assert_eq!(mid_handshake.error().code(), ErrorCode::PENDING_SESSION);
|
||||
|
||||
let mut socket = mid_handshake.handshake().unwrap();
|
||||
|
||||
socket.write_all(&[0]).unwrap();
|
||||
});
|
||||
|
||||
let server = server.build();
|
||||
|
||||
let mut client = server.client();
|
||||
|
||||
client
|
||||
.ctx()
|
||||
.set_session_cache_mode(SslSessionCacheMode::CLIENT);
|
||||
|
||||
client.connect();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_session_callback_swapped_ctx() {
|
||||
static CALLED_BACK: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
let mut server = Server::builder();
|
||||
server.ctx().set_session_id_context(b"foo").unwrap();
|
||||
|
||||
let server = server.build();
|
||||
|
||||
let mut client = server.client();
|
||||
|
||||
client
|
||||
.ctx()
|
||||
.set_session_cache_mode(SslSessionCacheMode::CLIENT | SslSessionCacheMode::NO_INTERNAL);
|
||||
client
|
||||
.ctx()
|
||||
.set_new_session_callback(|_, _| CALLED_BACK.store(true, Ordering::SeqCst));
|
||||
|
||||
let mut client = client.build().builder();
|
||||
|
||||
let ctx = SslContextBuilder::new(SslMethod::tls()).unwrap().build();
|
||||
client.ssl().set_ssl_context(&ctx).unwrap();
|
||||
|
||||
client.connect();
|
||||
|
||||
assert!(CALLED_BACK.load(Ordering::SeqCst));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn session_cache_size() {
|
||||
let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
|
||||
ctx.set_session_cache_size(1234);
|
||||
let ctx = ctx.build();
|
||||
assert_eq!(ctx.session_cache_size(), 1234);
|
||||
}
|
||||
@ -1,242 +0,0 @@
|
||||
use super::server::Server;
|
||||
use crate::ssl::test::MessageDigest;
|
||||
use crate::ssl::HmacCtxRef;
|
||||
use crate::ssl::SslRef;
|
||||
use crate::ssl::SslSession;
|
||||
use crate::ssl::SslSessionCacheMode;
|
||||
use crate::ssl::TicketKeyCallbackResult;
|
||||
use crate::symm::Cipher;
|
||||
use crate::symm::CipherCtxRef;
|
||||
use std::sync::atomic::{AtomicU8, Ordering};
|
||||
use std::sync::OnceLock;
|
||||
|
||||
static SUCCESS_ENCRYPTION_CALLED_BACK: AtomicU8 = AtomicU8::new(0);
|
||||
static SUCCESS_DECRYPTION_CALLED_BACK: AtomicU8 = AtomicU8::new(0);
|
||||
static NOOP_ENCRYPTION_CALLED_BACK: AtomicU8 = AtomicU8::new(0);
|
||||
static NOOP_DECRYPTION_CALLED_BACK: AtomicU8 = AtomicU8::new(0);
|
||||
|
||||
#[test]
|
||||
fn resume_session() {
|
||||
static SESSION_TICKET: OnceLock<SslSession> = OnceLock::new();
|
||||
static NST_RECIEVED_COUNT: AtomicU8 = AtomicU8::new(0);
|
||||
|
||||
let mut server = Server::builder();
|
||||
server.expected_connections_count(2);
|
||||
let server = server.build();
|
||||
|
||||
let mut client = server.client();
|
||||
client
|
||||
.ctx()
|
||||
.set_session_cache_mode(SslSessionCacheMode::CLIENT);
|
||||
client.ctx().set_new_session_callback(|_, session| {
|
||||
NST_RECIEVED_COUNT.fetch_add(1, Ordering::SeqCst);
|
||||
// The server sends multiple session tickets but we only care to retrieve one.
|
||||
let _ = SESSION_TICKET.set(session);
|
||||
});
|
||||
let ssl_stream = client.connect();
|
||||
|
||||
assert!(!ssl_stream.ssl().session_reused());
|
||||
assert!(SESSION_TICKET.get().is_some());
|
||||
assert_eq!(NST_RECIEVED_COUNT.load(Ordering::SeqCst), 2);
|
||||
|
||||
// Retrieve the session ticket
|
||||
let session_ticket = SESSION_TICKET.get().unwrap();
|
||||
|
||||
// Attempt to resume the connection using the session ticket
|
||||
let client_2 = server.client();
|
||||
let mut ssl_builder = client_2.build().builder();
|
||||
unsafe { ssl_builder.ssl().set_session(session_ticket).unwrap() };
|
||||
let ssl_stream_2 = ssl_builder.connect();
|
||||
|
||||
assert!(ssl_stream_2.ssl().session_reused());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn custom_callback_success() {
|
||||
static SESSION_TICKET: OnceLock<SslSession> = OnceLock::new();
|
||||
static NST_RECIEVED_COUNT: AtomicU8 = AtomicU8::new(0);
|
||||
|
||||
let mut server = Server::builder();
|
||||
server.expected_connections_count(2);
|
||||
unsafe {
|
||||
server
|
||||
.ctx()
|
||||
.set_ticket_key_callback(test_success_tickey_key_callback);
|
||||
};
|
||||
let server = server.build();
|
||||
|
||||
let mut client = server.client();
|
||||
client
|
||||
.ctx()
|
||||
.set_session_cache_mode(SslSessionCacheMode::CLIENT);
|
||||
client.ctx().set_new_session_callback(|_, session| {
|
||||
NST_RECIEVED_COUNT.fetch_add(1, Ordering::SeqCst);
|
||||
// The server sends multiple session tickets but we only care to retrieve one.
|
||||
let _ = SESSION_TICKET.set(session);
|
||||
});
|
||||
let ssl_stream = client.connect();
|
||||
|
||||
assert!(!ssl_stream.ssl().session_reused());
|
||||
assert!(SESSION_TICKET.get().is_some());
|
||||
assert_eq!(SUCCESS_ENCRYPTION_CALLED_BACK.load(Ordering::SeqCst), 2);
|
||||
assert_eq!(SUCCESS_DECRYPTION_CALLED_BACK.load(Ordering::SeqCst), 0);
|
||||
assert_eq!(NST_RECIEVED_COUNT.load(Ordering::SeqCst), 2);
|
||||
|
||||
// Retrieve the session ticket
|
||||
let session_ticket = SESSION_TICKET.get().unwrap();
|
||||
|
||||
// Attempt to resume the connection using the session ticket
|
||||
let client_2 = server.client();
|
||||
let mut ssl_builder = client_2.build().builder();
|
||||
unsafe { ssl_builder.ssl().set_session(session_ticket).unwrap() };
|
||||
let ssl_stream_2 = ssl_builder.connect();
|
||||
|
||||
assert!(ssl_stream_2.ssl().session_reused());
|
||||
assert_eq!(SUCCESS_ENCRYPTION_CALLED_BACK.load(Ordering::SeqCst), 4);
|
||||
assert_eq!(SUCCESS_DECRYPTION_CALLED_BACK.load(Ordering::SeqCst), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn custom_callback_unrecognized_decryption_ticket() {
|
||||
static SESSION_TICKET: OnceLock<SslSession> = OnceLock::new();
|
||||
static NST_RECIEVED_COUNT: AtomicU8 = AtomicU8::new(0);
|
||||
|
||||
let mut server = Server::builder();
|
||||
server.expected_connections_count(2);
|
||||
unsafe {
|
||||
server
|
||||
.ctx()
|
||||
.set_ticket_key_callback(test_noop_tickey_key_callback);
|
||||
};
|
||||
let server = server.build();
|
||||
|
||||
let mut client = server.client();
|
||||
client
|
||||
.ctx()
|
||||
.set_session_cache_mode(SslSessionCacheMode::CLIENT);
|
||||
client.ctx().set_new_session_callback(|_, session| {
|
||||
NST_RECIEVED_COUNT.fetch_add(1, Ordering::SeqCst);
|
||||
// The server sends multiple session tickets but we only care to retrieve one.
|
||||
let _ = SESSION_TICKET.set(session);
|
||||
});
|
||||
let ssl_stream = client.connect();
|
||||
|
||||
assert!(!ssl_stream.ssl().session_reused());
|
||||
assert!(SESSION_TICKET.get().is_some());
|
||||
assert_eq!(NOOP_ENCRYPTION_CALLED_BACK.load(Ordering::SeqCst), 2);
|
||||
assert_eq!(NOOP_DECRYPTION_CALLED_BACK.load(Ordering::SeqCst), 0);
|
||||
assert_eq!(NST_RECIEVED_COUNT.load(Ordering::SeqCst), 2);
|
||||
|
||||
// Retrieve the session ticket
|
||||
let session_ticket = SESSION_TICKET.get().unwrap();
|
||||
|
||||
// Attempt to resume the connection using the session ticket
|
||||
let client_2 = server.client();
|
||||
let mut ssl_builder = client_2.build().builder();
|
||||
unsafe { ssl_builder.ssl().set_session(session_ticket).unwrap() };
|
||||
let ssl_stream_2 = ssl_builder.connect();
|
||||
|
||||
// Second connection was NOT resumed due to TicketKeyCallbackResult::Noop on decryption
|
||||
assert!(!ssl_stream_2.ssl().session_reused());
|
||||
assert_eq!(NOOP_ENCRYPTION_CALLED_BACK.load(Ordering::SeqCst), 4);
|
||||
assert_eq!(NOOP_DECRYPTION_CALLED_BACK.load(Ordering::SeqCst), 1);
|
||||
}
|
||||
|
||||
// Successfully return a session ticket in encryption mode but return a
|
||||
// TicketKeyCallbackResult::Noop in decryption mode.
|
||||
fn test_noop_tickey_key_callback(
|
||||
_ssl: &SslRef,
|
||||
key_name: &mut [u8; 16],
|
||||
iv: &mut [u8; ffi::EVP_MAX_IV_LENGTH as usize],
|
||||
evp_ctx: &mut CipherCtxRef,
|
||||
hmac_ctx: &mut HmacCtxRef,
|
||||
encrypt: bool,
|
||||
) -> TicketKeyCallbackResult {
|
||||
// These should only be used for testing purposes.
|
||||
const TEST_KEY_NAME: [u8; 16] = [5; 16];
|
||||
const TEST_CBC_IV: [u8; ffi::EVP_MAX_IV_LENGTH as usize] = [1; ffi::EVP_MAX_IV_LENGTH as usize];
|
||||
const TEST_AES_128_CBC_KEY: [u8; 16] = [2; 16];
|
||||
const TEST_HMAC_KEY: [u8; 32] = [3; 32];
|
||||
|
||||
let digest = MessageDigest::sha256();
|
||||
let cipher = Cipher::aes_128_cbc();
|
||||
|
||||
if encrypt {
|
||||
NOOP_ENCRYPTION_CALLED_BACK.fetch_add(1, Ordering::SeqCst);
|
||||
|
||||
// Ensure key_name and iv are initialized and set test values.
|
||||
assert_eq!(key_name, &[0; 16]);
|
||||
assert_eq!(iv, &[0; 16]);
|
||||
key_name.copy_from_slice(&TEST_KEY_NAME);
|
||||
iv.copy_from_slice(&TEST_CBC_IV);
|
||||
|
||||
// Set the encryption context.
|
||||
evp_ctx
|
||||
.init_encrypt(&cipher, &TEST_AES_128_CBC_KEY, &TEST_CBC_IV)
|
||||
.unwrap();
|
||||
|
||||
// Set the hmac context.
|
||||
hmac_ctx.init(&TEST_HMAC_KEY, &digest).unwrap();
|
||||
|
||||
TicketKeyCallbackResult::Success
|
||||
} else {
|
||||
NOOP_DECRYPTION_CALLED_BACK.fetch_add(1, Ordering::SeqCst);
|
||||
|
||||
// Check key_name matches.
|
||||
assert_eq!(key_name, &TEST_KEY_NAME);
|
||||
|
||||
TicketKeyCallbackResult::Noop
|
||||
}
|
||||
}
|
||||
|
||||
// Custom callback to encrypt and decrypt session tickets
|
||||
fn test_success_tickey_key_callback(
|
||||
_ssl: &SslRef,
|
||||
key_name: &mut [u8; 16],
|
||||
iv: &mut [u8; ffi::EVP_MAX_IV_LENGTH as usize],
|
||||
evp_ctx: &mut CipherCtxRef,
|
||||
hmac_ctx: &mut HmacCtxRef,
|
||||
encrypt: bool,
|
||||
) -> TicketKeyCallbackResult {
|
||||
// These should only be used for testing purposes.
|
||||
const TEST_KEY_NAME: [u8; 16] = [5; 16];
|
||||
const TEST_CBC_IV: [u8; ffi::EVP_MAX_IV_LENGTH as usize] = [1; ffi::EVP_MAX_IV_LENGTH as usize];
|
||||
const TEST_AES_128_CBC_KEY: [u8; 16] = [2; 16];
|
||||
const TEST_HMAC_KEY: [u8; 32] = [3; 32];
|
||||
|
||||
let digest = MessageDigest::sha256();
|
||||
let cipher = Cipher::aes_128_cbc();
|
||||
|
||||
if encrypt {
|
||||
SUCCESS_ENCRYPTION_CALLED_BACK.fetch_add(1, Ordering::SeqCst);
|
||||
|
||||
// Ensure key_name and iv are initialized and set test values.
|
||||
assert_eq!(key_name, &[0; 16]);
|
||||
assert_eq!(iv, &[0; 16]);
|
||||
key_name.copy_from_slice(&TEST_KEY_NAME);
|
||||
iv.copy_from_slice(&TEST_CBC_IV);
|
||||
|
||||
// Set the encryption context.
|
||||
evp_ctx
|
||||
.init_encrypt(&cipher, &TEST_AES_128_CBC_KEY, &TEST_CBC_IV)
|
||||
.unwrap();
|
||||
|
||||
// Set the hmac context.
|
||||
hmac_ctx.init(&TEST_HMAC_KEY, &digest).unwrap();
|
||||
} else {
|
||||
SUCCESS_DECRYPTION_CALLED_BACK.fetch_add(1, Ordering::SeqCst);
|
||||
|
||||
// Check key_name matches.
|
||||
assert_eq!(key_name, &TEST_KEY_NAME);
|
||||
|
||||
// Set the decryption context.
|
||||
evp_ctx
|
||||
.init_decrypt(&cipher, &TEST_AES_128_CBC_KEY, iv)
|
||||
.unwrap();
|
||||
|
||||
// Set the hmac context.
|
||||
hmac_ctx.init(&TEST_HMAC_KEY, &digest).unwrap();
|
||||
}
|
||||
|
||||
TicketKeyCallbackResult::Success
|
||||
}
|
||||
@ -1,150 +0,0 @@
|
||||
use super::server::Server;
|
||||
use crate::hash::MessageDigest;
|
||||
use crate::ssl::SslVerifyMode;
|
||||
use crate::x509::store::X509StoreBuilder;
|
||||
use crate::x509::X509;
|
||||
use hex;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
#[test]
|
||||
fn untrusted() {
|
||||
let mut server = Server::builder();
|
||||
server.should_error();
|
||||
let server = server.build();
|
||||
|
||||
let mut client = server.client();
|
||||
client.ctx().set_verify(SslVerifyMode::PEER);
|
||||
|
||||
client.connect_err();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trusted() {
|
||||
let server = Server::builder().build();
|
||||
let client = server.client_with_root_ca();
|
||||
|
||||
client.connect();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trusted_with_set_cert() {
|
||||
let server = Server::builder().build();
|
||||
|
||||
let mut store = X509StoreBuilder::new().unwrap();
|
||||
let x509 = X509::from_pem(super::ROOT_CERT).unwrap();
|
||||
store.add_cert(&x509).unwrap();
|
||||
|
||||
let mut client = server.client();
|
||||
client.ctx().set_verify(SslVerifyMode::PEER);
|
||||
client.ctx().set_verify_cert_store(store.build()).unwrap();
|
||||
|
||||
client.connect();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn untrusted_callback_override_ok() {
|
||||
let server = Server::builder().build();
|
||||
|
||||
let mut client = server.client();
|
||||
client
|
||||
.ctx()
|
||||
.set_verify_callback(SslVerifyMode::PEER, |_, x509| {
|
||||
assert!(x509.current_cert().is_some());
|
||||
assert!(x509.verify_result().is_err());
|
||||
|
||||
true
|
||||
});
|
||||
|
||||
client.connect();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn untrusted_callback_override_bad() {
|
||||
let mut server = Server::builder();
|
||||
server.should_error();
|
||||
let server = server.build();
|
||||
|
||||
let mut client = server.client();
|
||||
client
|
||||
.ctx()
|
||||
.set_verify_callback(SslVerifyMode::PEER, |_, _| false);
|
||||
|
||||
client.connect_err();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trusted_callback_override_ok() {
|
||||
let server = Server::builder().build();
|
||||
let mut client = server.client_with_root_ca();
|
||||
|
||||
client
|
||||
.ctx()
|
||||
.set_verify_callback(SslVerifyMode::PEER, |_, x509| {
|
||||
assert!(x509.current_cert().is_some());
|
||||
assert_eq!(x509.verify_result(), Ok(()));
|
||||
|
||||
true
|
||||
});
|
||||
|
||||
client.connect();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trusted_callback_override_bad() {
|
||||
let mut server = Server::builder();
|
||||
|
||||
server.should_error();
|
||||
|
||||
let server = server.build();
|
||||
let mut client = server.client_with_root_ca();
|
||||
|
||||
client
|
||||
.ctx()
|
||||
.set_verify_callback(SslVerifyMode::PEER, |_, _| false);
|
||||
|
||||
client.connect_err();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn callback() {
|
||||
static CALLED_BACK: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
let server = Server::builder().build();
|
||||
|
||||
let mut client = server.client();
|
||||
let expected = "59172d9313e84459bcff27f967e79e6e9217e584";
|
||||
client
|
||||
.ctx()
|
||||
.set_verify_callback(SslVerifyMode::PEER, move |_, x509| {
|
||||
CALLED_BACK.store(true, Ordering::SeqCst);
|
||||
let cert = x509.current_cert().unwrap();
|
||||
let digest = cert.digest(MessageDigest::sha1()).unwrap();
|
||||
assert_eq!(hex::encode(digest), expected);
|
||||
true
|
||||
});
|
||||
|
||||
client.connect();
|
||||
assert!(CALLED_BACK.load(Ordering::SeqCst));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ssl_callback() {
|
||||
static CALLED_BACK: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
let server = Server::builder().build();
|
||||
|
||||
let mut client = server.client().build().builder();
|
||||
let expected = "59172d9313e84459bcff27f967e79e6e9217e584";
|
||||
client
|
||||
.ssl()
|
||||
.set_verify_callback(SslVerifyMode::PEER, move |_, x509| {
|
||||
CALLED_BACK.store(true, Ordering::SeqCst);
|
||||
let cert = x509.current_cert().unwrap();
|
||||
let digest = cert.digest(MessageDigest::sha1()).unwrap();
|
||||
assert_eq!(hex::encode(digest), expected);
|
||||
true
|
||||
});
|
||||
|
||||
client.connect();
|
||||
assert!(CALLED_BACK.load(Ordering::SeqCst));
|
||||
}
|
||||
@ -48,7 +48,7 @@ impl<T: Stackable> Drop for Stack<T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
while self.pop().is_some() {}
|
||||
OPENSSL_sk_free(self.0.cast());
|
||||
OPENSSL_sk_free(self.0 as *mut _);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -58,7 +58,7 @@ impl<T: Stackable> Stack<T> {
|
||||
unsafe {
|
||||
ffi::init();
|
||||
let ptr = cvt_p(OPENSSL_sk_new_null())?;
|
||||
Ok(Stack(ptr.cast()))
|
||||
Ok(Stack(ptr as *mut _))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -79,13 +79,13 @@ impl<T: Stackable> iter::IntoIterator for Stack<T> {
|
||||
|
||||
impl<T: Stackable> AsRef<StackRef<T>> for Stack<T> {
|
||||
fn as_ref(&self) -> &StackRef<T> {
|
||||
self
|
||||
&*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Stackable> Borrow<StackRef<T>> for Stack<T> {
|
||||
fn borrow(&self) -> &StackRef<T> {
|
||||
self
|
||||
&*self
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,7 +132,7 @@ impl<T: Stackable> Drop for IntoIter<T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
for _ in &mut *self {}
|
||||
OPENSSL_sk_free(self.stack.cast());
|
||||
OPENSSL_sk_free(self.stack as *mut _);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -144,7 +144,7 @@ impl<T: Stackable> Iterator for IntoIter<T> {
|
||||
unsafe {
|
||||
self.idxs
|
||||
.next()
|
||||
.map(|i| T::from_ptr(OPENSSL_sk_value(self.stack.cast(), i).cast()))
|
||||
.map(|i| T::from_ptr(OPENSSL_sk_value(self.stack as *mut _, i) as *mut _))
|
||||
}
|
||||
}
|
||||
|
||||
@ -158,7 +158,7 @@ impl<T: Stackable> DoubleEndedIterator for IntoIter<T> {
|
||||
unsafe {
|
||||
self.idxs
|
||||
.next_back()
|
||||
.map(|i| T::from_ptr(OPENSSL_sk_value(self.stack.cast(), i).cast()))
|
||||
.map(|i| T::from_ptr(OPENSSL_sk_value(self.stack as *mut _, i) as *mut _))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -176,30 +176,27 @@ unsafe impl<T: Stackable> ForeignTypeRef for StackRef<T> {
|
||||
|
||||
impl<T: Stackable> StackRef<T> {
|
||||
fn as_stack(&self) -> *mut OPENSSL_STACK {
|
||||
self.as_ptr().cast()
|
||||
self.as_ptr() as *mut _
|
||||
}
|
||||
|
||||
/// Returns the number of items in the stack.
|
||||
#[must_use]
|
||||
pub fn len(&self) -> usize {
|
||||
unsafe { OPENSSL_sk_num(self.as_stack()) }
|
||||
unsafe { OPENSSL_sk_num(self.as_stack()) as usize }
|
||||
}
|
||||
|
||||
/// Determines if the stack is empty.
|
||||
#[must_use]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn iter(&self) -> Iter<'_, T> {
|
||||
pub fn iter(&self) -> Iter<T> {
|
||||
Iter {
|
||||
stack: self,
|
||||
idxs: 0..self.len(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter_mut(&mut self) -> IterMut<'_, T> {
|
||||
pub fn iter_mut(&mut self) -> IterMut<T> {
|
||||
IterMut {
|
||||
idxs: 0..self.len(),
|
||||
stack: self,
|
||||
@ -208,7 +205,6 @@ impl<T: Stackable> StackRef<T> {
|
||||
|
||||
/// Returns a reference to the element at the given index in the
|
||||
/// stack or `None` if the index is out of bounds
|
||||
#[must_use]
|
||||
pub fn get(&self, idx: usize) -> Option<&T::Ref> {
|
||||
unsafe {
|
||||
if idx >= self.len() {
|
||||
@ -234,7 +230,7 @@ impl<T: Stackable> StackRef<T> {
|
||||
/// Pushes a value onto the top of the stack.
|
||||
pub fn push(&mut self, data: T) -> Result<(), ErrorStack> {
|
||||
unsafe {
|
||||
cvt_0(OPENSSL_sk_push(self.as_stack(), data.as_ptr().cast()))?;
|
||||
cvt_0(OPENSSL_sk_push(self.as_stack(), data.as_ptr() as *mut _))?;
|
||||
mem::forget(data);
|
||||
Ok(())
|
||||
}
|
||||
@ -247,23 +243,13 @@ impl<T: Stackable> StackRef<T> {
|
||||
if ptr.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(T::from_ptr(ptr.cast()))
|
||||
Some(T::from_ptr(ptr as *mut _))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn _get(&self, idx: usize) -> *mut T::CType {
|
||||
OPENSSL_sk_value(self.as_stack(), idx).cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for StackRef<T>
|
||||
where
|
||||
T: Stackable,
|
||||
T::Ref: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_list().entries(self).finish()
|
||||
OPENSSL_sk_value(self.as_stack(), idx) as *mut _
|
||||
}
|
||||
}
|
||||
|
||||
@ -333,7 +319,7 @@ impl<'a, T: Stackable> Iterator for Iter<'a, T> {
|
||||
unsafe {
|
||||
self.idxs
|
||||
.next()
|
||||
.map(|i| T::Ref::from_ptr(OPENSSL_sk_value(self.stack.as_stack(), i).cast()))
|
||||
.map(|i| T::Ref::from_ptr(OPENSSL_sk_value(self.stack.as_stack(), i) as *mut _))
|
||||
}
|
||||
}
|
||||
|
||||
@ -347,12 +333,12 @@ impl<'a, T: Stackable> DoubleEndedIterator for Iter<'a, T> {
|
||||
unsafe {
|
||||
self.idxs
|
||||
.next_back()
|
||||
.map(|i| T::Ref::from_ptr(OPENSSL_sk_value(self.stack.as_stack(), i).cast()))
|
||||
.map(|i| T::Ref::from_ptr(OPENSSL_sk_value(self.stack.as_stack(), i) as *mut _))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Stackable> ExactSizeIterator for Iter<'_, T> {}
|
||||
impl<'a, T: Stackable> ExactSizeIterator for Iter<'a, T> {}
|
||||
|
||||
/// A mutable iterator over the stack's contents.
|
||||
pub struct IterMut<'a, T: Stackable + 'a> {
|
||||
@ -367,7 +353,7 @@ impl<'a, T: Stackable> Iterator for IterMut<'a, T> {
|
||||
unsafe {
|
||||
self.idxs
|
||||
.next()
|
||||
.map(|i| T::Ref::from_ptr_mut(OPENSSL_sk_value(self.stack.as_stack(), i).cast()))
|
||||
.map(|i| T::Ref::from_ptr_mut(OPENSSL_sk_value(self.stack.as_stack(), i) as *mut _))
|
||||
}
|
||||
}
|
||||
|
||||
@ -381,9 +367,9 @@ impl<'a, T: Stackable> DoubleEndedIterator for IterMut<'a, T> {
|
||||
unsafe {
|
||||
self.idxs
|
||||
.next_back()
|
||||
.map(|i| T::Ref::from_ptr_mut(OPENSSL_sk_value(self.stack.as_stack(), i).cast()))
|
||||
.map(|i| T::Ref::from_ptr_mut(OPENSSL_sk_value(self.stack.as_stack(), i) as *mut _))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Stackable> ExactSizeIterator for IterMut<'_, T> {}
|
||||
impl<'a, T: Stackable> ExactSizeIterator for IterMut<'a, T> {}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use crate::ffi;
|
||||
use foreign_types::ForeignTypeRef;
|
||||
use libc::c_char;
|
||||
use libc::{c_char, c_void};
|
||||
use std::convert::AsRef;
|
||||
use std::ffi::CStr;
|
||||
use std::fmt;
|
||||
@ -13,9 +13,6 @@ foreign_type_and_impl_send_sync! {
|
||||
type CType = c_char;
|
||||
fn drop = free;
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// MUST be UTF-8.
|
||||
pub struct OpensslString;
|
||||
}
|
||||
|
||||
@ -37,7 +34,7 @@ impl Stackable for OpensslString {
|
||||
|
||||
impl AsRef<str> for OpensslString {
|
||||
fn as_ref(&self) -> &str {
|
||||
self
|
||||
&**self
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,7 +57,7 @@ impl Deref for OpensslStringRef {
|
||||
|
||||
impl AsRef<str> for OpensslStringRef {
|
||||
fn as_ref(&self) -> &str {
|
||||
self
|
||||
&*self
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,5 +80,5 @@ impl fmt::Debug for OpensslStringRef {
|
||||
}
|
||||
|
||||
unsafe fn free(buf: *mut c_char) {
|
||||
crate::ffi::OPENSSL_free(buf.cast());
|
||||
crate::ffi::OPENSSL_free(buf as *mut c_void);
|
||||
}
|
||||
|
||||
@ -53,15 +53,13 @@
|
||||
//! ```
|
||||
|
||||
use crate::ffi;
|
||||
use foreign_types::ForeignTypeRef;
|
||||
use openssl_macros::corresponds;
|
||||
use libc::{c_int, c_uint};
|
||||
use std::cmp;
|
||||
use std::ffi::c_int;
|
||||
use std::ptr;
|
||||
|
||||
use crate::error::ErrorStack;
|
||||
use crate::nid::Nid;
|
||||
use crate::{cvt, cvt_p, try_int};
|
||||
use crate::{cvt, cvt_p};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum Mode {
|
||||
@ -69,83 +67,22 @@ pub enum Mode {
|
||||
Decrypt,
|
||||
}
|
||||
|
||||
foreign_type_and_impl_send_sync! {
|
||||
type CType = ffi::EVP_CIPHER_CTX;
|
||||
fn drop = ffi::EVP_CIPHER_CTX_free;
|
||||
|
||||
pub struct CipherCtx;
|
||||
}
|
||||
|
||||
impl CipherCtxRef {
|
||||
/// Configures CipherCtx for a fresh encryption operation using `cipher`.
|
||||
///
|
||||
#[corresponds(EVP_EncryptInit_ex)]
|
||||
pub fn init_encrypt(
|
||||
&mut self,
|
||||
cipher: &Cipher,
|
||||
key: &[u8],
|
||||
iv: &[u8; ffi::EVP_MAX_IV_LENGTH as usize],
|
||||
) -> Result<(), ErrorStack> {
|
||||
ffi::init();
|
||||
|
||||
if key.len() != cipher.key_len() {
|
||||
return Err(ErrorStack::internal_error_str("invalid key size"));
|
||||
}
|
||||
|
||||
unsafe {
|
||||
cvt(ffi::EVP_EncryptInit_ex(
|
||||
self.as_ptr(),
|
||||
cipher.as_ptr(),
|
||||
// ENGINE api is deprecated
|
||||
ptr::null_mut(),
|
||||
key.as_ptr(),
|
||||
iv.as_ptr(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Configures CipherCtx for a fresh decryption operation using `cipher`.
|
||||
///
|
||||
#[corresponds(EVP_DecryptInit_ex)]
|
||||
pub fn init_decrypt(
|
||||
&mut self,
|
||||
cipher: &Cipher,
|
||||
key: &[u8],
|
||||
iv: &[u8; ffi::EVP_MAX_IV_LENGTH as usize],
|
||||
) -> Result<(), ErrorStack> {
|
||||
ffi::init();
|
||||
|
||||
if key.len() != cipher.key_len() {
|
||||
return Err(ErrorStack::internal_error_str("invalid key size"));
|
||||
}
|
||||
|
||||
unsafe {
|
||||
cvt(ffi::EVP_DecryptInit_ex(
|
||||
self.as_ptr(),
|
||||
cipher.as_ptr(),
|
||||
// ENGINE api is deprecated
|
||||
ptr::null_mut(),
|
||||
key.as_ptr(),
|
||||
iv.as_ptr(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a particular cipher algorithm.
|
||||
///
|
||||
/// See OpenSSL doc at [`EVP_EncryptInit`] for more information on each algorithms.
|
||||
///
|
||||
/// [`EVP_EncryptInit`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_EncryptInit.html
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub struct Cipher(*const ffi::EVP_CIPHER);
|
||||
|
||||
impl Cipher {
|
||||
/// Looks up the cipher for a certain nid.
|
||||
#[corresponds(EVP_get_cipherbynid)]
|
||||
#[must_use]
|
||||
///
|
||||
/// This corresponds to [`EVP_get_cipherbynid`]
|
||||
///
|
||||
/// [`EVP_get_cipherbynid`]: https://www.openssl.org/docs/man1.0.2/crypto/EVP_get_cipherbyname.html
|
||||
pub fn from_nid(nid: Nid) -> Option<Cipher> {
|
||||
let ptr = unsafe { ffi::EVP_get_cipherbynid(nid.as_raw()) };
|
||||
let ptr = unsafe { ffi::EVP_get_cipherbyname(ffi::OBJ_nid2sn(nid.as_raw())) };
|
||||
if ptr.is_null() {
|
||||
None
|
||||
} else {
|
||||
@ -153,102 +90,82 @@ impl Cipher {
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn aes_128_ecb() -> Cipher {
|
||||
unsafe { Cipher(ffi::EVP_aes_128_ecb()) }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn aes_128_cbc() -> Cipher {
|
||||
unsafe { Cipher(ffi::EVP_aes_128_cbc()) }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn aes_128_ctr() -> Cipher {
|
||||
unsafe { Cipher(ffi::EVP_aes_128_ctr()) }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn aes_128_gcm() -> Cipher {
|
||||
unsafe { Cipher(ffi::EVP_aes_128_gcm()) }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn aes_128_ofb() -> Cipher {
|
||||
unsafe { Cipher(ffi::EVP_aes_128_ofb()) }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn aes_192_ecb() -> Cipher {
|
||||
unsafe { Cipher(ffi::EVP_aes_192_ecb()) }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn aes_192_cbc() -> Cipher {
|
||||
unsafe { Cipher(ffi::EVP_aes_192_cbc()) }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn aes_192_ctr() -> Cipher {
|
||||
unsafe { Cipher(ffi::EVP_aes_192_ctr()) }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn aes_192_gcm() -> Cipher {
|
||||
unsafe { Cipher(ffi::EVP_aes_192_gcm()) }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn aes_192_ofb() -> Cipher {
|
||||
unsafe { Cipher(ffi::EVP_aes_192_ofb()) }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn aes_256_ecb() -> Cipher {
|
||||
unsafe { Cipher(ffi::EVP_aes_256_ecb()) }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn aes_256_cbc() -> Cipher {
|
||||
unsafe { Cipher(ffi::EVP_aes_256_cbc()) }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn aes_256_ctr() -> Cipher {
|
||||
unsafe { Cipher(ffi::EVP_aes_256_ctr()) }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn aes_256_gcm() -> Cipher {
|
||||
unsafe { Cipher(ffi::EVP_aes_256_gcm()) }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn aes_256_ofb() -> Cipher {
|
||||
unsafe { Cipher(ffi::EVP_aes_256_ofb()) }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn des_cbc() -> Cipher {
|
||||
unsafe { Cipher(ffi::EVP_des_cbc()) }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn des_ecb() -> Cipher {
|
||||
unsafe { Cipher(ffi::EVP_des_ecb()) }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn des_ede3() -> Cipher {
|
||||
unsafe { Cipher(ffi::EVP_des_ede3()) }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn des_ede3_cbc() -> Cipher {
|
||||
unsafe { Cipher(ffi::EVP_des_ede3_cbc()) }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn rc4() -> Cipher {
|
||||
unsafe { Cipher(ffi::EVP_rc4()) }
|
||||
}
|
||||
@ -258,20 +175,17 @@ impl Cipher {
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure the pointer is valid for the `'static` lifetime.
|
||||
#[must_use]
|
||||
pub unsafe fn from_ptr(ptr: *const ffi::EVP_CIPHER) -> Cipher {
|
||||
Cipher(ptr)
|
||||
}
|
||||
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
#[must_use]
|
||||
pub fn as_ptr(&self) -> *const ffi::EVP_CIPHER {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Returns the length of keys used with this cipher.
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
#[must_use]
|
||||
pub fn key_len(&self) -> usize {
|
||||
unsafe { EVP_CIPHER_key_length(self.0) as usize }
|
||||
}
|
||||
@ -279,7 +193,6 @@ impl Cipher {
|
||||
/// Returns the length of the IV used with this cipher, or `None` if the
|
||||
/// cipher does not use an IV.
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
#[must_use]
|
||||
pub fn iv_len(&self) -> Option<usize> {
|
||||
unsafe {
|
||||
let len = EVP_CIPHER_iv_length(self.0) as usize;
|
||||
@ -297,18 +210,9 @@ impl Cipher {
|
||||
///
|
||||
/// Stream ciphers such as RC4 have a block size of 1.
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
#[must_use]
|
||||
pub fn block_size(&self) -> usize {
|
||||
unsafe { EVP_CIPHER_block_size(self.0) as usize }
|
||||
}
|
||||
|
||||
/// Returns the cipher's NID.
|
||||
#[corresponds(EVP_CIPHER_nid)]
|
||||
pub fn nid(&self) -> Nid {
|
||||
ffi::init();
|
||||
let nid = unsafe { ffi::EVP_CIPHER_nid(self.as_ptr()) };
|
||||
Nid::from_raw(nid)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Sync for Cipher {}
|
||||
@ -419,31 +323,34 @@ impl Crypter {
|
||||
mode,
|
||||
))?;
|
||||
|
||||
assert!(key.len() <= c_int::max_value() as usize);
|
||||
cvt(ffi::EVP_CIPHER_CTX_set_key_length(
|
||||
crypter.ctx,
|
||||
try_int(key.len())?,
|
||||
key.len() as c_uint,
|
||||
))?;
|
||||
|
||||
let key = key.as_ptr() as *mut _;
|
||||
let iv = match (iv, t.iv_len()) {
|
||||
(Some(iv), Some(len)) => {
|
||||
if iv.len() != len {
|
||||
assert!(iv.len() <= c_int::max_value() as usize);
|
||||
cvt(ffi::EVP_CIPHER_CTX_ctrl(
|
||||
crypter.ctx,
|
||||
ffi::EVP_CTRL_GCM_SET_IVLEN,
|
||||
try_int(iv.len())?,
|
||||
iv.len() as c_int,
|
||||
ptr::null_mut(),
|
||||
))?;
|
||||
}
|
||||
iv.as_ptr().cast_mut()
|
||||
iv.as_ptr() as *mut _
|
||||
}
|
||||
(Some(_) | None, None) => ptr::null_mut(),
|
||||
(Some(_), None) | (None, None) => ptr::null_mut(),
|
||||
(None, Some(_)) => panic!("an IV is required for this cipher"),
|
||||
};
|
||||
cvt(ffi::EVP_CipherInit_ex(
|
||||
crypter.ctx,
|
||||
ptr::null(),
|
||||
ptr::null_mut(),
|
||||
key.as_ptr().cast_mut(),
|
||||
key,
|
||||
iv,
|
||||
mode,
|
||||
))?;
|
||||
@ -458,7 +365,7 @@ impl Crypter {
|
||||
/// be a multiple of the cipher's block size.
|
||||
pub fn pad(&mut self, padding: bool) {
|
||||
unsafe {
|
||||
ffi::EVP_CIPHER_CTX_set_padding(self.ctx, c_int::from(padding));
|
||||
ffi::EVP_CIPHER_CTX_set_padding(self.ctx, padding as c_int);
|
||||
}
|
||||
}
|
||||
|
||||
@ -467,13 +374,15 @@ impl Crypter {
|
||||
/// When decrypting cipher text using an AEAD cipher, this must be called before `finalize`.
|
||||
pub fn set_tag(&mut self, tag: &[u8]) -> Result<(), ErrorStack> {
|
||||
unsafe {
|
||||
assert!(tag.len() <= c_int::max_value() as usize);
|
||||
// NB: this constant is actually more general than just GCM.
|
||||
cvt(ffi::EVP_CIPHER_CTX_ctrl(
|
||||
self.ctx,
|
||||
ffi::EVP_CTRL_GCM_SET_TAG,
|
||||
try_int(tag.len())?,
|
||||
tag.as_ptr().cast_mut().cast(),
|
||||
tag.len() as c_int,
|
||||
tag.as_ptr() as *mut _,
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
@ -483,13 +392,15 @@ impl Crypter {
|
||||
/// to use a value different than the default 12 bytes.
|
||||
pub fn set_tag_len(&mut self, tag_len: usize) -> Result<(), ErrorStack> {
|
||||
unsafe {
|
||||
assert!(tag_len <= c_int::max_value() as usize);
|
||||
// NB: this constant is actually more general than just GCM.
|
||||
cvt(ffi::EVP_CIPHER_CTX_ctrl(
|
||||
self.ctx,
|
||||
ffi::EVP_CTRL_GCM_SET_TAG,
|
||||
try_int(tag_len)?,
|
||||
tag_len as c_int,
|
||||
ptr::null_mut(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
@ -499,14 +410,16 @@ impl Crypter {
|
||||
/// CCM mode.
|
||||
pub fn set_data_len(&mut self, data_len: usize) -> Result<(), ErrorStack> {
|
||||
unsafe {
|
||||
assert!(data_len <= c_int::max_value() as usize);
|
||||
let mut len = 0;
|
||||
cvt(ffi::EVP_CipherUpdate(
|
||||
self.ctx,
|
||||
ptr::null_mut(),
|
||||
&mut len,
|
||||
ptr::null_mut(),
|
||||
try_int(data_len)?,
|
||||
data_len as c_int,
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
@ -517,14 +430,16 @@ impl Crypter {
|
||||
/// `update`.
|
||||
pub fn aad_update(&mut self, input: &[u8]) -> Result<(), ErrorStack> {
|
||||
unsafe {
|
||||
assert!(input.len() <= c_int::max_value() as usize);
|
||||
let mut len = 0;
|
||||
cvt(ffi::EVP_CipherUpdate(
|
||||
self.ctx,
|
||||
ptr::null_mut(),
|
||||
&mut len,
|
||||
input.as_ptr(),
|
||||
try_int(input.len())?,
|
||||
input.len() as c_int,
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
@ -540,6 +455,8 @@ impl Crypter {
|
||||
///
|
||||
/// Panics for block ciphers if `output.len() < input.len() + block_size`,
|
||||
/// where `block_size` is the block size of the cipher (see `Cipher::block_size`).
|
||||
///
|
||||
/// Panics if `output.len() > c_int::max_value()`.
|
||||
pub fn update(&mut self, input: &[u8], output: &mut [u8]) -> Result<usize, ErrorStack> {
|
||||
unsafe {
|
||||
let block_size = if self.block_size > 1 {
|
||||
@ -548,14 +465,16 @@ impl Crypter {
|
||||
0
|
||||
};
|
||||
assert!(output.len() >= input.len() + block_size);
|
||||
let mut outl = try_int(output.len())?;
|
||||
assert!(output.len() <= c_int::max_value() as usize);
|
||||
let mut outl = output.len() as c_int;
|
||||
let inl = input.len() as c_int;
|
||||
|
||||
cvt(ffi::EVP_CipherUpdate(
|
||||
self.ctx,
|
||||
output.as_mut_ptr(),
|
||||
&mut outl,
|
||||
input.as_ptr(),
|
||||
try_int(input.len())?,
|
||||
inl,
|
||||
))?;
|
||||
|
||||
Ok(outl as usize)
|
||||
@ -578,7 +497,7 @@ impl Crypter {
|
||||
if self.block_size > 1 {
|
||||
assert!(output.len() >= self.block_size);
|
||||
}
|
||||
let mut outl = cmp::min(output.len(), c_int::MAX as usize) as c_int;
|
||||
let mut outl = cmp::min(output.len(), c_int::max_value() as usize) as c_int;
|
||||
|
||||
cvt(ffi::EVP_CipherFinal_ex(
|
||||
self.ctx,
|
||||
@ -600,12 +519,14 @@ impl Crypter {
|
||||
/// bytes, for example.
|
||||
pub fn get_tag(&self, tag: &mut [u8]) -> Result<(), ErrorStack> {
|
||||
unsafe {
|
||||
assert!(tag.len() <= c_int::max_value() as usize);
|
||||
cvt(ffi::EVP_CIPHER_CTX_ctrl(
|
||||
self.ctx,
|
||||
ffi::EVP_CTRL_GCM_GET_TAG,
|
||||
try_int(tag.len())?,
|
||||
tag.as_mut_ptr().cast(),
|
||||
tag.len() as c_int,
|
||||
tag.as_mut_ptr() as *mut _,
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1040,129 +961,4 @@ mod tests {
|
||||
.unwrap();
|
||||
assert_eq!(pt, hex::encode(out));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nid_roundtrip() {
|
||||
for cipher in [
|
||||
Cipher::aes_128_gcm(),
|
||||
Cipher::aes_192_gcm(),
|
||||
Cipher::aes_256_gcm(),
|
||||
Cipher::aes_128_ecb(),
|
||||
Cipher::aes_128_cbc(),
|
||||
Cipher::aes_128_ctr(),
|
||||
Cipher::aes_128_ofb(),
|
||||
Cipher::aes_192_ecb(),
|
||||
Cipher::aes_192_cbc(),
|
||||
Cipher::aes_192_ctr(),
|
||||
Cipher::aes_192_ofb(),
|
||||
Cipher::aes_256_ecb(),
|
||||
Cipher::aes_256_cbc(),
|
||||
Cipher::aes_256_ctr(),
|
||||
Cipher::aes_256_ofb(),
|
||||
Cipher::des_ecb(),
|
||||
Cipher::des_ede3_cbc(),
|
||||
Cipher::des_cbc(),
|
||||
Cipher::rc4(),
|
||||
] {
|
||||
let name = cipher.nid().short_name().unwrap_or("unknown");
|
||||
assert_eq!(Cipher::from_nid(cipher.nid()), Some(cipher), "{}", name);
|
||||
}
|
||||
|
||||
assert_eq!(Cipher::from_nid(Cipher::des_ede3().nid()), None);
|
||||
}
|
||||
|
||||
// Make sure the NIDs don't actually change upstream.
|
||||
#[test]
|
||||
fn test_nid_regression() {
|
||||
struct TestCase {
|
||||
cipher: Cipher,
|
||||
nid: c_int,
|
||||
}
|
||||
|
||||
for t in [
|
||||
TestCase {
|
||||
cipher: Cipher::aes_128_ecb(),
|
||||
nid: 418,
|
||||
},
|
||||
TestCase {
|
||||
cipher: Cipher::aes_128_cbc(),
|
||||
nid: 419,
|
||||
},
|
||||
TestCase {
|
||||
cipher: Cipher::aes_128_ctr(),
|
||||
nid: 904,
|
||||
},
|
||||
TestCase {
|
||||
cipher: Cipher::aes_128_gcm(),
|
||||
nid: 895,
|
||||
},
|
||||
TestCase {
|
||||
cipher: Cipher::aes_128_ofb(),
|
||||
nid: 420,
|
||||
},
|
||||
TestCase {
|
||||
cipher: Cipher::aes_192_ecb(),
|
||||
nid: 422,
|
||||
},
|
||||
TestCase {
|
||||
cipher: Cipher::aes_192_cbc(),
|
||||
nid: 423,
|
||||
},
|
||||
TestCase {
|
||||
cipher: Cipher::aes_192_ctr(),
|
||||
nid: 905,
|
||||
},
|
||||
TestCase {
|
||||
cipher: Cipher::aes_192_gcm(),
|
||||
nid: 898,
|
||||
},
|
||||
TestCase {
|
||||
cipher: Cipher::aes_192_ofb(),
|
||||
nid: 424,
|
||||
},
|
||||
TestCase {
|
||||
cipher: Cipher::aes_256_ecb(),
|
||||
nid: 426,
|
||||
},
|
||||
TestCase {
|
||||
cipher: Cipher::aes_256_cbc(),
|
||||
nid: 427,
|
||||
},
|
||||
TestCase {
|
||||
cipher: Cipher::aes_256_ctr(),
|
||||
nid: 906,
|
||||
},
|
||||
TestCase {
|
||||
cipher: Cipher::aes_256_gcm(),
|
||||
nid: 901,
|
||||
},
|
||||
TestCase {
|
||||
cipher: Cipher::aes_256_ofb(),
|
||||
nid: 428,
|
||||
},
|
||||
TestCase {
|
||||
cipher: Cipher::des_ecb(),
|
||||
nid: 29,
|
||||
},
|
||||
TestCase {
|
||||
cipher: Cipher::des_ede3_cbc(),
|
||||
nid: 44,
|
||||
},
|
||||
TestCase {
|
||||
cipher: Cipher::des_cbc(),
|
||||
nid: 31,
|
||||
},
|
||||
TestCase {
|
||||
cipher: Cipher::rc4(),
|
||||
nid: 5,
|
||||
},
|
||||
TestCase {
|
||||
cipher: Cipher::des_ede3(),
|
||||
nid: 33,
|
||||
},
|
||||
] {
|
||||
let name = t.cipher.nid().short_name().unwrap_or("unknown");
|
||||
assert_eq!(t.cipher.nid().as_raw(), t.nid, "{}", name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
use crate::error::ErrorStack;
|
||||
use foreign_types::{ForeignType, ForeignTypeRef};
|
||||
use libc::{c_char, c_int, c_void};
|
||||
use std::any::Any;
|
||||
use std::panic::{self, AssertUnwindSafe};
|
||||
use std::slice;
|
||||
|
||||
use crate::error::ErrorStack;
|
||||
|
||||
/// Wraps a user-supplied callback and a slot for panics thrown inside the callback (while FFI
|
||||
/// frames are on the stack).
|
||||
///
|
||||
@ -46,17 +46,17 @@ pub unsafe extern "C" fn invoke_passwd_cb<F>(
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> Result<usize, ErrorStack>,
|
||||
{
|
||||
let callback = &mut *cb_state.cast::<CallbackState<F>>();
|
||||
let callback = &mut *(cb_state as *mut CallbackState<F>);
|
||||
|
||||
let result = panic::catch_unwind(AssertUnwindSafe(|| {
|
||||
let pass_slice = slice::from_raw_parts_mut(buf.cast::<u8>(), size as usize);
|
||||
let pass_slice = slice::from_raw_parts_mut(buf as *mut u8, size as usize);
|
||||
callback.cb.take().unwrap()(pass_slice)
|
||||
}));
|
||||
|
||||
match result {
|
||||
Ok(Ok(len)) => len as c_int,
|
||||
Ok(Err(err)) => {
|
||||
err.put();
|
||||
Ok(Err(_)) => {
|
||||
// FIXME restore error stack
|
||||
0
|
||||
}
|
||||
Err(err) => {
|
||||
@ -65,30 +65,3 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub trait ForeignTypeExt: ForeignType {
|
||||
unsafe fn from_ptr_opt(ptr: *mut Self::CType) -> Option<Self> {
|
||||
if ptr.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(Self::from_ptr(ptr))
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<FT: ForeignType> ForeignTypeExt for FT {}
|
||||
|
||||
pub trait ForeignTypeRefExt: ForeignTypeRef {
|
||||
unsafe fn from_const_ptr<'a>(ptr: *const Self::CType) -> &'a Self {
|
||||
Self::from_ptr(ptr.cast_mut())
|
||||
}
|
||||
|
||||
unsafe fn from_const_ptr_opt<'a>(ptr: *const Self::CType) -> Option<&'a Self> {
|
||||
if ptr.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(Self::from_const_ptr(ptr.cast_mut()))
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<FT: ForeignTypeRef> ForeignTypeRefExt for FT {}
|
||||
|
||||
@ -42,13 +42,11 @@ use crate::ffi::{
|
||||
/// Version 0.9.5a had an interim interpretation that is like the current one, except the patch level got the highest bit set, to keep continuity. The number was therefore 0x0090581f
|
||||
///
|
||||
/// The return value of this function can be compared to the macro to make sure that the correct version of the library has been loaded, especially when using DLLs on Windows systems.
|
||||
#[must_use]
|
||||
pub fn number() -> i64 {
|
||||
unsafe { OpenSSL_version_num() as i64 }
|
||||
}
|
||||
|
||||
/// The text variant of the version number and the release date. For example, "OpenSSL 0.9.5a 1 Apr 2000".
|
||||
#[must_use]
|
||||
pub fn version() -> &'static str {
|
||||
unsafe {
|
||||
CStr::from_ptr(OpenSSL_version(OPENSSL_VERSION))
|
||||
@ -59,7 +57,6 @@ pub fn version() -> &'static str {
|
||||
|
||||
/// The compiler flags set for the compilation process in the form "compiler: ..." if available or
|
||||
/// "compiler: information not available" otherwise.
|
||||
#[must_use]
|
||||
pub fn c_flags() -> &'static str {
|
||||
unsafe {
|
||||
CStr::from_ptr(OpenSSL_version(OPENSSL_CFLAGS))
|
||||
@ -69,7 +66,6 @@ pub fn c_flags() -> &'static str {
|
||||
}
|
||||
|
||||
/// The date of the build process in the form "built on: ..." if available or "built on: date not available" otherwise.
|
||||
#[must_use]
|
||||
pub fn built_on() -> &'static str {
|
||||
unsafe {
|
||||
CStr::from_ptr(OpenSSL_version(OPENSSL_BUILT_ON))
|
||||
@ -79,7 +75,6 @@ pub fn built_on() -> &'static str {
|
||||
}
|
||||
|
||||
/// The "Configure" target of the library build in the form "platform: ..." if available or "platform: information not available" otherwise.
|
||||
#[must_use]
|
||||
pub fn platform() -> &'static str {
|
||||
unsafe {
|
||||
CStr::from_ptr(OpenSSL_version(OPENSSL_PLATFORM))
|
||||
@ -89,7 +84,6 @@ pub fn platform() -> &'static str {
|
||||
}
|
||||
|
||||
/// The "OPENSSLDIR" setting of the library build in the form "OPENSSLDIR: "..."" if available or "OPENSSLDIR: N/A" otherwise.
|
||||
#[must_use]
|
||||
pub fn dir() -> &'static str {
|
||||
unsafe {
|
||||
CStr::from_ptr(OpenSSL_version(OPENSSL_DIR))
|
||||
|
||||
@ -1,509 +0,0 @@
|
||||
//! Certificate revocation lists describe certificates that have been revoked
|
||||
//! by their issuer and should no longer be trusted.
|
||||
//!
|
||||
//! An `X509CRL` can be provided along with an issuing `X509` to verify that
|
||||
//! issued certificates have not been revoked.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```rust
|
||||
//! use boring::hash::MessageDigest;
|
||||
//! use boring::pkey::{PKey, Private};
|
||||
//! use boring::x509::crl::{X509CRLBuilder, X509Revoked};
|
||||
//! use boring::x509::extension::BasicConstraints;
|
||||
//! use boring::x509::verify::X509VerifyFlags;
|
||||
//! use boring::x509::X509Extension;
|
||||
//! use boring::x509::X509;
|
||||
//! use boring::x509::store::{X509Store, X509StoreBuilder};
|
||||
//! use boring::asn1::Asn1Time;
|
||||
//! use boring::bn::BigNum;
|
||||
//! use boring::error::ErrorStack;
|
||||
//!
|
||||
//! fn crl_checking_store(issuer: X509, pkey: PKey<Private>) -> Result<X509Store, ErrorStack> {
|
||||
//! let mut builder = X509CRLBuilder::new()?;
|
||||
//! builder.set_issuer_name(issuer.subject_name())?;
|
||||
//! builder.add_revoked(X509Revoked::from_parts(
|
||||
//! &*BigNum::from_u32(1)?.to_asn1_integer()?,
|
||||
//! &*Asn1Time::days_from_now(0)?
|
||||
//! )?)?;
|
||||
//! builder.set_last_update(&*Asn1Time::days_from_now(0)?)?;
|
||||
//! builder.set_next_update(&*Asn1Time::days_from_now(30)?)?;
|
||||
//! builder.sign(&pkey, MessageDigest::sha256())?;
|
||||
//!
|
||||
//! let mut store_builder = X509StoreBuilder::new()?;
|
||||
//! store_builder.add_cert(issuer)?;
|
||||
//! store_builder.add_crl(builder.build())?;
|
||||
//! store_builder
|
||||
//! .param_mut()
|
||||
//! .set_flags(X509VerifyFlags::CRL_CHECK | X509VerifyFlags::CRL_CHECK_ALL);
|
||||
//! Ok(store_builder.build())
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
use crate::asn1::{Asn1BitStringRef, Asn1IntegerRef, Asn1TimeRef};
|
||||
use crate::foreign_types::ForeignType;
|
||||
use crate::foreign_types::ForeignTypeRef;
|
||||
use crate::hash::{DigestBytes, MessageDigest};
|
||||
use crate::pkey::{HasPrivate, HasPublic, PKeyRef};
|
||||
use crate::stack::{StackRef, Stackable};
|
||||
use crate::x509::X509ExtensionRef;
|
||||
use crate::x509::{X509AlgorithmRef, X509Extension, X509NameRef};
|
||||
use crate::{cvt, cvt_n, cvt_p, ErrorStack};
|
||||
use std::convert::TryInto;
|
||||
use std::fmt::Formatter;
|
||||
use std::{fmt, mem, ptr};
|
||||
|
||||
foreign_type_and_impl_send_sync! {
|
||||
type CType = ffi::X509_REVOKED;
|
||||
fn drop = ffi::X509_REVOKED_free;
|
||||
|
||||
/// An `X509_REVOKED` containing information about a revoked certificate
|
||||
pub struct X509Revoked;
|
||||
}
|
||||
|
||||
impl Stackable for X509Revoked {
|
||||
type StackType = ffi::stack_st_X509_REVOKED;
|
||||
}
|
||||
|
||||
impl X509Revoked {
|
||||
/// Create an `X509Revoked`
|
||||
///
|
||||
/// This corresponds to [`X509_REVOKED_new`] followed by calls to
|
||||
/// [`X509_REVOKED_set_serialNumber`] and [`X509_REVOKED_set_revocationDate`]
|
||||
/// with the provided parameters
|
||||
///
|
||||
/// [`X509_REVOKED_new`]: https://www.openssl.org/docs/man1.1.1/man3/X509_REVOKED_new.html
|
||||
/// [`X509_REVOKED_set_serialNumber`]: https://www.openssl.org/docs/man1.1.1/man3/X509_REVOKED_set_serialNumber.html
|
||||
/// [`X509_REVOKED_set_revocationDate`]: https://www.openssl.org/docs/man1.1.1/man3/X509_REVOKED_set_revocationDate.html
|
||||
pub fn from_parts(
|
||||
serial_number: &Asn1IntegerRef,
|
||||
revocation_date: &Asn1TimeRef,
|
||||
) -> Result<X509Revoked, ErrorStack> {
|
||||
unsafe {
|
||||
ffi::init();
|
||||
let revoked = cvt_p(ffi::X509_REVOKED_new())?;
|
||||
cvt(ffi::X509_REVOKED_set_serialNumber(
|
||||
revoked,
|
||||
serial_number.as_ptr(),
|
||||
))?;
|
||||
cvt(ffi::X509_REVOKED_set_revocationDate(
|
||||
revoked,
|
||||
revocation_date.as_ptr(),
|
||||
))?;
|
||||
Ok(X509Revoked::from_ptr(revoked))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl X509RevokedRef {
|
||||
/// Returns the serial number of the revoked certificate
|
||||
///
|
||||
/// This corresponds to [`X509_REVOKED_get0_serialNumber`].
|
||||
///
|
||||
/// [`X509_REVOKED_get0_serialNumber`]: https://www.openssl.org/docs/man1.1.1/man3/X509_REVOKED_get0_serialNumber.html
|
||||
pub fn serial_number(&self) -> &Asn1IntegerRef {
|
||||
unsafe {
|
||||
let r = ffi::X509_REVOKED_get0_serialNumber(self.as_ptr());
|
||||
assert!(!r.is_null());
|
||||
Asn1IntegerRef::from_ptr(r as *mut _)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns certificate's revocation date
|
||||
///
|
||||
/// This corresponds to [`X509_REVOKED_get0_revocationDate`].
|
||||
///
|
||||
/// [`X509_REVOKED_get0_revocationDate`]: https://www.openssl.org/docs/man1.1.1/man3/X509_REVOKED_get0_revocationDate
|
||||
pub fn revocation_date(&self) -> &Asn1TimeRef {
|
||||
unsafe {
|
||||
let date = ffi::X509_REVOKED_get0_revocationDate(self.as_ptr());
|
||||
assert!(!date.is_null());
|
||||
Asn1TimeRef::from_ptr(date as *mut _)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for X509RevokedRef {
|
||||
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
|
||||
let sn = self.serial_number().to_bn().and_then(|bn| bn.to_hex_str());
|
||||
let sn = sn.as_ref().map(|x| &***x).unwrap_or("");
|
||||
|
||||
fmt.debug_struct("X509Revoked")
|
||||
.field("serial_number", &sn)
|
||||
.field("revocation_date", self.revocation_date())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
foreign_type_and_impl_send_sync! {
|
||||
type CType = ffi::X509_CRL;
|
||||
fn drop = ffi::X509_CRL_free;
|
||||
|
||||
/// An `X509CRL` certificate revocation list
|
||||
pub struct X509CRL;
|
||||
}
|
||||
|
||||
impl Stackable for X509CRL {
|
||||
type StackType = ffi::stack_st_X509_CRL;
|
||||
}
|
||||
|
||||
impl X509CRL {
|
||||
from_pem! {
|
||||
/// Deserializes a PEM-encoded X509CRL structure.
|
||||
///
|
||||
/// The input should have a header of `-----BEGIN X509 CRL-----`
|
||||
///
|
||||
/// This corresponds to [`PEM_read_bio_X509_CRL`].
|
||||
///
|
||||
/// [`PEM_read_bio_X509_CRL`]: https://www.openssl.org/docs/man1.1.1/man3/PEM_read_bio_X509_CRL
|
||||
from_pem,
|
||||
X509CRL,
|
||||
ffi::PEM_read_bio_X509_CRL
|
||||
}
|
||||
|
||||
from_der! {
|
||||
/// Deserializes a DER-encoded X509 structure.
|
||||
///
|
||||
/// This corresponds to [`d2i_X509_CRL`].
|
||||
///
|
||||
/// [`d2i_X509_CRL`]: https://www.openssl.org/docs/man1.1.1/man3/d2i_X509_CRL.html
|
||||
from_der,
|
||||
X509CRL,
|
||||
ffi::d2i_X509_CRL,
|
||||
::libc::c_long
|
||||
}
|
||||
}
|
||||
|
||||
impl X509CRLRef {
|
||||
/// Returns the CRL's last update time
|
||||
///
|
||||
/// This corresponds to [`X509_CRL_get0_lastUpdate`]
|
||||
///
|
||||
/// [`X509_CRL_get0_lastUpdate`]: https://www.openssl.org/docs/man1.1.1/man3/X509_CRL_get0_lastUpdate
|
||||
pub fn last_update(&self) -> Option<&Asn1TimeRef> {
|
||||
unsafe {
|
||||
let date = ffi::X509_CRL_get0_lastUpdate(self.as_ptr());
|
||||
if date.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(Asn1TimeRef::from_ptr(date as *mut _))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the CRL's next update time
|
||||
///
|
||||
/// This corresponds to [`X509_CRL_get0_nextUpdate`]
|
||||
///
|
||||
/// [`X509_CRL_get0_nextUpdate`]: https://www.openssl.org/docs/man1.1.1/man3/X509_CRL_get0_nextUpdate
|
||||
pub fn next_update(&self) -> Option<&Asn1TimeRef> {
|
||||
unsafe {
|
||||
let date = ffi::X509_CRL_get0_nextUpdate(self.as_ptr());
|
||||
if date.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(Asn1TimeRef::from_ptr(date as *mut _))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the CRL's issuer name
|
||||
///
|
||||
/// This corresponds to [`X509_CRL_get_issuer`]
|
||||
///
|
||||
/// [`X509_CRL_get_issuer`]: https://www.openssl.org/docs/man1.1.1/man3/X509_CRL_get_issuer
|
||||
pub fn issuer(&self) -> &X509NameRef {
|
||||
unsafe {
|
||||
let name = ffi::X509_CRL_get_issuer(self.as_ptr());
|
||||
|
||||
assert!(!name.is_null());
|
||||
X509NameRef::from_ptr(name)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the CRL's extensions
|
||||
///
|
||||
/// This corresponds to [`X509_CRL_get0_extensions`]
|
||||
///
|
||||
/// [`X509_CRL_get0_extensions`]: https://www.openssl.org/docs/man1.1.1/man3/X509_CRL_get0_extensions
|
||||
pub fn extensions(&self) -> Option<&StackRef<X509Extension>> {
|
||||
unsafe {
|
||||
let extensions = ffi::X509_CRL_get0_extensions(self.as_ptr());
|
||||
if extensions.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(StackRef::from_ptr(extensions as *mut _))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the revoked certificates in this CRL
|
||||
///
|
||||
/// This corresponds to [`X509_CRL_get_REVOKED`]
|
||||
///
|
||||
/// [`X509_CRL_get_REVOKED`]: https://www.openssl.org/docs/man1.1.1/man3/X509_CRL_get_REVOKED
|
||||
pub fn revoked(&self) -> Option<&StackRef<X509Revoked>> {
|
||||
unsafe {
|
||||
let revoked = ffi::X509_CRL_get_REVOKED(self.as_ptr());
|
||||
if revoked.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(StackRef::from_ptr(revoked))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the CRL's signature and signature algorithm
|
||||
///
|
||||
/// This corresponds to [`X509_CRL_get0_signature`]
|
||||
///
|
||||
/// [`X509_CRL_get0_signature`]: https://www.openssl.org/docs/man1.1.1/man3/X509_CRL_get0_signature
|
||||
pub fn signature(&self) -> (&Asn1BitStringRef, &X509AlgorithmRef) {
|
||||
unsafe {
|
||||
let mut signature = ptr::null();
|
||||
let mut algor = ptr::null();
|
||||
ffi::X509_CRL_get0_signature(self.as_ptr(), &mut signature, &mut algor);
|
||||
assert!(!algor.is_null());
|
||||
assert!(!signature.is_null());
|
||||
(
|
||||
Asn1BitStringRef::from_ptr(signature as *mut _),
|
||||
X509AlgorithmRef::from_ptr(algor as *mut _),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a digest of the DER representation of the CRL
|
||||
///
|
||||
/// This corresponds to [`X509_CRL_digest`]
|
||||
///
|
||||
/// [`X509_CRL_digest`]: https://www.openssl.org/docs/man1.1.1/man3/X509_CRL_digest
|
||||
pub fn digest(&self, hash_type: MessageDigest) -> Result<DigestBytes, ErrorStack> {
|
||||
unsafe {
|
||||
let mut digest = DigestBytes {
|
||||
buf: [0; ffi::EVP_MAX_MD_SIZE as usize],
|
||||
len: ffi::EVP_MAX_MD_SIZE as usize,
|
||||
};
|
||||
let mut len = ffi::EVP_MAX_MD_SIZE.try_into().unwrap();
|
||||
cvt(ffi::X509_CRL_digest(
|
||||
self.as_ptr(),
|
||||
hash_type.as_ptr(),
|
||||
digest.buf.as_mut_ptr() as *mut _,
|
||||
&mut len,
|
||||
))?;
|
||||
digest.len = len as usize;
|
||||
|
||||
Ok(digest)
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if the CRL is signed using the given public key.
|
||||
///
|
||||
/// Only the signature is checked: no other checks (such as certificate chain validity)
|
||||
/// are performed.
|
||||
///
|
||||
/// Returns `true` if verification succeeds.
|
||||
///
|
||||
/// This corresponds to [`X509_CRL_verify"].
|
||||
///
|
||||
/// [`X509_CRL_verify`]: https://www.openssl.org/docs/man1.1.1/man3/X509_CRL_verify
|
||||
pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
|
||||
where
|
||||
T: HasPublic,
|
||||
{
|
||||
unsafe { cvt_n(ffi::X509_CRL_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
|
||||
}
|
||||
|
||||
to_pem! {
|
||||
/// Serializes the CRL into a PEM-encoded X509 CRL structure.
|
||||
///
|
||||
/// The output will have a header of `-----BEGIN X509 CRL-----`
|
||||
///
|
||||
/// This corresponds to [`PEM_write_bio_X509_CRL`]
|
||||
///
|
||||
/// [`PEM_write_bio_X509_CRL`]: https://www.openssl.org/docs/man1.1.1/man3/PEM_write_bio_X509_CRL
|
||||
to_pem,
|
||||
ffi::PEM_write_bio_X509_CRL
|
||||
}
|
||||
|
||||
to_der! {
|
||||
/// Serializes the CRL into a DER-encoded X509 CRL structure
|
||||
///
|
||||
/// This corresponds to `i2d_X509_CRL`
|
||||
///
|
||||
/// [`i2d_X509_CRL`]: https://www.openssl.org/docs/man1.1.1/man3/i2d_X509_CRL
|
||||
to_der,
|
||||
ffi::i2d_X509_CRL
|
||||
}
|
||||
}
|
||||
impl ToOwned for X509CRLRef {
|
||||
type Owned = X509CRL;
|
||||
|
||||
fn to_owned(&self) -> X509CRL {
|
||||
unsafe {
|
||||
ffi::X509_CRL_up_ref(self.as_ptr());
|
||||
X509CRL::from_ptr(self.as_ptr())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for X509CRL {
|
||||
fn clone(&self) -> X509CRL {
|
||||
X509CRLRef::to_owned(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for X509CRLRef {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
let mut debug_struct = formatter.debug_struct("X509CRL");
|
||||
debug_struct.field("issuer", self.issuer());
|
||||
debug_struct.field("signature_algorithm", &self.signature().1.object());
|
||||
|
||||
if let Some(next_update) = self.next_update() {
|
||||
debug_struct.field("next_update", next_update);
|
||||
}
|
||||
if let Some(last_update) = self.last_update() {
|
||||
debug_struct.field("last_update", last_update);
|
||||
}
|
||||
if let Some(revoked) = self.revoked() {
|
||||
debug_struct.field("revoked", &revoked);
|
||||
}
|
||||
if let Some(extensions) = self.extensions() {
|
||||
debug_struct.field("extensions", &extensions);
|
||||
}
|
||||
debug_struct.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for X509CRL {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
let x: &X509CRLRef = self;
|
||||
x.fmt(formatter)
|
||||
}
|
||||
}
|
||||
|
||||
/// A builder used to construct an `X509CRL`
|
||||
pub struct X509CRLBuilder(X509CRL);
|
||||
|
||||
impl X509CRLBuilder {
|
||||
/// Creates a new builder.
|
||||
pub fn new() -> Result<X509CRLBuilder, ErrorStack> {
|
||||
unsafe {
|
||||
ffi::init();
|
||||
cvt_p(ffi::X509_CRL_new()).map(|p| X509CRLBuilder(X509CRL::from_ptr(p)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Append an `X509Extension` to the certificate revocation list
|
||||
///
|
||||
/// This corresponds to [`X509_CRL_add_ext`]
|
||||
///
|
||||
/// [`X509_CRL_add_ext`]: https://www.openssl.org/docs/man1.1.1/man3/X509_CRL_add_ext
|
||||
pub fn append_extension(&mut self, extension: &X509ExtensionRef) -> Result<(), ErrorStack> {
|
||||
unsafe {
|
||||
// -1 indicates append to end
|
||||
cvt(ffi::X509_CRL_add_ext(
|
||||
self.0.as_ptr(),
|
||||
extension.as_ptr(),
|
||||
-1,
|
||||
))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Signs the certificate revocation list with a private key.
|
||||
///
|
||||
/// This corresponds to [`X509_CRL_sign`]
|
||||
///
|
||||
/// [`X509_CRL_sign`]: https://www.openssl.org/docs/man1.1.1/man3/X509_CRL_sign
|
||||
pub fn sign<T>(&mut self, key: &PKeyRef<T>, hash: MessageDigest) -> Result<(), ErrorStack>
|
||||
where
|
||||
T: HasPrivate,
|
||||
{
|
||||
unsafe {
|
||||
cvt(ffi::X509_CRL_sign(
|
||||
self.0.as_ptr(),
|
||||
key.as_ptr(),
|
||||
hash.as_ptr(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a revoked certificate to the certificate revocation list
|
||||
///
|
||||
/// This corresponds to [`X509_CRL_add0_revoked`]
|
||||
///
|
||||
/// [`X509_CRL_add0_revoked`]: https://www.openssl.org/docs/man1.1.1/man3/X509_CRL_add0_revoked
|
||||
pub fn add_revoked(&mut self, revoked: X509Revoked) -> Result<(), ErrorStack> {
|
||||
unsafe {
|
||||
cvt(ffi::X509_CRL_add0_revoked(
|
||||
self.0.as_ptr(),
|
||||
revoked.as_ptr(),
|
||||
))?;
|
||||
mem::forget(revoked);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the issuer name of the certificate revocation list.
|
||||
///
|
||||
/// This corresponds to [`X509_CRL_set_issuer_name`]
|
||||
///
|
||||
/// [`X509_CRL_set_issuer_name`]: https://www.openssl.org/docs/man1.1.1/man3/X509_CRL_set_issuer_name
|
||||
pub fn set_issuer_name(&mut self, issuer_name: &X509NameRef) -> Result<(), ErrorStack> {
|
||||
unsafe {
|
||||
cvt(ffi::X509_CRL_set_issuer_name(
|
||||
self.0.as_ptr(),
|
||||
issuer_name.as_ptr(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the version of the certificate revocation list.
|
||||
///
|
||||
/// Note that the version is zero-indexed; that is, a certificate corresponding to version 3 of
|
||||
/// the X.509 standard should pass `2` to this method.
|
||||
///
|
||||
/// This corresponds to [`X509_CRL_set_version`]
|
||||
///
|
||||
/// [`X509_CRL_set_version`]: https://www.openssl.org/docs/man1.1.1/man3/X509_CRL_set_version
|
||||
pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> {
|
||||
unsafe { cvt(ffi::X509_CRL_set_version(self.0.as_ptr(), version.into())).map(|_| ()) }
|
||||
}
|
||||
|
||||
/// Sets the last update time on the certificate revocation list.
|
||||
///
|
||||
/// This corresponds to [`X509_CRL_set1_lastUpdate`]
|
||||
///
|
||||
/// [`X509_CRL_set1_lastUpdate`]: https://www.openssl.org/docs/man1.1.1/man3/X509_CRL_set1_lastUpdate
|
||||
pub fn set_last_update(&mut self, last_update: &Asn1TimeRef) -> Result<(), ErrorStack> {
|
||||
unsafe {
|
||||
cvt(ffi::X509_CRL_set1_lastUpdate(
|
||||
self.0.as_ptr(),
|
||||
last_update.as_ptr(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the next update time on the certificate revocation list.
|
||||
///
|
||||
/// This corresponds to [`X509_CRL_set1_nextUpdate`]
|
||||
///
|
||||
/// [`X509_CRL_set1_nextUpdate`]: https://www.openssl.org/docs/man1.1.1/man3/X509_CRL_set1_nextUpdate
|
||||
pub fn set_next_update(&mut self, next_update: &Asn1TimeRef) -> Result<(), ErrorStack> {
|
||||
unsafe {
|
||||
cvt(ffi::X509_CRL_set1_nextUpdate(
|
||||
self.0.as_ptr(),
|
||||
next_update.as_ptr(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Consumes the builder, returning the certificate revocation list
|
||||
pub fn build(self) -> X509CRL {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
@ -17,11 +17,9 @@
|
||||
//! ```
|
||||
use std::fmt::Write;
|
||||
|
||||
use crate::asn1::Asn1Object;
|
||||
use crate::error::ErrorStack;
|
||||
use crate::nid::Nid;
|
||||
use crate::x509::{GeneralName, Stack, X509Extension, X509v3Context};
|
||||
use foreign_types::ForeignType;
|
||||
use crate::x509::{X509Extension, X509v3Context};
|
||||
|
||||
/// An extension which indicates whether a certificate is a CA certificate.
|
||||
pub struct BasicConstraints {
|
||||
@ -38,7 +36,6 @@ impl Default for BasicConstraints {
|
||||
|
||||
impl BasicConstraints {
|
||||
/// Construct a new `BasicConstraints` extension.
|
||||
#[must_use]
|
||||
pub fn new() -> BasicConstraints {
|
||||
BasicConstraints {
|
||||
critical: false,
|
||||
@ -79,7 +76,7 @@ impl BasicConstraints {
|
||||
value.push_str("FALSE");
|
||||
}
|
||||
if let Some(pathlen) = self.pathlen {
|
||||
write!(value, ",pathlen:{pathlen}").unwrap();
|
||||
write!(value, ",pathlen:{}", pathlen).unwrap();
|
||||
}
|
||||
X509Extension::new_nid(None, None, Nid::BASIC_CONSTRAINTS, &value)
|
||||
}
|
||||
@ -107,7 +104,6 @@ impl Default for KeyUsage {
|
||||
|
||||
impl KeyUsage {
|
||||
/// Construct a new `KeyUsage` extension.
|
||||
#[must_use]
|
||||
pub fn new() -> KeyUsage {
|
||||
KeyUsage {
|
||||
critical: false,
|
||||
@ -225,7 +221,18 @@ impl KeyUsage {
|
||||
/// for which the certificate public key can be used for.
|
||||
pub struct ExtendedKeyUsage {
|
||||
critical: bool,
|
||||
items: Vec<String>,
|
||||
server_auth: bool,
|
||||
client_auth: bool,
|
||||
code_signing: bool,
|
||||
email_protection: bool,
|
||||
time_stamping: bool,
|
||||
ms_code_ind: bool,
|
||||
ms_code_com: bool,
|
||||
ms_ctl_sign: bool,
|
||||
ms_sgc: bool,
|
||||
ms_efs: bool,
|
||||
ns_sgc: bool,
|
||||
other: Vec<String>,
|
||||
}
|
||||
|
||||
impl Default for ExtendedKeyUsage {
|
||||
@ -236,11 +243,21 @@ impl Default for ExtendedKeyUsage {
|
||||
|
||||
impl ExtendedKeyUsage {
|
||||
/// Construct a new `ExtendedKeyUsage` extension.
|
||||
#[must_use]
|
||||
pub fn new() -> ExtendedKeyUsage {
|
||||
ExtendedKeyUsage {
|
||||
critical: false,
|
||||
items: vec![],
|
||||
server_auth: false,
|
||||
client_auth: false,
|
||||
code_signing: false,
|
||||
email_protection: false,
|
||||
time_stamping: false,
|
||||
ms_code_ind: false,
|
||||
ms_code_com: false,
|
||||
ms_ctl_sign: false,
|
||||
ms_sgc: false,
|
||||
ms_efs: false,
|
||||
ns_sgc: false,
|
||||
other: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
@ -252,69 +269,95 @@ impl ExtendedKeyUsage {
|
||||
|
||||
/// Sets the `serverAuth` flag to `true`.
|
||||
pub fn server_auth(&mut self) -> &mut ExtendedKeyUsage {
|
||||
self.other("serverAuth")
|
||||
self.server_auth = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the `clientAuth` flag to `true`.
|
||||
pub fn client_auth(&mut self) -> &mut ExtendedKeyUsage {
|
||||
self.other("clientAuth")
|
||||
self.client_auth = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the `codeSigning` flag to `true`.
|
||||
pub fn code_signing(&mut self) -> &mut ExtendedKeyUsage {
|
||||
self.other("codeSigning")
|
||||
self.code_signing = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the `timeStamping` flag to `true`.
|
||||
pub fn time_stamping(&mut self) -> &mut ExtendedKeyUsage {
|
||||
self.other("timeStamping")
|
||||
self.time_stamping = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the `msCodeInd` flag to `true`.
|
||||
pub fn ms_code_ind(&mut self) -> &mut ExtendedKeyUsage {
|
||||
self.other("msCodeInd")
|
||||
self.ms_code_ind = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the `msCodeCom` flag to `true`.
|
||||
pub fn ms_code_com(&mut self) -> &mut ExtendedKeyUsage {
|
||||
self.other("msCodeCom")
|
||||
self.ms_code_com = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the `msCTLSign` flag to `true`.
|
||||
pub fn ms_ctl_sign(&mut self) -> &mut ExtendedKeyUsage {
|
||||
self.other("msCTLSign")
|
||||
self.ms_ctl_sign = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the `msSGC` flag to `true`.
|
||||
pub fn ms_sgc(&mut self) -> &mut ExtendedKeyUsage {
|
||||
self.other("msSGC")
|
||||
self.ms_sgc = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the `msEFS` flag to `true`.
|
||||
pub fn ms_efs(&mut self) -> &mut ExtendedKeyUsage {
|
||||
self.other("msEFS")
|
||||
self.ms_efs = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the `nsSGC` flag to `true`.
|
||||
pub fn ns_sgc(&mut self) -> &mut ExtendedKeyUsage {
|
||||
self.other("nsSGC")
|
||||
self.ns_sgc = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets a flag not already defined.
|
||||
pub fn other(&mut self, other: &str) -> &mut ExtendedKeyUsage {
|
||||
self.items.push(other.to_string());
|
||||
self.other.push(other.to_owned());
|
||||
self
|
||||
}
|
||||
|
||||
/// Return the `ExtendedKeyUsage` extension as an `X509Extension`.
|
||||
pub fn build(&self) -> Result<X509Extension, ErrorStack> {
|
||||
let mut stack = Stack::new()?;
|
||||
for item in &self.items {
|
||||
stack.push(Asn1Object::from_str(item)?)?;
|
||||
}
|
||||
unsafe {
|
||||
X509Extension::new_internal(Nid::EXT_KEY_USAGE, self.critical, stack.as_ptr().cast())
|
||||
let mut value = String::new();
|
||||
let mut first = true;
|
||||
append(&mut value, &mut first, self.critical, "critical");
|
||||
append(&mut value, &mut first, self.server_auth, "serverAuth");
|
||||
append(&mut value, &mut first, self.client_auth, "clientAuth");
|
||||
append(&mut value, &mut first, self.code_signing, "codeSigning");
|
||||
append(
|
||||
&mut value,
|
||||
&mut first,
|
||||
self.email_protection,
|
||||
"emailProtection",
|
||||
);
|
||||
append(&mut value, &mut first, self.time_stamping, "timeStamping");
|
||||
append(&mut value, &mut first, self.ms_code_ind, "msCodeInd");
|
||||
append(&mut value, &mut first, self.ms_code_com, "msCodeCom");
|
||||
append(&mut value, &mut first, self.ms_ctl_sign, "msCTLSign");
|
||||
append(&mut value, &mut first, self.ms_sgc, "msSGC");
|
||||
append(&mut value, &mut first, self.ms_efs, "msEFS");
|
||||
append(&mut value, &mut first, self.ns_sgc, "nsSGC");
|
||||
for other in &self.other {
|
||||
append(&mut value, &mut first, true, other);
|
||||
}
|
||||
X509Extension::new_nid(None, None, Nid::EXT_KEY_USAGE, &value)
|
||||
}
|
||||
}
|
||||
|
||||
@ -332,7 +375,6 @@ impl Default for SubjectKeyIdentifier {
|
||||
|
||||
impl SubjectKeyIdentifier {
|
||||
/// Construct a new `SubjectKeyIdentifier` extension.
|
||||
#[must_use]
|
||||
pub fn new() -> SubjectKeyIdentifier {
|
||||
SubjectKeyIdentifier { critical: false }
|
||||
}
|
||||
@ -369,7 +411,6 @@ impl Default for AuthorityKeyIdentifier {
|
||||
|
||||
impl AuthorityKeyIdentifier {
|
||||
/// Construct a new `AuthorityKeyIdentifier` extension.
|
||||
#[must_use]
|
||||
pub fn new() -> AuthorityKeyIdentifier {
|
||||
AuthorityKeyIdentifier {
|
||||
critical: false,
|
||||
@ -415,19 +456,11 @@ impl AuthorityKeyIdentifier {
|
||||
}
|
||||
}
|
||||
|
||||
enum RustGeneralName {
|
||||
Dns(String),
|
||||
Email(String),
|
||||
Uri(String),
|
||||
Ip(String),
|
||||
Rid(String),
|
||||
}
|
||||
|
||||
/// An extension that allows additional identities to be bound to the subject
|
||||
/// of the certificate.
|
||||
pub struct SubjectAlternativeName {
|
||||
critical: bool,
|
||||
items: Vec<RustGeneralName>,
|
||||
names: Vec<String>,
|
||||
}
|
||||
|
||||
impl Default for SubjectAlternativeName {
|
||||
@ -438,11 +471,10 @@ impl Default for SubjectAlternativeName {
|
||||
|
||||
impl SubjectAlternativeName {
|
||||
/// Construct a new `SubjectAlternativeName` extension.
|
||||
#[must_use]
|
||||
pub fn new() -> SubjectAlternativeName {
|
||||
SubjectAlternativeName {
|
||||
critical: false,
|
||||
items: vec![],
|
||||
names: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
@ -454,73 +486,55 @@ impl SubjectAlternativeName {
|
||||
|
||||
/// Sets the `email` flag.
|
||||
pub fn email(&mut self, email: &str) -> &mut SubjectAlternativeName {
|
||||
self.items.push(RustGeneralName::Email(email.to_string()));
|
||||
self.names.push(format!("email:{}", email));
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the `uri` flag.
|
||||
pub fn uri(&mut self, uri: &str) -> &mut SubjectAlternativeName {
|
||||
self.items.push(RustGeneralName::Uri(uri.to_string()));
|
||||
self.names.push(format!("URI:{}", uri));
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the `dns` flag.
|
||||
pub fn dns(&mut self, dns: &str) -> &mut SubjectAlternativeName {
|
||||
self.items.push(RustGeneralName::Dns(dns.to_string()));
|
||||
self.names.push(format!("DNS:{}", dns));
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the `rid` flag.
|
||||
pub fn rid(&mut self, rid: &str) -> &mut SubjectAlternativeName {
|
||||
self.items.push(RustGeneralName::Rid(rid.to_string()));
|
||||
self.names.push(format!("RID:{}", rid));
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the `ip` flag.
|
||||
pub fn ip(&mut self, ip: &str) -> &mut SubjectAlternativeName {
|
||||
self.items.push(RustGeneralName::Ip(ip.to_string()));
|
||||
self.names.push(format!("IP:{}", ip));
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the `dirName` flag.
|
||||
///
|
||||
/// Not currently actually supported, always panics.
|
||||
#[deprecated = "dir_name is deprecated and always panics. Please file a bug if you have a use case for this."]
|
||||
pub fn dir_name(&mut self, _dir_name: &str) -> &mut SubjectAlternativeName {
|
||||
unimplemented!(
|
||||
"This has not yet been adapted for the new internals. File a bug if you need this."
|
||||
);
|
||||
pub fn dir_name(&mut self, dir_name: &str) -> &mut SubjectAlternativeName {
|
||||
self.names.push(format!("dirName:{}", dir_name));
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the `otherName` flag.
|
||||
///
|
||||
/// Not currently actually supported, always panics.
|
||||
#[deprecated = "other_name is deprecated and always panics. Please file a bug if you have a use case for this."]
|
||||
pub fn other_name(&mut self, _other_name: &str) -> &mut SubjectAlternativeName {
|
||||
unimplemented!(
|
||||
"This has not yet been adapted for the new internals. File a bug if you need this."
|
||||
);
|
||||
pub fn other_name(&mut self, other_name: &str) -> &mut SubjectAlternativeName {
|
||||
self.names.push(format!("otherName:{}", other_name));
|
||||
self
|
||||
}
|
||||
|
||||
/// Return a `SubjectAlternativeName` extension as an `X509Extension`.
|
||||
pub fn build(&self, _ctx: &X509v3Context<'_>) -> Result<X509Extension, ErrorStack> {
|
||||
let mut stack = Stack::new()?;
|
||||
for item in &self.items {
|
||||
let gn = match item {
|
||||
RustGeneralName::Dns(s) => GeneralName::new_dns(s.as_bytes())?,
|
||||
RustGeneralName::Email(s) => GeneralName::new_email(s.as_bytes())?,
|
||||
RustGeneralName::Uri(s) => GeneralName::new_uri(s.as_bytes())?,
|
||||
RustGeneralName::Ip(s) => {
|
||||
GeneralName::new_ip(s.parse().map_err(|_| ErrorStack::get())?)?
|
||||
}
|
||||
RustGeneralName::Rid(s) => GeneralName::new_rid(Asn1Object::from_str(s)?)?,
|
||||
};
|
||||
stack.push(gn)?;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
X509Extension::new_internal(Nid::SUBJECT_ALT_NAME, self.critical, stack.as_ptr().cast())
|
||||
pub fn build(&self, ctx: &X509v3Context) -> Result<X509Extension, ErrorStack> {
|
||||
let mut value = String::new();
|
||||
let mut first = true;
|
||||
append(&mut value, &mut first, self.critical, "critical");
|
||||
for name in &self.names {
|
||||
append(&mut value, &mut first, true, name);
|
||||
}
|
||||
X509Extension::new_nid(None, Some(ctx), Nid::SUBJECT_ALT_NAME, &value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -8,7 +8,6 @@
|
||||
//! ```rust
|
||||
//! use boring::x509::store::{X509StoreBuilder, X509Store};
|
||||
//! use boring::x509::{X509, X509Name};
|
||||
//! use boring::asn1::Asn1Time;
|
||||
//! use boring::pkey::PKey;
|
||||
//! use boring::hash::MessageDigest;
|
||||
//! use boring::rsa::Rsa;
|
||||
@ -23,33 +22,26 @@
|
||||
//! let name = name.build();
|
||||
//! let mut builder = X509::builder().unwrap();
|
||||
//!
|
||||
//! // Sep 27th, 2016
|
||||
//! let sample_time = Asn1Time::from_unix(1474934400).unwrap();
|
||||
//!
|
||||
//! builder.set_version(2).unwrap();
|
||||
//! builder.set_subject_name(&name).unwrap();
|
||||
//! builder.set_issuer_name(&name).unwrap();
|
||||
//! builder.set_pubkey(&pkey).unwrap();
|
||||
//! builder.set_not_before(&sample_time);
|
||||
//! builder.set_not_after(&sample_time);
|
||||
//! builder.sign(&pkey, MessageDigest::sha256()).unwrap();
|
||||
//!
|
||||
//! let certificate: X509 = builder.build();
|
||||
//! let mut builder = X509StoreBuilder::new().unwrap();
|
||||
//! let _ = builder.add_cert(&certificate);
|
||||
//! let _ = builder.add_cert(certificate);
|
||||
//! let store: X509Store = builder.build();
|
||||
//! ```
|
||||
|
||||
use crate::error::ErrorStack;
|
||||
use crate::ffi;
|
||||
use crate::stack::StackRef;
|
||||
use crate::x509::crl::X509CRL;
|
||||
use crate::x509::verify::{X509VerifyFlags, X509VerifyParamRef};
|
||||
use crate::x509::{X509Object, X509Ref};
|
||||
use crate::{cvt, cvt_p};
|
||||
use foreign_types::{ForeignType, ForeignTypeRef};
|
||||
use openssl_macros::corresponds;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::mem;
|
||||
|
||||
use crate::error::ErrorStack;
|
||||
use crate::stack::StackRef;
|
||||
use crate::x509::{X509Object, X509};
|
||||
use crate::{cvt, cvt_p};
|
||||
|
||||
foreign_type_and_impl_send_sync! {
|
||||
type CType = ffi::X509_STORE;
|
||||
@ -72,36 +64,18 @@ impl X509StoreBuilder {
|
||||
}
|
||||
|
||||
/// Constructs the `X509Store`.
|
||||
#[must_use]
|
||||
pub fn build(self) -> X509Store {
|
||||
X509Store(ManuallyDrop::new(self).0)
|
||||
let store = X509Store(self.0);
|
||||
mem::forget(self);
|
||||
store
|
||||
}
|
||||
}
|
||||
|
||||
impl X509StoreBuilderRef {
|
||||
/// Adds a certificate to the certificate store.
|
||||
#[corresponds(X509_STORE_add_cert)]
|
||||
pub fn add_cert(&mut self, cert: impl AsRef<X509Ref>) -> Result<(), ErrorStack> {
|
||||
let cert = cert.as_ref();
|
||||
unsafe { cvt(ffi::X509_STORE_add_cert(self.as_ptr(), cert.as_ptr())) }
|
||||
}
|
||||
|
||||
/// Adds a CRL to the certificate store.
|
||||
///
|
||||
/// This corresponds to [`X509_STORE_add_crl`].
|
||||
///
|
||||
/// [`X509_STORE_add_crl`]: https://www.openssl.org/docs/man1.1.1/man3/X509_STORE_add_crl
|
||||
pub fn add_crl(&mut self, crl: X509CRL) -> Result<(), ErrorStack> {
|
||||
unsafe { cvt(ffi::X509_STORE_add_crl(self.as_ptr(), crl.as_ptr())).map(|_| ()) }
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the X509 verification configuration.
|
||||
///
|
||||
/// This corresponds to [`X509_STORE_get0_param`].
|
||||
///
|
||||
/// [`X509_STORE_add_crl`]: https://www.openssl.org/docs/man1.1.1/man3/X509_STORE_get0_param
|
||||
pub fn param_mut(&mut self) -> &mut X509VerifyParamRef {
|
||||
unsafe { X509VerifyParamRef::from_ptr_mut(ffi::X509_STORE_get0_param(self.as_ptr())) }
|
||||
// FIXME should take an &X509Ref
|
||||
pub fn add_cert(&mut self, cert: X509) -> Result<(), ErrorStack> {
|
||||
unsafe { cvt(ffi::X509_STORE_add_cert(self.as_ptr(), cert.as_ptr())).map(|_| ()) }
|
||||
}
|
||||
|
||||
/// Load certificates from their default locations.
|
||||
@ -109,37 +83,8 @@ impl X509StoreBuilderRef {
|
||||
/// These locations are read from the `SSL_CERT_FILE` and `SSL_CERT_DIR`
|
||||
/// environment variables if present, or defaults specified at OpenSSL
|
||||
/// build time otherwise.
|
||||
#[corresponds(X509_STORE_set_default_paths)]
|
||||
pub fn set_default_paths(&mut self) -> Result<(), ErrorStack> {
|
||||
unsafe { cvt(ffi::X509_STORE_set_default_paths(self.as_ptr())) }
|
||||
}
|
||||
|
||||
/// Sets certificate chain validation related flags.
|
||||
#[corresponds(X509_STORE_set_flags)]
|
||||
pub fn set_flags(&mut self, flags: X509VerifyFlags) {
|
||||
unsafe {
|
||||
cvt(ffi::X509_STORE_set_flags(self.as_ptr(), flags.bits())).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the X509 verification configuration.
|
||||
#[corresponds(X509_STORE_get0_param)]
|
||||
pub fn verify_param_mut(&mut self) -> &mut X509VerifyParamRef {
|
||||
unsafe { X509VerifyParamRef::from_ptr_mut(ffi::X509_STORE_get0_param(self.as_ptr())) }
|
||||
}
|
||||
|
||||
/// Sets certificate chain validation related parameters.
|
||||
#[corresponds(X509_STORE_set1_param)]
|
||||
pub fn set_param(&mut self, param: &X509VerifyParamRef) -> Result<(), ErrorStack> {
|
||||
unsafe { cvt(ffi::X509_STORE_set1_param(self.as_ptr(), param.as_ptr())) }
|
||||
}
|
||||
|
||||
/// For testing only
|
||||
#[cfg(test)]
|
||||
pub fn objects_len(&self) -> usize {
|
||||
unsafe {
|
||||
StackRef::<X509Object>::from_ptr(ffi::X509_STORE_get0_objects(self.as_ptr())).len()
|
||||
}
|
||||
unsafe { cvt(ffi::X509_STORE_set_default_paths(self.as_ptr())).map(|_| ()) }
|
||||
}
|
||||
}
|
||||
|
||||
@ -151,59 +96,11 @@ foreign_type_and_impl_send_sync! {
|
||||
pub struct X509Store;
|
||||
}
|
||||
|
||||
impl ToOwned for X509StoreRef {
|
||||
type Owned = X509Store;
|
||||
|
||||
fn to_owned(&self) -> X509Store {
|
||||
unsafe {
|
||||
ffi::X509_STORE_up_ref(self.as_ptr());
|
||||
X509Store::from_ptr(self.as_ptr())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for X509Store {
|
||||
fn clone(&self) -> X509Store {
|
||||
(**self).to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
impl X509StoreRef {
|
||||
/// **Warning: this method is unsound**
|
||||
///
|
||||
/// Get a reference to the cache of certificates in this store.
|
||||
///
|
||||
/// # Safety
|
||||
/// References may be invalidated by any access to the shared cache.
|
||||
#[deprecated(
|
||||
note = "This method is unsound https://github.com/sfackler/rust-openssl/issues/2096"
|
||||
)]
|
||||
#[corresponds(X509_STORE_get0_objects)]
|
||||
#[must_use]
|
||||
pub fn objects(&self) -> &StackRef<X509Object> {
|
||||
unsafe { StackRef::from_ptr(ffi::X509_STORE_get0_objects(self.as_ptr())) }
|
||||
}
|
||||
|
||||
/// For testing only, where it doesn't have to expose an unsafe pointer
|
||||
#[cfg(test)]
|
||||
#[allow(deprecated)]
|
||||
#[must_use]
|
||||
pub fn objects_len(&self) -> usize {
|
||||
self.objects().len()
|
||||
unsafe { StackRef::from_ptr(X509_STORE_get0_objects(self.as_ptr())) }
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(clippy::redundant_clone)]
|
||||
#[should_panic = "Shared X509Store can't be mutated"]
|
||||
fn set_cert_store_pevents_mutability() {
|
||||
use crate::ssl::*;
|
||||
|
||||
let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
|
||||
let store = X509StoreBuilder::new().unwrap().build();
|
||||
|
||||
ctx.set_cert_store(store.clone());
|
||||
|
||||
// This is bad.
|
||||
let _aliased_store = ctx.cert_store_mut();
|
||||
}
|
||||
use crate::ffi::X509_STORE_get0_objects;
|
||||
|
||||
381
boring/src/x509/tests.rs
Normal file
381
boring/src/x509/tests.rs
Normal file
@ -0,0 +1,381 @@
|
||||
use hex::{self, FromHex};
|
||||
|
||||
use crate::asn1::Asn1Time;
|
||||
use crate::bn::{BigNum, MsbOption};
|
||||
use crate::hash::MessageDigest;
|
||||
use crate::nid::Nid;
|
||||
use crate::pkey::{PKey, Private};
|
||||
use crate::rsa::Rsa;
|
||||
use crate::stack::Stack;
|
||||
use crate::x509::extension::{
|
||||
AuthorityKeyIdentifier, BasicConstraints, ExtendedKeyUsage, KeyUsage, SubjectAlternativeName,
|
||||
SubjectKeyIdentifier,
|
||||
};
|
||||
use crate::x509::store::X509StoreBuilder;
|
||||
use crate::x509::{X509Name, X509Req, X509StoreContext, X509VerifyResult, X509};
|
||||
|
||||
fn pkey() -> PKey<Private> {
|
||||
let rsa = Rsa::generate(2048).unwrap();
|
||||
PKey::from_rsa(rsa).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cert_loading() {
|
||||
let cert = include_bytes!("../../test/cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
let fingerprint = cert.digest(MessageDigest::sha1()).unwrap();
|
||||
|
||||
let hash_str = "59172d9313e84459bcff27f967e79e6e9217e584";
|
||||
let hash_vec = Vec::from_hex(hash_str).unwrap();
|
||||
|
||||
assert_eq!(hash_vec, &*fingerprint);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_debug() {
|
||||
let cert = include_bytes!("../../test/cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
let debugged = format!("{:#?}", cert);
|
||||
|
||||
assert!(debugged.contains(r#"serial_number: "8771f7bdee982fa5""#));
|
||||
assert!(debugged.contains(r#"signature_algorithm: sha256WithRSAEncryption"#));
|
||||
assert!(debugged.contains(r#"countryName = "AU""#));
|
||||
assert!(debugged.contains(r#"stateOrProvinceName = "Some-State""#));
|
||||
assert!(debugged.contains(r#"not_before: Aug 14 17:00:03 2016 GMT"#));
|
||||
assert!(debugged.contains(r#"not_after: Aug 12 17:00:03 2026 GMT"#));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cert_issue_validity() {
|
||||
let cert = include_bytes!("../../test/cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
let not_before = cert.not_before().to_string();
|
||||
let not_after = cert.not_after().to_string();
|
||||
|
||||
assert_eq!(not_before, "Aug 14 17:00:03 2016 GMT");
|
||||
assert_eq!(not_after, "Aug 12 17:00:03 2026 GMT");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_save_der() {
|
||||
let cert = include_bytes!("../../test/cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
|
||||
let der = cert.to_der().unwrap();
|
||||
assert!(!der.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_subject_read_cn() {
|
||||
let cert = include_bytes!("../../test/cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
let subject = cert.subject_name();
|
||||
let cn = subject.entries_by_nid(Nid::COMMONNAME).next().unwrap();
|
||||
assert_eq!(cn.data().as_slice(), b"foobar.com")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nid_values() {
|
||||
let cert = include_bytes!("../../test/nid_test_cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
let subject = cert.subject_name();
|
||||
|
||||
let cn = subject.entries_by_nid(Nid::COMMONNAME).next().unwrap();
|
||||
assert_eq!(cn.data().as_slice(), b"example.com");
|
||||
|
||||
let email = subject
|
||||
.entries_by_nid(Nid::PKCS9_EMAILADDRESS)
|
||||
.next()
|
||||
.unwrap();
|
||||
assert_eq!(email.data().as_slice(), b"test@example.com");
|
||||
|
||||
let friendly = subject.entries_by_nid(Nid::FRIENDLYNAME).next().unwrap();
|
||||
assert_eq!(&**friendly.data().as_utf8().unwrap(), "Example");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nameref_iterator() {
|
||||
let cert = include_bytes!("../../test/nid_test_cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
let subject = cert.subject_name();
|
||||
let mut all_entries = subject.entries();
|
||||
|
||||
let email = all_entries.next().unwrap();
|
||||
assert_eq!(
|
||||
email.object().nid().as_raw(),
|
||||
Nid::PKCS9_EMAILADDRESS.as_raw()
|
||||
);
|
||||
assert_eq!(email.data().as_slice(), b"test@example.com");
|
||||
|
||||
let cn = all_entries.next().unwrap();
|
||||
assert_eq!(cn.object().nid().as_raw(), Nid::COMMONNAME.as_raw());
|
||||
assert_eq!(cn.data().as_slice(), b"example.com");
|
||||
|
||||
let friendly = all_entries.next().unwrap();
|
||||
assert_eq!(friendly.object().nid().as_raw(), Nid::FRIENDLYNAME.as_raw());
|
||||
assert_eq!(&**friendly.data().as_utf8().unwrap(), "Example");
|
||||
|
||||
if all_entries.next().is_some() {
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nid_uid_value() {
|
||||
let cert = include_bytes!("../../test/nid_uid_test_cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
let subject = cert.subject_name();
|
||||
|
||||
let cn = subject.entries_by_nid(Nid::USERID).next().unwrap();
|
||||
assert_eq!(cn.data().as_slice(), b"this is the userId");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_subject_alt_name() {
|
||||
let cert = include_bytes!("../../test/alt_name_cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
|
||||
let subject_alt_names = cert.subject_alt_names().unwrap();
|
||||
assert_eq!(5, subject_alt_names.len());
|
||||
assert_eq!(Some("example.com"), subject_alt_names[0].dnsname());
|
||||
assert_eq!(subject_alt_names[1].ipaddress(), Some(&[127, 0, 0, 1][..]));
|
||||
assert_eq!(
|
||||
subject_alt_names[2].ipaddress(),
|
||||
Some(&b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01"[..])
|
||||
);
|
||||
assert_eq!(Some("test@example.com"), subject_alt_names[3].email());
|
||||
assert_eq!(Some("http://www.example.com"), subject_alt_names[4].uri());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_subject_alt_name_iter() {
|
||||
let cert = include_bytes!("../../test/alt_name_cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
|
||||
let subject_alt_names = cert.subject_alt_names().unwrap();
|
||||
let mut subject_alt_names_iter = subject_alt_names.iter();
|
||||
assert_eq!(
|
||||
subject_alt_names_iter.next().unwrap().dnsname(),
|
||||
Some("example.com")
|
||||
);
|
||||
assert_eq!(
|
||||
subject_alt_names_iter.next().unwrap().ipaddress(),
|
||||
Some(&[127, 0, 0, 1][..])
|
||||
);
|
||||
assert_eq!(
|
||||
subject_alt_names_iter.next().unwrap().ipaddress(),
|
||||
Some(&b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01"[..])
|
||||
);
|
||||
assert_eq!(
|
||||
subject_alt_names_iter.next().unwrap().email(),
|
||||
Some("test@example.com")
|
||||
);
|
||||
assert_eq!(
|
||||
subject_alt_names_iter.next().unwrap().uri(),
|
||||
Some("http://www.example.com")
|
||||
);
|
||||
assert!(subject_alt_names_iter.next().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn x509_builder() {
|
||||
let pkey = pkey();
|
||||
|
||||
let mut name = X509Name::builder().unwrap();
|
||||
name.append_entry_by_nid(Nid::COMMONNAME, "foobar.com")
|
||||
.unwrap();
|
||||
let name = name.build();
|
||||
|
||||
let mut builder = X509::builder().unwrap();
|
||||
builder.set_version(2).unwrap();
|
||||
builder.set_subject_name(&name).unwrap();
|
||||
builder.set_issuer_name(&name).unwrap();
|
||||
builder
|
||||
.set_not_before(&Asn1Time::days_from_now(0).unwrap())
|
||||
.unwrap();
|
||||
builder
|
||||
.set_not_after(&Asn1Time::days_from_now(365).unwrap())
|
||||
.unwrap();
|
||||
builder.set_pubkey(&pkey).unwrap();
|
||||
|
||||
let mut serial = BigNum::new().unwrap();
|
||||
serial.rand(128, MsbOption::MAYBE_ZERO, false).unwrap();
|
||||
builder
|
||||
.set_serial_number(&serial.to_asn1_integer().unwrap())
|
||||
.unwrap();
|
||||
|
||||
let basic_constraints = BasicConstraints::new().critical().ca().build().unwrap();
|
||||
builder.append_extension(basic_constraints).unwrap();
|
||||
let key_usage = KeyUsage::new()
|
||||
.digital_signature()
|
||||
.key_encipherment()
|
||||
.build()
|
||||
.unwrap();
|
||||
builder.append_extension(key_usage).unwrap();
|
||||
let ext_key_usage = ExtendedKeyUsage::new()
|
||||
.client_auth()
|
||||
.server_auth()
|
||||
.other("2.999.1")
|
||||
.build()
|
||||
.unwrap();
|
||||
builder.append_extension(ext_key_usage).unwrap();
|
||||
let subject_key_identifier = SubjectKeyIdentifier::new()
|
||||
.build(&builder.x509v3_context(None, None))
|
||||
.unwrap();
|
||||
builder.append_extension(subject_key_identifier).unwrap();
|
||||
let authority_key_identifier = AuthorityKeyIdentifier::new()
|
||||
.keyid(true)
|
||||
.build(&builder.x509v3_context(None, None))
|
||||
.unwrap();
|
||||
builder.append_extension(authority_key_identifier).unwrap();
|
||||
let subject_alternative_name = SubjectAlternativeName::new()
|
||||
.dns("example.com")
|
||||
.build(&builder.x509v3_context(None, None))
|
||||
.unwrap();
|
||||
builder.append_extension(subject_alternative_name).unwrap();
|
||||
|
||||
builder.sign(&pkey, MessageDigest::sha256()).unwrap();
|
||||
|
||||
let x509 = builder.build();
|
||||
|
||||
assert!(pkey.public_eq(&x509.public_key().unwrap()));
|
||||
assert!(x509.verify(&pkey).unwrap());
|
||||
|
||||
let cn = x509
|
||||
.subject_name()
|
||||
.entries_by_nid(Nid::COMMONNAME)
|
||||
.next()
|
||||
.unwrap();
|
||||
assert_eq!(cn.data().as_slice(), b"foobar.com");
|
||||
assert_eq!(serial, x509.serial_number().to_bn().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn x509_req_builder() {
|
||||
let pkey = pkey();
|
||||
|
||||
let mut name = X509Name::builder().unwrap();
|
||||
name.append_entry_by_nid(Nid::COMMONNAME, "foobar.com")
|
||||
.unwrap();
|
||||
let name = name.build();
|
||||
|
||||
let mut builder = X509Req::builder().unwrap();
|
||||
builder.set_version(2).unwrap();
|
||||
builder.set_subject_name(&name).unwrap();
|
||||
builder.set_pubkey(&pkey).unwrap();
|
||||
|
||||
let mut extensions = Stack::new().unwrap();
|
||||
let key_usage = KeyUsage::new()
|
||||
.digital_signature()
|
||||
.key_encipherment()
|
||||
.build()
|
||||
.unwrap();
|
||||
extensions.push(key_usage).unwrap();
|
||||
let subject_alternative_name = SubjectAlternativeName::new()
|
||||
.dns("example.com")
|
||||
.build(&builder.x509v3_context(None))
|
||||
.unwrap();
|
||||
extensions.push(subject_alternative_name).unwrap();
|
||||
builder.add_extensions(&extensions).unwrap();
|
||||
|
||||
builder.sign(&pkey, MessageDigest::sha256()).unwrap();
|
||||
|
||||
let req = builder.build();
|
||||
assert!(req.public_key().unwrap().public_eq(&pkey));
|
||||
assert_eq!(req.extensions().unwrap().len(), extensions.len());
|
||||
assert!(req.verify(&pkey).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stack_from_pem() {
|
||||
let certs = include_bytes!("../../test/certs.pem");
|
||||
let certs = X509::stack_from_pem(certs).unwrap();
|
||||
|
||||
assert_eq!(certs.len(), 2);
|
||||
assert_eq!(
|
||||
hex::encode(certs[0].digest(MessageDigest::sha1()).unwrap()),
|
||||
"59172d9313e84459bcff27f967e79e6e9217e584"
|
||||
);
|
||||
assert_eq!(
|
||||
hex::encode(certs[1].digest(MessageDigest::sha1()).unwrap()),
|
||||
"c0cbdf7cdd03c9773e5468e1f6d2da7d5cbb1875"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issued() {
|
||||
let cert = include_bytes!("../../test/cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
let ca = include_bytes!("../../test/root-ca.pem");
|
||||
let ca = X509::from_pem(ca).unwrap();
|
||||
|
||||
assert_eq!(ca.issued(&cert), X509VerifyResult::OK);
|
||||
assert_ne!(cert.issued(&cert), X509VerifyResult::OK);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn signature() {
|
||||
let cert = include_bytes!("../../test/cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
let signature = cert.signature();
|
||||
assert_eq!(
|
||||
hex::encode(signature.as_slice()),
|
||||
"4af607b889790b43470442cfa551cdb8b6d0b0340d2958f76b9e3ef6ad4992230cead6842587f0ecad5\
|
||||
78e6e11a221521e940187e3d6652de14e84e82f6671f097cc47932e022add3c0cb54a26bf27fa84c107\
|
||||
4971caa6bee2e42d34a5b066c427f2d452038082b8073993399548088429de034fdd589dcfb0dd33be7\
|
||||
ebdfdf698a28d628a89568881d658151276bde333600969502c4e62e1d3470a683364dfb241f78d310a\
|
||||
89c119297df093eb36b7fd7540224f488806780305d1e79ffc938fe2275441726522ab36d88348e6c51\
|
||||
f13dcc46b5e1cdac23c974fd5ef86aa41e91c9311655090a52333bc79687c748d833595d4c5f987508f\
|
||||
e121997410d37c"
|
||||
);
|
||||
let algorithm = cert.signature_algorithm();
|
||||
assert_eq!(algorithm.object().nid(), Nid::SHA256WITHRSAENCRYPTION);
|
||||
assert_eq!(algorithm.object().to_string(), "sha256WithRSAEncryption");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(clippy::redundant_clone)]
|
||||
fn clone_x509() {
|
||||
let cert = include_bytes!("../../test/cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
drop(cert.clone());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_verify_cert() {
|
||||
let cert = include_bytes!("../../test/cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
let ca = include_bytes!("../../test/root-ca.pem");
|
||||
let ca = X509::from_pem(ca).unwrap();
|
||||
let chain = Stack::new().unwrap();
|
||||
|
||||
let mut store_bldr = X509StoreBuilder::new().unwrap();
|
||||
store_bldr.add_cert(ca).unwrap();
|
||||
let store = store_bldr.build();
|
||||
|
||||
let mut context = X509StoreContext::new().unwrap();
|
||||
assert!(context
|
||||
.init(&store, &cert, &chain, |c| c.verify_cert())
|
||||
.unwrap());
|
||||
assert!(context
|
||||
.init(&store, &cert, &chain, |c| c.verify_cert())
|
||||
.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_verify_fails() {
|
||||
let cert = include_bytes!("../../test/cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
let ca = include_bytes!("../../test/alt_name_cert.pem");
|
||||
let ca = X509::from_pem(ca).unwrap();
|
||||
let chain = Stack::new().unwrap();
|
||||
|
||||
let mut store_bldr = X509StoreBuilder::new().unwrap();
|
||||
store_bldr.add_cert(ca).unwrap();
|
||||
let store = store_bldr.build();
|
||||
|
||||
let mut context = X509StoreContext::new().unwrap();
|
||||
assert!(!context
|
||||
.init(&store, &cert, &chain, |c| c.verify_cert())
|
||||
.unwrap());
|
||||
}
|
||||
@ -1,856 +0,0 @@
|
||||
use hex::{self, FromHex};
|
||||
|
||||
use crate::asn1::Asn1Time;
|
||||
use crate::bn::{BigNum, MsbOption};
|
||||
use crate::hash::MessageDigest;
|
||||
use crate::nid::Nid;
|
||||
use crate::pkey::{PKey, Private};
|
||||
use crate::rsa::Rsa;
|
||||
use crate::stack::{Stack, Stackable};
|
||||
use crate::x509::crl::{X509CRLBuilder, X509Revoked, X509CRL};
|
||||
use crate::x509::extension::{
|
||||
AuthorityKeyIdentifier, BasicConstraints, ExtendedKeyUsage, KeyUsage, SubjectAlternativeName,
|
||||
SubjectKeyIdentifier,
|
||||
};
|
||||
use crate::x509::store::X509StoreBuilder;
|
||||
use crate::x509::verify::X509VerifyFlags;
|
||||
use crate::x509::{X509Extension, X509Name, X509Req, X509StoreContext, X509VerifyError, X509};
|
||||
|
||||
mod trusted_first;
|
||||
|
||||
fn pkey() -> PKey<Private> {
|
||||
let rsa = Rsa::generate(2048).unwrap();
|
||||
PKey::from_rsa(rsa).unwrap()
|
||||
}
|
||||
|
||||
fn stack_of<T>(item: T) -> Stack<T>
|
||||
where
|
||||
T: Stackable,
|
||||
{
|
||||
let mut stack = Stack::new().expect("unable to initialize stack");
|
||||
stack.push(item).expect("failed to add to stack");
|
||||
stack
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cert_loading() {
|
||||
let cert = include_bytes!("../../../test/cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
let fingerprint = cert.digest(MessageDigest::sha1()).unwrap();
|
||||
|
||||
let hash_str = "59172d9313e84459bcff27f967e79e6e9217e584";
|
||||
let hash_vec = Vec::from_hex(hash_str).unwrap();
|
||||
|
||||
assert_eq!(hash_vec, &*fingerprint);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_debug() {
|
||||
let cert = include_bytes!("../../../test/cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
let debugged = format!("{cert:#?}");
|
||||
|
||||
assert!(debugged.contains(r#"serial_number: "8771f7bdee982fa5""#));
|
||||
assert!(debugged.contains(r#"signature_algorithm: sha256WithRSAEncryption"#));
|
||||
assert!(debugged.contains(r#"countryName = "AU""#));
|
||||
assert!(debugged.contains(r#"stateOrProvinceName = "Some-State""#));
|
||||
assert!(debugged.contains(r#"not_before: Aug 14 17:00:03 2016 GMT"#));
|
||||
assert!(debugged.contains(r#"not_after: Aug 12 17:00:03 2026 GMT"#));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cert_issue_validity() {
|
||||
let cert = include_bytes!("../../../test/cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
let not_before = cert.not_before().to_string();
|
||||
let not_after = cert.not_after().to_string();
|
||||
|
||||
assert_eq!(not_before, "Aug 14 17:00:03 2016 GMT");
|
||||
assert_eq!(not_after, "Aug 12 17:00:03 2026 GMT");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_save_der() {
|
||||
let cert = include_bytes!("../../../test/cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
|
||||
let der = cert.to_der().unwrap();
|
||||
assert!(!der.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_subject_read_cn() {
|
||||
let cert = include_bytes!("../../../test/cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
let subject = cert.subject_name();
|
||||
let cn = subject.entries_by_nid(Nid::COMMONNAME).next().unwrap();
|
||||
assert_eq!(cn.data().as_slice(), b"foobar.com");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nid_values() {
|
||||
let cert = include_bytes!("../../../test/nid_test_cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
let subject = cert.subject_name();
|
||||
|
||||
let cn = subject.entries_by_nid(Nid::COMMONNAME).next().unwrap();
|
||||
assert_eq!(cn.data().as_slice(), b"example.com");
|
||||
|
||||
let email = subject
|
||||
.entries_by_nid(Nid::PKCS9_EMAILADDRESS)
|
||||
.next()
|
||||
.unwrap();
|
||||
assert_eq!(email.data().as_slice(), b"test@example.com");
|
||||
|
||||
let friendly = subject.entries_by_nid(Nid::FRIENDLYNAME).next().unwrap();
|
||||
assert_eq!(&**friendly.data().as_utf8().unwrap(), "Example");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nameref_iterator() {
|
||||
let cert = include_bytes!("../../../test/nid_test_cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
let subject = cert.subject_name();
|
||||
let mut all_entries = subject.entries();
|
||||
|
||||
let email = all_entries.next().unwrap();
|
||||
assert_eq!(
|
||||
email.object().nid().as_raw(),
|
||||
Nid::PKCS9_EMAILADDRESS.as_raw()
|
||||
);
|
||||
assert_eq!(email.data().as_slice(), b"test@example.com");
|
||||
|
||||
let cn = all_entries.next().unwrap();
|
||||
assert_eq!(cn.object().nid().as_raw(), Nid::COMMONNAME.as_raw());
|
||||
assert_eq!(cn.data().as_slice(), b"example.com");
|
||||
|
||||
let friendly = all_entries.next().unwrap();
|
||||
assert_eq!(friendly.object().nid().as_raw(), Nid::FRIENDLYNAME.as_raw());
|
||||
assert_eq!(&**friendly.data().as_utf8().unwrap(), "Example");
|
||||
|
||||
if all_entries.next().is_some() {
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nid_uid_value() {
|
||||
let cert = include_bytes!("../../../test/nid_uid_test_cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
let subject = cert.subject_name();
|
||||
|
||||
let cn = subject.entries_by_nid(Nid::USERID).next().unwrap();
|
||||
assert_eq!(cn.data().as_slice(), b"this is the userId");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_subject_alt_name() {
|
||||
let cert = include_bytes!("../../../test/alt_name_cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
|
||||
let subject_alt_names = cert.subject_alt_names().unwrap();
|
||||
assert_eq!(5, subject_alt_names.len());
|
||||
assert_eq!(Some("example.com"), subject_alt_names[0].dnsname());
|
||||
assert_eq!(subject_alt_names[1].ipaddress(), Some(&[127, 0, 0, 1][..]));
|
||||
assert_eq!(
|
||||
subject_alt_names[2].ipaddress(),
|
||||
Some(&b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01"[..])
|
||||
);
|
||||
assert_eq!(Some("test@example.com"), subject_alt_names[3].email());
|
||||
assert_eq!(Some("http://www.example.com"), subject_alt_names[4].uri());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_subject_alt_name_iter() {
|
||||
let cert = include_bytes!("../../../test/alt_name_cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
|
||||
let subject_alt_names = cert.subject_alt_names().unwrap();
|
||||
let mut subject_alt_names_iter = subject_alt_names.iter();
|
||||
assert_eq!(
|
||||
subject_alt_names_iter.next().unwrap().dnsname(),
|
||||
Some("example.com")
|
||||
);
|
||||
assert_eq!(
|
||||
subject_alt_names_iter.next().unwrap().ipaddress(),
|
||||
Some(&[127, 0, 0, 1][..])
|
||||
);
|
||||
assert_eq!(
|
||||
subject_alt_names_iter.next().unwrap().ipaddress(),
|
||||
Some(&b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01"[..])
|
||||
);
|
||||
assert_eq!(
|
||||
subject_alt_names_iter.next().unwrap().email(),
|
||||
Some("test@example.com")
|
||||
);
|
||||
assert_eq!(
|
||||
subject_alt_names_iter.next().unwrap().uri(),
|
||||
Some("http://www.example.com")
|
||||
);
|
||||
assert!(subject_alt_names_iter.next().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_subject_key_id() {
|
||||
// nid_test_cert_pem has SKI, but no AKI
|
||||
let cert = include_bytes!("../../../test/nid_test_cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
|
||||
let ski = cert.subject_key_id().expect("unable to extract SKI");
|
||||
assert_eq!(
|
||||
ski.as_slice(),
|
||||
[
|
||||
80, 107, 158, 237, 95, 61, 235, 100, 212, 115, 249, 244, 219, 163, 124, 55, 141, 2, 76,
|
||||
5
|
||||
]
|
||||
);
|
||||
|
||||
let aki = cert.authority_key_id();
|
||||
assert!(aki.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_x509_name_print_ex() {
|
||||
let cert = include_bytes!("../../../test/cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
|
||||
let name_no_flags = cert
|
||||
.subject_name()
|
||||
.print_ex(0)
|
||||
.expect("failed to print cert subject name");
|
||||
assert_eq!(
|
||||
name_no_flags,
|
||||
"C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=foobar.com"
|
||||
);
|
||||
|
||||
let name_rfc2253 = cert
|
||||
.subject_name()
|
||||
.print_ex(ffi::XN_FLAG_RFC2253)
|
||||
.expect("failed to print cert subject name");
|
||||
assert_eq!(
|
||||
name_rfc2253,
|
||||
"CN=foobar.com,O=Internet Widgits Pty Ltd,ST=Some-State,C=AU"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn x509_builder() {
|
||||
let pkey = pkey();
|
||||
|
||||
let mut name = X509Name::builder().unwrap();
|
||||
name.append_entry_by_nid(Nid::COMMONNAME, "foobar.com")
|
||||
.unwrap();
|
||||
let name = name.build();
|
||||
|
||||
let mut builder = X509::builder().unwrap();
|
||||
builder.set_version(2).unwrap();
|
||||
builder.set_subject_name(&name).unwrap();
|
||||
builder.set_issuer_name(&name).unwrap();
|
||||
builder
|
||||
.set_not_before(&Asn1Time::days_from_now(0).unwrap())
|
||||
.unwrap();
|
||||
builder
|
||||
.set_not_after(&Asn1Time::days_from_now(365).unwrap())
|
||||
.unwrap();
|
||||
builder.set_pubkey(&pkey).unwrap();
|
||||
|
||||
let mut serial = BigNum::new().unwrap();
|
||||
serial.rand(128, MsbOption::MAYBE_ZERO, false).unwrap();
|
||||
builder
|
||||
.set_serial_number(&serial.to_asn1_integer().unwrap())
|
||||
.unwrap();
|
||||
|
||||
let basic_constraints = BasicConstraints::new().critical().ca().build().unwrap();
|
||||
builder
|
||||
.append_extension(basic_constraints.as_ref())
|
||||
.unwrap();
|
||||
let key_usage = KeyUsage::new()
|
||||
.digital_signature()
|
||||
.key_encipherment()
|
||||
.build()
|
||||
.unwrap();
|
||||
builder.append_extension(&key_usage).unwrap();
|
||||
let ext_key_usage = ExtendedKeyUsage::new()
|
||||
.client_auth()
|
||||
.server_auth()
|
||||
.other("2.999.1")
|
||||
.build()
|
||||
.unwrap();
|
||||
builder.append_extension(&ext_key_usage).unwrap();
|
||||
let subject_key_identifier = SubjectKeyIdentifier::new()
|
||||
.build(&builder.x509v3_context(None, None))
|
||||
.unwrap();
|
||||
builder.append_extension(&subject_key_identifier).unwrap();
|
||||
let authority_key_identifier = AuthorityKeyIdentifier::new()
|
||||
.keyid(true)
|
||||
.build(&builder.x509v3_context(None, None))
|
||||
.unwrap();
|
||||
builder.append_extension(&authority_key_identifier).unwrap();
|
||||
let subject_alternative_name = SubjectAlternativeName::new()
|
||||
.dns("example.com")
|
||||
.build(&builder.x509v3_context(None, None))
|
||||
.unwrap();
|
||||
builder.append_extension(&subject_alternative_name).unwrap();
|
||||
|
||||
builder.sign(&pkey, MessageDigest::sha256()).unwrap();
|
||||
|
||||
let x509 = builder.build();
|
||||
|
||||
assert!(pkey.public_eq(&x509.public_key().unwrap()));
|
||||
assert!(x509.verify(&pkey).unwrap());
|
||||
assert_eq!(x509.extensions().unwrap().len(), 6);
|
||||
|
||||
let cn = x509
|
||||
.subject_name()
|
||||
.entries_by_nid(Nid::COMMONNAME)
|
||||
.next()
|
||||
.unwrap();
|
||||
assert_eq!(cn.data().as_slice(), b"foobar.com");
|
||||
assert_eq!(serial, x509.serial_number().to_bn().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn x509_crl_builder() {
|
||||
let mut builder = X509CRLBuilder::new().unwrap();
|
||||
|
||||
let mut name = X509Name::builder().unwrap();
|
||||
name.append_entry_by_nid(Nid::COMMONNAME, "foobar.com")
|
||||
.unwrap();
|
||||
let name = name.build();
|
||||
builder.set_issuer_name(&name).unwrap();
|
||||
|
||||
let mut serial = BigNum::new().unwrap();
|
||||
serial.rand(128, MsbOption::MAYBE_ZERO, false).unwrap();
|
||||
let serial_asn = serial.to_asn1_integer().unwrap();
|
||||
let revoked =
|
||||
X509Revoked::from_parts(&serial_asn, &Asn1Time::days_from_now(0).unwrap()).unwrap();
|
||||
builder.add_revoked(revoked).unwrap();
|
||||
|
||||
builder
|
||||
.set_last_update(&Asn1Time::days_from_now(0).unwrap())
|
||||
.unwrap();
|
||||
builder
|
||||
.set_next_update(&Asn1Time::days_from_now(30).unwrap())
|
||||
.unwrap();
|
||||
|
||||
let pkey = pkey();
|
||||
builder.sign(&pkey, MessageDigest::sha256()).unwrap();
|
||||
|
||||
let crl = builder.build();
|
||||
|
||||
let cn = crl.issuer().entries_by_nid(Nid::COMMONNAME).next().unwrap();
|
||||
assert_eq!(cn.data().as_slice(), b"foobar.com");
|
||||
let revoked_sn = crl
|
||||
.revoked()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.next()
|
||||
.unwrap()
|
||||
.serial_number();
|
||||
assert_eq!(serial, revoked_sn.to_bn().unwrap());
|
||||
assert!(crl.verify(&pkey).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn x509_extension_new() {
|
||||
assert!(X509Extension::new(None, None, "crlDistributionPoints", "section").is_err());
|
||||
assert!(X509Extension::new(None, None, "proxyCertInfo", "").is_err());
|
||||
assert!(X509Extension::new(None, None, "certificatePolicies", "").is_err());
|
||||
assert!(X509Extension::new(None, None, "subjectAltName", "dirName:section").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn x509_extension_to_der() {
|
||||
let builder = X509::builder().unwrap();
|
||||
|
||||
for (ext, expected) in [
|
||||
(
|
||||
BasicConstraints::new().critical().ca().build().unwrap(),
|
||||
b"0\x0f\x06\x03U\x1d\x13\x01\x01\xff\x04\x050\x03\x01\x01\xff" as &[u8],
|
||||
),
|
||||
(
|
||||
SubjectAlternativeName::new()
|
||||
.dns("example.com,DNS:example2.com")
|
||||
.build(&builder.x509v3_context(None, None))
|
||||
.unwrap(),
|
||||
b"0'\x06\x03U\x1d\x11\x04 0\x1e\x82\x1cexample.com,DNS:example2.com",
|
||||
),
|
||||
(
|
||||
SubjectAlternativeName::new()
|
||||
.rid("1.2.3.4")
|
||||
.uri("https://example.com")
|
||||
.build(&builder.x509v3_context(None, None))
|
||||
.unwrap(),
|
||||
b"0#\x06\x03U\x1d\x11\x04\x1c0\x1a\x88\x03*\x03\x04\x86\x13https://example.com",
|
||||
),
|
||||
(
|
||||
ExtendedKeyUsage::new()
|
||||
.server_auth()
|
||||
.other("2.999.1")
|
||||
.other("clientAuth")
|
||||
.build()
|
||||
.unwrap(),
|
||||
b"0\x22\x06\x03U\x1d%\x04\x1b0\x19\x06\x08+\x06\x01\x05\x05\x07\x03\x01\x06\x03\x887\x01\x06\x08+\x06\x01\x05\x05\x07\x03\x02",
|
||||
),
|
||||
] {
|
||||
assert_eq!(&ext.to_der().unwrap(), expected);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eku_invalid_other() {
|
||||
assert!(ExtendedKeyUsage::new()
|
||||
.other("1.1.1.1.1,2.2.2.2.2")
|
||||
.build()
|
||||
.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn x509_req_builder() {
|
||||
let pkey = pkey();
|
||||
|
||||
let mut name = X509Name::builder().unwrap();
|
||||
name.append_entry_by_nid(Nid::COMMONNAME, "foobar.com")
|
||||
.unwrap();
|
||||
let name = name.build();
|
||||
|
||||
let mut builder = X509Req::builder().unwrap();
|
||||
builder.set_version(0).unwrap();
|
||||
builder.set_subject_name(&name).unwrap();
|
||||
builder.set_pubkey(&pkey).unwrap();
|
||||
|
||||
let mut extensions = Stack::new().unwrap();
|
||||
let key_usage = KeyUsage::new()
|
||||
.digital_signature()
|
||||
.key_encipherment()
|
||||
.build()
|
||||
.unwrap();
|
||||
extensions.push(key_usage).unwrap();
|
||||
let subject_alternative_name = SubjectAlternativeName::new()
|
||||
.dns("example.com")
|
||||
.build(&builder.x509v3_context(None))
|
||||
.unwrap();
|
||||
extensions.push(subject_alternative_name).unwrap();
|
||||
builder.add_extensions(&extensions).unwrap();
|
||||
|
||||
builder.sign(&pkey, MessageDigest::sha256()).unwrap();
|
||||
|
||||
let req = builder.build();
|
||||
assert!(req.public_key().unwrap().public_eq(&pkey));
|
||||
assert_eq!(req.extensions().unwrap().len(), extensions.len());
|
||||
assert!(req.verify(&pkey).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stack_from_pem() {
|
||||
let certs = include_bytes!("../../../test/certs.pem");
|
||||
let certs = X509::stack_from_pem(certs).unwrap();
|
||||
|
||||
assert_eq!(certs.len(), 2);
|
||||
assert_eq!(
|
||||
hex::encode(certs[0].digest(MessageDigest::sha1()).unwrap()),
|
||||
"59172d9313e84459bcff27f967e79e6e9217e584"
|
||||
);
|
||||
assert_eq!(
|
||||
hex::encode(certs[1].digest(MessageDigest::sha1()).unwrap()),
|
||||
"c0cbdf7cdd03c9773e5468e1f6d2da7d5cbb1875"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issued() {
|
||||
let cert = include_bytes!("../../../test/cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
let ca = include_bytes!("../../../test/root-ca.pem");
|
||||
let ca = X509::from_pem(ca).unwrap();
|
||||
|
||||
assert_eq!(ca.issued(&cert), Ok(()));
|
||||
assert!(cert.issued(&cert).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn signature() {
|
||||
let cert = include_bytes!("../../../test/cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
let signature = cert.signature();
|
||||
assert_eq!(
|
||||
hex::encode(signature.as_slice()),
|
||||
"4af607b889790b43470442cfa551cdb8b6d0b0340d2958f76b9e3ef6ad4992230cead6842587f0ecad5\
|
||||
78e6e11a221521e940187e3d6652de14e84e82f6671f097cc47932e022add3c0cb54a26bf27fa84c107\
|
||||
4971caa6bee2e42d34a5b066c427f2d452038082b8073993399548088429de034fdd589dcfb0dd33be7\
|
||||
ebdfdf698a28d628a89568881d658151276bde333600969502c4e62e1d3470a683364dfb241f78d310a\
|
||||
89c119297df093eb36b7fd7540224f488806780305d1e79ffc938fe2275441726522ab36d88348e6c51\
|
||||
f13dcc46b5e1cdac23c974fd5ef86aa41e91c9311655090a52333bc79687c748d833595d4c5f987508f\
|
||||
e121997410d37c"
|
||||
);
|
||||
let algorithm = cert.signature_algorithm();
|
||||
assert_eq!(algorithm.object().nid(), Nid::SHA256WITHRSAENCRYPTION);
|
||||
assert_eq!(algorithm.object().to_string(), "sha256WithRSAEncryption");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(clippy::redundant_clone)]
|
||||
fn clone_x509() {
|
||||
let cert = include_bytes!("../../../test/cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
drop(cert.clone());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_verify_cert() {
|
||||
let cert = include_bytes!("../../../test/cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
let ca = include_bytes!("../../../test/root-ca.pem");
|
||||
let ca = X509::from_pem(ca).unwrap();
|
||||
let chain = Stack::new().unwrap();
|
||||
|
||||
let mut store_bldr = X509StoreBuilder::new().unwrap();
|
||||
store_bldr.add_cert(&ca).unwrap();
|
||||
let store = store_bldr.build();
|
||||
let empty_store = X509StoreBuilder::new().unwrap().build();
|
||||
|
||||
let mut context = X509StoreContext::new().unwrap();
|
||||
assert!(context
|
||||
.init(&store, &cert, &chain, |c| c.verify_cert())
|
||||
.unwrap());
|
||||
assert!(!context
|
||||
.init(&empty_store, &cert, &chain, |c| c.verify_cert())
|
||||
.unwrap());
|
||||
assert!(context
|
||||
.init(&store, &cert, &chain, |c| c.verify_cert())
|
||||
.unwrap());
|
||||
|
||||
context
|
||||
.reset_with_context_data(empty_store, cert.clone(), Stack::new().unwrap())
|
||||
.unwrap();
|
||||
assert!(!context.verify_cert().unwrap());
|
||||
|
||||
context.reset_with_context_data(store, cert, chain).unwrap();
|
||||
assert!(context.verify_cert().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_verify_fails() {
|
||||
let cert = include_bytes!("../../../test/cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
let ca = include_bytes!("../../../test/alt_name_cert.pem");
|
||||
let ca = X509::from_pem(ca).unwrap();
|
||||
let chain = Stack::new().unwrap();
|
||||
|
||||
let mut store_bldr = X509StoreBuilder::new().unwrap();
|
||||
store_bldr.add_cert(&ca).unwrap();
|
||||
let store = store_bldr.build();
|
||||
|
||||
let mut context = X509StoreContext::new().unwrap();
|
||||
assert!(!context
|
||||
.init(&store, &cert, &chain, |c| c.verify_cert())
|
||||
.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_verify_revoked() {
|
||||
let cert = include_bytes!("../../../test/cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
let ca = include_bytes!("../../../test/root-ca.pem");
|
||||
let ca = X509::from_pem(ca).unwrap();
|
||||
let crl = include_bytes!("../../../test/crl.pem");
|
||||
let crl = X509CRL::from_pem(crl).unwrap();
|
||||
let chain = Stack::new().unwrap();
|
||||
|
||||
let mut store_bldr = X509StoreBuilder::new().unwrap();
|
||||
store_bldr.add_cert(&ca).unwrap();
|
||||
store_bldr.add_crl(crl).unwrap();
|
||||
store_bldr
|
||||
.param_mut()
|
||||
.set_flags(X509VerifyFlags::CRL_CHECK | X509VerifyFlags::CRL_CHECK_ALL);
|
||||
let store = store_bldr.build();
|
||||
|
||||
let mut context = X509StoreContext::new().unwrap();
|
||||
assert!(!context
|
||||
.init(&store, &cert, &chain, |c| c.verify_cert())
|
||||
.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_crl_signature() {
|
||||
let ca = include_bytes!("../../../test/root-ca.pem");
|
||||
let ca = X509::from_pem(ca).unwrap();
|
||||
|
||||
let crl = include_bytes!("../../../test/bad_sig.pem");
|
||||
let crl = X509CRL::from_pem(crl).unwrap();
|
||||
assert!(!crl.verify(&ca.public_key().unwrap()).unwrap());
|
||||
|
||||
let crl = include_bytes!("../../../test/crl.pem");
|
||||
let crl = X509CRL::from_pem(crl).unwrap();
|
||||
assert!(crl.verify(&ca.public_key().unwrap()).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_untrusted_valid_crl() {
|
||||
let cert = include_bytes!("../../../test/cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
let ca = include_bytes!("../../../test/root-ca.pem");
|
||||
let ca = X509::from_pem(ca).unwrap();
|
||||
let chain = Stack::new().unwrap();
|
||||
|
||||
let mut store_bldr = X509StoreBuilder::new().unwrap();
|
||||
store_bldr.add_cert(&ca).unwrap();
|
||||
store_bldr
|
||||
.param_mut()
|
||||
.set_flags(X509VerifyFlags::CRL_CHECK | X509VerifyFlags::CRL_CHECK_ALL);
|
||||
store_bldr.param_mut().set_time(1656633600); // 2022-07-01, everything is valid
|
||||
let store = store_bldr.build();
|
||||
|
||||
// cert is not revoked
|
||||
let crl = include_bytes!("../../../test/empty_crl.pem");
|
||||
let crl = X509CRL::from_pem(crl).unwrap();
|
||||
let mut context = X509StoreContext::new().unwrap();
|
||||
assert!(context
|
||||
.init(&store, &cert, &chain, |c| c
|
||||
.verify_cert_with_crls(stack_of(crl)))
|
||||
.unwrap());
|
||||
|
||||
// cert is revoked
|
||||
let crl = include_bytes!("../../../test/crl.pem");
|
||||
let crl = X509CRL::from_pem(crl).unwrap();
|
||||
let mut context = X509StoreContext::new().unwrap();
|
||||
context
|
||||
.init(&store, &cert, &chain, |c| {
|
||||
assert!(!c.verify_cert_with_crls(stack_of(crl)).unwrap());
|
||||
assert_eq!(c.verify_result(), Err(X509VerifyError::CERT_REVOKED));
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_untrusted_invalid_crl() {
|
||||
let cert = include_bytes!("../../../test/cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
let ca = include_bytes!("../../../test/root-ca.pem");
|
||||
let ca = X509::from_pem(ca).unwrap();
|
||||
let chain = Stack::new().unwrap();
|
||||
|
||||
let mut store_bldr = X509StoreBuilder::new().unwrap();
|
||||
store_bldr.add_cert(&ca).unwrap();
|
||||
store_bldr
|
||||
.param_mut()
|
||||
.set_flags(X509VerifyFlags::CRL_CHECK | X509VerifyFlags::CRL_CHECK_ALL);
|
||||
store_bldr.param_mut().set_time(1656633600); // 2022-07-01, everything is valid
|
||||
let store = store_bldr.build();
|
||||
|
||||
// this CRL was issued by a different CA (not in the trusted store)
|
||||
let crl = include_bytes!("../../../test/invalid_crl.pem");
|
||||
let crl = X509CRL::from_pem(crl).unwrap();
|
||||
let mut context = X509StoreContext::new().unwrap();
|
||||
context
|
||||
.init(&store, &cert, &chain, |c| {
|
||||
assert!(!c.verify_cert_with_crls(stack_of(crl)).unwrap());
|
||||
assert_eq!(c.verify_result(), Err(X509VerifyError::UNABLE_TO_GET_CRL));
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// this CRL has an invalid signature
|
||||
let crl = include_bytes!("../../../test/bad_sig.pem");
|
||||
let crl = X509CRL::from_pem(crl).unwrap();
|
||||
let mut context = X509StoreContext::new().unwrap();
|
||||
context
|
||||
.init(&store, &cert, &chain, |c| {
|
||||
assert!(!c.verify_cert_with_crls(stack_of(crl)).unwrap());
|
||||
assert_eq!(
|
||||
c.verify_result(),
|
||||
Err(X509VerifyError::CRL_SIGNATURE_FAILURE)
|
||||
);
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_revoked_serial_numbers() {
|
||||
let cert = include_bytes!("../../../test/cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
let cert_sn = cert.serial_number().to_bn().unwrap();
|
||||
|
||||
let crl = include_bytes!("../../../test/crl.pem");
|
||||
let crl = X509CRL::from_pem(crl).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
crl.revoked()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.filter(|revoked| revoked.serial_number().to_bn().unwrap() == cert_sn)
|
||||
.count(),
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serialize_crl() {
|
||||
let crl = include_bytes!("../../../test/crl.pem");
|
||||
let crl = X509CRL::from_pem(crl).unwrap();
|
||||
let digest = hex::encode(crl.digest(MessageDigest::sha1()).unwrap());
|
||||
|
||||
let serialized = crl.to_pem().unwrap();
|
||||
let crl_deserialized = X509CRL::from_pem(&serialized).unwrap();
|
||||
let new_digest = crl_deserialized.digest(MessageDigest::sha1()).unwrap();
|
||||
assert_eq!(digest, hex::encode(new_digest));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_crl_extensions() {
|
||||
let crl = include_bytes!("../../../test/crl.pem");
|
||||
let crl = X509CRL::from_pem(crl).unwrap();
|
||||
let extensions = crl.extensions();
|
||||
assert_eq!(
|
||||
extensions
|
||||
.unwrap()
|
||||
.iter()
|
||||
.filter(|ext| ext.object().nid() == Nid::AUTHORITY_KEY_IDENTIFIER)
|
||||
.count(),
|
||||
1
|
||||
);
|
||||
assert_eq!(
|
||||
extensions
|
||||
.unwrap()
|
||||
.iter()
|
||||
.filter(|ext| ext.object().nid() == Nid::CRL_NUMBER)
|
||||
.count(),
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_debug_crl() {
|
||||
let crl = include_bytes!("../../../test/crl.pem");
|
||||
let crl = X509CRL::from_pem(crl).unwrap();
|
||||
let debugged = format!("{:#?}", crl);
|
||||
assert!(debugged.contains(r#"countryName = "AU""#));
|
||||
assert!(debugged.contains(r#"stateOrProvinceName = "Some-State""#));
|
||||
assert!(debugged.contains(r#"organizationName = "Internet Widgits Pty Ltd""#));
|
||||
assert!(debugged.contains(r#"last_update: Jun 21 20:22:02 2022 GMT"#));
|
||||
assert!(debugged.contains(r#"next_update: Jun 18 20:22:02 2032 GMT"#));
|
||||
assert!(debugged.contains(r#"revocation_date: Jun 21 20:21:55 2022 GMT"#));
|
||||
assert!(debugged.contains(r#"serial_number: "8771f7bdee982fa5""#));
|
||||
assert!(debugged.contains(r#"object_nid: X509v3 Authority Key Identifier"#));
|
||||
assert!(debugged.contains(r#"object_nid: X509v3 CRL Number"#));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_custom_time_valid() {
|
||||
let cert = include_bytes!("../../../test/cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
let ca = include_bytes!("../../../test/root-ca.pem");
|
||||
let ca = X509::from_pem(ca).unwrap();
|
||||
let chain = Stack::new().unwrap();
|
||||
|
||||
let mut store_bldr = X509StoreBuilder::new().unwrap();
|
||||
store_bldr.add_cert(&ca).unwrap();
|
||||
store_bldr.param_mut().set_time(1656633600); // 2022-07-01, everything is valid
|
||||
let store = store_bldr.build();
|
||||
|
||||
let mut context = X509StoreContext::new().unwrap();
|
||||
assert!(context
|
||||
.init(&store, &cert, &chain, |c| c.verify_cert())
|
||||
.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_custom_time_expired() {
|
||||
let cert = include_bytes!("../../../test/cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
let ca = include_bytes!("../../../test/root-ca.pem");
|
||||
let ca = X509::from_pem(ca).unwrap();
|
||||
let chain = Stack::new().unwrap();
|
||||
|
||||
let mut store_bldr = X509StoreBuilder::new().unwrap();
|
||||
store_bldr.add_cert(&ca).unwrap();
|
||||
store_bldr.param_mut().set_time(1786838400); // 2026-08-16, after the root and leaf expiration
|
||||
let store = store_bldr.build();
|
||||
|
||||
let mut context = X509StoreContext::new().unwrap();
|
||||
assert!(!context
|
||||
.init(&store, &cert, &chain, |c| c.verify_cert())
|
||||
.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_custom_time_too_soon() {
|
||||
let cert = include_bytes!("../../../test/cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
let ca = include_bytes!("../../../test/root-ca.pem");
|
||||
let ca = X509::from_pem(ca).unwrap();
|
||||
let chain = Stack::new().unwrap();
|
||||
|
||||
let mut store_bldr = X509StoreBuilder::new().unwrap();
|
||||
store_bldr.add_cert(&ca).unwrap();
|
||||
store_bldr.param_mut().set_time(1262304000); // 2010-01-01, before the root and leaf issuance
|
||||
let store = store_bldr.build();
|
||||
|
||||
let mut context = X509StoreContext::new().unwrap();
|
||||
assert!(!context
|
||||
.init(&store, &cert, &chain, |c| c.verify_cert())
|
||||
.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_custom_time_crl() {
|
||||
let cert = include_bytes!("../../../test/cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
let ca = include_bytes!("../../../test/root-ca.pem");
|
||||
let ca = X509::from_pem(ca).unwrap();
|
||||
let crl = include_bytes!("../../../test/crl.pem");
|
||||
let crl = X509CRL::from_pem(crl).unwrap();
|
||||
let chain = Stack::new().unwrap();
|
||||
|
||||
let mut store_bldr = X509StoreBuilder::new().unwrap();
|
||||
store_bldr.add_cert(&ca).unwrap();
|
||||
store_bldr
|
||||
.param_mut()
|
||||
.set_flags(X509VerifyFlags::CRL_CHECK | X509VerifyFlags::CRL_CHECK_ALL);
|
||||
store_bldr.param_mut().set_time(1640995200); // 2022-01-01, before the CRL's issue date
|
||||
let store = store_bldr.build();
|
||||
|
||||
let mut context = X509StoreContext::new().unwrap();
|
||||
assert!(!context
|
||||
.init(&store, &cert, &chain, |c| c
|
||||
.verify_cert_with_crls(stack_of(crl)))
|
||||
.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_save_subject_der() {
|
||||
let cert = include_bytes!("../../../test/cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
|
||||
let der = cert.subject_name().to_der().unwrap();
|
||||
println!("der: {der:?}");
|
||||
assert!(!der.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_load_subject_der() {
|
||||
// The subject from ../../../test/cert.pem
|
||||
const SUBJECT_DER: &[u8] = &[
|
||||
48, 90, 49, 11, 48, 9, 6, 3, 85, 4, 6, 19, 2, 65, 85, 49, 19, 48, 17, 6, 3, 85, 4, 8, 12,
|
||||
10, 83, 111, 109, 101, 45, 83, 116, 97, 116, 101, 49, 33, 48, 31, 6, 3, 85, 4, 10, 12, 24,
|
||||
73, 110, 116, 101, 114, 110, 101, 116, 32, 87, 105, 100, 103, 105, 116, 115, 32, 80, 116,
|
||||
121, 32, 76, 116, 100, 49, 19, 48, 17, 6, 3, 85, 4, 3, 12, 10, 102, 111, 111, 98, 97, 114,
|
||||
46, 99, 111, 109,
|
||||
];
|
||||
X509Name::from_der(SUBJECT_DER).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_check_ip_asc() {
|
||||
// Covers 127.0.0.1 and 0:0:0:0:0:0:0:1
|
||||
let cert = include_bytes!("../../../test/alt_name_cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
|
||||
assert!(cert.check_ip_asc("127.0.0.1").unwrap());
|
||||
assert!(!cert.check_ip_asc("127.0.0.2").unwrap());
|
||||
|
||||
assert!(cert.check_ip_asc("0:0:0:0:0:0:0:1").unwrap());
|
||||
assert!(!cert.check_ip_asc("0:0:0:0:0:0:0:2").unwrap());
|
||||
}
|
||||
@ -1,104 +0,0 @@
|
||||
//! See https://github.com/google/boringssl/blob/cc696073cffe7978d489297fbdeac4c0030384aa/crypto/x509/x509_test.cc#L3977-L3980
|
||||
|
||||
use crate::stack::Stack;
|
||||
use crate::x509::store::X509StoreBuilder;
|
||||
use crate::x509::verify::{X509VerifyFlags, X509VerifyParamRef};
|
||||
use crate::x509::{X509Ref, X509StoreContext, X509VerifyError, X509VerifyResult, X509};
|
||||
|
||||
#[test]
|
||||
fn test_verify_cert() {
|
||||
let root2 = X509::from_pem(include_bytes!("../../../test/root-ca-2.pem")).unwrap();
|
||||
let root1 = X509::from_pem(include_bytes!("../../../test/root-ca.pem")).unwrap();
|
||||
let root1_cross = X509::from_pem(include_bytes!("../../../test/root-ca-cross.pem")).unwrap();
|
||||
let intermediate = X509::from_pem(include_bytes!("../../../test/intermediate-ca.pem")).unwrap();
|
||||
let leaf = X509::from_pem(include_bytes!("../../../test/cert-with-intermediate.pem")).unwrap();
|
||||
|
||||
assert_eq!(Ok(()), verify(&leaf, &[&root1], &[&intermediate], |_| {}));
|
||||
|
||||
#[cfg(not(feature = "legacy-compat-deprecated"))]
|
||||
assert_eq!(
|
||||
Ok(()),
|
||||
verify(
|
||||
&leaf,
|
||||
&[&root1, &root2],
|
||||
&[&intermediate, &root1_cross],
|
||||
|_| {}
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(feature = "legacy-compat-deprecated")]
|
||||
assert_eq!(
|
||||
Err(X509VerifyError::CERT_HAS_EXPIRED),
|
||||
verify(
|
||||
&leaf,
|
||||
&[&root1, &root2],
|
||||
&[&intermediate, &root1_cross],
|
||||
|_| {}
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Ok(()),
|
||||
verify(
|
||||
&leaf,
|
||||
&[&root1, &root2],
|
||||
&[&intermediate, &root1_cross],
|
||||
|param| param.set_flags(X509VerifyFlags::TRUSTED_FIRST),
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Err(X509VerifyError::CERT_HAS_EXPIRED),
|
||||
verify(
|
||||
&leaf,
|
||||
&[&root1, &root2],
|
||||
&[&intermediate, &root1_cross],
|
||||
|param| param.clear_flags(X509VerifyFlags::TRUSTED_FIRST),
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Ok(()),
|
||||
verify(&leaf, &[&root1], &[&intermediate, &root1_cross], |param| {
|
||||
param.clear_flags(X509VerifyFlags::TRUSTED_FIRST);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
fn verify(
|
||||
cert: &X509Ref,
|
||||
trusted: &[&X509Ref],
|
||||
untrusted: &[&X509Ref],
|
||||
configure: impl FnOnce(&mut X509VerifyParamRef),
|
||||
) -> X509VerifyResult {
|
||||
let trusted = {
|
||||
let mut builder = X509StoreBuilder::new().unwrap();
|
||||
|
||||
for cert in trusted {
|
||||
builder.add_cert(cert).unwrap();
|
||||
}
|
||||
|
||||
builder.build()
|
||||
};
|
||||
|
||||
let untrusted = {
|
||||
let mut stack = Stack::new().unwrap();
|
||||
|
||||
for cert in untrusted {
|
||||
stack.push((**cert).to_owned()).unwrap();
|
||||
}
|
||||
|
||||
stack
|
||||
};
|
||||
|
||||
let mut store_ctx = X509StoreContext::new().unwrap();
|
||||
|
||||
store_ctx
|
||||
.init(&trusted, cert, &untrusted, |ctx| {
|
||||
configure(ctx.verify_param_mut());
|
||||
ctx.verify_cert().unwrap();
|
||||
|
||||
Ok(ctx.verify_result())
|
||||
})
|
||||
.expect("failed to obtain X509VerifyResult")
|
||||
}
|
||||
@ -1,16 +1,13 @@
|
||||
use crate::ffi;
|
||||
use foreign_types::{ForeignType, ForeignTypeRef};
|
||||
use libc::{c_int, c_uint, c_ulong, time_t};
|
||||
use openssl_macros::corresponds;
|
||||
use foreign_types::ForeignTypeRef;
|
||||
use libc::c_uint;
|
||||
use std::net::IpAddr;
|
||||
|
||||
use crate::cvt;
|
||||
use crate::error::ErrorStack;
|
||||
use crate::{cvt, cvt_p};
|
||||
|
||||
bitflags! {
|
||||
/// Flags used to check an `X509` certificate.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Hash)]
|
||||
#[repr(transparent)]
|
||||
pub struct X509CheckFlags: c_uint {
|
||||
const ALWAYS_CHECK_SUBJECT = ffi::X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT as _;
|
||||
const NO_WILDCARDS = ffi::X509_CHECK_FLAG_NO_WILDCARDS as _;
|
||||
@ -18,35 +15,9 @@ bitflags! {
|
||||
const MULTI_LABEL_WILDCARDS = ffi::X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS as _;
|
||||
const SINGLE_LABEL_SUBDOMAINS = ffi::X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS as _;
|
||||
const NEVER_CHECK_SUBJECT = ffi::X509_CHECK_FLAG_NEVER_CHECK_SUBJECT as _;
|
||||
#[cfg(feature = "underscore-wildcards")]
|
||||
const UNDERSCORE_WILDCARDS = ffi::X509_CHECK_FLAG_UNDERSCORE_WILDCARDS as _;
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// Flags used to check an `X509` certificate.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Hash)]
|
||||
#[repr(transparent)]
|
||||
#[doc(alias = "X509Flags")]
|
||||
pub struct X509VerifyFlags: c_ulong {
|
||||
const CB_ISSUER_CHECK = ffi::X509_V_FLAG_CB_ISSUER_CHECK as _;
|
||||
const USE_CHECK_TIME = ffi::X509_V_FLAG_USE_CHECK_TIME as _;
|
||||
const CRL_CHECK = ffi::X509_V_FLAG_CRL_CHECK as _;
|
||||
const CRL_CHECK_ALL = ffi::X509_V_FLAG_CRL_CHECK_ALL as _;
|
||||
const IGNORE_CRITICAL = ffi::X509_V_FLAG_IGNORE_CRITICAL as _;
|
||||
const X509_STRICT = ffi::X509_V_FLAG_X509_STRICT as _;
|
||||
const ALLOW_PROXY_CERTS = ffi::X509_V_FLAG_ALLOW_PROXY_CERTS as _;
|
||||
const POLICY_CHECK = ffi::X509_V_FLAG_POLICY_CHECK as _;
|
||||
const EXPLICIT_POLICY = ffi::X509_V_FLAG_EXPLICIT_POLICY as _;
|
||||
const INHIBIT_ANY = ffi::X509_V_FLAG_INHIBIT_ANY as _;
|
||||
const INHIBIT_MAP = ffi::X509_V_FLAG_INHIBIT_MAP as _;
|
||||
const NOTIFY_POLICY = ffi::X509_V_FLAG_NOTIFY_POLICY as _;
|
||||
const EXTENDED_CRL_SUPPORT = ffi::X509_V_FLAG_EXTENDED_CRL_SUPPORT as _;
|
||||
const USE_DELTAS = ffi::X509_V_FLAG_USE_DELTAS as _;
|
||||
const CHECK_SS_SIGNATURE = ffi::X509_V_FLAG_CHECK_SS_SIGNATURE as _;
|
||||
const TRUSTED_FIRST = ffi::X509_V_FLAG_TRUSTED_FIRST as _;
|
||||
const PARTIAL_CHAIN = ffi::X509_V_FLAG_PARTIAL_CHAIN as _;
|
||||
const NO_ALT_CHAINS = ffi::X509_V_FLAG_NO_ALT_CHAINS as _;
|
||||
#[deprecated(since = "0.10.6", note = "renamed to NO_WILDCARDS")]
|
||||
const FLAG_NO_WILDCARDS = ffi::X509_CHECK_FLAG_NO_WILDCARDS as _;
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,90 +29,39 @@ foreign_type_and_impl_send_sync! {
|
||||
pub struct X509VerifyParam;
|
||||
}
|
||||
|
||||
impl X509VerifyParam {
|
||||
/// Create an X509VerifyParam
|
||||
#[corresponds(X509_VERIFY_PARAM_new)]
|
||||
pub fn new() -> Result<Self, ErrorStack> {
|
||||
unsafe {
|
||||
ffi::init();
|
||||
let handle = cvt_p(ffi::X509_VERIFY_PARAM_new())?;
|
||||
Ok(Self::from_ptr(handle))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl X509VerifyParamRef {
|
||||
/// Set verification flags.
|
||||
#[corresponds(X509_VERIFY_PARAM_set_flags)]
|
||||
pub fn set_flags(&mut self, flags: X509VerifyFlags) {
|
||||
unsafe {
|
||||
cvt(ffi::X509_VERIFY_PARAM_set_flags(
|
||||
self.as_ptr(),
|
||||
flags.bits(),
|
||||
))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear verification flags.
|
||||
#[corresponds(X509_VERIFY_PARAM_clear_flags)]
|
||||
pub fn clear_flags(&mut self, flags: X509VerifyFlags) {
|
||||
unsafe {
|
||||
cvt(ffi::X509_VERIFY_PARAM_clear_flags(
|
||||
self.as_ptr(),
|
||||
flags.bits(),
|
||||
))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Set the host flags.
|
||||
#[corresponds(X509_VERIFY_PARAM_set_hostflags)]
|
||||
///
|
||||
/// This corresponds to [`X509_VERIFY_PARAM_set_hostflags`].
|
||||
///
|
||||
/// [`X509_VERIFY_PARAM_set_hostflags`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_VERIFY_PARAM_set_hostflags.html
|
||||
pub fn set_hostflags(&mut self, hostflags: X509CheckFlags) {
|
||||
unsafe {
|
||||
ffi::X509_VERIFY_PARAM_set_hostflags(self.as_ptr(), hostflags.bits());
|
||||
ffi::X509_VERIFY_PARAM_set_hostflags(self.as_ptr(), hostflags.bits);
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets verification flags.
|
||||
#[corresponds(X509_VERIFY_PARAM_get_flags)]
|
||||
#[must_use]
|
||||
pub fn flags(&self) -> X509VerifyFlags {
|
||||
let bits = unsafe { ffi::X509_VERIFY_PARAM_get_flags(self.as_ptr()) };
|
||||
X509VerifyFlags::from_bits_retain(bits)
|
||||
}
|
||||
|
||||
/// Set the expected DNS hostname.
|
||||
#[corresponds(X509_VERIFY_PARAM_set1_host)]
|
||||
///
|
||||
/// This corresponds to [`X509_VERIFY_PARAM_set1_host`].
|
||||
///
|
||||
/// [`X509_VERIFY_PARAM_set1_host`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_VERIFY_PARAM_set1_host.html
|
||||
pub fn set_host(&mut self, host: &str) -> Result<(), ErrorStack> {
|
||||
unsafe {
|
||||
// len == 0 means "run strlen" :(
|
||||
let raw_host = if host.is_empty() { "\0" } else { host };
|
||||
cvt(ffi::X509_VERIFY_PARAM_set1_host(
|
||||
self.as_ptr(),
|
||||
raw_host.as_ptr().cast(),
|
||||
host.as_ptr() as *const _,
|
||||
host.len(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the expected email address.
|
||||
#[corresponds(X509_VERIFY_PARAM_set1_email)]
|
||||
pub fn set_email(&mut self, email: &str) -> Result<(), ErrorStack> {
|
||||
unsafe {
|
||||
// len == 0 means "run strlen" :(
|
||||
let raw_email = if email.is_empty() { "\0" } else { email };
|
||||
cvt(ffi::X509_VERIFY_PARAM_set1_email(
|
||||
self.as_ptr(),
|
||||
raw_email.as_ptr().cast(),
|
||||
email.len(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the expected IPv4 or IPv6 address.
|
||||
#[corresponds(X509_VERIFY_PARAM_set1_ip)]
|
||||
///
|
||||
/// This corresponds to [`X509_VERIFY_PARAM_set1_ip`].
|
||||
///
|
||||
/// [`X509_VERIFY_PARAM_set1_ip`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_VERIFY_PARAM_set1_ip.html
|
||||
pub fn set_ip(&mut self, ip: IpAddr) -> Result<(), ErrorStack> {
|
||||
unsafe {
|
||||
let mut buf = [0; 16];
|
||||
@ -157,29 +77,10 @@ impl X509VerifyParamRef {
|
||||
};
|
||||
cvt(ffi::X509_VERIFY_PARAM_set1_ip(
|
||||
self.as_ptr(),
|
||||
buf.as_ptr().cast(),
|
||||
buf.as_ptr() as *const _,
|
||||
len,
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the verification time, where time is of type time_t, traditionaly defined as seconds since the epoch
|
||||
#[corresponds(X509_VERIFY_PARAM_set_time)]
|
||||
pub fn set_time(&mut self, time: time_t) {
|
||||
unsafe { ffi::X509_VERIFY_PARAM_set_time(self.as_ptr(), time) }
|
||||
}
|
||||
|
||||
/// Set the verification depth
|
||||
#[corresponds(X509_VERIFY_PARAM_set_depth)]
|
||||
pub fn set_depth(&mut self, depth: c_int) {
|
||||
unsafe { ffi::X509_VERIFY_PARAM_set_depth(self.as_ptr(), depth) }
|
||||
}
|
||||
|
||||
/// Copies parameters from `src`.
|
||||
///
|
||||
/// If a parameter is unset in `src`, the existing value in `self`` is preserved.
|
||||
#[corresponds(X509_VERIFY_PARAM_set1)]
|
||||
pub fn copy_from(&mut self, src: &Self) -> Result<(), ErrorStack> {
|
||||
unsafe { cvt(ffi::X509_VERIFY_PARAM_set1(self.as_ptr(), src.as_ptr())) }
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +0,0 @@
|
||||
-----BEGIN X509 CRL-----
|
||||
MIIBqTCBkjANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwK
|
||||
U29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkFw0y
|
||||
MjA2MTYyMjQ0NDFaFw0yMzA2MTYyMjQ0NDFaMBwwGgIJAIdx973umC+lFw0yMjA2
|
||||
MTYyMjIxMDVaMA0GCSqGSIb3DQEBBQUAA4IBAQApqFdwm46jxkJK8J5kprGm6cp8
|
||||
b7XMKB1epvhJGIkXHjp7O+2rxYGIExcNlM7jPcwqnUE0E50qGrqSMEupmtaBH03a
|
||||
fmmDKyhLema7KD64UaERLqWjaW2DPeX9VX6vL4ECc6zTVLxfmYzxVt6A9hhqCm3b
|
||||
fu8klWczGTa79r/WhTbA7uVf5+OI98da5tlxw+DlAQfqd34L2qq5aFg2dcTGqIdz
|
||||
3pxP6UlTyj0ZPK3tUtTpIURVO2/MX3j5V+QjWz81UeCv0gQcmOiIVSRUGwi9c6JY
|
||||
jDqBIvDY6df0riz5is1SS+D94sp1iovBlluwpq4kB8xyDuwt7vblkzleS2YU
|
||||
-----END X509 CRL-----
|
||||
@ -1,20 +0,0 @@
|
||||
notAfter=Aug 12 11:30:03 2026 GMT
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDKDCCAhACFGwwuilXOHjBjQ584FD9drp9Uh/LMA0GCSqGSIb3DQEBCwUAMEUx
|
||||
CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl
|
||||
cm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMjMxMjE4MTEzMDAzWhcNMjYwODEyMTEz
|
||||
MDAzWjBcMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UE
|
||||
BwwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRUwEwYDVQQDDAwqLmZvb2Jhci5j
|
||||
b20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCo9CWMRLMXo1CF/iOR
|
||||
h9B4NhtJF/8tR9PlG95sNvyWuQQ/8jfev+8zErplxfLkt0pJqcoiZG8g9NU0kU6o
|
||||
5T+/1QgZclCAoZaS0Jqxmoo2Yk/1Qsj16pnMBc10uSDk6V9aJSX1vKwONVNSwiHA
|
||||
1MhX+i7Wf7/K0niq+k7hOkhleFkWgZtUq41gXh1VfOugka7UktYnk9mrBbAMjmal
|
||||
oZNn2pMMAQxVg4ThiLm3zvuWqvXASWzUZc7IAd1GbN4AtDuhs252eqE9E4iTHk7F
|
||||
14wAS1JWqv666hReGHrmZJGx0xQTM9vPD1HN5t2U3KTfhO/mTlAUWVyg9tCtOzbo
|
||||
Kgs1AgMBAAEwDQYJKoZIhvcNAQELBQADggEBAHG83qKMl5bPoL2s7TaJZ909NaQO
|
||||
4C69ueXlD4HJEFe7L9mkeQoDaF7RwWSBwN2RZT5hzQhghRotqLA06XwKbQHji/R7
|
||||
sYYVUHunobFUHsr51tFN1BIDoAWJa0N2rm/OxbcK471eWNKjMiS2vvvPdaMxxHAx
|
||||
IsjAJBJec4IxNIUNNKqCS/xNYcdiyrmmU3oFWGqb0As/eDOBw0Amd0aayasFJrRV
|
||||
3KZI5OcFg/J3XvdaxMJD+RPyUysKRXg6K8jzYc/PB8LhWVXpLxjEzeO2IHCaZprh
|
||||
dUTP8+Ob+ioxujvlslxc4nrrUD5EWwnpEIr7e4af27JHQVaNyHbRw6wI2uk=
|
||||
-----END CERTIFICATE-----
|
||||
@ -1,20 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDMzCCAhsCFBEiNxpuknaO7Pw1Yi88UW4aiGo0MA0GCSqGSIb3DQEBCwUAMFIx
|
||||
CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMS4wLAYDVQQKDCVJbnRl
|
||||
cm5ldCBXaWRnaXRzIFB0eSBMdGQgSW50ZXJtZWRpYXRlMB4XDTI0MDEwMzE0MjIz
|
||||
MFoXDTI2MDgxMjE0MjIzMFowWjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUt
|
||||
U3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDETMBEGA1UE
|
||||
AwwKZm9vYmFyLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKj0
|
||||
JYxEsxejUIX+I5GH0Hg2G0kX/y1H0+Ub3mw2/Ja5BD/yN96/7zMSumXF8uS3Skmp
|
||||
yiJkbyD01TSRTqjlP7/VCBlyUIChlpLQmrGaijZiT/VCyPXqmcwFzXS5IOTpX1ol
|
||||
JfW8rA41U1LCIcDUyFf6LtZ/v8rSeKr6TuE6SGV4WRaBm1SrjWBeHVV866CRrtSS
|
||||
1ieT2asFsAyOZqWhk2fakwwBDFWDhOGIubfO+5aq9cBJbNRlzsgB3UZs3gC0O6Gz
|
||||
bnZ6oT0TiJMeTsXXjABLUlaq/rrqFF4YeuZkkbHTFBMz288PUc3m3ZTcpN+E7+ZO
|
||||
UBRZXKD20K07NugqCzUCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEANWlOvyLEHdPV
|
||||
8rMdfqLTZZyA79L1N3bP1FWS97fF36Y9EnTKChenwkBob1abY4jQ2/LICKND+ux8
|
||||
xDlmMlYRH4aM5bXAjOcdpmq9R9SuzsK/2m79xONF//AX4zb0s5b+QEwdYkfJ5jiO
|
||||
xMrnatwHQhFvQIQvuTo2o0WZEnkubNYDxVh7UOv9cOQjwm0+58CIEG5SHR9grG5u
|
||||
TTswu7DswgpfSCKKPaFCF4pWxLfryYwadO0/4Ot/ZbElbAdJYC8CI1QC14knk2cD
|
||||
0ZG9jaVPP9wCAt/ZIu8NbsZN7DNbISaXVfMju+xSdey8B3FLRkLq9TKmnLum5LR2
|
||||
TyM6hDIh8A==
|
||||
-----END CERTIFICATE-----
|
||||
@ -1,13 +0,0 @@
|
||||
-----BEGIN X509 CRL-----
|
||||
MIIB3jCBxwIBATANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJBVTETMBEGA1UE
|
||||
CAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRk
|
||||
Fw0yMjA2MjEyMDIyMDJaFw0zMjA2MTgyMDIyMDJaMBwwGgIJAIdx973umC+lFw0y
|
||||
MjA2MjEyMDIxNTVaoDAwLjAfBgNVHSMEGDAWgBRs06UDqw1fLMmNipyIp4h3uDf9
|
||||
mjALBgNVHRQEBAICEAEwDQYJKoZIhvcNAQELBQADggEBABgsWr/sVZqXm1AzgGCJ
|
||||
JBMJW1oUY18aqroxo4kAIoI4QveLmHxi1Wm2I4dqdc6pM09SJhU5v5CfqpJ2BDc0
|
||||
JBfEk8KKi5O/OYyLcUKa4dEpAlYPgeDyLc6zF8rGLtJoDIYuk4JUeuuByoXt0Sh+
|
||||
7vx6UzuI7EH+mr4ZjnyAkD3f9jZy+mDcTm/+0REuh4iZ1AotE2YuQWQgxc1Y8TlD
|
||||
eK+ks1zBKI23s0hPBxJQunmz2k3Uu9Yf+Sg0KxCiDgJZWFiGSw/6DtnT0oYAFGaD
|
||||
mCyQWtwmS6zGBg+p76wNXkwyJMVvSDgrXSZ55bmImNmA38yKqOLOpB5i+FAS3r4V
|
||||
ApQ=
|
||||
-----END X509 CRL-----
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1 +0,0 @@
|
||||
§5D$lþ°SLb~Ê.<11>V<À.j¢ç¯:}´rˆ¶…
|
||||
@ -1,11 +0,0 @@
|
||||
-----BEGIN X509 CRL-----
|
||||
MIIBijB0MA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApT
|
||||
b21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQXDTIy
|
||||
MDYxNjIyNDU1NVoXDTIzMDYxNjIyNDU1NVowDQYJKoZIhvcNAQEFBQADggEBAEtx
|
||||
nBr3aI0qlegMVJsJn3GfkMzaPVTSTHuw76Dzdl9eGDj0hXzAzZW5k4WBHvaInzNT
|
||||
NKkeISoQJHLH981R9sQU2zA8sTESLTJGyCFu05Y6XhdmqX4ywmzVRjL6p/aoHNdZ
|
||||
H1mxgK16wG+Sv0pd+9qNJgC/cNFmNbWzbiEAi5kID4IUxSmId/FZsXsms1EjqDH4
|
||||
DFIwLIQO/kR5zwE5fZ5EjqUBdAxoSVHfD+OPKl4x2t8CHMmao+ih2FOfd70+NLBD
|
||||
2oxaJMjZL/SIf8vYxjpjimMR+7yJ5J5P1j/RBfG3LwwUDP0RtWLIvRQo/dZUyXTg
|
||||
LuC1vNuUoObe12z/NQ4=
|
||||
-----END X509 CRL-----
|
||||
@ -1,27 +0,0 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEApyZi0joAIH8M7GaitH27lQs1XZaXwv5PI9bIeSkIX4BafIcU
|
||||
I7hdPLreJoP6EsuoswXyhgZU8FQvNRjMAGPzvVviG+vY/l6jFI+JqW7Pphr35QJO
|
||||
jqxwW/K/wYei0FS6C3SF+CJ2yIgG4YlJM9Hoq/EpfIxmyuZ2yHD/JiQ5kxNP7vKT
|
||||
77f7k5zpCVG1kiQZKQT4iysQDz2e1ozi3W7NCDqnvM9+EX1DREiCqRUwuzIuTlkA
|
||||
Z+8HpET5wS2Pm5uJQXDCid0slYMcuOL6JoHekbs69wkt4powcPPhXWoTCyUjpqsx
|
||||
w0KPiBzViJUsd8JUq8ge+jVqP2pzvkQk4BWTMwIDAQABAoIBAFBiEHIjPH5kOzXQ
|
||||
4fxE3xn9KuvYCSHYJP0KRJyn1AQBeQKb/15yQjx7bWw+WdwCHx4BBTHZB64P/ifd
|
||||
xfWGG+h7sJBW6qLhpjG0GbLmvGuYWpDCfD72xI4jfn42mWDw7gumPOsov9EOQaji
|
||||
2dZW4zsVHitsZd672HHqjXmtQBbvEcm/cuPoL2/s9crKwHbF90kb9BOYcz+/U138
|
||||
zPfXFM5AbivpiQHA7CmMrQ833op42fQCfrxGs1XkZPO30EPpCtmHE6+Xhv+GGWp/
|
||||
EF7D6pwkT4OElgzAP6cthPqZOtQLE2LCM/mTy/cFRtrBjKPlZ/UBrxjaLI1FHoAk
|
||||
TYX81GECgYEA451EWFCrX5vWEMJE7zCALiQbxxOhQCIEufvkxUvDnsqjTPj/ylpZ
|
||||
usu+YC5BcgwVxjso8dEL8pcd8pAFVLeX8wBDeGfTdhUmPPZmx8ViFbZTVq/Tz+Jw
|
||||
tHo0foq8gfTLJrvdzuyFEHR6z5O16EtKOa+G1gIF/atV8hO/oRXLXvECgYEAu/7B
|
||||
SpzbBJoxame27uaakYYVKuAD2wVAmfP9XuTV6IvE845sgmzTYURxSnO0RZCuNj5x
|
||||
7u//HdGoFuA3o4Un4XLx1qot2op9ql1xuD/V3aqrvhpzvoCklks6t+PW3/Sef6TH
|
||||
21TOCahpoQqD/UrSrHEJXC7lmkfcPCir3QflnGMCgYBV2AFnwXzwwShaB7rR7xvY
|
||||
yxuC2H9vXaUks8DTPEDaCZjPNfXaznqa/a6ePbPHHJG1wqgtk2cLJj1QN0sbaWaw
|
||||
akAIEDhrh4x1X4TiASp9/9askgGznLZfCtvzgcWYycc4o5ADM6b3zsZmtVHc+1BS
|
||||
M0YKPpcd1dnDQ/l4+mxKMQKBgQCenCB+j/plVqaMjLaVty//yW2AgAIgvryzZ1yE
|
||||
vHMRQSNJDgfUvnZVIUaoNxiIfLnPAD5mBkxq3yF/M2sd5lEwcCdEIs6PDLtbin1Q
|
||||
o2MQI1fFC1JODwFN4GjJD0ySJTO4o9EO5uzyzwlXmqSjhoZagQARq2uCEFDq3LGr
|
||||
yWba2wKBgDT+5qjFweI5RmE1pDvUH9gOPNflyOpyjnbueunmTn1rzcRhx9xgM3QB
|
||||
ehWCRR1Y/vAnu5uupf0rG/Y/gtvIVyC3F+0csNox7T9e0t4sdgORYOVWbvsIF2t9
|
||||
2HYjY782ws3EBF5yNKJDgV2sNjA0Wpb6lahkxRut314jnwGplR2w
|
||||
-----END RSA PRIVATE KEY-----
|
||||
@ -1,21 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDdTCCAl2gAwIBAgIUE+pcEHU5e4wkMpp2qoyHy+wH8aMwDQYJKoZIhvcNAQEL
|
||||
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
|
||||
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNDAxMDMxMDM0MDdaFw0yNjA4
|
||||
MTIxMDM0MDdaMFIxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMS4w
|
||||
LAYDVQQKDCVJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQgSW50ZXJtZWRpYXRlMIIB
|
||||
IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApyZi0joAIH8M7GaitH27lQs1
|
||||
XZaXwv5PI9bIeSkIX4BafIcUI7hdPLreJoP6EsuoswXyhgZU8FQvNRjMAGPzvVvi
|
||||
G+vY/l6jFI+JqW7Pphr35QJOjqxwW/K/wYei0FS6C3SF+CJ2yIgG4YlJM9Hoq/Ep
|
||||
fIxmyuZ2yHD/JiQ5kxNP7vKT77f7k5zpCVG1kiQZKQT4iysQDz2e1ozi3W7NCDqn
|
||||
vM9+EX1DREiCqRUwuzIuTlkAZ+8HpET5wS2Pm5uJQXDCid0slYMcuOL6JoHekbs6
|
||||
9wkt4powcPPhXWoTCyUjpqsxw0KPiBzViJUsd8JUq8ge+jVqP2pzvkQk4BWTMwID
|
||||
AQABo1AwTjAdBgNVHQ4EFgQUZ3ELefSmoenETTa39CQwVzdoLZcwHwYDVR0jBBgw
|
||||
FoAUbNOlA6sNXyzJjYqciKeId7g3/ZowDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0B
|
||||
AQsFAAOCAQEAHCXvCU1XQM5a7hhgrMcKQRto9GEVIljnPv5H7x+wPvCfR1By/kzI
|
||||
fsl+hA1q02ymLtOW16aq4si4exsQl4SktC+5hyhu0yOCevRYXCcrh5NrNbwTFnK/
|
||||
LIP4dRz4XBxC9pYg0rqvo+v64at6EBTXxYfBHo9Cj0QuZIJoYmGEOojdE0PudZdc
|
||||
b1iuXk9FZlUueFq8uSkHD7EpxonPS9iWQA5dbw1Q+0hWqvA/npAvFXBHkF/Jyaht
|
||||
fpqoUrr4LYUr/ShC1IVHG7TAEElnOGz0dY6uxkr1B6YxBvCJ6gesk2cJBC1i3g9I
|
||||
9xvt4zxQNQynX0IHcar8xfgZqD4ZdWQY0w==
|
||||
-----END CERTIFICATE-----
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user