Compare commits

..

2 Commits

Author SHA1 Message Date
minish 751088d7e0
Support my laptop 2025-11-04 18:51:47 -05:00
minish 60ab50e89e
Add barbosshack prefix patch 2025-11-04 17:12:42 -05:00
37 changed files with 1585 additions and 1096 deletions

4
.github/FUNDING.yml vendored
View File

@ -3,7 +3,7 @@
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username open_collective: # Replace with a single Open Collective username
ko_fi: ko_fi: '0x676e67'
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username liberapay: # Replace with a single Liberapay username
@ -12,4 +12,4 @@ lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cl
polar: # Replace with a single Polar username polar: # Replace with a single Polar username
buy_me_a_coffee: # Replace with a single Buy Me a Coffee username buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
thanks_dev: # Replace with a single thanks.dev username thanks_dev: # Replace with a single thanks.dev username
custom: ['https://github.com/0x676e67/0x676e67/blob/main/SPONSOR.md'] custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

View File

@ -25,8 +25,8 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Install Rustfmt - name: Install Rust
run: rustup default stable && rustup component add rustfmt run: rustup update stable && rustup default stable
- name: Check formatting - name: Check formatting
run: cargo fmt --all -- --check run: cargo fmt --all -- --check
@ -38,11 +38,10 @@ jobs:
with: with:
submodules: 'recursive' submodules: 'recursive'
- name: Install Rust - name: Install Rust
run: rustup update --no-self-update stable && rustup default stable && rustup component add clippy run: rustup update stable && rustup default stable
- name: Get rust version - name: Get rust version
id: rust-version id: rust-version
run: | run: echo "::set-output name=version::$(rustc --version)"
echo "version=$(rustc --version)" >> $GITHUB_OUTPUT
- name: Cache cargo index - name: Cache cargo index
uses: actions/cache@v4 uses: actions/cache@v4
with: with:
@ -67,7 +66,7 @@ jobs:
- name: Run clippy - name: Run clippy
run: cargo clippy --all --all-targets run: cargo clippy --all --all-targets
- name: Check docs - name: Check docs
run: cargo doc --no-deps -p boring2 -p boring-sys2 --features underscore-wildcards run: cargo doc --no-deps -p boring2 -p boring-sys2 --features pq-experimental,underscore-wildcards
env: env:
DOCS_RS: 1 DOCS_RS: 1
test: test:
@ -158,8 +157,8 @@ jobs:
apt_packages: gcc-arm-linux-gnueabi g++-arm-linux-gnueabi apt_packages: gcc-arm-linux-gnueabi g++-arm-linux-gnueabi
check_only: true check_only: true
custom_env: custom_env:
CC_arm-unknown-linux-gnueabi: arm-linux-gnueabi-gcc CC: arm-linux-gnueabi-gcc
CXX_arm-unknown-linux-gnueabi: arm-linux-gnueabi-g++ CXX: arm-linux-gnueabi-g++
CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABI_LINKER: arm-linux-gnueabi-g++ CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABI_LINKER: arm-linux-gnueabi-g++
extra_test_args: --workspace --exclude compio-boring2 extra_test_args: --workspace --exclude compio-boring2
- thing: aarch64-linux - thing: aarch64-linux
@ -169,8 +168,8 @@ jobs:
apt_packages: crossbuild-essential-arm64 apt_packages: crossbuild-essential-arm64
check_only: true check_only: true
custom_env: custom_env:
CC_aarch64_unknown_linux_gnu: aarch64-linux-gnu-gcc CC: aarch64-linux-gnu-gcc
CXX_aarch64_unknown_linux_gnu: aarch64-linux-gnu-g++ CXX: aarch64-linux-gnu-g++
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-g++ CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-g++
- thing: arm64-macos - thing: arm64-macos
target: aarch64-apple-darwin target: aarch64-apple-darwin
@ -215,10 +214,6 @@ jobs:
# run: rustup update ${{ matrix.rust }} --no-self-update && rustup default ${{ matrix.rust }} # run: rustup update ${{ matrix.rust }} --no-self-update && rustup default ${{ matrix.rust }}
# shell: bash # shell: bash
- run: rustup target add ${{ matrix.target }} - run: rustup target add ${{ matrix.target }}
- name: Install golang
uses: actions/setup-go@v5
with:
go-version: '>=1.22.0'
- name: Install target-specific APT dependencies - name: Install target-specific APT dependencies
if: "matrix.apt_packages != ''" if: "matrix.apt_packages != ''"
run: sudo apt update && sudo apt install -y ${{ matrix.apt_packages }} run: sudo apt update && sudo apt install -y ${{ matrix.apt_packages }}
@ -277,10 +272,6 @@ jobs:
- name: Install Rust (rustup) - name: Install Rust (rustup)
run: rustup update stable --no-self-update && rustup default stable && rustup target add ${{ matrix.target }} run: rustup update stable --no-self-update && rustup default stable && rustup target add ${{ matrix.target }}
shell: bash shell: bash
- name: Install golang
uses: actions/setup-go@v5
with:
go-version: '>=1.22.0'
- name: Install ${{ matrix.target }} toolchain - name: Install ${{ matrix.target }} toolchain
run: brew tap messense/macos-cross-toolchains && brew install ${{ matrix.target }} run: brew tap messense/macos-cross-toolchains && brew install ${{ matrix.target }}
- name: Set BORING_BSSL_SYSROOT - name: Set BORING_BSSL_SYSROOT
@ -300,8 +291,14 @@ jobs:
submodules: 'recursive' submodules: 'recursive'
- name: Install Rust (rustup) - name: Install Rust (rustup)
run: rustup update stable --no-self-update && rustup default stable run: rustup update stable --no-self-update && rustup default stable
- name: Run `underscore-wildcards` tests - run: cargo test --features pq-experimental
run: cargo test --features underscore-wildcards name: Run `pq-experimental` tests
- run: cargo test --features kx-safe-default,pq-experimental
name: Run `kx-safe-default` tests
- run: cargo test --features pq-experimental,underscore-wildcards
name: Run `pq-experimental,underscore-wildcards` tests
- run: cargo test --features underscore-wildcards
name: Run `underscore-wildcards` tests
crates: crates:
name: crates name: crates
@ -338,5 +335,5 @@ jobs:
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v2
with: with:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
prerelease: ${{ contains(github.ref, 'beta') || contains(github.ref, 'rc') }} prerelease: ${{ contains(github.ref, 'alpha') || contains(github.ref, 'beta') || contains(github.ref, 'rc') }}
generate_release_notes: true generate_release_notes: true

3
.gitmodules vendored
View File

@ -2,3 +2,6 @@
path = boring-sys/deps/boringssl path = boring-sys/deps/boringssl
url = https://github.com/google/boringssl.git url = https://github.com/google/boringssl.git
ignore = dirty ignore = dirty
[submodule "boring-sys/deps/boringssl-fips"]
path = boring-sys/deps/boringssl-fips
url = https://github.com/google/boringssl.git

View File

@ -26,12 +26,11 @@ tokio-boring = { package = "tokio-boring2", version = "5.0.0-alpha.10", path = "
compio-boring = { package = "compio-boring2", version = "5.0.0-alpha.10", path = "./compio-boring" } compio-boring = { package = "compio-boring2", version = "5.0.0-alpha.10", path = "./compio-boring" }
bindgen = { version = "0.72.0", default-features = false, features = ["runtime"] } bindgen = { version = "0.72.0", default-features = false, features = ["runtime"] }
bitflags = "2.9"
brotli = "8.0"
bytes = "1" bytes = "1"
cmake = "0.1.54" cmake = "0.1.18"
fs_extra = "1.3.0" fs_extra = "1.3.0"
fslock = "0.2" fslock = "0.2"
bitflags = "2.4"
foreign-types = "0.5" foreign-types = "0.5"
libc = "0.2" libc = "0.2"
hex = "0.4" hex = "0.4"
@ -43,5 +42,6 @@ antidote = "1.0.0"
linked_hash_set = "0.1" linked_hash_set = "0.1"
openssl-macros = "0.1.1" openssl-macros = "0.1.1"
autocfg = "1.3.0" autocfg = "1.3.0"
brotli = "8"
compio = { version = "0.16.0" } compio = { version = "0.16.0" }
compio-io = { version = "0.8.0" } compio-io = { version = "0.8.0" }

View File

@ -8,9 +8,9 @@ BoringSSL bindings for the Rust programming language and HTTP client for [wreq](
This package implements only the TLS extensions specification and supports the original [boring](https://github.com/cloudflare/boring) library with the following features: This package implements only the TLS extensions specification and supports the original [boring](https://github.com/cloudflare/boring) library with the following features:
- RPK is not supported
- Required TLS extensions for Safari and Firefox - Required TLS extensions for Safari and Firefox
- kDHE, ffdhe2048, and ffdhe3072 implementations - kDHE, ffdhe2048, and ffdhe3072 implementations
- RPK is not supported
- Support for LoongArch P64 and P32 architectures - Support for LoongArch P64 and P32 architectures
## Documentation ## Documentation

View File

@ -13,12 +13,13 @@ build = "build/main.rs"
readme = "README.md" readme = "README.md"
categories = ["cryptography", "external-ffi-bindings"] categories = ["cryptography", "external-ffi-bindings"]
edition = { workspace = true } edition = { workspace = true }
rust-version = "1.77"
include = [ include = [
"/*.md", "/*.md",
"/*.toml", "/*.toml",
"/LICENSE-MIT", "/LICENSE-MIT",
"/cmake/*.cmake", "/cmake/*.cmake",
# boringssl (non-FIPS)
"/deps/boringssl/src/util/32-bit-toolchain.cmake",
"/deps/boringssl/**/*.[chS]", "/deps/boringssl/**/*.[chS]",
"/deps/boringssl/**/*.asm", "/deps/boringssl/**/*.asm",
"/deps/boringssl/sources.json", "/deps/boringssl/sources.json",
@ -26,18 +27,34 @@ include = [
"/deps/boringssl/src/crypto/obj/objects.txt", "/deps/boringssl/src/crypto/obj/objects.txt",
"/deps/boringssl/src/util/32-bit-toolchain.cmake", "/deps/boringssl/src/util/32-bit-toolchain.cmake",
"/deps/boringssl/**/*.bzl", "/deps/boringssl/**/*.bzl",
"/deps/boringssl/**/*.cc", "/deps/boringssl/src/**/*.cc",
"/deps/boringssl/**/CMakeLists.txt", "/deps/boringssl/**/CMakeLists.txt",
"/deps/boringssl/**/sources.cmake", "/deps/boringssl/**/sources.cmake",
"/deps/boringssl/**/util/go_tests.txt",
"/deps/boringssl/LICENSE", "/deps/boringssl/LICENSE",
# boringssl (FIPS)
"/deps/boringssl-fips/src/util/32-bit-toolchain.cmake",
"/deps/boringssl-fips/**/*.[chS]",
"/deps/boringssl-fips/**/*.asm",
"/deps/boringssl-fips/**/*.pl",
"/deps/boringssl-fips/**/*.go",
"/deps/boringssl-fips/**/go.mod",
"/deps/boringssl-fips/**/go.sum",
"/deps/boringssl-fips/sources.json",
"/deps/boringssl-fips/crypto/obj/obj_mac.num",
"/deps/boringssl-fips/crypto/obj/objects.txt",
"/deps/boringssl-fips/crypto/err/*.errordata",
"/deps/boringssl-fips/**/*.bzl",
"/deps/boringssl-fips/**/*.cc",
"/deps/boringssl-fips/**/CMakeLists.txt",
"/deps/boringssl-fips/**/sources.cmake",
"/deps/boringssl-fips/LICENSE",
"/build/*", "/build/*",
"/src", "/src",
"/patches", "/patches",
] ]
[package.metadata.docs.rs] [package.metadata.docs.rs]
features = ["underscore-wildcards"] features = ["pq-experimental", "underscore-wildcards"]
rustdoc-args = ["--cfg", "docsrs"] rustdoc-args = ["--cfg", "docsrs"]
[features] [features]
@ -49,15 +66,32 @@ rustdoc-args = ["--cfg", "docsrs"]
# for instructions and more details on the boringssl FIPS flag. # for instructions and more details on the boringssl FIPS flag.
fips = [] fips = []
# Use a precompiled FIPS-validated version of BoringSSL. Meant to be used with
# FIPS-20230428 or newer. Users must set `BORING_BSSL_FIPS_PATH` to use this
# feature, or else the build will fail.
fips-precompiled = []
# Link with precompiled FIPS-validated `bcm.o` module.
fips-link-precompiled = []
# Applies a patch (`patches/boring-pq.patch`) to the boringSSL source code that
# enables support for PQ key exchange. 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`.
pq-experimental = []
# Applies a patch (`patches/underscore-wildcards.patch`) to enable # Applies a patch (`patches/underscore-wildcards.patch`) to enable
# `ffi::X509_CHECK_FLAG_UNDERSCORE_WILDCARDS`. This feature is necessary in # `ffi::X509_CHECK_FLAG_UNDERSCORE_WILDCARDS`. Same caveats as
# order to compile the bindings for the default branch of boringSSL # those for `pq-experimental` feature apply.
# (`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 = [] underscore-wildcards = []
# Add a prefix to all symbols in libcrypto and libssl to prevent conflicts
# with other OpenSSL or BoringSSL versions that might be linked in the same process.
prefix-symbols = []
[build-dependencies] [build-dependencies]
autocfg = { workspace = true }
bindgen = { workspace = true } bindgen = { workspace = true }
cmake = { workspace = true } cmake = { workspace = true }
fs_extra = { workspace = true } fs_extra = { workspace = true }

View File

@ -16,14 +16,19 @@ pub(crate) struct Config {
pub(crate) struct Features { pub(crate) struct Features {
pub(crate) fips: bool, pub(crate) fips: bool,
pub(crate) fips_precompiled: bool,
pub(crate) fips_link_precompiled: bool,
pub(crate) pq_experimental: bool,
pub(crate) rpk: bool, pub(crate) rpk: bool,
pub(crate) underscore_wildcards: bool, pub(crate) underscore_wildcards: bool,
pub(crate) prefix_symbols: bool,
} }
pub(crate) struct Env { pub(crate) struct Env {
pub(crate) path: Option<PathBuf>, pub(crate) path: Option<PathBuf>,
pub(crate) include_path: Option<PathBuf>, pub(crate) include_path: Option<PathBuf>,
pub(crate) source_path: Option<PathBuf>, pub(crate) source_path: Option<PathBuf>,
pub(crate) precompiled_bcm_o: Option<PathBuf>,
pub(crate) assume_patched: bool, pub(crate) assume_patched: bool,
pub(crate) sysroot: Option<PathBuf>, pub(crate) sysroot: Option<PathBuf>,
pub(crate) compiler_external_toolchain: Option<PathBuf>, pub(crate) compiler_external_toolchain: Option<PathBuf>,
@ -32,9 +37,6 @@ pub(crate) struct Env {
pub(crate) android_ndk_home: Option<PathBuf>, pub(crate) android_ndk_home: Option<PathBuf>,
pub(crate) cmake_toolchain_file: Option<PathBuf>, pub(crate) cmake_toolchain_file: Option<PathBuf>,
pub(crate) cpp_runtime_lib: Option<OsString>, pub(crate) cpp_runtime_lib: Option<OsString>,
/// C compiler (ignored if using FIPS)
pub(crate) cc: Option<OsString>,
pub(crate) cxx: Option<OsString>,
pub(crate) docs_rs: bool, pub(crate) docs_rs: bool,
} }
@ -50,10 +52,10 @@ impl Config {
let features = Features::from_env(); let features = Features::from_env();
let env = Env::from_env(&host, &target, features.is_fips_like()); let env = Env::from_env(&host, &target, features.is_fips_like());
let is_bazel = env let mut is_bazel = false;
.source_path if let Some(src_path) = &env.source_path {
.as_ref() is_bazel = src_path.join("src").exists();
.is_some_and(|path| path.join("src").exists()); }
let config = Self { let config = Self {
manifest_dir, manifest_dir,
@ -77,6 +79,10 @@ impl Config {
panic!("`fips` and `rpk` features are mutually exclusive"); panic!("`fips` and `rpk` features are mutually exclusive");
} }
if self.features.fips_precompiled && self.features.rpk {
panic!("`fips-precompiled` and `rpk` features are mutually exclusive");
}
let is_precompiled_native_lib = self.env.path.is_some(); let is_precompiled_native_lib = self.env.path.is_some();
let is_external_native_lib_source = let is_external_native_lib_source =
!is_precompiled_native_lib && self.env.source_path.is_none(); !is_precompiled_native_lib && self.env.source_path.is_none();
@ -88,7 +94,9 @@ impl Config {
); );
} }
let features_with_patches_enabled = self.features.rpk || self.features.underscore_wildcards; let features_with_patches_enabled = self.features.rpk
|| self.features.pq_experimental
|| self.features.underscore_wildcards;
let patches_required = features_with_patches_enabled && !self.env.assume_patched; let patches_required = features_with_patches_enabled && !self.env.assume_patched;
@ -97,41 +105,62 @@ impl Config {
"cargo:warning=precompiled BoringSSL was provided, so patches will be ignored" "cargo:warning=precompiled BoringSSL was provided, so patches will be ignored"
); );
} }
// todo(rmehra): should this even be a restriction? why not let people link a custom bcm.o?
// precompiled boringssl will include libcrypto.a
if is_precompiled_native_lib && self.features.fips_link_precompiled {
panic!("precompiled BoringSSL was provided, so FIPS configuration can't be applied");
}
if !is_precompiled_native_lib && self.features.fips_precompiled {
panic!("`fips-precompiled` feature requires `BORING_BSSL_FIPS_PATH` to be set");
}
} }
} }
impl Features { impl Features {
fn from_env() -> Self { fn from_env() -> Self {
let fips = env::var_os("CARGO_FEATURE_FIPS").is_some(); let fips = env::var_os("CARGO_FEATURE_FIPS").is_some();
let fips_precompiled = env::var_os("CARGO_FEATURE_FIPS_PRECOMPILED").is_some();
let fips_link_precompiled = env::var_os("CARGO_FEATURE_FIPS_LINK_PRECOMPILED").is_some();
let pq_experimental = env::var_os("CARGO_FEATURE_PQ_EXPERIMENTAL").is_some();
let rpk = env::var_os("CARGO_FEATURE_RPK").is_some(); let rpk = env::var_os("CARGO_FEATURE_RPK").is_some();
let underscore_wildcards = env::var_os("CARGO_FEATURE_UNDERSCORE_WILDCARDS").is_some(); let underscore_wildcards = env::var_os("CARGO_FEATURE_UNDERSCORE_WILDCARDS").is_some();
let prefix_symbols = env::var_os("CARGO_FEATURE_PREFIX_SYMBOLS").is_some();
Self { Self {
fips, fips,
fips_precompiled,
fips_link_precompiled,
pq_experimental,
rpk, rpk,
underscore_wildcards, underscore_wildcards,
prefix_symbols,
} }
} }
pub(crate) fn is_fips_like(&self) -> bool { pub(crate) fn is_fips_like(&self) -> bool {
self.fips self.fips || self.fips_precompiled || self.fips_link_precompiled
} }
} }
impl Env { impl Env {
fn from_env(host: &str, target: &str, is_fips_like: bool) -> Self { fn from_env(target: &str, host: &str, is_fips_like: bool) -> Self {
const NORMAL_PREFIX: &str = "BORING_BSSL"; const NORMAL_PREFIX: &str = "BORING_BSSL";
const FIPS_PREFIX: &str = "BORING_BSSL_FIPS"; const FIPS_PREFIX: &str = "BORING_BSSL_FIPS";
let var_prefix = if host == target { "HOST" } else { "TARGET" };
let target_with_underscores = target.replace('-', "_"); let target_with_underscores = target.replace('-', "_");
let target_only_var = |name: &str| { // 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}")) var(&format!("{name}_{target}"))
.or_else(|| var(&format!("{name}_{target_with_underscores}"))) .or_else(|| var(&format!("{name}_{target_with_underscores}")))
.or_else(|| var(&format!("{var_prefix}_{name}"))) .or_else(|| var(&format!("{kind}_{name}")))
.or_else(|| var(name))
}; };
let target_var = |name: &str| target_only_var(name).or_else(|| var(name));
let boringssl_var = |name: &str| { let boringssl_var = |name: &str| {
// The passed name is the non-fips version of the environment variable, // The passed name is the non-fips version of the environment variable,
@ -149,6 +178,7 @@ impl Env {
path: boringssl_var("BORING_BSSL_PATH").map(PathBuf::from), path: boringssl_var("BORING_BSSL_PATH").map(PathBuf::from),
include_path: boringssl_var("BORING_BSSL_INCLUDE_PATH").map(PathBuf::from), include_path: boringssl_var("BORING_BSSL_INCLUDE_PATH").map(PathBuf::from),
source_path: boringssl_var("BORING_BSSL_SOURCE_PATH").map(PathBuf::from), source_path: boringssl_var("BORING_BSSL_SOURCE_PATH").map(PathBuf::from),
precompiled_bcm_o: boringssl_var("BORING_BSSL_PRECOMPILED_BCM_O").map(PathBuf::from),
assume_patched: boringssl_var("BORING_BSSL_ASSUME_PATCHED") assume_patched: boringssl_var("BORING_BSSL_ASSUME_PATCHED")
.is_some_and(|v| !v.is_empty()), .is_some_and(|v| !v.is_empty()),
sysroot: boringssl_var("BORING_BSSL_SYSROOT").map(PathBuf::from), sysroot: boringssl_var("BORING_BSSL_SYSROOT").map(PathBuf::from),
@ -159,9 +189,6 @@ impl Env {
android_ndk_home: target_var("ANDROID_NDK_HOME").map(Into::into), android_ndk_home: target_var("ANDROID_NDK_HOME").map(Into::into),
cmake_toolchain_file: target_var("CMAKE_TOOLCHAIN_FILE").map(Into::into), cmake_toolchain_file: target_var("CMAKE_TOOLCHAIN_FILE").map(Into::into),
cpp_runtime_lib: target_var("BORING_BSSL_RUST_CPPLIB"), cpp_runtime_lib: target_var("BORING_BSSL_RUST_CPPLIB"),
// matches the `cc` crate
cc: target_only_var("CC"),
cxx: target_only_var("CXX"),
docs_rs: var("DOCS_RS").is_some(), docs_rs: var("DOCS_RS").is_some(),
} }
} }

View File

@ -1,5 +1,6 @@
use fslock::LockFile; use fslock::LockFile;
use std::env; use std::env;
use std::ffi::OsStr;
use std::ffi::OsString; use std::ffi::OsString;
use std::fs; use std::fs;
use std::io; use std::io;
@ -9,8 +10,10 @@ use std::process::{Command, Output};
use std::sync::OnceLock; use std::sync::OnceLock;
use crate::config::Config; use crate::config::Config;
use crate::prefix::{prefix_symbols, PrefixCallback};
mod config; mod config;
mod prefix;
fn should_use_cmake_cross_compilation(config: &Config) -> bool { fn should_use_cmake_cross_compilation(config: &Config) -> bool {
if config.host == config.target { if config.host == config.target {
@ -50,7 +53,6 @@ const CMAKE_PARAMS_APPLE: &[(&str, &[(&str, &str)])] = &[
&[ &[
("CMAKE_OSX_ARCHITECTURES", "arm64"), ("CMAKE_OSX_ARCHITECTURES", "arm64"),
("CMAKE_OSX_SYSROOT", "iphoneos"), ("CMAKE_OSX_SYSROOT", "iphoneos"),
("CMAKE_MACOSX_BUNDLE", "OFF"),
], ],
), ),
( (
@ -58,7 +60,6 @@ const CMAKE_PARAMS_APPLE: &[(&str, &[(&str, &str)])] = &[
&[ &[
("CMAKE_OSX_ARCHITECTURES", "arm64"), ("CMAKE_OSX_ARCHITECTURES", "arm64"),
("CMAKE_OSX_SYSROOT", "iphonesimulator"), ("CMAKE_OSX_SYSROOT", "iphonesimulator"),
("CMAKE_MACOSX_BUNDLE", "OFF"),
], ],
), ),
( (
@ -66,7 +67,6 @@ const CMAKE_PARAMS_APPLE: &[(&str, &[(&str, &str)])] = &[
&[ &[
("CMAKE_OSX_ARCHITECTURES", "x86_64"), ("CMAKE_OSX_ARCHITECTURES", "x86_64"),
("CMAKE_OSX_SYSROOT", "iphonesimulator"), ("CMAKE_OSX_SYSROOT", "iphonesimulator"),
("CMAKE_MACOSX_BUNDLE", "OFF"),
], ],
), ),
// macOS // macOS
@ -117,7 +117,11 @@ fn get_boringssl_source_path(config: &Config) -> &PathBuf {
static SOURCE_PATH: OnceLock<PathBuf> = OnceLock::new(); static SOURCE_PATH: OnceLock<PathBuf> = OnceLock::new();
SOURCE_PATH.get_or_init(|| { SOURCE_PATH.get_or_init(|| {
let submodule_dir = "boringssl"; let submodule_dir = if config.features.fips {
"boringssl-fips"
} else {
"boringssl"
};
let src_path = config.out_dir.join(submodule_dir); let src_path = config.out_dir.join(submodule_dir);
@ -126,11 +130,10 @@ fn get_boringssl_source_path(config: &Config) -> &PathBuf {
if !submodule_path.join("CMakeLists.txt").exists() { if !submodule_path.join("CMakeLists.txt").exists() {
println!("cargo:warning=fetching boringssl git submodule"); println!("cargo:warning=fetching boringssl git submodule");
run_command( run_command(&["git"], |c| {
Command::new("git") c.args(["submodule", "update", "--init", "--recursive"])
.args(["submodule", "update", "--init", "--recursive"]) .arg(&submodule_path)
.arg(&submodule_path), })
)
.unwrap(); .unwrap();
} }
@ -151,7 +154,7 @@ fn get_boringssl_source_path(config: &Config) -> &PathBuf {
/// ///
/// MSVC generator on Windows place static libs in a target sub-folder, /// MSVC generator on Windows place static libs in a target sub-folder,
/// so adjust library location based on platform and build target. /// so adjust library location based on platform and build target.
/// See issue: <https://github.com/alexcrichton/cmake-rs/issues/18> /// See issue: https://github.com/alexcrichton/cmake-rs/issues/18
fn get_boringssl_platform_output_path(config: &Config) -> String { fn get_boringssl_platform_output_path(config: &Config) -> String {
if config.target.ends_with("-msvc") { if config.target.ends_with("-msvc") {
// Code under this branch should match the logic in cmake-rs // Code under this branch should match the logic in cmake-rs
@ -192,7 +195,7 @@ fn get_boringssl_platform_output_path(config: &Config) -> String {
} }
} }
/// Returns a new `cmake::Config` for building BoringSSL. /// Returns a new cmake::Config for building BoringSSL.
/// ///
/// It will add platform-specific parameters if needed. /// It will add platform-specific parameters if needed.
fn get_boringssl_cmake_config(config: &Config) -> cmake::Config { fn get_boringssl_cmake_config(config: &Config) -> cmake::Config {
@ -215,15 +218,6 @@ fn get_boringssl_cmake_config(config: &Config) -> cmake::Config {
.define("CMAKE_ASM_COMPILER_TARGET", &config.target); .define("CMAKE_ASM_COMPILER_TARGET", &config.target);
} }
if !config.features.fips {
if let Some(cc) = &config.env.cc {
boringssl_cmake.define("CMAKE_C_COMPILER", cc);
}
if let Some(cxx) = &config.env.cxx {
boringssl_cmake.define("CMAKE_CXX_COMPILER", cxx);
}
}
if let Some(sysroot) = &config.env.sysroot { if let Some(sysroot) = &config.env.sysroot {
boringssl_cmake.define("CMAKE_SYSROOT", sysroot); boringssl_cmake.define("CMAKE_SYSROOT", sysroot);
} }
@ -339,6 +333,55 @@ fn get_boringssl_cmake_config(config: &Config) -> cmake::Config {
boringssl_cmake 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) -> Option<String> {
let output = match Command::new(tool).arg("--version").output() {
Ok(o) => o,
Err(e) => {
println!("cargo:warning=missing {tool}, trying other compilers: {e}");
// NOTE: hard-codes that the loop below checks the version
return None;
}
};
if !output.status.success() {
return Some(String::new());
}
let output = std::str::from_utf8(&output.stdout).expect("invalid utf8 output");
Some(output.lines().next().expect("empty output").to_string())
}
const REQUIRED_CLANG_VERSION: &str = "12.0.0";
for (cc, cxx) in [
("clang-12", "clang++-12"),
("clang", "clang++"),
("cc", "c++"),
] {
let (Some(cc_version), Some(cxx_version)) = (version(cc), version(cxx)) else {
continue;
};
if cc_version.contains(REQUIRED_CLANG_VERSION) {
assert!(
cxx_version.contains(REQUIRED_CLANG_VERSION),
"mismatched versions of cc and c++"
);
return (cc, cxx);
} else if cc == "cc" {
panic!(
"unsupported clang version \"{cc_version}\": FIPS requires clang {REQUIRED_CLANG_VERSION}"
);
} else if !cc_version.is_empty() {
println!(
"cargo:warning=FIPS requires clang version {REQUIRED_CLANG_VERSION}, skipping incompatible version \"{cc_version}\""
);
}
}
unreachable!()
}
fn pick_best_android_ndk_toolchain(toolchains_dir: &Path) -> std::io::Result<OsString> { fn pick_best_android_ndk_toolchain(toolchains_dir: &Path) -> std::io::Result<OsString> {
let toolchains = std::fs::read_dir(toolchains_dir)?.collect::<Result<Vec<_>, _>>()?; let toolchains = std::fs::read_dir(toolchains_dir)?.collect::<Result<Vec<_>, _>>()?;
// First look for one of the toolchains that Google has documented. // First look for one of the toolchains that Google has documented.
@ -434,12 +477,14 @@ fn ensure_patches_applied(config: &Config) -> io::Result<()> {
); );
return Ok(()); return Ok(());
} else if config.env.source_path.is_some() } else if config.env.source_path.is_some()
&& (config.features.rpk || config.features.underscore_wildcards) && (config.features.rpk
|| config.features.pq_experimental
|| config.features.underscore_wildcards)
{ {
panic!( panic!(
"BORING_BSSL_ASSUME_PATCHED must be set when setting "BORING_BSSL_ASSUME_PATCHED must be set when setting
BORING_BSSL_SOURCE_PATH and using any of the following BORING_BSSL_SOURCE_PATH and using any of the following
features: rpk, underscore-wildcards" features: rpk, pq-experimental, underscore-wildcards"
); );
} }
@ -451,7 +496,7 @@ fn ensure_patches_applied(config: &Config) -> io::Result<()> {
// NOTE: init git in the copied files, so we can apply patches // NOTE: init git in the copied files, so we can apply patches
if !has_git { if !has_git {
run_command(Command::new("git").arg("init").current_dir(src_path))?; run_command(&["git"], |c| c.arg("init").current_dir(src_path))?;
} }
println!("cargo:warning=applying 44b3df6f03d85c901767250329c571db405122d5 patch to boringssl"); println!("cargo:warning=applying 44b3df6f03d85c901767250329c571db405122d5 patch to boringssl");
@ -490,32 +535,46 @@ fn apply_patch(config: &Config, patch_name: &str) -> io::Result<()> {
args.push("-p2"); args.push("-p2");
} }
run_command( run_command(&["git"], |c| {
Command::new("git") c.args(&args).arg(&cmd_path).current_dir(src_path)
.args(&args) })?;
.arg(cmd_path)
.current_dir(src_path),
)?;
Ok(()) Ok(())
} }
fn run_command(command: &mut Command) -> io::Result<Output> { fn run_command(
let out = command.output()?; names: &[impl AsRef<OsStr>],
f: impl Fn(&mut Command) -> &mut Command,
) -> io::Result<Output> {
for name in names {
let mut command = Command::new(name);
f(&mut command);
println!("{}", std::str::from_utf8(&out.stdout).unwrap()); let out = match command.output() {
eprintln!("{}", std::str::from_utf8(&out.stderr).unwrap()); Ok(out) => out,
Err(e) if e.kind() == io::ErrorKind::NotFound => continue,
if !out.status.success() { Err(e) => {
let err = match out.status.code() { eprintln!("{command:?} failed to execute: {e}");
Some(code) => format!("{command:?} exited with status: {code}"), continue;
None => format!("{command:?} was terminated by signal"), }
}; };
return Err(io::Error::other(err)); println!("{}", std::str::from_utf8(&out.stdout).unwrap());
eprintln!("{}", std::str::from_utf8(&out.stderr).unwrap());
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));
}
return Ok(out);
} }
Ok(out) Err(io::ErrorKind::NotFound.into())
} }
fn built_boring_source_path(config: &Config) -> &PathBuf { fn built_boring_source_path(config: &Config) -> &PathBuf {
@ -538,17 +597,64 @@ fn built_boring_source_path(config: &Config) -> &PathBuf {
} }
if config.features.fips { if config.features.fips {
cfg.define("CMAKE_C_COMPILER", "clang") let (clang, clangxx) = verify_fips_clang_version();
.define("CMAKE_CXX_COMPILER", "clang++") cfg.define("CMAKE_C_COMPILER", clang)
.define("CMAKE_ASM_COMPILER", "clang") .define("CMAKE_CXX_COMPILER", clangxx)
.define("CMAKE_ASM_COMPILER", clang)
.define("FIPS", "1"); .define("FIPS", "1");
} }
if config.features.fips_link_precompiled {
cfg.define("FIPS", "1");
}
if config.features.prefix_symbols {
cfg.define("CMAKE_POSITION_INDEPENDENT_CODE", "ON");
}
cfg.build_target("ssl").build(); cfg.build_target("ssl").build();
cfg.build_target("crypto").build() cfg.build_target("crypto").build()
}) })
} }
fn link_in_precompiled_bcm_o(config: &Config) {
println!("cargo:warning=linking in precompiled `bcm.o` module");
let bssl_dir = built_boring_source_path(config);
let bcm_o_src_path = config.env.precompiled_bcm_o.as_ref()
.expect("`fips-link-precompiled` requires `BORING_BSSL_FIPS_PRECOMPILED_BCM_O` env variable to be specified");
let libcrypto_path = bssl_dir
.join("build/crypto/libcrypto.a")
.canonicalize()
.unwrap();
let bcm_o_dst_path = bssl_dir.join("build/bcm-fips.o");
fs::copy(bcm_o_src_path, &bcm_o_dst_path).unwrap();
// check that fips module is named as expected
let ar = &["ar", "llvm-ar"];
let out = run_command(ar, |c| c.arg("t").arg(&libcrypto_path).arg("bcm.o")).unwrap();
assert_eq!(
String::from_utf8(out.stdout).unwrap().trim(),
"bcm.o",
"failed to verify FIPS module name"
);
// insert fips bcm.o before bcm.o into libcrypto.a,
// so for all duplicate symbols the older fips bcm.o is used
// (this causes the need for extra linker flags to deal with duplicate symbols)
// (as long as the newer module does not define new symbols, one may also remove it,
// but once there are new symbols it would cause missing symbols at linking stage)
run_command(ar, |c| {
c.args(["rb", "bcm.o"])
.args([&libcrypto_path, &bcm_o_dst_path])
})
.unwrap();
}
fn get_cpp_runtime_lib(config: &Config) -> Option<String> { fn get_cpp_runtime_lib(config: &Config) -> Option<String> {
if let Some(ref cpp_lib) = config.env.cpp_runtime_lib { if let Some(ref cpp_lib) = config.env.cpp_runtime_lib {
return cpp_lib.clone().into_string().ok(); return cpp_lib.clone().into_string().ok();
@ -571,6 +677,9 @@ fn main() {
if !config.env.docs_rs { if !config.env.docs_rs {
emit_link_directives(&config); emit_link_directives(&config);
} }
if config.features.prefix_symbols {
prefix_symbols(&config);
}
generate_bindings(&config); generate_bindings(&config);
} }
@ -607,6 +716,10 @@ fn emit_link_directives(config: &Config) {
); );
} }
if config.features.fips_link_precompiled {
link_in_precompiled_bcm_o(config);
}
if let Some(cpp_lib) = get_cpp_runtime_lib(config) { if let Some(cpp_lib) = get_cpp_runtime_lib(config) {
println!("cargo:rustc-link-lib={cpp_lib}"); println!("cargo:rustc-link-lib={cpp_lib}");
} }
@ -635,8 +748,12 @@ fn generate_bindings(config: &Config) {
} }
}); });
let target_rust_version = // bindgen 0.70 replaced the run-time layout tests with compile-time ones,
bindgen::RustTarget::stable(77, 0).expect("bindgen does not recognize target rust version"); // but they depend on std::mem::offset_of, stabilized in 1.77.
let supports_layout_tests = autocfg::new().probe_rustc_version(1, 77);
let Ok(target_rust_version) = bindgen::RustTarget::stable(68, 0) else {
panic!("bindgen does not recognize target rust version");
};
let mut builder = bindgen::Builder::default() let mut builder = bindgen::Builder::default()
.rust_target(target_rust_version) // bindgen MSRV is 1.70, so this is enough .rust_target(target_rust_version) // bindgen MSRV is 1.70, so this is enough
@ -652,7 +769,7 @@ fn generate_bindings(config: &Config) {
.generate_comments(true) .generate_comments(true)
.fit_macro_constants(false) .fit_macro_constants(false)
.size_t_is_usize(true) .size_t_is_usize(true)
.layout_tests(config.env.debug.is_some()) .layout_tests(supports_layout_tests)
.prepend_enum_name(true) .prepend_enum_name(true)
.blocklist_type("max_align_t") // Not supported by bindgen on all targets, not used by BoringSSL .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_args(get_extra_clang_args_for_bindgen(config))
@ -665,6 +782,10 @@ fn generate_bindings(config: &Config) {
.clang_arg(sysroot.display().to_string()); .clang_arg(sysroot.display().to_string());
} }
if config.features.prefix_symbols {
builder = builder.parse_callbacks(Box::new(PrefixCallback));
}
let headers = [ let headers = [
"aes.h", "aes.h",
"asn1_mac.h", "asn1_mac.h",
@ -679,6 +800,7 @@ fn generate_bindings(config: &Config) {
"des.h", "des.h",
"dtls1.h", "dtls1.h",
"hkdf.h", "hkdf.h",
#[cfg(not(feature = "fips"))]
"hpke.h", "hpke.h",
"hmac.h", "hmac.h",
"hrss.h", "hrss.h",
@ -703,24 +825,7 @@ fn generate_bindings(config: &Config) {
} }
let bindings = builder.generate().expect("Unable to generate bindings"); let bindings = builder.generate().expect("Unable to generate bindings");
let mut source_code = Vec::new();
bindings bindings
.write(Box::new(&mut source_code)) .write_to_file(config.out_dir.join("bindings.rs"))
.expect("Couldn't serialize bindings!"); .expect("Couldn't write 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(),
);
} }

View File

@ -0,0 +1,94 @@
use crate::{config::Config, pick_best_android_ndk_toolchain, run_command};
use std::{fs, io::Write, path::PathBuf};
// The prefix to add to all symbols
// RBSSL = Rust BoringSSL, chosen arbitrarily to avoid collisions with other projects
const PREFIX: &str = "RBSSL";
// Callback to add a `link_name` macro with the prefix to all generated bindings
#[derive(Debug)]
pub struct PrefixCallback;
impl bindgen::callbacks::ParseCallbacks for PrefixCallback {
fn generated_link_name_override(
&self,
item_info: bindgen::callbacks::ItemInfo<'_>,
) -> Option<String> {
Some(format!("{PREFIX}_{}", item_info.name))
}
}
fn android_toolchain(config: &Config) -> PathBuf {
let mut android_bin_path = config
.env
.android_ndk_home
.clone()
.expect("Please set ANDROID_NDK_HOME for Android build");
android_bin_path.extend(["toolchains", "llvm", "prebuilt"]);
android_bin_path.push(pick_best_android_ndk_toolchain(&android_bin_path).unwrap());
android_bin_path.push("bin");
android_bin_path
}
pub fn prefix_symbols(config: &Config) {
// List static libraries to prefix symbols in
eprintln!("{:?}", config.out_dir);
eprintln!("{:?}", config.out_dir);
eprintln!("{:?}", config.out_dir);
eprintln!("{:?}", config.out_dir);
let static_libs: Vec<PathBuf> = [
config.out_dir.join("build"),
config.out_dir.join("build").join("ssl"),
config.out_dir.join("build").join("crypto"),
config.out_dir.join("build").join("Debug"),
config.out_dir.join("build").join("Release"),
]
.iter()
.flat_map(|dir| {
["libssl.a", "libcrypto.a", "ssl.lib", "crypto.lib"]
.into_iter()
.map(move |file| PathBuf::from(dir).join(file))
})
.filter(|p| p.exists())
.collect();
// Use `nm` to list symbols in these static libraries
let nm: &[PathBuf] = match &*config.target_os {
"android" => &[android_toolchain(config).join("llvm-nm")],
_ => &[PathBuf::from("nm"), PathBuf::from("llvm-nm")],
};
let out = run_command(nm, |c| c.args(&static_libs)).unwrap();
let mut redefine_syms: Vec<String> = String::from_utf8_lossy(&out.stdout)
.lines()
.filter(|l| {
[" T ", " D ", " B ", " C ", " R ", " W "]
.iter()
.any(|s| l.contains(s))
})
.filter_map(|l| l.split_whitespace().nth(2).map(|s| s.to_string()))
.filter(|l| !l.starts_with("_"))
.map(|l| format!("{l} {PREFIX}_{l}"))
.collect();
redefine_syms.sort();
redefine_syms.dedup();
let redefine_syms_path = config.out_dir.join("redefine_syms.txt");
let mut f = fs::File::create(&redefine_syms_path).unwrap();
for sym in &redefine_syms {
writeln!(f, "{sym}").unwrap();
}
f.flush().unwrap();
// Use `objcopy` to prefix symbols in these static libraries
let objcopy: &[PathBuf] = match &*config.target_os {
"android" => &[android_toolchain(config).join("llvm-objcopy")],
_ => &[PathBuf::from("objcopy"), PathBuf::from("llvm-objcopy")],
};
for static_lib in &static_libs {
run_command(objcopy, |c| {
c.arg(format!("--redefine-syms={}", redefine_syms_path.display()))
.arg(static_lib)
})
.unwrap();
}
}

File diff suppressed because it is too large Load Diff

View File

@ -58,4 +58,4 @@ https://github.com/google/boringssl/compare/master...cloudflare:boringssl:unders
OPENSSL_EXPORT int X509_check_host(X509 *x, const char *chk, size_t chklen, OPENSSL_EXPORT int X509_check_host(X509 *x, const char *chk, size_t chklen,
unsigned int flags, char **peername); unsigned int flags, char **peername);
-- --

View File

@ -13,25 +13,76 @@ edition = { workspace = true }
rust-version = "1.80" rust-version = "1.80"
[package.metadata.docs.rs] [package.metadata.docs.rs]
features = ["underscore-wildcards"] features = ["pq-experimental", "underscore-wildcards"]
rustdoc-args = ["--cfg", "docsrs"] rustdoc-args = ["--cfg", "docsrs"]
[features] [features]
default = ["prefix-symbols"]
# Controlling the build # Controlling the build
# NOTE: This feature is deprecated. It is needed for the submoduled
# boringssl-fips, which is extremely old and requires modifications to the
# bindings, as some newer APIs don't exist and some function signatures have
# changed. It is highly recommended to use `fips-precompiled` instead.
#
# This feature sets `fips-compat` on behalf of the user to guarantee bindings
# compatibility with the submoduled boringssl-fips.
#
# Use a FIPS-validated version of BoringSSL. # Use a FIPS-validated version of BoringSSL.
fips = ["boring-sys/fips"] fips = ["fips-compat", "boring-sys/fips"]
# **DO NOT USE** This will be removed without warning in future releases. # Build with compatibility for the submoduled boringssl-fips, without enabling
legacy-compat-deprecated = [] # the `fips` feature itself (useful e.g. if `fips-link-precompiled` is used
# with an older BoringSSL version).
fips-compat = []
# Applies a patch to enable `ffi::X509_CHECK_FLAG_UNDERSCORE_WILDCARDS`. This # Use a precompiled FIPS-validated version of BoringSSL. Meant to be used with
# feature is necessary in order to compile the bindings for the default branch # FIPS-20230428 or newer. Users must set `BORING_BSSL_FIPS_PATH` to use this
# of boringSSL. Alternatively, a version of boringSSL that implements the same # feature, or else the build will fail.
# feature set can be provided by setting `BORING_BSSL{,_FIPS}_SOURCE_PATH` and fips-precompiled = ["boring-sys/fips-precompiled"]
# `BORING_BSSL{,_FIPS}_ASSUME_PATCHED`.
# Link with precompiled FIPS-validated `bcm.o` module.
fips-link-precompiled = ["boring-sys/fips-link-precompiled"]
# Applies a patch to the boringSSL source code that enables support for PQ key
# exchange. 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`.
pq-experimental = ["boring-sys/pq-experimental"]
# Applies a patch to enable
# `ffi::X509_CHECK_FLAG_UNDERSCORE_WILDCARDS`. Same caveats as
# those for `pq-experimental` feature apply.
underscore-wildcards = ["boring-sys/underscore-wildcards"] underscore-wildcards = ["boring-sys/underscore-wildcards"]
# Add a prefix to all symbols in libcrypto and libssl to prevent conflicts
# with other OpenSSL or BoringSSL versions that might be linked in the same process.
prefix-symbols = ["boring-sys/prefix-symbols"]
# Controlling key exchange preferences at compile time
# Choose key exchange preferences at compile time. This prevents the user from
# choosing their own preferences.
kx-safe-default = []
# Support PQ key exchange. The client will prefer classical key exchange, but
# will upgrade to PQ key exchange if requested by the server. This is the
# safest option if you don't know if the peer supports PQ key exchange. This
# feature implies "kx-safe-default".
kx-client-pq-supported = ["kx-safe-default"]
# Prefer PQ key exchange. The client will prefer PQ exchange, but fallback to
# classical key exchange if requested by the server. This is the best option if
# you know the peer supports PQ key exchange. This feature implies
# "kx-safe-default" and "kx-client-pq-supported".
kx-client-pq-preferred = ["kx-safe-default", "kx-client-pq-supported"]
# Disable key exchange involving non-NIST key exchange on the client side.
# Implies "kx-safe-default".
kx-client-nist-required = ["kx-safe-default"]
[dependencies] [dependencies]
bitflags = { workspace = true } bitflags = { workspace = true }
foreign-types = { workspace = true } foreign-types = { workspace = true }

View File

@ -43,19 +43,18 @@ fn mk_ca_cert() -> Result<(X509, PKey<Private>), ErrorStack> {
let not_after = Asn1Time::days_from_now(365)?; let not_after = Asn1Time::days_from_now(365)?;
cert_builder.set_not_after(&not_after)?; cert_builder.set_not_after(&not_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( cert_builder.append_extension(
KeyUsage::new() KeyUsage::new()
.critical() .critical()
.key_cert_sign() .key_cert_sign()
.crl_sign() .crl_sign()
.build()? .build()?,
.as_ref(),
)?; )?;
let subject_key_identifier = let subject_key_identifier =
SubjectKeyIdentifier::new().build(&cert_builder.x509v3_context(None, None))?; 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())?; cert_builder.sign(&privkey, MessageDigest::sha256())?;
let cert = cert_builder.build(); let cert = cert_builder.build();
@ -107,7 +106,7 @@ fn mk_ca_signed_cert(
let not_after = Asn1Time::days_from_now(365)?; let not_after = Asn1Time::days_from_now(365)?;
cert_builder.set_not_after(&not_after)?; cert_builder.set_not_after(&not_after)?;
cert_builder.append_extension(BasicConstraints::new().build()?.as_ref())?; cert_builder.append_extension(BasicConstraints::new().build()?)?;
cert_builder.append_extension( cert_builder.append_extension(
KeyUsage::new() KeyUsage::new()
@ -115,25 +114,24 @@ fn mk_ca_signed_cert(
.non_repudiation() .non_repudiation()
.digital_signature() .digital_signature()
.key_encipherment() .key_encipherment()
.build()? .build()?,
.as_ref(),
)?; )?;
let subject_key_identifier = let subject_key_identifier =
SubjectKeyIdentifier::new().build(&cert_builder.x509v3_context(Some(ca_cert), None))?; 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() let auth_key_identifier = AuthorityKeyIdentifier::new()
.keyid(false) .keyid(false)
.issuer(false) .issuer(false)
.build(&cert_builder.x509v3_context(Some(ca_cert), None))?; .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() let subject_alt_name = SubjectAlternativeName::new()
.dns("*.example.com") .dns("*.example.com")
.dns("hello.com") .dns("hello.com")
.build(&cert_builder.x509v3_context(Some(ca_cert), None))?; .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())?; cert_builder.sign(ca_privkey, MessageDigest::sha256())?;
let cert = cert_builder.build(); let cert = cert_builder.build();
@ -149,7 +147,7 @@ fn real_main() -> Result<(), ErrorStack> {
match ca_cert.issued(&cert) { match ca_cert.issued(&cert) {
Ok(()) => println!("Certificate verified!"), Ok(()) => println!("Certificate verified!"),
Err(ver_err) => println!("Failed to verify certificate: {ver_err}"), Err(ver_err) => println!("Failed to verify certificate: {ver_err}"),
} };
Ok(()) Ok(())
} }
@ -158,5 +156,5 @@ fn main() {
match real_main() { match real_main() {
Ok(()) => println!("Finished."), Ok(()) => println!("Finished."),
Err(e) => println!("Error: {e}"), Err(e) => println!("Error: {e}"),
} };
} }

View File

@ -63,19 +63,20 @@ foreign_type_and_impl_send_sync! {
impl fmt::Display for Asn1GeneralizedTimeRef { impl fmt::Display for Asn1GeneralizedTimeRef {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let bio = MemBio::new().ok(); unsafe {
let msg = bio let mem_bio = match MemBio::new() {
.as_ref() Err(_) => return f.write_str("error"),
.and_then(|mem_bio| unsafe { Ok(m) => m,
cvt(ffi::ASN1_GENERALIZEDTIME_print( };
mem_bio.as_ptr(), let print_result = cvt(ffi::ASN1_GENERALIZEDTIME_print(
self.as_ptr(), mem_bio.as_ptr(),
)) self.as_ptr(),
.ok()?; ));
str::from_utf8(mem_bio.get_buf()).ok() match print_result {
}) Err(_) => f.write_str("error"),
.unwrap_or("error"); Ok(_) => f.write_str(str::from_utf8_unchecked(mem_bio.get_buf())),
f.write_str(msg) }
}
} }
} }
@ -527,20 +528,7 @@ impl Asn1BitStringRef {
#[corresponds(ASN1_STRING_get0_data)] #[corresponds(ASN1_STRING_get0_data)]
#[must_use] #[must_use]
pub fn as_slice(&self) -> &[u8] { pub fn as_slice(&self) -> &[u8] {
unsafe { unsafe { slice::from_raw_parts(ASN1_STRING_get0_data(self.as_ptr() as *mut _), self.len()) }
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()
} }
/// Returns the number of bytes in the string. /// Returns the number of bytes in the string.
@ -613,11 +601,10 @@ impl fmt::Display for Asn1ObjectRef {
self.as_ptr(), self.as_ptr(),
0, 0,
); );
fmt.write_str( match str::from_utf8(&buf[..len as usize]) {
buf.get(..len as usize) Err(_) => fmt.write_str("error"),
.and_then(|s| str::from_utf8(s).ok()) Ok(s) => fmt.write_str(s),
.unwrap_or("error"), }
)
} }
} }
} }

View File

@ -19,9 +19,9 @@ impl Drop for MemBioSlice<'_> {
impl<'a> MemBioSlice<'a> { impl<'a> MemBioSlice<'a> {
pub fn new(buf: &'a [u8]) -> Result<MemBioSlice<'a>, ErrorStack> { pub fn new(buf: &'a [u8]) -> Result<MemBioSlice<'a>, ErrorStack> {
#[cfg(not(feature = "legacy-compat-deprecated"))] #[cfg(not(feature = "fips-compat"))]
type BufLen = isize; type BufLen = isize;
#[cfg(feature = "legacy-compat-deprecated")] #[cfg(feature = "fips-compat")]
type BufLen = libc::c_int; type BufLen = libc::c_int;
ffi::init(); ffi::init();
@ -68,10 +68,7 @@ impl MemBio {
unsafe { unsafe {
let mut ptr = ptr::null_mut(); let mut ptr = ptr::null_mut();
let len = ffi::BIO_get_mem_data(self.0, &mut ptr); let len = ffi::BIO_get_mem_data(self.0, &mut ptr);
if ptr.is_null() { slice::from_raw_parts(ptr as *const _ as *const _, len as usize)
return &[];
}
slice::from_raw_parts(ptr.cast_const().cast(), len as usize)
} }
} }
} }

View File

@ -32,7 +32,7 @@ where
} }
to_der! { to_der! {
/// Serializes the parameters into a DER-encoded PKCS#3 `DHparameter` structure. /// Serializes the parameters into a DER-encoded PKCS#3 DHparameter structure.
#[corresponds(i2d_DHparams)] #[corresponds(i2d_DHparams)]
params_to_der, params_to_der,
ffi::i2d_DHparams ffi::i2d_DHparams

View File

@ -20,7 +20,6 @@ use openssl_macros::corresponds;
use std::borrow::Cow; use std::borrow::Cow;
use std::error; use std::error;
use std::ffi::CStr; use std::ffi::CStr;
use std::ffi::CString;
use std::fmt; use std::fmt;
use std::io; use std::io;
use std::ptr; use std::ptr;
@ -28,8 +27,6 @@ use std::str;
use crate::ffi; use crate::ffi;
pub use crate::ffi::ErrLib;
/// Collection of [`Error`]s from OpenSSL. /// Collection of [`Error`]s from OpenSSL.
/// ///
/// [`Error`]: struct.Error.html /// [`Error`]: struct.Error.html
@ -38,9 +35,6 @@ pub struct ErrorStack(Vec<Error>);
impl ErrorStack { impl ErrorStack {
/// Pops the contents of the OpenSSL error stack, and returns it. /// 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)] #[corresponds(ERR_get_error_line_data)]
#[must_use = "Use ErrorStack::clear() to drop the error stack"] #[must_use = "Use ErrorStack::clear() to drop the error stack"]
pub fn get() -> ErrorStack { pub fn get() -> ErrorStack {
@ -62,13 +56,7 @@ impl ErrorStack {
/// Used to report errors from the Rust crate /// Used to report errors from the Rust crate
#[cold] #[cold]
pub(crate) fn internal_error(err: impl error::Error) -> Self { pub(crate) fn internal_error(err: impl error::Error) -> Self {
Self(vec![Error::new_internal(Data::String(err.to_string()))]) Self(vec![Error::new_internal(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. /// Empties the current thread's error queue.
@ -103,7 +91,7 @@ impl fmt::Display for ErrorStack {
write!( write!(
fmt, fmt,
"[{}]", "[{}]",
err.reason() err.reason_internal()
.or_else(|| err.library()) .or_else(|| err.library())
.unwrap_or("unknown reason") .unwrap_or("unknown reason")
)?; )?;
@ -132,15 +120,7 @@ pub struct Error {
code: c_uint, code: c_uint,
file: *const c_char, file: *const c_char,
line: c_uint, line: c_uint,
data: Data, data: Option<Cow<'static, str>>,
}
#[derive(Clone)]
enum Data {
None,
CString(CString),
String(String),
Static(&'static str),
} }
unsafe impl Sync for Error {} unsafe impl Sync for Error {}
@ -166,9 +146,11 @@ impl Error {
// The memory referenced by data is only valid until that slot is overwritten // 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 // 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 { 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 = String::from_utf8_lossy(bytes).into_owned();
Some(data.into())
} else { } else {
Data::None None
}; };
Some(Error { Some(Error {
code, code,
@ -192,29 +174,31 @@ impl Error {
self.file, self.file,
self.line, self.line,
); );
if let Some(cstr) = self.data_cstr() { let ptr = match self.data {
ffi::ERR_set_error_data(cstr.as_ptr().cast_mut(), ffi::ERR_FLAG_STRING); 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**. /// 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. /// Use [`Error::library_code()`] and [`Error::reason_code()`] instead.
/// Packed error codes are different than [SSL error codes](crate::ssl::ErrorCode). /// Packed error codes are different than [SSL error codes](crate::ssl::ErrorCode).
#[must_use] #[must_use]
#[deprecated(note = "use library_reason() to compare error codes")]
pub fn code(&self) -> c_uint { pub fn code(&self) -> c_uint {
self.code self.code
} }
@ -230,16 +214,14 @@ impl Error {
if cstr.is_null() { if cstr.is_null() {
return None; return None;
} }
CStr::from_ptr(cstr.cast()) let bytes = CStr::from_ptr(cstr as *const _).to_bytes();
.to_str() str::from_utf8(bytes).ok()
.ok()
.filter(|&msg| msg != "unknown library")
} }
} }
/// Returns the raw OpenSSL error constant for the library reporting the error (`ERR_LIB_{name}`). /// 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. /// Error [reason codes](Error::reason_code) are not globally unique, but scoped to each library.
#[must_use] #[must_use]
pub fn library_code(&self) -> c_int { pub fn library_code(&self) -> c_int {
ffi::ERR_GET_LIB(self.code) ffi::ERR_GET_LIB(self.code)
@ -252,23 +234,20 @@ impl Error {
/// Returns the reason for the error. /// Returns the reason for the error.
#[must_use] #[must_use]
pub fn reason(&self) -> Option<&str> { pub fn reason(&self) -> Option<&'static str> {
if self.is_internal() {
return self.data();
}
unsafe { unsafe {
let cstr = ffi::ERR_reason_error_string(self.code); let cstr = ffi::ERR_reason_error_string(self.code);
if cstr.is_null() { if cstr.is_null() {
return None; return None;
} }
CStr::from_ptr(cstr.cast()).to_str().ok() let bytes = CStr::from_ptr(cstr as *const _).to_bytes();
str::from_utf8(bytes).ok()
} }
} }
/// Returns [library-specific](Error::library_code) reason code corresponding to some of the `{lib}_R_{reason}` constants. /// 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. /// 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. /// 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. /// Other libraries may use [`ERR_R_*`](ffi::ERR_R_FATAL) or their own codes.
@ -284,9 +263,8 @@ impl Error {
if self.file.is_null() { if self.file.is_null() {
return ""; return "";
} }
CStr::from_ptr(self.file.cast()) let bytes = CStr::from_ptr(self.file as *const _).to_bytes();
.to_str() str::from_utf8(bytes).unwrap_or_default()
.unwrap_or_default()
} }
} }
@ -302,37 +280,30 @@ impl Error {
/// Returns additional data describing the error. /// Returns additional data describing the error.
#[must_use] #[must_use]
pub fn data(&self) -> Option<&str> { pub fn data(&self) -> Option<&str> {
match &self.data { self.data.as_deref()
Data::None => None,
Data::CString(cstring) => cstring.to_str().ok(),
Data::String(s) => Some(s),
Data::Static(s) => Some(s),
}
} }
#[must_use] fn new_internal(msg: String) -> Self {
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 { Self {
code: ffi::ERR_PACK(ffi::ERR_LIB_NONE.0 as _, 0, 0) as _, code: ffi::ERR_PACK(ffi::ERR_LIB_NONE.0 as _, 0, 0) as _,
file: BORING_INTERNAL.as_ptr(), file: BORING_INTERNAL.as_ptr(),
line: 0, line: 0,
data: msg, data: Some(msg.into()),
} }
} }
fn is_internal(&self) -> bool { fn is_internal(&self) -> bool {
std::ptr::eq(self.file, BORING_INTERNAL.as_ptr()) std::ptr::eq(self.file, BORING_INTERNAL.as_ptr())
} }
// reason() needs 'static
fn reason_internal(&self) -> Option<&str> {
if self.is_internal() {
self.data()
} else {
self.reason()
}
}
} }
impl fmt::Debug for Error { impl fmt::Debug for Error {
@ -363,7 +334,7 @@ impl fmt::Display for Error {
write!( write!(
fmt, fmt,
"{}\n\nCode: {:08X}\nLoc: {}:{}", "{}\n\nCode: {:08X}\nLoc: {}:{}",
self.reason().unwrap_or("unknown TLS error"), self.reason_internal().unwrap_or("unknown TLS error"),
&self.code, &self.code,
self.file(), self.file(),
self.line() self.line()

View File

@ -15,8 +15,16 @@ pub fn enabled() -> bool {
#[test] #[test]
fn is_enabled() { fn is_enabled() {
#[cfg(feature = "fips")] #[cfg(any(
feature = "fips",
feature = "fips-precompiled",
feature = "fips-link-precompiled"
))]
assert!(enabled()); assert!(enabled());
#[cfg(not(feature = "fips"))] #[cfg(not(any(
feature = "fips",
feature = "fips-precompiled",
feature = "fips-link-precompiled"
)))]
assert!(!enabled()); assert!(!enabled());
} }

View File

@ -1,32 +0,0 @@
use crate::cvt;
use crate::error::ErrorStack;
use crate::foreign_types::ForeignTypeRef;
use crate::hash::MessageDigest;
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.
///
/// https://commondatastorage.googleapis.com/chromium-boringssl-docs/hmac.h.html#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(),
))
.map(|_| ())
}
}
}

View File

@ -132,7 +132,7 @@ pub mod error;
pub mod ex_data; pub mod ex_data;
pub mod fips; pub mod fips;
pub mod hash; pub mod hash;
pub mod hmac; #[cfg(not(feature = "fips"))]
pub mod hpke; pub mod hpke;
pub mod memcmp; pub mod memcmp;
pub mod nid; pub mod nid;

View File

@ -88,9 +88,7 @@ impl Nid {
pub fn long_name(&self) -> Result<&'static str, ErrorStack> { pub fn long_name(&self) -> Result<&'static str, ErrorStack> {
unsafe { unsafe {
let nameptr = cvt_p(ffi::OBJ_nid2ln(self.0) as *mut c_char)?; let nameptr = cvt_p(ffi::OBJ_nid2ln(self.0) as *mut c_char)?;
CStr::from_ptr(nameptr) str::from_utf8(CStr::from_ptr(nameptr).to_bytes()).map_err(ErrorStack::internal_error)
.to_str()
.map_err(ErrorStack::internal_error)
} }
} }
@ -100,9 +98,7 @@ impl Nid {
pub fn short_name(&self) -> Result<&'static str, ErrorStack> { pub fn short_name(&self) -> Result<&'static str, ErrorStack> {
unsafe { unsafe {
let nameptr = cvt_p(ffi::OBJ_nid2sn(self.0) as *mut c_char)?; let nameptr = cvt_p(ffi::OBJ_nid2sn(self.0) as *mut c_char)?;
CStr::from_ptr(nameptr) str::from_utf8(CStr::from_ptr(nameptr).to_bytes()).map_err(ErrorStack::internal_error)
.to_str()
.map_err(ErrorStack::internal_error)
} }
} }

View File

@ -260,7 +260,7 @@ mod test {
.unwrap(); .unwrap();
builder.set_subject_name(&name).unwrap(); builder.set_subject_name(&name).unwrap();
builder.set_issuer_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.set_pubkey(&pkey).unwrap();
builder.sign(&pkey, MessageDigest::sha256()).unwrap(); builder.sign(&pkey, MessageDigest::sha256()).unwrap();
let cert = builder.build(); let cert = builder.build();

View File

@ -95,7 +95,7 @@ impl SslContextBuilder {
let finish = fut_result.or(Err(SelectCertError::ERROR))?; let finish = fut_result.or(Err(SelectCertError::ERROR))?;
finish(client_hello).or(Err(SelectCertError::ERROR)) finish(client_hello).or(Err(SelectCertError::ERROR))
}); })
} }
/// Configures a custom private key method on the context. /// Configures a custom private key method on the context.
@ -144,7 +144,7 @@ impl SslContextBuilder {
} }
}; };
self.set_get_session_callback(async_callback); self.set_get_session_callback(async_callback)
} }
/// Configures certificate verification. /// Configures certificate verification.
@ -167,7 +167,7 @@ impl SslContextBuilder {
where where
F: Fn(&mut SslRef) -> Result<BoxCustomVerifyFuture, SslAlert> + Send + Sync + 'static, F: Fn(&mut SslRef) -> Result<BoxCustomVerifyFuture, SslAlert> + Send + Sync + 'static,
{ {
self.set_custom_verify_callback(mode, async_custom_verify_callback(callback)); self.set_custom_verify_callback(mode, async_custom_verify_callback(callback))
} }
} }
@ -176,7 +176,7 @@ impl SslRef {
where where
F: Fn(&mut SslRef) -> Result<BoxCustomVerifyFuture, SslAlert> + Send + Sync + 'static, F: Fn(&mut SslRef) -> Result<BoxCustomVerifyFuture, SslAlert> + Send + Sync + 'static,
{ {
self.set_custom_verify_callback(mode, async_custom_verify_callback(callback)); 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`. /// Sets the task waker to be used in async callbacks installed on this `Ssl`.

View File

@ -8,15 +8,12 @@ use super::{
}; };
use crate::error::ErrorStack; use crate::error::ErrorStack;
use crate::ffi; use crate::ffi;
use crate::hmac::HmacCtxRef;
use crate::ssl::TicketKeyCallbackResult;
use crate::symm::CipherCtxRef;
use crate::x509::{X509StoreContext, X509StoreContextRef}; use crate::x509::{X509StoreContext, X509StoreContextRef};
use foreign_types::ForeignType; use foreign_types::ForeignType;
use foreign_types::ForeignTypeRef; 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::ffi::CStr;
use std::mem::{self, MaybeUninit};
use std::ptr; use std::ptr;
use std::slice; use std::slice;
use std::str; use std::str;
@ -272,68 +269,6 @@ where
} }
} }
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(super) unsafe extern "C" fn raw_alpn_select<F>(
ssl: *mut ffi::SSL, ssl: *mut ffi::SSL,
out: *mut *const c_uchar, out: *mut *const c_uchar,
@ -464,7 +399,7 @@ pub(super) unsafe extern "C" fn raw_remove_session<F>(
.ex_data(SslContext::cached_ex_index::<F>()) .ex_data(SslContext::cached_ex_index::<F>())
.expect("BUG: remove session callback missing"); .expect("BUG: remove session callback missing");
callback(ctx, session); callback(ctx, session)
} }
type DataPtr = *const c_uchar; type DataPtr = *const c_uchar;
@ -516,14 +451,14 @@ where
{ {
// SAFETY: boring provides valid inputs. // SAFETY: boring provides valid inputs.
let ssl = unsafe { SslRef::from_ptr(ssl as *mut _) }; let ssl = unsafe { SslRef::from_ptr(ssl as *mut _) };
let line = unsafe { CStr::from_ptr(line).to_string_lossy() }; let line = unsafe { str::from_utf8_unchecked(CStr::from_ptr(line).to_bytes()) };
let callback = ssl let callback = ssl
.ssl_context() .ssl_context()
.ex_data(SslContext::cached_ex_index::<F>()) .ex_data(SslContext::cached_ex_index::<F>())
.expect("BUG: get session callback missing"); .expect("BUG: get session callback missing");
callback(ssl, &line); callback(ssl, line);
} }
pub(super) unsafe extern "C" fn raw_sign<M>( pub(super) unsafe extern "C" fn raw_sign<M>(
@ -767,10 +702,14 @@ impl<'a> CryptoBufferBuilder<'a> {
let buffer_capacity = unsafe { ffi::CRYPTO_BUFFER_len(self.buffer) }; let buffer_capacity = unsafe { ffi::CRYPTO_BUFFER_len(self.buffer) };
if self.cursor.position() != buffer_capacity as u64 { if self.cursor.position() != buffer_capacity as u64 {
// Make sure all bytes in buffer initialized as required by Boring SSL. // Make sure all bytes in buffer initialized as required by Boring SSL.
return Err(ErrorStack::internal_error_str("invalid len")); return Err(ErrorStack::get());
}
unsafe {
let mut result = ptr::null_mut();
ptr::swap(&mut self.buffer, &mut result);
std::mem::forget(self);
Ok(result)
} }
// Drop is no-op if the buffer is null
Ok(mem::replace(&mut self.buffer, ptr::null_mut()))
} }
} }

View File

@ -81,7 +81,6 @@ use crate::dh::DhRef;
use crate::ec::EcKeyRef; use crate::ec::EcKeyRef;
use crate::error::ErrorStack; use crate::error::ErrorStack;
use crate::ex_data::Index; use crate::ex_data::Index;
use crate::hmac::HmacCtxRef;
use crate::nid::Nid; use crate::nid::Nid;
use crate::pkey::{HasPrivate, PKeyRef, Params, Private}; use crate::pkey::{HasPrivate, PKeyRef, Params, Private};
use crate::srtp::{SrtpProtectionProfile, SrtpProtectionProfileRef}; use crate::srtp::{SrtpProtectionProfile, SrtpProtectionProfileRef};
@ -91,7 +90,6 @@ use crate::ssl::callbacks::*;
use crate::ssl::ech::SslEchKeys; use crate::ssl::ech::SslEchKeys;
use crate::ssl::error::InnerError; use crate::ssl::error::InnerError;
use crate::stack::{Stack, StackRef, Stackable}; use crate::stack::{Stack, StackRef, Stackable};
use crate::symm::CipherCtxRef;
use crate::x509::store::{X509Store, X509StoreBuilder, X509StoreBuilderRef, X509StoreRef}; use crate::x509::store::{X509Store, X509StoreBuilder, X509StoreBuilderRef, X509StoreRef};
use crate::x509::verify::X509VerifyParamRef; use crate::x509::verify::X509VerifyParamRef;
use crate::x509::{ use crate::x509::{
@ -116,6 +114,7 @@ mod async_callbacks;
mod bio; mod bio;
mod callbacks; mod callbacks;
mod connector; mod connector;
#[cfg(not(feature = "fips"))]
mod ech; mod ech;
mod error; mod error;
mod mut_only; mod mut_only;
@ -698,13 +697,131 @@ impl From<u16> for SslSignatureAlgorithm {
} }
} }
/// Numeric identifier of a TLS curve.
#[repr(transparent)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct SslCurveNid(c_int);
/// A TLS Curve.
#[repr(transparent)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct SslCurve(c_int);
impl SslCurve {
pub const SECP224R1: SslCurve = SslCurve(ffi::SSL_CURVE_SECP224R1 as _);
pub const SECP256R1: SslCurve = SslCurve(ffi::SSL_CURVE_SECP256R1 as _);
pub const SECP384R1: SslCurve = SslCurve(ffi::SSL_CURVE_SECP384R1 as _);
pub const SECP521R1: SslCurve = SslCurve(ffi::SSL_CURVE_SECP521R1 as _);
pub const X25519: SslCurve = SslCurve(ffi::SSL_CURVE_X25519 as _);
pub const FFDHE2048: SslCurve = SslCurve(ffi::SSL_CURVE_DHE2048 as _);
pub const FFDHE3072: SslCurve = SslCurve(ffi::SSL_CURVE_DHE3072 as _);
#[cfg(feature = "pq-experimental")]
#[cfg(not(any(feature = "fips", feature = "fips-precompiled")))]
pub const X25519_KYBER768_DRAFT00: SslCurve =
SslCurve(ffi::SSL_CURVE_X25519_KYBER768_DRAFT00 as _);
#[cfg(all(
not(any(feature = "fips", feature = "fips-precompiled")),
feature = "pq-experimental"
))]
pub const X25519_KYBER768_DRAFT00_OLD: SslCurve =
SslCurve(ffi::SSL_CURVE_X25519_KYBER768_DRAFT00_OLD as _);
#[cfg(all(
not(any(feature = "fips", feature = "fips-precompiled")),
feature = "pq-experimental"
))]
pub const X25519_KYBER512_DRAFT00: SslCurve =
SslCurve(ffi::SSL_CURVE_X25519_KYBER512_DRAFT00 as _);
#[cfg(all(
not(any(feature = "fips", feature = "fips-precompiled")),
feature = "pq-experimental"
))]
pub const P256_KYBER768_DRAFT00: SslCurve = SslCurve(ffi::SSL_CURVE_P256_KYBER768_DRAFT00 as _);
#[cfg(all(
not(any(feature = "fips", feature = "fips-precompiled")),
feature = "pq-experimental"
))]
pub const X25519_MLKEM768: SslCurve = SslCurve(ffi::SSL_CURVE_X25519_MLKEM768 as _);
/// Returns the curve name
#[corresponds(SSL_get_curve_name)]
#[must_use]
pub fn name(&self) -> Option<&'static str> {
unsafe {
let ptr = ffi::SSL_get_curve_name(self.0 as u16);
if ptr.is_null() {
return None;
}
CStr::from_ptr(ptr).to_str().ok()
}
}
// We need to allow dead_code here because `SslRef::set_curves` is conditionally compiled
// against the absence of the `kx-safe-default` feature and thus this function is never used.
//
// **NOTE**: This function only exists because the version of boringssl we currently use does
// not expose SSL_CTX_set1_group_ids. Because `SslRef::curve()` returns the public SSL_CURVE id
// as opposed to the internal NID, but `SslContextBuilder::set_curves()` requires the internal
// NID, we need this mapping in place to avoid breaking changes to the public API. Once the
// underlying boringssl version is upgraded, this should be removed in favor of the new
// SSL_CTX_set1_group_ids API.
#[allow(dead_code)]
pub fn nid(&self) -> Option<SslCurveNid> {
match self.0 {
ffi::SSL_CURVE_SECP224R1 => Some(ffi::NID_secp224r1),
ffi::SSL_CURVE_SECP256R1 => Some(ffi::NID_X9_62_prime256v1),
ffi::SSL_CURVE_SECP384R1 => Some(ffi::NID_secp384r1),
ffi::SSL_CURVE_SECP521R1 => Some(ffi::NID_secp521r1),
ffi::SSL_CURVE_X25519 => Some(ffi::NID_X25519),
#[cfg(not(any(feature = "fips", feature = "fips-precompiled")))]
ffi::SSL_CURVE_X25519_KYBER768_DRAFT00 => Some(ffi::NID_X25519Kyber768Draft00),
#[cfg(all(
not(any(feature = "fips", feature = "fips-precompiled")),
feature = "pq-experimental"
))]
ffi::SSL_CURVE_X25519_KYBER768_DRAFT00_OLD => Some(ffi::NID_X25519Kyber768Draft00Old),
#[cfg(all(
not(any(feature = "fips", feature = "fips-precompiled")),
feature = "pq-experimental"
))]
ffi::SSL_CURVE_X25519_KYBER512_DRAFT00 => Some(ffi::NID_X25519Kyber512Draft00),
#[cfg(all(
not(any(feature = "fips", feature = "fips-precompiled")),
feature = "pq-experimental"
))]
ffi::SSL_CURVE_P256_KYBER768_DRAFT00 => Some(ffi::NID_P256Kyber768Draft00),
#[cfg(all(
not(any(feature = "fips", feature = "fips-precompiled")),
feature = "pq-experimental"
))]
ffi::SSL_CURVE_X25519_MLKEM768 => Some(ffi::NID_X25519MLKEM768),
ffi::SSL_CURVE_DHE2048 => Some(ffi::NID_ffdhe2048),
ffi::SSL_CURVE_DHE3072 => Some(ffi::NID_ffdhe3072),
_ => None,
}
.map(SslCurveNid)
}
}
/// A compliance policy. /// A compliance policy.
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg(not(feature = "fips-compat"))]
pub struct CompliancePolicy(ffi::ssl_compliance_policy_t); pub struct CompliancePolicy(ffi::ssl_compliance_policy_t);
#[cfg(not(feature = "fips-compat"))]
impl CompliancePolicy { impl CompliancePolicy {
/// Does nothing, however setting this does not undo other policies, so trying to set this is an error. /// Does nothing, however setting this does not undo other policies, so trying to set this is an error.
#[cfg(not(feature = "legacy-compat-deprecated"))]
pub const NONE: Self = Self(ffi::ssl_compliance_policy_t::ssl_compliance_policy_none); pub const NONE: Self = Self(ffi::ssl_compliance_policy_t::ssl_compliance_policy_none);
/// Configures a TLS connection to try and be compliant with NIST requirements, but does not guarantee success. /// Configures a TLS connection to try and be compliant with NIST requirements, but does not guarantee success.
@ -714,7 +831,6 @@ impl CompliancePolicy {
/// Partially configures a TLS connection to be compliant with WPA3. Callers must enforce certificate chain requirements themselves. /// Partially configures a TLS connection to be compliant with WPA3. Callers must enforce certificate chain requirements themselves.
/// Use of this policy is less secure than the default and not recommended. /// Use of this policy is less secure than the default and not recommended.
#[cfg(not(feature = "legacy-compat-deprecated"))]
pub const WPA3_192_202304: Self = pub const WPA3_192_202304: Self =
Self(ffi::ssl_compliance_policy_t::ssl_compliance_policy_wpa3_192_202304); Self(ffi::ssl_compliance_policy_t::ssl_compliance_policy_wpa3_192_202304);
} }
@ -809,53 +925,6 @@ pub enum SslInfoCallbackValue {
Alert(SslInfoCallbackAlert), Alert(SslInfoCallbackAlert),
} }
/// Ticket key callback status.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum TicketKeyCallbackResult {
/// Abort the handshake.
Error,
/// Continue with a full handshake.
///
/// When in decryption mode, this indicates that the peer supplied session ticket was not
/// recognized. When in encryption mode, this instructs boring to not send a session ticket.
///
/// # Note
///
/// This is a decryption specific status code when using the submoduled BoringSSL.
Noop,
/// Resumption callback was successful.
///
/// When in decryption mode, attempt an abbreviated handshake via session resumption. When in
/// encryption mode, provide a new ticket to the client.
Success,
/// Resumption callback was successful. Attempt an abbreviated handshake, and additionally
/// provide new session tickets to the peer.
///
/// Session resumption short-circuits some security checks of a full-handshake, in exchange for
/// potential performance gains. For this reason, a session ticket should only be valid for a
/// limited time. Providing the peer with renewed session tickets allows them to continue
/// session resumption with the new tickets.
///
/// # Note
///
/// This is a decryption specific status code.
DecryptSuccessRenew,
}
impl From<TicketKeyCallbackResult> for c_int {
fn from(value: TicketKeyCallbackResult) -> Self {
match value {
TicketKeyCallbackResult::Error => -1,
TicketKeyCallbackResult::Noop => 0,
TicketKeyCallbackResult::Success => 1,
TicketKeyCallbackResult::DecryptSuccessRenew => 2,
}
}
}
#[derive(Hash, Copy, Clone, PartialOrd, Ord, Eq, PartialEq, Debug)] #[derive(Hash, Copy, Clone, PartialOrd, Ord, Eq, PartialEq, Debug)]
pub struct SslInfoCallbackAlert(c_int); pub struct SslInfoCallbackAlert(c_int);
@ -897,6 +966,7 @@ impl SslContextBuilder {
unsafe { unsafe {
init(); init();
let ctx = cvt_p(ffi::SSL_CTX_new(method.as_ptr()))?; let ctx = cvt_p(ffi::SSL_CTX_new(method.as_ptr()))?;
Ok(SslContextBuilder::from_ptr(ctx)) Ok(SslContextBuilder::from_ptr(ctx))
} }
} }
@ -907,10 +977,9 @@ impl SslContextBuilder {
/// ///
/// The caller must ensure that the pointer is valid and uniquely owned by the builder. /// The caller must ensure that the pointer is valid and uniquely owned by the builder.
pub unsafe fn from_ptr(ctx: *mut ffi::SSL_CTX) -> SslContextBuilder { pub unsafe fn from_ptr(ctx: *mut ffi::SSL_CTX) -> SslContextBuilder {
let ctx = SslContext::from_ptr(ctx);
SslContextBuilder { SslContextBuilder {
ctx: SslContext::from_ptr(ctx),
has_shared_cert_store: false, has_shared_cert_store: false,
ctx,
} }
} }
@ -1056,49 +1125,6 @@ impl SslContextBuilder {
} }
} }
/// Configures a custom session ticket key callback for session resumption.
///
/// Session Resumption uses the security context (aka. session tickets) of a previous
/// connection to establish a new connection via an abbreviated handshake. Skipping portions of
/// a handshake can potentially yield performance gains.
///
/// An attacker that compromises a server's session ticket key can impersonate the server and,
/// prior to TLS 1.3, retroactively decrypt all application traffic from sessions using that
/// ticket key. Thus ticket keys must be regularly rotated for forward secrecy.
///
/// CipherCtx and HmacCtx are guaranteed to be initialized.
///
/// # Panics
///
/// This method panics if this `Ssl` is associated with a RPK context.
///
/// # Safety
///
/// The application is responsible for correctly setting the key_name, iv, encryption context
/// and hmac context. See the [`SSL_CTX_set_tlsext_ticket_key_cb`] docs for additional info.
///
/// [`SSL_CTX_set_tlsext_ticket_key_cb`]: https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_CTX_set_tlsext_ticket_key_cb
#[corresponds(SSL_CTX_set_tlsext_ticket_key_cb)]
pub unsafe fn set_ticket_key_callback<F>(&mut self, callback: F)
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,
{
unsafe {
self.replace_ex_data(SslContext::cached_ex_index::<F>(), callback);
ffi::SSL_CTX_set_tlsext_ticket_key_cb(self.as_ptr(), Some(raw_ticket_key::<F>))
};
}
/// Sets the certificate verification depth. /// Sets the certificate verification depth.
/// ///
/// If the peer's certificate chain is longer than this value, verification will fail. /// If the peer's certificate chain is longer than this value, verification will fail.
@ -1467,10 +1493,7 @@ impl SslContextBuilder {
#[corresponds(SSL_CTX_set_alpn_protos)] #[corresponds(SSL_CTX_set_alpn_protos)]
pub fn set_alpn_protos(&mut self, protocols: &[u8]) -> Result<(), ErrorStack> { pub fn set_alpn_protos(&mut self, protocols: &[u8]) -> Result<(), ErrorStack> {
unsafe { unsafe {
#[cfg_attr( #[cfg_attr(not(feature = "fips-compat"), allow(clippy::unnecessary_cast))]
not(feature = "legacy-compat-deprecated"),
allow(clippy::unnecessary_cast)
)]
{ {
assert!(protocols.len() <= ProtosLen::MAX as usize); assert!(protocols.len() <= ProtosLen::MAX as usize);
} }
@ -1696,7 +1719,7 @@ impl SslContextBuilder {
+ Sync + Sync
+ Send, + Send,
{ {
self.set_psk_client_callback(callback); self.set_psk_client_callback(callback)
} }
/// Sets the callback for providing an identity and pre-shared key for a TLS-PSK server. /// Sets the callback for providing an identity and pre-shared key for a TLS-PSK server.
@ -1913,8 +1936,21 @@ impl SslContextBuilder {
unsafe { ffi::SSL_CTX_set_preserve_tls13_cipher_list(self.as_ptr(), enable as _) } unsafe { ffi::SSL_CTX_set_preserve_tls13_cipher_list(self.as_ptr(), enable as _) }
} }
/// Sets whether the ChaCha20 preference should be enabled.
///
/// Controls the priority of TLS 1.3 cipher suites. When set to `true`, the client prefers:
/// AES_128_GCM, CHACHA20_POLY1305, then AES_256_GCM. Useful in environments with specific
/// encryption requirements.
#[deprecated(note = "use `set_preserve_tls13_cipher_list` instead")]
#[cfg(not(feature = "fips"))]
#[corresponds(SSL_CTX_set_prefer_chacha20)]
pub fn set_prefer_chacha20(&mut self, enable: bool) {
unsafe { ffi::SSL_CTX_set_preserve_tls13_cipher_list(self.as_ptr(), enable as _) }
}
/// Sets the indices of the extensions to be permuted. /// Sets the indices of the extensions to be permuted.
#[corresponds(SSL_CTX_set_extension_order)] #[corresponds(SSL_CTX_set_extension_order)]
#[cfg(not(feature = "fips-compat"))]
pub fn set_extension_permutation( pub fn set_extension_permutation(
&mut self, &mut self,
indices: &[ExtensionType], indices: &[ExtensionType],
@ -1930,7 +1966,12 @@ impl SslContextBuilder {
} }
/// Configures whether ClientHello extensions should be permuted. /// Configures whether ClientHello extensions should be permuted.
///
/// Note: This is gated to non-fips because the fips feature builds with a separate
/// version of BoringSSL which doesn't yet include these APIs.
/// Once the submoduled fips commit is upgraded, these gates can be removed.
#[corresponds(SSL_CTX_set_permute_extensions)] #[corresponds(SSL_CTX_set_permute_extensions)]
#[cfg(not(feature = "fips-compat"))]
pub fn set_permute_extensions(&mut self, enabled: bool) { pub fn set_permute_extensions(&mut self, enabled: bool) {
unsafe { ffi::SSL_CTX_set_permute_extensions(self.as_ptr(), enabled as _) } unsafe { ffi::SSL_CTX_set_permute_extensions(self.as_ptr(), enabled as _) }
} }
@ -1964,6 +2005,11 @@ impl SslContextBuilder {
} }
/// Sets the context's supported curves. /// Sets the context's supported curves.
//
// If the "kx-*" flags are used to set key exchange preference, then don't allow the user to
// set them here. This ensures we don't override the user's preference without telling them:
// when the flags are used, the preferences are set just before connecting or accepting.
#[cfg(not(feature = "kx-safe-default"))]
#[corresponds(SSL_CTX_set1_curves_list)] #[corresponds(SSL_CTX_set1_curves_list)]
pub fn set_curves_list(&mut self, curves: &str) -> Result<(), ErrorStack> { pub fn set_curves_list(&mut self, curves: &str) -> Result<(), ErrorStack> {
let curves = CString::new(curves).map_err(ErrorStack::internal_error)?; let curves = CString::new(curves).map_err(ErrorStack::internal_error)?;
@ -1976,10 +2022,34 @@ impl SslContextBuilder {
} }
} }
/// Sets the context's supported curves.
//
// If the "kx-*" flags are used to set key exchange preference, then don't allow the user to
// set them here. This ensures we don't override the user's preference without telling them:
// when the flags are used, the preferences are set just before connecting or accepting.
#[corresponds(SSL_CTX_set1_curves)]
#[cfg(not(feature = "kx-safe-default"))]
pub fn set_curves(&mut self, curves: &[SslCurve]) -> Result<(), ErrorStack> {
let curves: Vec<i32> = curves
.iter()
.filter_map(|curve| curve.nid().map(|nid| nid.0))
.collect();
unsafe {
cvt_0i(ffi::SSL_CTX_set1_curves(
self.as_ptr(),
curves.as_ptr() as *const _,
curves.len(),
))
.map(|_| ())
}
}
/// Sets the context's compliance policy. /// Sets the context's compliance policy.
/// ///
/// This feature isn't available in the certified version of BoringSSL. /// This feature isn't available in the certified version of BoringSSL.
#[corresponds(SSL_CTX_set_compliance_policy)] #[corresponds(SSL_CTX_set_compliance_policy)]
#[cfg(not(feature = "fips-compat"))]
pub fn set_compliance_policy(&mut self, policy: CompliancePolicy) -> Result<(), ErrorStack> { pub fn set_compliance_policy(&mut self, policy: CompliancePolicy) -> Result<(), ErrorStack> {
unsafe { cvt_0i(ffi::SSL_CTX_set_compliance_policy(self.as_ptr(), policy.0)).map(|_| ()) } unsafe { cvt_0i(ffi::SSL_CTX_set_compliance_policy(self.as_ptr(), policy.0)).map(|_| ()) }
} }
@ -2000,6 +2070,7 @@ impl SslContextBuilder {
/// ECHConfigs to allow stale DNS caches to update. Unlike most `SSL_CTX` APIs, this function /// ECHConfigs to allow stale DNS caches to update. Unlike most `SSL_CTX` APIs, this function
/// is safe to call even after the `SSL_CTX` has been associated with connections on various /// is safe to call even after the `SSL_CTX` has been associated with connections on various
/// threads. /// threads.
#[cfg(not(feature = "fips"))]
#[corresponds(SSL_CTX_set1_ech_keys)] #[corresponds(SSL_CTX_set1_ech_keys)]
pub fn set_ech_keys(&self, keys: &SslEchKeys) -> Result<(), ErrorStack> { pub fn set_ech_keys(&self, keys: &SslEchKeys) -> Result<(), ErrorStack> {
unsafe { cvt(ffi::SSL_CTX_set1_ech_keys(self.as_ptr(), keys.as_ptr())).map(|_| ()) } unsafe { cvt(ffi::SSL_CTX_set1_ech_keys(self.as_ptr(), keys.as_ptr())).map(|_| ()) }
@ -2252,6 +2323,7 @@ impl SslContextRef {
/// ECHConfigs to allow stale DNS caches to update. Unlike most `SSL_CTX` APIs, this function /// ECHConfigs to allow stale DNS caches to update. Unlike most `SSL_CTX` APIs, this function
/// is safe to call even after the `SSL_CTX` has been associated with connections on various /// is safe to call even after the `SSL_CTX` has been associated with connections on various
/// threads. /// threads.
#[cfg(not(feature = "fips"))]
#[corresponds(SSL_CTX_set1_ech_keys)] #[corresponds(SSL_CTX_set1_ech_keys)]
pub fn set_ech_keys(&self, keys: &SslEchKeys) -> Result<(), ErrorStack> { pub fn set_ech_keys(&self, keys: &SslEchKeys) -> Result<(), ErrorStack> {
unsafe { cvt(ffi::SSL_CTX_set1_ech_keys(self.as_ptr(), keys.as_ptr())).map(|_| ()) } unsafe { cvt(ffi::SSL_CTX_set1_ech_keys(self.as_ptr(), keys.as_ptr())).map(|_| ()) }
@ -2265,9 +2337,9 @@ impl SslContextRef {
#[derive(Debug)] #[derive(Debug)]
pub struct GetSessionPendingError; pub struct GetSessionPendingError;
#[cfg(not(feature = "legacy-compat-deprecated"))] #[cfg(not(feature = "fips-compat"))]
type ProtosLen = usize; type ProtosLen = usize;
#[cfg(feature = "legacy-compat-deprecated")] #[cfg(feature = "fips-compat")]
type ProtosLen = libc::c_uint; type ProtosLen = libc::c_uint;
/// Information about the state of a cipher. /// Information about the state of a cipher.
@ -2439,7 +2511,7 @@ impl SslCipherRef {
CStr::from_ptr(ptr as *const _) CStr::from_ptr(ptr as *const _)
}; };
version.to_str().unwrap() str::from_utf8(version.to_bytes()).unwrap()
} }
/// Returns the number of bits used for the cipher. /// Returns the number of bits used for the cipher.
@ -2465,7 +2537,7 @@ impl SslCipherRef {
// SSL_CIPHER_description requires a buffer of at least 128 bytes. // SSL_CIPHER_description requires a buffer of at least 128 bytes.
let mut buf = [0; 128]; let mut buf = [0; 128];
let ptr = ffi::SSL_CIPHER_description(self.as_ptr(), buf.as_mut_ptr(), 128); let ptr = ffi::SSL_CIPHER_description(self.as_ptr(), buf.as_mut_ptr(), 128);
CStr::from_ptr(ptr.cast()).to_string_lossy().into_owned() String::from_utf8(CStr::from_ptr(ptr as *const _).to_bytes().to_vec()).unwrap()
} }
} }
@ -2653,13 +2725,32 @@ impl Ssl {
} }
} }
/// Creates a new [`Ssl`]. /// Creates a new `Ssl`.
///
// FIXME should take &SslContextRef
#[corresponds(SSL_new)] #[corresponds(SSL_new)]
pub fn new(ctx: &SslContextRef) -> Result<Ssl, ErrorStack> { pub fn new(ctx: &SslContext) -> Result<Ssl, ErrorStack> {
unsafe { unsafe {
let ptr = cvt_p(ffi::SSL_new(ctx.as_ptr()))?; let ptr = cvt_p(ffi::SSL_new(ctx.as_ptr()))?;
let mut ssl = Ssl::from_ptr(ptr); let mut ssl = Ssl::from_ptr(ptr);
ssl.set_ex_data(*SESSION_CTX_INDEX, ctx.to_owned()); ssl.set_ex_data(*SESSION_CTX_INDEX, ctx.clone());
Ok(ssl)
}
}
/// Creates a new [`Ssl`].
///
/// This function does the same as [`Self:new`] except that it takes &[SslContextRef].
// Both functions exist for backward compatibility (no breaking API).
#[corresponds(SSL_new)]
pub fn new_from_ref(ctx: &SslContextRef) -> Result<Ssl, ErrorStack> {
unsafe {
let ptr = cvt_p(ffi::SSL_new(ctx.as_ptr()))?;
let mut ssl = Ssl::from_ptr(ptr);
SSL_CTX_up_ref(ctx.as_ptr());
let ctx_owned = SslContext::from_ptr(ctx.as_ptr());
ssl.set_ex_data(*SESSION_CTX_INDEX, ctx_owned);
Ok(ssl) Ok(ssl)
} }
@ -2784,31 +2875,63 @@ impl SslRef {
} }
} }
/// Returns the curve ID (aka group ID) used for this `SslRef`. /// Sets the ongoing session's supported groups by their named identifiers
/// (formerly referred to as curves).
#[corresponds(SSL_set1_groups)]
pub fn set_group_nids(&mut self, group_nids: &[SslCurveNid]) -> Result<(), ErrorStack> {
unsafe {
cvt_0i(ffi::SSL_set1_curves(
self.as_ptr(),
group_nids.as_ptr() as *const _,
group_nids.len(),
))
.map(|_| ())
}
}
#[cfg(feature = "kx-safe-default")]
fn client_set_default_curves_list(&mut self) {
let curves = if cfg!(feature = "kx-client-pq-preferred") {
if cfg!(feature = "kx-client-nist-required") {
"P256Kyber768Draft00:P-256:P-384:P-521"
} else {
"X25519MLKEM768:X25519Kyber768Draft00:X25519:P256Kyber768Draft00:P-256:P-384:P-521"
}
} else if cfg!(feature = "kx-client-pq-supported") {
if cfg!(feature = "kx-client-nist-required") {
"P-256:P-384:P-521:P256Kyber768Draft00"
} else {
"X25519:P-256:P-384:P-521:X25519MLKEM768:X25519Kyber768Draft00:P256Kyber768Draft00"
}
} else {
if cfg!(feature = "kx-client-nist-required") {
"P-256:P-384:P-521"
} else {
"X25519:P-256:P-384:P-521"
}
};
self.set_curves_list(curves)
.expect("invalid default client curves list");
}
#[cfg(feature = "kx-safe-default")]
fn server_set_default_curves_list(&mut self) {
self.set_curves_list(
"X25519MLKEM768:X25519Kyber768Draft00:P256Kyber768Draft00:X25519:P-256:P-384",
)
.expect("invalid default server curves list");
}
/// Returns the [`SslCurve`] used for this `SslRef`.
#[corresponds(SSL_get_curve_id)] #[corresponds(SSL_get_curve_id)]
#[must_use] #[must_use]
pub fn curve(&self) -> Option<u16> { pub fn curve(&self) -> Option<SslCurve> {
let curve_id = unsafe { ffi::SSL_get_curve_id(self.as_ptr()) }; let curve_id = unsafe { ffi::SSL_get_curve_id(self.as_ptr()) };
if curve_id == 0 { if curve_id == 0 {
return None; return None;
} }
Some(curve_id) Some(SslCurve(curve_id.into()))
}
/// Returns the curve name used for this `SslRef`.
#[corresponds(SSL_get_curve_name)]
#[must_use]
pub fn curve_name(&self) -> Option<&'static str> {
let curve_id = self.curve()?;
unsafe {
let ptr = ffi::SSL_get_curve_name(curve_id);
if ptr.is_null() {
return None;
}
CStr::from_ptr(ptr).to_str().ok()
}
} }
/// Returns an `ErrorCode` value for the most recent operation on this `SslRef`. /// Returns an `ErrorCode` value for the most recent operation on this `SslRef`.
@ -2924,6 +3047,11 @@ impl SslRef {
/// Configures whether ClientHello extensions should be permuted. /// Configures whether ClientHello extensions should be permuted.
#[corresponds(SSL_set_permute_extensions)] #[corresponds(SSL_set_permute_extensions)]
///
/// Note: This is gated to non-fips because the fips feature builds with a separate
/// version of BoringSSL which doesn't yet include these APIs.
/// Once the submoduled fips commit is upgraded, these gates can be removed.
#[cfg(not(feature = "fips-compat"))]
pub fn set_permute_extensions(&mut self, enabled: bool) { pub fn set_permute_extensions(&mut self, enabled: bool) {
unsafe { ffi::SSL_set_permute_extensions(self.as_ptr(), enabled as _) } unsafe { ffi::SSL_set_permute_extensions(self.as_ptr(), enabled as _) }
} }
@ -2934,10 +3062,7 @@ impl SslRef {
#[corresponds(SSL_set_alpn_protos)] #[corresponds(SSL_set_alpn_protos)]
pub fn set_alpn_protos(&mut self, protocols: &[u8]) -> Result<(), ErrorStack> { pub fn set_alpn_protos(&mut self, protocols: &[u8]) -> Result<(), ErrorStack> {
unsafe { unsafe {
#[cfg_attr( #[cfg_attr(not(feature = "fips-compat"), allow(clippy::unnecessary_cast))]
not(feature = "legacy-compat-deprecated"),
allow(clippy::unnecessary_cast)
)]
{ {
assert!(protocols.len() <= ProtosLen::MAX as usize); assert!(protocols.len() <= ProtosLen::MAX as usize);
} }
@ -2981,8 +3106,6 @@ impl SslRef {
} }
/// Returns a short string describing the state of the session. /// Returns a short string describing the state of the session.
///
/// Returns empty string if the state wasn't valid UTF-8.
#[corresponds(SSL_state_string)] #[corresponds(SSL_state_string)]
#[must_use] #[must_use]
pub fn state_string(&self) -> &'static str { pub fn state_string(&self) -> &'static str {
@ -2991,12 +3114,10 @@ impl SslRef {
CStr::from_ptr(ptr as *const _) CStr::from_ptr(ptr as *const _)
}; };
state.to_str().unwrap_or_default() str::from_utf8(state.to_bytes()).unwrap()
} }
/// Returns a longer string describing the state of the session. /// Returns a longer string describing the state of the session.
///
/// Returns empty string if the state wasn't valid UTF-8.
#[corresponds(SSL_state_string_long)] #[corresponds(SSL_state_string_long)]
#[must_use] #[must_use]
pub fn state_string_long(&self) -> &'static str { pub fn state_string_long(&self) -> &'static str {
@ -3005,7 +3126,7 @@ impl SslRef {
CStr::from_ptr(ptr as *const _) CStr::from_ptr(ptr as *const _)
}; };
state.to_str().unwrap_or_default() str::from_utf8(state.to_bytes()).unwrap()
} }
/// Sets the host name to be sent to the server for Server Name Indication (SNI). /// Sets the host name to be sent to the server for Server Name Indication (SNI).
@ -3099,8 +3220,6 @@ impl SslRef {
} }
/// Returns a string describing the protocol version of the session. /// Returns a string describing the protocol version of the session.
///
/// This may panic if the string isn't valid UTF-8 for some reason. Use [`Self::version2`] instead.
#[corresponds(SSL_get_version)] #[corresponds(SSL_get_version)]
#[must_use] #[must_use]
pub fn version_str(&self) -> &'static str { pub fn version_str(&self) -> &'static str {
@ -3109,7 +3228,7 @@ impl SslRef {
CStr::from_ptr(ptr as *const _) CStr::from_ptr(ptr as *const _)
}; };
version.to_str().unwrap() str::from_utf8(version.to_bytes()).unwrap()
} }
/// Sets the minimum supported protocol version. /// Sets the minimum supported protocol version.
@ -3615,6 +3734,7 @@ impl SslRef {
/// Clients should use `get_ech_name_override` to verify the server certificate in case of ECH /// Clients should use `get_ech_name_override` to verify the server certificate in case of ECH
/// rejection, and follow up with `get_ech_retry_configs` to retry the connection with a fresh /// rejection, and follow up with `get_ech_retry_configs` to retry the connection with a fresh
/// set of ECHConfigs. If the retry also fails, clients should report a connection failure. /// set of ECHConfigs. If the retry also fails, clients should report a connection failure.
#[cfg(not(feature = "fips"))]
#[corresponds(SSL_set1_ech_config_list)] #[corresponds(SSL_set1_ech_config_list)]
pub fn set_ech_config_list(&mut self, ech_config_list: &[u8]) -> Result<(), ErrorStack> { pub fn set_ech_config_list(&mut self, ech_config_list: &[u8]) -> Result<(), ErrorStack> {
unsafe { unsafe {
@ -3633,6 +3753,7 @@ impl SslRef {
/// Clients should call this function when handling an `SSL_R_ECH_REJECTED` error code to /// Clients should call this function when handling an `SSL_R_ECH_REJECTED` error code to
/// recover from potential key mismatches. If the result is `Some`, the client should retry the /// recover from potential key mismatches. If the result is `Some`, the client should retry the
/// connection using the returned `ECHConfigList`. /// connection using the returned `ECHConfigList`.
#[cfg(not(feature = "fips"))]
#[corresponds(SSL_get0_ech_retry_configs)] #[corresponds(SSL_get0_ech_retry_configs)]
#[must_use] #[must_use]
pub fn get_ech_retry_configs(&self) -> Option<&[u8]> { pub fn get_ech_retry_configs(&self) -> Option<&[u8]> {
@ -3655,6 +3776,7 @@ impl SslRef {
/// Clients should call this function during the certificate verification callback to /// Clients should call this function during the certificate verification callback to
/// ensure the server's certificate is valid for the public name, which is required to /// ensure the server's certificate is valid for the public name, which is required to
/// authenticate retry configs. /// authenticate retry configs.
#[cfg(not(feature = "fips"))]
#[corresponds(SSL_get0_ech_name_override)] #[corresponds(SSL_get0_ech_name_override)]
#[must_use] #[must_use]
pub fn get_ech_name_override(&self) -> Option<&[u8]> { pub fn get_ech_name_override(&self) -> Option<&[u8]> {
@ -3672,6 +3794,7 @@ impl SslRef {
} }
// Whether or not `SSL` negotiated ECH. // Whether or not `SSL` negotiated ECH.
#[cfg(not(feature = "fips"))]
#[corresponds(SSL_ech_accepted)] #[corresponds(SSL_ech_accepted)]
#[must_use] #[must_use]
pub fn ech_accepted(&self) -> bool { pub fn ech_accepted(&self) -> bool {
@ -3679,6 +3802,7 @@ impl SslRef {
} }
// Whether or not to enable ECH grease on `SSL`. // Whether or not to enable ECH grease on `SSL`.
#[cfg(not(feature = "fips"))]
#[corresponds(SSL_set_enable_ech_grease)] #[corresponds(SSL_set_enable_ech_grease)]
pub fn set_enable_ech_grease(&self, enable: bool) { pub fn set_enable_ech_grease(&self, enable: bool) {
let enable = if enable { 1 } else { 0 }; let enable = if enable { 1 } else { 0 };
@ -3689,6 +3813,7 @@ impl SslRef {
} }
/// Sets the compliance policy on `SSL`. /// Sets the compliance policy on `SSL`.
#[cfg(not(feature = "fips-compat"))]
#[corresponds(SSL_set_compliance_policy)] #[corresponds(SSL_set_compliance_policy)]
pub fn set_compliance_policy(&mut self, policy: CompliancePolicy) -> Result<(), ErrorStack> { pub fn set_compliance_policy(&mut self, policy: CompliancePolicy) -> Result<(), ErrorStack> {
unsafe { cvt_0i(ffi::SSL_set_compliance_policy(self.as_ptr(), policy.0)).map(|_| ()) } unsafe { cvt_0i(ffi::SSL_set_compliance_policy(self.as_ptr(), policy.0)).map(|_| ()) }
@ -3757,11 +3882,10 @@ impl<S> MidHandshakeSslStream<S> {
Ok(self.stream) Ok(self.stream)
} else { } else {
self.error = self.stream.make_error(ret); self.error = self.stream.make_error(ret);
Err(if self.error.would_block() { match self.error.would_block() {
HandshakeError::WouldBlock(self) true => Err(HandshakeError::WouldBlock(self)),
} else { false => Err(HandshakeError::Failure(self)),
HandshakeError::Failure(self) }
})
} }
} }
} }
@ -3796,23 +3920,26 @@ where
} }
impl<S: Read + Write> SslStream<S> { impl<S: Read + Write> SslStream<S> {
fn new_base(ssl: Ssl, stream: S) -> Self {
unsafe {
let (bio, method) = bio::new(stream).unwrap();
ffi::SSL_set_bio(ssl.as_ptr(), bio, bio);
SslStream {
ssl: ManuallyDrop::new(ssl),
method: ManuallyDrop::new(method),
_p: PhantomData,
}
}
}
/// Creates a new `SslStream`. /// Creates a new `SslStream`.
/// ///
/// This function performs no IO; the stream will not have performed any part of the handshake /// This function performs no IO; the stream will not have performed any part of the handshake
/// with the peer. The `connect` and `accept` methods can be used to /// with the peer. The `connect` and `accept` methods can be used to
/// explicitly perform the handshake. /// explicitly perform the handshake.
pub fn new(ssl: Ssl, stream: S) -> Result<Self, ErrorStack> { pub fn new(ssl: Ssl, stream: S) -> Result<Self, ErrorStack> {
let (bio, method) = bio::new(stream)?; Ok(Self::new_base(ssl, stream))
unsafe {
ffi::SSL_set_bio(ssl.as_ptr(), bio, bio);
}
Ok(SslStream {
ssl: ManuallyDrop::new(ssl),
method: ManuallyDrop::new(method),
_p: PhantomData,
})
} }
/// Constructs an `SslStream` from a pointer to the underlying OpenSSL `SSL` struct. /// Constructs an `SslStream` from a pointer to the underlying OpenSSL `SSL` struct.
@ -3824,7 +3951,7 @@ impl<S: Read + Write> SslStream<S> {
/// The caller must ensure the pointer is valid. /// The caller must ensure the pointer is valid.
pub unsafe fn from_raw_parts(ssl: *mut ffi::SSL, stream: S) -> Self { pub unsafe fn from_raw_parts(ssl: *mut ffi::SSL, stream: S) -> Self {
let ssl = Ssl::from_ptr(ssl); let ssl = Ssl::from_ptr(ssl);
Self::new(ssl, stream).unwrap() Self::new_base(ssl, stream)
} }
/// Like `read`, but takes a possibly-uninitialized slice. /// Like `read`, but takes a possibly-uninitialized slice.
@ -4091,7 +4218,7 @@ where
/// Begin creating an `SslStream` atop `stream` /// Begin creating an `SslStream` atop `stream`
pub fn new(ssl: Ssl, stream: S) -> Self { pub fn new(ssl: Ssl, stream: S) -> Self {
Self { Self {
inner: SslStream::new(ssl, stream).unwrap(), inner: SslStream::new_base(ssl, stream),
} }
} }
@ -4116,6 +4243,9 @@ where
pub fn setup_connect(mut self) -> MidHandshakeSslStream<S> { pub fn setup_connect(mut self) -> MidHandshakeSslStream<S> {
self.set_connect_state(); self.set_connect_state();
#[cfg(feature = "kx-safe-default")]
self.inner.ssl.client_set_default_curves_list();
MidHandshakeSslStream { MidHandshakeSslStream {
stream: self.inner, stream: self.inner,
error: Error { error: Error {
@ -4145,6 +4275,9 @@ where
pub fn setup_accept(mut self) -> MidHandshakeSslStream<S> { pub fn setup_accept(mut self) -> MidHandshakeSslStream<S> {
self.set_accept_state(); self.set_accept_state();
#[cfg(feature = "kx-safe-default")]
self.inner.ssl.server_set_default_curves_list();
MidHandshakeSslStream { MidHandshakeSslStream {
stream: self.inner, stream: self.inner,
error: Error { error: Error {
@ -4176,11 +4309,16 @@ where
Ok(stream) Ok(stream)
} else { } else {
let error = stream.make_error(ret); let error = stream.make_error(ret);
Err(if error.would_block() { match error.would_block() {
HandshakeError::WouldBlock(MidHandshakeSslStream { stream, error }) true => Err(HandshakeError::WouldBlock(MidHandshakeSslStream {
} else { stream,
HandshakeError::Failure(MidHandshakeSslStream { stream, error }) error,
}) })),
false => Err(HandshakeError::Failure(MidHandshakeSslStream {
stream,
error,
})),
}
} }
} }
} }

View File

@ -13,24 +13,26 @@ use crate::pkey::PKey;
use crate::srtp::SrtpProfileId; use crate::srtp::SrtpProfileId;
use crate::ssl::test::server::Server; use crate::ssl::test::server::Server;
use crate::ssl::SslVersion; use crate::ssl::SslVersion;
use crate::ssl::{self, SslCurve};
use crate::ssl::{ use crate::ssl::{
self, ExtensionType, ShutdownResult, ShutdownState, Ssl, SslAcceptor, SslAcceptorBuilder, ExtensionType, ShutdownResult, ShutdownState, Ssl, SslAcceptor, SslAcceptorBuilder,
SslConnector, SslContext, SslFiletype, SslMethod, SslOptions, SslStream, SslVerifyMode, SslConnector, SslContext, SslFiletype, SslMethod, SslOptions, SslStream, SslVerifyMode,
}; };
use crate::x509::store::X509StoreBuilder; use crate::x509::store::X509StoreBuilder;
use crate::x509::verify::X509CheckFlags; use crate::x509::verify::X509CheckFlags;
use crate::x509::{X509Name, X509}; use crate::x509::{X509Name, X509};
#[cfg(not(feature = "fips"))]
use super::CompliancePolicy; use super::CompliancePolicy;
mod cert_compressor; mod cert_compressor;
mod cert_verify; mod cert_verify;
mod custom_verify; mod custom_verify;
#[cfg(not(feature = "fips"))]
mod ech; mod ech;
mod private_key_method; mod private_key_method;
mod server; mod server;
mod session; mod session;
mod session_resumption;
mod verify; mod verify;
static ROOT_CERT: &[u8] = include_bytes!("../../../test/root-ca.pem"); static ROOT_CERT: &[u8] = include_bytes!("../../../test/root-ca.pem");
@ -952,15 +954,59 @@ fn sni_callback_swapped_ctx() {
assert!(CALLED_BACK.load(Ordering::SeqCst)); assert!(CALLED_BACK.load(Ordering::SeqCst));
} }
#[cfg(feature = "kx-safe-default")]
#[test]
fn client_set_default_curves_list() {
let ssl_ctx = crate::ssl::SslContextBuilder::new(SslMethod::tls())
.unwrap()
.build();
let mut ssl = Ssl::new(&ssl_ctx).unwrap();
// Panics if Kyber768 missing in boringSSL.
ssl.client_set_default_curves_list();
}
#[cfg(feature = "kx-safe-default")]
#[test]
fn server_set_default_curves_list() {
let ssl_ctx = crate::ssl::SslContextBuilder::new(SslMethod::tls())
.unwrap()
.build();
let mut ssl = Ssl::new(&ssl_ctx).unwrap();
// Panics if Kyber768 missing in boringSSL.
ssl.server_set_default_curves_list();
}
#[test] #[test]
fn get_curve() { fn get_curve() {
let server = Server::builder().build(); let server = Server::builder().build();
let client = server.client_with_root_ca(); let client = server.client_with_root_ca();
let client_stream = client.connect(); let client_stream = client.connect();
let curve = client_stream.ssl().curve(); let curve = client_stream.ssl().curve().expect("curve");
assert!(curve.is_some()); assert!(curve.name().is_some());
let curve_name = client_stream.ssl().curve_name(); }
assert!(curve_name.is_some());
#[test]
fn get_curve_name() {
assert_eq!(SslCurve::SECP224R1.name(), Some("P-224"));
assert_eq!(SslCurve::SECP256R1.name(), Some("P-256"));
assert_eq!(SslCurve::SECP384R1.name(), Some("P-384"));
assert_eq!(SslCurve::SECP521R1.name(), Some("P-521"));
assert_eq!(SslCurve::X25519.name(), Some("X25519"));
}
#[cfg(not(feature = "kx-safe-default"))]
#[test]
fn set_curves() {
let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
ctx.set_curves(&[
SslCurve::SECP224R1,
SslCurve::SECP256R1,
SslCurve::SECP384R1,
SslCurve::X25519,
])
.expect("Failed to set curves");
} }
#[test] #[test]
@ -991,6 +1037,7 @@ fn test_get_ciphers() {
} }
#[test] #[test]
#[cfg(not(feature = "fips"))]
fn test_set_compliance() { fn test_set_compliance() {
let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
ctx.set_compliance_policy(CompliancePolicy::FIPS_202205) ctx.set_compliance_policy(CompliancePolicy::FIPS_202205)
@ -1071,6 +1118,7 @@ fn test_info_callback() {
assert!(CALLED_BACK.load(Ordering::Relaxed)); assert!(CALLED_BACK.load(Ordering::Relaxed));
} }
#[cfg(not(feature = "fips-compat"))]
#[test] #[test]
fn test_ssl_set_compliance() { fn test_ssl_set_compliance() {
let ctx = SslContext::builder(SslMethod::tls()).unwrap().build(); let ctx = SslContext::builder(SslMethod::tls()).unwrap().build();

View File

@ -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
}

View File

@ -13,9 +13,6 @@ foreign_type_and_impl_send_sync! {
type CType = c_char; type CType = c_char;
fn drop = free; fn drop = free;
/// # Safety
///
/// MUST be UTF-8.
pub struct OpensslString; pub struct OpensslString;
} }

View File

@ -53,7 +53,6 @@
//! ``` //! ```
use crate::ffi; use crate::ffi;
use foreign_types::ForeignTypeRef;
use libc::{c_int, c_uint}; use libc::{c_int, c_uint};
use openssl_macros::corresponds; use openssl_macros::corresponds;
use std::cmp; use std::cmp;
@ -69,71 +68,6 @@ pub enum Mode {
Decrypt, 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`.
///
/// https://commondatastorage.googleapis.com/chromium-boringssl-docs/cipher.h.html#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(),
))
.map(|_| ())
}
}
/// Configures CipherCtx for a fresh decryption operation using `cipher`.
///
/// https://commondatastorage.googleapis.com/chromium-boringssl-docs/cipher.h.html#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(),
))
.map(|_| ())
}
}
}
/// Represents a particular cipher algorithm. /// Represents a particular cipher algorithm.
/// ///
/// See OpenSSL doc at [`EVP_EncryptInit`] for more information on each algorithms. /// See OpenSSL doc at [`EVP_EncryptInit`] for more information on each algorithms.

View File

@ -55,8 +55,8 @@ where
match result { match result {
Ok(Ok(len)) => len as c_int, Ok(Ok(len)) => len as c_int,
Ok(Err(err)) => { Ok(Err(_)) => {
err.put(); // FIXME restore error stack
0 0
} }
Err(err) => { Err(err) => {

View File

@ -19,6 +19,7 @@ use std::mem;
use std::net::IpAddr; use std::net::IpAddr;
use std::path::Path; use std::path::Path;
use std::ptr; use std::ptr;
use std::slice;
use std::str; use std::str;
use std::sync::{LazyLock, Once}; use std::sync::{LazyLock, Once};
@ -484,9 +485,16 @@ impl X509Builder {
} }
} }
/// Adds an X509 extension value to the certificate.
///
/// This works just as `append_extension` except it takes ownership of the `X509Extension`.
pub fn append_extension(&mut self, extension: X509Extension) -> Result<(), ErrorStack> {
self.append_extension2(&extension)
}
/// Adds an X509 extension value to the certificate. /// Adds an X509 extension value to the certificate.
#[corresponds(X509_add_ext)] #[corresponds(X509_add_ext)]
pub fn append_extension(&mut self, extension: &X509ExtensionRef) -> Result<(), ErrorStack> { pub fn append_extension2(&mut self, extension: &X509ExtensionRef) -> Result<(), ErrorStack> {
unsafe { unsafe {
cvt(ffi::X509_add_ext(self.0.as_ptr(), extension.as_ptr(), -1))?; cvt(ffi::X509_add_ext(self.0.as_ptr(), extension.as_ptr(), -1))?;
Ok(()) Ok(())
@ -857,7 +865,7 @@ impl fmt::Debug for X509 {
if let Ok(public_key) = &self.public_key() { if let Ok(public_key) = &self.public_key() {
debug_struct.field("public_key", public_key); debug_struct.field("public_key", public_key);
} };
// TODO: Print extensions once they are supported on the X509 struct. // TODO: Print extensions once they are supported on the X509 struct.
debug_struct.finish() debug_struct.finish()
@ -1113,9 +1121,9 @@ impl X509NameBuilder {
} }
} }
#[cfg(not(feature = "legacy-compat-deprecated"))] #[cfg(not(feature = "fips-compat"))]
type ValueLen = isize; type ValueLen = isize;
#[cfg(feature = "legacy-compat-deprecated")] #[cfg(feature = "fips-compat")]
type ValueLen = i32; type ValueLen = i32;
foreign_type_and_impl_send_sync! { foreign_type_and_impl_send_sync! {
@ -1527,8 +1535,6 @@ impl X509VerifyError {
} }
/// Return a human readable error string from the verification error. /// Return a human readable error string from the verification error.
///
/// Returns empty string if the message was not UTF-8.
#[corresponds(X509_verify_cert_error_string)] #[corresponds(X509_verify_cert_error_string)]
#[allow(clippy::trivially_copy_pass_by_ref)] #[allow(clippy::trivially_copy_pass_by_ref)]
#[must_use] #[must_use]
@ -1537,7 +1543,7 @@ impl X509VerifyError {
unsafe { unsafe {
let s = ffi::X509_verify_cert_error_string(c_long::from(self.0)); let s = ffi::X509_verify_cert_error_string(c_long::from(self.0));
CStr::from_ptr(s).to_str().unwrap_or_default() str::from_utf8(CStr::from_ptr(s).to_bytes()).unwrap()
} }
} }
} }
@ -1689,12 +1695,14 @@ impl GeneralNameRef {
return None; return None;
} }
let asn = Asn1BitStringRef::from_ptr((*self.as_ptr()).d.ia5); let ptr = ASN1_STRING_get0_data((*self.as_ptr()).d.ia5 as *mut _);
let len = ffi::ASN1_STRING_length((*self.as_ptr()).d.ia5 as *mut _);
let slice = slice::from_raw_parts(ptr, len as usize);
// IA5Strings are stated to be ASCII (specifically IA5). Hopefully // IA5Strings are stated to be ASCII (specifically IA5). Hopefully
// OpenSSL checks that when loading a certificate but if not we'll // OpenSSL checks that when loading a certificate but if not we'll
// use this instead of from_utf8_unchecked just in case. // use this instead of from_utf8_unchecked just in case.
asn.to_str() str::from_utf8(slice).ok()
} }
} }
@ -1724,7 +1732,10 @@ impl GeneralNameRef {
return None; return None;
} }
Some(Asn1BitStringRef::from_ptr((*self.as_ptr()).d.ip).as_slice()) let ptr = ASN1_STRING_get0_data((*self.as_ptr()).d.ip as *mut _);
let len = ffi::ASN1_STRING_length((*self.as_ptr()).d.ip as *mut _);
Some(slice::from_raw_parts(ptr, len as usize))
} }
} }
} }
@ -1800,8 +1811,8 @@ impl Stackable for X509Object {
use crate::ffi::{X509_get0_signature, X509_getm_notAfter, X509_getm_notBefore, X509_up_ref}; use crate::ffi::{X509_get0_signature, X509_getm_notAfter, X509_getm_notBefore, X509_up_ref};
use crate::ffi::{ use crate::ffi::{
X509_ALGOR_get0, X509_REQ_get_subject_name, X509_REQ_get_version, X509_STORE_CTX_get0_chain, ASN1_STRING_get0_data, X509_ALGOR_get0, X509_REQ_get_subject_name, X509_REQ_get_version,
X509_set1_notAfter, X509_set1_notBefore, X509_STORE_CTX_get0_chain, X509_set1_notAfter, X509_set1_notBefore,
}; };
use crate::ffi::X509_OBJECT_get0_X509; use crate::ffi::X509_OBJECT_get0_X509;

View File

@ -250,36 +250,34 @@ fn x509_builder() {
.unwrap(); .unwrap();
let basic_constraints = BasicConstraints::new().critical().ca().build().unwrap(); let basic_constraints = BasicConstraints::new().critical().ca().build().unwrap();
builder builder.append_extension(basic_constraints).unwrap();
.append_extension(basic_constraints.as_ref())
.unwrap();
let key_usage = KeyUsage::new() let key_usage = KeyUsage::new()
.digital_signature() .digital_signature()
.key_encipherment() .key_encipherment()
.build() .build()
.unwrap(); .unwrap();
builder.append_extension(&key_usage).unwrap(); builder.append_extension(key_usage).unwrap();
let ext_key_usage = ExtendedKeyUsage::new() let ext_key_usage = ExtendedKeyUsage::new()
.client_auth() .client_auth()
.server_auth() .server_auth()
.other("2.999.1") .other("2.999.1")
.build() .build()
.unwrap(); .unwrap();
builder.append_extension(&ext_key_usage).unwrap(); builder.append_extension(ext_key_usage).unwrap();
let subject_key_identifier = SubjectKeyIdentifier::new() let subject_key_identifier = SubjectKeyIdentifier::new()
.build(&builder.x509v3_context(None, None)) .build(&builder.x509v3_context(None, None))
.unwrap(); .unwrap();
builder.append_extension(&subject_key_identifier).unwrap(); builder.append_extension(subject_key_identifier).unwrap();
let authority_key_identifier = AuthorityKeyIdentifier::new() let authority_key_identifier = AuthorityKeyIdentifier::new()
.keyid(true) .keyid(true)
.build(&builder.x509v3_context(None, None)) .build(&builder.x509v3_context(None, None))
.unwrap(); .unwrap();
builder.append_extension(&authority_key_identifier).unwrap(); builder.append_extension(authority_key_identifier).unwrap();
let subject_alternative_name = SubjectAlternativeName::new() let subject_alternative_name = SubjectAlternativeName::new()
.dns("example.com") .dns("example.com")
.build(&builder.x509v3_context(None, None)) .build(&builder.x509v3_context(None, None))
.unwrap(); .unwrap();
builder.append_extension(&subject_alternative_name).unwrap(); builder.append_extension(subject_alternative_name).unwrap();
builder.sign(&pkey, MessageDigest::sha256()).unwrap(); builder.sign(&pkey, MessageDigest::sha256()).unwrap();

View File

@ -15,7 +15,7 @@ fn test_verify_cert() {
assert_eq!(Ok(()), verify(&leaf, &[&root1], &[&intermediate], |_| {})); assert_eq!(Ok(()), verify(&leaf, &[&root1], &[&intermediate], |_| {}));
#[cfg(not(feature = "legacy-compat-deprecated"))] #[cfg(not(feature = "fips-compat"))]
assert_eq!( assert_eq!(
Ok(()), Ok(()),
verify( verify(
@ -26,7 +26,7 @@ fn test_verify_cert() {
) )
); );
#[cfg(feature = "legacy-compat-deprecated")] #[cfg(feature = "fips-compat")]
assert_eq!( assert_eq!(
Err(X509VerifyError::CERT_HAS_EXPIRED), Err(X509VerifyError::CERT_HAS_EXPIRED),
verify( verify(
@ -61,7 +61,7 @@ fn test_verify_cert() {
Ok(()), Ok(()),
verify(&leaf, &[&root1], &[&intermediate, &root1_cross], |param| { verify(&leaf, &[&root1], &[&intermediate, &root1_cross], |param| {
param.clear_flags(X509Flags::TRUSTED_FIRST) param.clear_flags(X509Flags::TRUSTED_FIRST)
}) },)
); );
} }

View File

@ -12,12 +12,29 @@ An implementation of SSL streams for Compio backed by BoringSSL
""" """
[package.metadata.docs.rs] [package.metadata.docs.rs]
features = ["pq-experimental"]
rustdoc-args = ["--cfg", "docsrs"] rustdoc-args = ["--cfg", "docsrs"]
[features] [features]
# Use a FIPS-validated version of boringssl. # Use a FIPS-validated version of boringssl.
fips = ["boring/fips", "boring-sys/fips"] fips = ["boring/fips", "boring-sys/fips"]
# Use a FIPS build of BoringSSL, but don't set "fips-compat".
#
# As of boringSSL commit a430310d6563c0734ddafca7731570dfb683dc19, we no longer
# need to make exceptions for the types of BufLen, ProtosLen, and ValueLen,
# which means the "fips-compat" feature is no longer needed.
#
# TODO(cjpatton) Delete this feature and modify "fips" so that it doesn't imply
# "fips-compat".
fips-precompiled = ["boring/fips-precompiled"]
# Link with precompiled FIPS-validated `bcm.o` module.
fips-link-precompiled = ["boring/fips-link-precompiled", "boring-sys/fips-link-precompiled"]
# Enables experimental post-quantum crypto (https://blog.cloudflare.com/post-quantum-for-all/)
pq-experimental = ["boring/pq-experimental"]
[dependencies] [dependencies]
boring = { workspace = true } boring = { workspace = true }
boring-sys = { workspace = true } boring-sys = { workspace = true }

View File

@ -15,8 +15,12 @@ maintenance = { status = "passively-maintained" }
[features] [features]
default = ["runtime-tokio"] default = ["runtime-tokio"]
runtime-tokio = ["quinn/runtime-tokio"] runtime-tokio = ["quinn/runtime-tokio"]
# Enables experimental post-quantum crypto (https://blog.cloudflare.com/post-quantum-for-all/)
pq-experimental = ["boring/pq-experimental"]
[dependencies] [dependencies]
boring = { workspace = true } boring = { workspace = true }
boring-sys = { workspace = true } boring-sys = { workspace = true }

View File

@ -12,12 +12,29 @@ An implementation of SSL streams for Tokio backed by BoringSSL
""" """
[package.metadata.docs.rs] [package.metadata.docs.rs]
features = ["pq-experimental"]
rustdoc-args = ["--cfg", "docsrs"] rustdoc-args = ["--cfg", "docsrs"]
[features] [features]
# Use a FIPS-validated version of boringssl. # Use a FIPS-validated version of boringssl.
fips = ["boring/fips", "boring-sys/fips"] fips = ["boring/fips", "boring-sys/fips"]
# Use a FIPS build of BoringSSL, but don't set "fips-compat".
#
# As of boringSSL commit a430310d6563c0734ddafca7731570dfb683dc19, we no longer
# need to make exceptions for the types of BufLen, ProtosLen, and ValueLen,
# which means the "fips-compat" feature is no longer needed.
#
# TODO(cjpatton) Delete this feature and modify "fips" so that it doesn't imply
# "fips-compat".
fips-precompiled = ["boring/fips-precompiled"]
# Link with precompiled FIPS-validated `bcm.o` module.
fips-link-precompiled = ["boring/fips-link-precompiled", "boring-sys/fips-link-precompiled"]
# Enables experimental post-quantum crypto (https://blog.cloudflare.com/post-quantum-for-all/)
pq-experimental = ["boring/pq-experimental"]
[dependencies] [dependencies]
boring = { workspace = true } boring = { workspace = true }
boring-sys = { workspace = true } boring-sys = { workspace = true }

View File

@ -22,7 +22,7 @@ impl<S> AsyncStreamBridge<S> {
} }
pub(crate) fn set_waker(&mut self, ctx: Option<&mut Context<'_>>) { pub(crate) fn set_waker(&mut self, ctx: Option<&mut Context<'_>>) {
self.waker = ctx.map(|ctx| ctx.waker().clone()); self.waker = ctx.map(|ctx| ctx.waker().clone())
} }
/// # Panics /// # Panics