Merge in upstream v4.21.1
This commit is contained in:
commit
a721d89d08
74
.github/workflows/ci.yml
vendored
74
.github/workflows/ci.yml
vendored
@ -4,10 +4,11 @@ on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- v4.x
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
- v4.x
|
||||
env:
|
||||
RUSTFLAGS: -Dwarnings
|
||||
RUST_BACKTRACE: 1
|
||||
@ -18,8 +19,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust
|
||||
run: rustup update stable && rustup default stable
|
||||
- name: Install Rustfmt
|
||||
run: rustup default stable && rustup component add rustfmt
|
||||
- name: Check formatting
|
||||
run: cargo fmt --all -- --check
|
||||
|
||||
@ -31,10 +32,11 @@ jobs:
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
- name: Install Rust
|
||||
run: rustup update stable && rustup default stable
|
||||
run: rustup update --no-self-update stable && rustup default stable && rustup component add clippy
|
||||
- name: Get rust version
|
||||
id: rust-version
|
||||
run: echo "::set-output name=version::$(rustc --version)"
|
||||
run: |
|
||||
echo "version=$(rustc --version)" >> $GITHUB_OUTPUT
|
||||
- name: Cache cargo index
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
@ -58,6 +60,21 @@ jobs:
|
||||
key: clippy-target-${{ runner.os }}-${{ steps.rust-version.outputs.version }}-${{ hashFiles('Cargo.lock') }}
|
||||
- name: Run clippy
|
||||
run: cargo clippy --all --all-targets
|
||||
- name: Check docs
|
||||
run: cargo doc --no-deps -p boring -p boring-sys --features rpk,pq-experimental,underscore-wildcards
|
||||
env:
|
||||
CARGO_BUILD_RUSTDOCFLAGS: "--cfg=docsrs"
|
||||
RUST_BOOTSTRAP: 1
|
||||
DOCS_RS: 1
|
||||
- name: Cargo.toml boring versions consistency
|
||||
shell: bash
|
||||
run: |
|
||||
WORKSPACE_VERSION=$(grep -F '[workspace.package]' -A1 Cargo.toml | grep -F version | grep -Eo '".*"')
|
||||
if [[ -z "$WORKSPACE_VERSION" ]]; then echo 2>&1 "error: can't find boring version"; exit 1; fi
|
||||
if grep -E 'boring.* =' Cargo.toml | grep -vF "$WORKSPACE_VERSION"; then
|
||||
echo 2>&1 "error: boring dependencies must match workspace version $WORKSPACE_VERSION"
|
||||
exit 1
|
||||
fi
|
||||
test:
|
||||
name: Test
|
||||
runs-on: ${{ matrix.os }}
|
||||
@ -141,8 +158,8 @@ jobs:
|
||||
apt_packages: gcc-arm-linux-gnueabi g++-arm-linux-gnueabi
|
||||
check_only: true
|
||||
custom_env:
|
||||
CC: arm-linux-gnueabi-gcc
|
||||
CXX: arm-linux-gnueabi-g++
|
||||
CC_arm-unknown-linux-gnueabi: arm-linux-gnueabi-gcc
|
||||
CXX_arm-unknown-linux-gnueabi: arm-linux-gnueabi-g++
|
||||
CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABI_LINKER: arm-linux-gnueabi-g++
|
||||
- thing: aarch64-linux
|
||||
target: aarch64-unknown-linux-gnu
|
||||
@ -151,8 +168,8 @@ jobs:
|
||||
apt_packages: crossbuild-essential-arm64
|
||||
check_only: true
|
||||
custom_env:
|
||||
CC: aarch64-linux-gnu-gcc
|
||||
CXX: aarch64-linux-gnu-g++
|
||||
CC_aarch64_unknown_linux_gnu: aarch64-linux-gnu-gcc
|
||||
CXX_aarch64_unknown_linux_gnu: aarch64-linux-gnu-g++
|
||||
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-g++
|
||||
- thing: arm64-macos
|
||||
target: aarch64-apple-darwin
|
||||
@ -301,45 +318,6 @@ jobs:
|
||||
- name: Build for ${{ matrix.target }}
|
||||
run: cargo build --target ${{ matrix.target }} --all-targets
|
||||
|
||||
cross-build-fips:
|
||||
name: Cross build from macOS to Linux (FIPS)
|
||||
runs-on: macos-13 # Need an Intel (x86_64) runner for Clang 12.0.0
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- target: x86_64-unknown-linux-gnu
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
- name: Install Rust (rustup)
|
||||
run: rustup update stable --no-self-update && rustup default stable && rustup target add ${{ matrix.target }}
|
||||
shell: bash
|
||||
- name: Install golang
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '>=1.22.0'
|
||||
- name: Install ${{ matrix.target }} toolchain
|
||||
run: brew tap messense/macos-cross-toolchains && brew install ${{ matrix.target }} && brew link x86_64-unknown-linux-gnu
|
||||
- name: Install Clang-12
|
||||
uses: KyleMayes/install-llvm-action@v1
|
||||
with:
|
||||
version: "12.0.0"
|
||||
directory: ${{ runner.temp }}/llvm
|
||||
- name: Add clang++-12 link
|
||||
working-directory: ${{ runner.temp }}/llvm/bin
|
||||
run: ln -s clang++ clang++-12
|
||||
- name: Set BORING_BSSL_FIPS_COMPILER_EXTERNAL_TOOLCHAIN
|
||||
run: echo "BORING_BSSL_FIPS_COMPILER_EXTERNAL_TOOLCHAIN=$(brew --prefix ${{ matrix.target }})/toolchain" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
- name: Set BORING_BSSL_FIPS_SYSROOT
|
||||
run: echo "BORING_BSSL_FIPS_SYSROOT=$BORING_BSSL_FIPS_COMPILER_EXTERNAL_TOOLCHAIN/${{ matrix.target }}/sysroot" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
- name: Set CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER
|
||||
run: echo "CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=${{ matrix.target }}-gcc" >> $GITHUB_ENV
|
||||
- name: Build for ${{ matrix.target }}
|
||||
run: cargo build --target ${{ matrix.target }} --all-targets --features fips
|
||||
|
||||
test-features:
|
||||
name: Test features
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
15
Cargo.toml
15
Cargo.toml
@ -8,7 +8,7 @@ members = [
|
||||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
version = "4.18.0"
|
||||
version = "4.21.1"
|
||||
repository = "https://github.com/cloudflare/boring"
|
||||
edition = "2021"
|
||||
|
||||
@ -19,16 +19,17 @@ tag-prefix = ""
|
||||
publish = false
|
||||
|
||||
[workspace.dependencies]
|
||||
boring-sys = { version = "4.18.0", path = "./boring-sys" }
|
||||
boring = { version = "4.18.0", path = "./boring" }
|
||||
tokio-boring = { version = "4.18.0", path = "./tokio-boring" }
|
||||
boring-sys = { version = "4.21.1", path = "./boring-sys" }
|
||||
boring = { version = "4.21.1", path = "./boring" }
|
||||
tokio-boring = { version = "4.21.1", path = "./tokio-boring" }
|
||||
|
||||
bindgen = { version = "0.72.0", default-features = false, features = ["runtime"] }
|
||||
bitflags = "2.9"
|
||||
brotli = "8.0"
|
||||
bytes = "1"
|
||||
cmake = "0.1.18"
|
||||
cmake = "0.1.54"
|
||||
fs_extra = "1.3.0"
|
||||
fslock = "0.2"
|
||||
bitflags = "2.4"
|
||||
foreign-types = "0.5"
|
||||
libc = "0.2"
|
||||
hex = "0.4"
|
||||
@ -48,5 +49,3 @@ openssl-macros = "0.1.1"
|
||||
tower = "0.4"
|
||||
tower-layer = "0.3"
|
||||
tower-service = "0.3"
|
||||
autocfg = "1.3.0"
|
||||
brotli = "6.0"
|
||||
|
||||
@ -1,3 +1,32 @@
|
||||
4.21.0
|
||||
|
||||
- 2026-01-05 Warn about set_curves() removal
|
||||
- 2026-01-05 Deprecate set_ex_data()
|
||||
- 2026-01-05 Fix build with --no-default-features
|
||||
- 2026-01-05 Make set_curves_list always available
|
||||
- 2026-01-19 Use fips-build-compatible ERR_add_error_data
|
||||
|
||||
4.20.0
|
||||
- 2025-08-26 Support TARGET_CC and CC_{target}
|
||||
- 2025-08-26 Fix swapped host/target args
|
||||
- 2025-06-13 CStr UTF-8 improvements
|
||||
- 2025-09-26 Skip Rust version detection for bindgen
|
||||
- 2025-09-26 Upgrade deps
|
||||
- 2025-06-13 Ensure that ERR_LIB type can be named
|
||||
- 2025-06-13 Add more reliable library_reason()
|
||||
- 2025-09-30 pq: fix MSVC C4146 warning
|
||||
- 2025-10-14 Freebsd build
|
||||
- 2025-10-01 Fix string data conversion in ErrorStack::put()
|
||||
|
||||
4.19.0
|
||||
- 2025-09-03 Add binding for X509_check_ip_asc
|
||||
- 2025-06-13 Use ERR_clear_error
|
||||
- 2025-06-13 Error descriptions and docs
|
||||
- 2025-06-13 Boring doesn't use function codes
|
||||
- 2025-09-03 Fix patched docs.rs builds
|
||||
- 2025-09-03 Test docs.rs docs
|
||||
- 2025-09-03 Fix doc links
|
||||
|
||||
4.18.0
|
||||
- 2025-05-29 Add set_verify_param
|
||||
- 2025-05-28 Add support for X509_STORE_CTX_get0_untrusted
|
||||
|
||||
@ -13,6 +13,7 @@ build = "build/main.rs"
|
||||
readme = "README.md"
|
||||
categories = ["cryptography", "external-ffi-bindings"]
|
||||
edition = { workspace = true }
|
||||
rust-version = "1.77"
|
||||
include = [
|
||||
"/*.md",
|
||||
"/*.toml",
|
||||
@ -89,7 +90,6 @@ pq-experimental = []
|
||||
underscore-wildcards = []
|
||||
|
||||
[build-dependencies]
|
||||
autocfg = { workspace = true }
|
||||
bindgen = { workspace = true }
|
||||
cmake = { workspace = true }
|
||||
fs_extra = { workspace = true }
|
||||
|
||||
@ -36,6 +36,9 @@ pub(crate) struct Env {
|
||||
pub(crate) android_ndk_home: Option<PathBuf>,
|
||||
pub(crate) cmake_toolchain_file: Option<PathBuf>,
|
||||
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,
|
||||
}
|
||||
|
||||
@ -51,10 +54,10 @@ impl Config {
|
||||
let features = Features::from_env();
|
||||
let env = Env::from_env(&host, &target, features.is_fips_like());
|
||||
|
||||
let mut is_bazel = false;
|
||||
if let Some(src_path) = &env.source_path {
|
||||
is_bazel = src_path.join("src").exists();
|
||||
}
|
||||
let is_bazel = env
|
||||
.source_path
|
||||
.as_ref()
|
||||
.is_some_and(|path| path.join("src").exists());
|
||||
|
||||
let config = Self {
|
||||
manifest_dir,
|
||||
@ -142,22 +145,19 @@ impl Features {
|
||||
}
|
||||
|
||||
impl Env {
|
||||
fn from_env(target: &str, host: &str, is_fips_like: bool) -> Self {
|
||||
fn from_env(host: &str, target: &str, is_fips_like: bool) -> Self {
|
||||
const NORMAL_PREFIX: &str = "BORING_BSSL";
|
||||
const FIPS_PREFIX: &str = "BORING_BSSL_FIPS";
|
||||
|
||||
let var_prefix = if host == target { "HOST" } else { "TARGET" };
|
||||
let target_with_underscores = target.replace('-', "_");
|
||||
|
||||
// Logic stolen from cmake-rs.
|
||||
let target_var = |name: &str| {
|
||||
let kind = if host == target { "HOST" } else { "TARGET" };
|
||||
|
||||
// TODO(rmehra): look for just `name` first, as most people just set that
|
||||
let target_only_var = |name: &str| {
|
||||
var(&format!("{name}_{target}"))
|
||||
.or_else(|| var(&format!("{name}_{target_with_underscores}")))
|
||||
.or_else(|| var(&format!("{kind}_{name}")))
|
||||
.or_else(|| var(name))
|
||||
.or_else(|| var(&format!("{var_prefix}_{name}")))
|
||||
};
|
||||
let target_var = |name: &str| target_only_var(name).or_else(|| var(name));
|
||||
|
||||
let boringssl_var = |name: &str| {
|
||||
// The passed name is the non-fips version of the environment variable,
|
||||
@ -186,6 +186,9 @@ impl Env {
|
||||
android_ndk_home: target_var("ANDROID_NDK_HOME").map(Into::into),
|
||||
cmake_toolchain_file: target_var("CMAKE_TOOLCHAIN_FILE").map(Into::into),
|
||||
cpp_runtime_lib: target_var("BORING_BSSL_RUST_CPPLIB"),
|
||||
// matches the `cc` crate
|
||||
cc: target_only_var("CC"),
|
||||
cxx: target_only_var("CXX"),
|
||||
docs_rs: var("DOCS_RS").is_some(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -152,7 +152,7 @@ fn get_boringssl_source_path(config: &Config) -> &PathBuf {
|
||||
///
|
||||
/// MSVC generator on Windows place static libs in a target sub-folder,
|
||||
/// so adjust library location based on platform and build target.
|
||||
/// See issue: https://github.com/alexcrichton/cmake-rs/issues/18
|
||||
/// See issue: <https://github.com/alexcrichton/cmake-rs/issues/18>
|
||||
fn get_boringssl_platform_output_path(config: &Config) -> String {
|
||||
if config.target.ends_with("-msvc") {
|
||||
// Code under this branch should match the logic in cmake-rs
|
||||
@ -193,7 +193,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.
|
||||
fn get_boringssl_cmake_config(config: &Config) -> cmake::Config {
|
||||
@ -216,6 +216,15 @@ fn get_boringssl_cmake_config(config: &Config) -> cmake::Config {
|
||||
.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 {
|
||||
boringssl_cmake.define("CMAKE_SYSROOT", sysroot);
|
||||
}
|
||||
@ -331,7 +340,7 @@ fn get_boringssl_cmake_config(config: &Config) -> cmake::Config {
|
||||
boringssl_cmake
|
||||
}
|
||||
|
||||
/// Verify that the toolchains match https://csrc.nist.gov/CSRC/media/projects/cryptographic-module-validation-program/documents/security-policies/140sp3678.pdf
|
||||
/// 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) {
|
||||
@ -468,6 +477,24 @@ fn get_extra_clang_args_for_bindgen(config: &Config) -> Vec<String> {
|
||||
}
|
||||
|
||||
fn ensure_patches_applied(config: &Config) -> io::Result<()> {
|
||||
if config.env.assume_patched || config.env.path.is_some() {
|
||||
println!(
|
||||
"cargo:warning=skipping git patches application, provided\
|
||||
native BoringSSL is expected to have the patches included"
|
||||
);
|
||||
return Ok(());
|
||||
} else if config.env.source_path.is_some()
|
||||
&& (config.features.rpk
|
||||
|| config.features.pq_experimental
|
||||
|| config.features.underscore_wildcards)
|
||||
{
|
||||
panic!(
|
||||
"BORING_BSSL_ASSUME_PATCHED must be set when setting
|
||||
BORING_BSSL_SOURCE_PATH and using any of the following
|
||||
features: rpk, pq-experimental, underscore-wildcards"
|
||||
);
|
||||
}
|
||||
|
||||
let mut lock_file = LockFile::open(&config.out_dir.join(".patch_lock"))?;
|
||||
let src_path = get_boringssl_source_path(config);
|
||||
let has_git = src_path.join(".git").exists();
|
||||
@ -552,25 +579,6 @@ fn built_boring_source_path(config: &Config) -> &PathBuf {
|
||||
static BUILD_SOURCE_PATH: OnceLock<PathBuf> = OnceLock::new();
|
||||
|
||||
BUILD_SOURCE_PATH.get_or_init(|| {
|
||||
if config.env.assume_patched {
|
||||
println!(
|
||||
"cargo:warning=skipping git patches application, provided\
|
||||
native BoringSSL is expected to have the patches included"
|
||||
);
|
||||
} else if config.env.source_path.is_some()
|
||||
&& (config.features.rpk
|
||||
|| config.features.pq_experimental
|
||||
|| config.features.underscore_wildcards)
|
||||
{
|
||||
panic!(
|
||||
"BORING_BSSL_ASSUME_PATCHED must be set when setting
|
||||
BORING_BSSL_SOURCE_PATH and using any of the following
|
||||
features: rpk, pq-experimental, underscore-wildcards"
|
||||
);
|
||||
} else {
|
||||
ensure_patches_applied(config).unwrap();
|
||||
}
|
||||
|
||||
let mut cfg = get_boringssl_cmake_config(config);
|
||||
|
||||
let num_jobs = std::env::var("NUM_JOBS").ok().or_else(|| {
|
||||
@ -651,7 +659,7 @@ fn get_cpp_runtime_lib(config: &Config) -> Option<String> {
|
||||
// TODO(rmehra): figure out how to do this for windows
|
||||
if env::var_os("CARGO_CFG_UNIX").is_some() {
|
||||
match env::var("CARGO_CFG_TARGET_OS").unwrap().as_ref() {
|
||||
"macos" | "ios" => Some("c++".into()),
|
||||
"macos" | "ios" | "freebsd" => Some("c++".into()),
|
||||
_ => Some("stdc++".into()),
|
||||
}
|
||||
} else {
|
||||
@ -661,6 +669,7 @@ fn get_cpp_runtime_lib(config: &Config) -> Option<String> {
|
||||
|
||||
fn main() {
|
||||
let config = Config::from_env();
|
||||
ensure_patches_applied(&config).unwrap();
|
||||
if !config.env.docs_rs {
|
||||
emit_link_directives(&config);
|
||||
}
|
||||
@ -732,12 +741,8 @@ fn generate_bindings(config: &Config) {
|
||||
}
|
||||
});
|
||||
|
||||
// bindgen 0.70 replaced the run-time layout tests with compile-time ones,
|
||||
// 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 target_rust_version =
|
||||
bindgen::RustTarget::stable(77, 0).expect("bindgen does not recognize target rust version");
|
||||
|
||||
let mut builder = bindgen::Builder::default()
|
||||
.rust_target(target_rust_version) // bindgen MSRV is 1.70, so this is enough
|
||||
@ -753,7 +758,7 @@ fn generate_bindings(config: &Config) {
|
||||
.generate_comments(true)
|
||||
.fit_macro_constants(false)
|
||||
.size_t_is_usize(true)
|
||||
.layout_tests(supports_layout_tests)
|
||||
.layout_tests(config.env.debug.is_some())
|
||||
.prepend_enum_name(true)
|
||||
.blocklist_type("max_align_t") // Not supported by bindgen on all targets, not used by BoringSSL
|
||||
.clang_args(get_extra_clang_args_for_bindgen(config))
|
||||
@ -779,6 +784,7 @@ fn generate_bindings(config: &Config) {
|
||||
"curve25519.h",
|
||||
"des.h",
|
||||
"dtls1.h",
|
||||
"err.h",
|
||||
"hkdf.h",
|
||||
#[cfg(not(feature = "fips"))]
|
||||
"hpke.h",
|
||||
@ -805,7 +811,24 @@ fn generate_bindings(config: &Config) {
|
||||
}
|
||||
|
||||
let bindings = builder.generate().expect("Unable to generate bindings");
|
||||
let mut source_code = Vec::new();
|
||||
bindings
|
||||
.write_to_file(config.out_dir.join("bindings.rs"))
|
||||
.expect("Couldn't write bindings!");
|
||||
.write(Box::new(&mut source_code))
|
||||
.expect("Couldn't serialize bindings!");
|
||||
ensure_err_lib_enum_is_named(&mut source_code);
|
||||
fs::write(config.out_dir.join("bindings.rs"), source_code).expect("Couldn't write bindings!");
|
||||
}
|
||||
|
||||
/// err.h has anonymous `enum { ERR_LIB_NONE = 1 }`, which makes a dodgy `_bindgen_ty_1` name
|
||||
fn ensure_err_lib_enum_is_named(source_code: &mut Vec<u8>) {
|
||||
let src = String::from_utf8_lossy(source_code);
|
||||
let enum_type = src
|
||||
.split_once("ERR_LIB_SSL:")
|
||||
.and_then(|(_, def)| Some(def.split_once("=")?.0))
|
||||
.unwrap_or("_bindgen_ty_1");
|
||||
|
||||
source_code.extend_from_slice(
|
||||
format!("\n/// Newtype for [`ERR_LIB_SSL`] constants\npub use {enum_type} as ErrLib;\n")
|
||||
.as_bytes(),
|
||||
);
|
||||
}
|
||||
|
||||
@ -940,7 +940,7 @@ index 776c085f9..ccb5b3d9b 100644
|
||||
+ for(i=0;i<len;i++)
|
||||
+ r |= a[i] ^ b[i];
|
||||
+
|
||||
+ return (-(uint64_t)r) >> 63;
|
||||
+ return (0-(uint64_t)r) >> 63;
|
||||
+}
|
||||
+
|
||||
+/*************************************************
|
||||
|
||||
@ -10,7 +10,6 @@
|
||||
non_upper_case_globals,
|
||||
unused_imports
|
||||
)]
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
|
||||
use std::convert::TryInto;
|
||||
use std::ffi::c_void;
|
||||
|
||||
@ -147,7 +147,7 @@ fn real_main() -> Result<(), ErrorStack> {
|
||||
match ca_cert.issued(&cert) {
|
||||
Ok(()) => println!("Certificate verified!"),
|
||||
Err(ver_err) => println!("Failed to verify certificate: {ver_err}"),
|
||||
};
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -156,5 +156,5 @@ fn main() {
|
||||
match real_main() {
|
||||
Ok(()) => println!("Finished."),
|
||||
Err(e) => println!("Error: {e}"),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,20 +63,19 @@ foreign_type_and_impl_send_sync! {
|
||||
|
||||
impl fmt::Display for Asn1GeneralizedTimeRef {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
unsafe {
|
||||
let mem_bio = match MemBio::new() {
|
||||
Err(_) => return f.write_str("error"),
|
||||
Ok(m) => m,
|
||||
};
|
||||
let print_result = cvt(ffi::ASN1_GENERALIZEDTIME_print(
|
||||
mem_bio.as_ptr(),
|
||||
self.as_ptr(),
|
||||
));
|
||||
match print_result {
|
||||
Err(_) => f.write_str("error"),
|
||||
Ok(_) => f.write_str(str::from_utf8_unchecked(mem_bio.get_buf())),
|
||||
}
|
||||
}
|
||||
let bio = MemBio::new().ok();
|
||||
let msg = bio
|
||||
.as_ref()
|
||||
.and_then(|mem_bio| unsafe {
|
||||
cvt(ffi::ASN1_GENERALIZEDTIME_print(
|
||||
mem_bio.as_ptr(),
|
||||
self.as_ptr(),
|
||||
))
|
||||
.ok()?;
|
||||
str::from_utf8(mem_bio.get_buf()).ok()
|
||||
})
|
||||
.unwrap_or("error");
|
||||
f.write_str(msg)
|
||||
}
|
||||
}
|
||||
|
||||
@ -528,7 +527,20 @@ impl Asn1BitStringRef {
|
||||
#[corresponds(ASN1_STRING_get0_data)]
|
||||
#[must_use]
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
unsafe { slice::from_raw_parts(ASN1_STRING_get0_data(self.as_ptr() as *mut _), self.len()) }
|
||||
unsafe {
|
||||
let ptr = ASN1_STRING_get0_data(self.as_ptr().cast());
|
||||
if ptr.is_null() {
|
||||
return &[];
|
||||
}
|
||||
slice::from_raw_parts(ptr, self.len())
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the Asn1BitString as a str, if possible.
|
||||
#[corresponds(ASN1_STRING_get0_data)]
|
||||
#[must_use]
|
||||
pub fn to_str(&self) -> Option<&str> {
|
||||
str::from_utf8(self.as_slice()).ok()
|
||||
}
|
||||
|
||||
/// Returns the number of bytes in the string.
|
||||
|
||||
@ -68,7 +68,10 @@ impl MemBio {
|
||||
unsafe {
|
||||
let mut ptr = ptr::null_mut();
|
||||
let len = ffi::BIO_get_mem_data(self.0, &mut ptr);
|
||||
slice::from_raw_parts(ptr as *const _ as *const _, len as usize)
|
||||
if ptr.is_null() {
|
||||
return &[];
|
||||
}
|
||||
slice::from_raw_parts(ptr.cast_const().cast(), len as usize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,7 +51,6 @@ impl<'a> Deriver<'a> {
|
||||
///
|
||||
/// It can be used to size the buffer passed to [`Deriver::derive`].
|
||||
#[corresponds(EVP_PKEY_derive)]
|
||||
/// [`EVP_PKEY_derive`]: https://www.openssl.org/docs/man1.0.2/crypto/EVP_PKEY_derive_init.html
|
||||
pub fn len(&mut self) -> Result<usize, ErrorStack> {
|
||||
unsafe {
|
||||
let mut len = 0;
|
||||
|
||||
@ -32,7 +32,7 @@ where
|
||||
}
|
||||
|
||||
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)]
|
||||
params_to_der,
|
||||
ffi::i2d_DHparams
|
||||
|
||||
@ -15,10 +15,12 @@
|
||||
//! Err(e) => println!("Parsing Error: {:?}", e),
|
||||
//! }
|
||||
//! ```
|
||||
use libc::{c_char, c_uint};
|
||||
use libc::{c_char, c_int, c_uint};
|
||||
use openssl_macros::corresponds;
|
||||
use std::borrow::Cow;
|
||||
use std::error;
|
||||
use std::ffi::CStr;
|
||||
use std::ffi::CString;
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use std::ptr;
|
||||
@ -26,6 +28,8 @@ use std::str;
|
||||
|
||||
use crate::ffi;
|
||||
|
||||
pub use crate::ffi::ErrLib;
|
||||
|
||||
/// Collection of [`Error`]s from OpenSSL.
|
||||
///
|
||||
/// [`Error`]: struct.Error.html
|
||||
@ -34,7 +38,8 @@ pub struct ErrorStack(Vec<Error>);
|
||||
|
||||
impl ErrorStack {
|
||||
/// Pops the contents of the OpenSSL error stack, and returns it.
|
||||
#[allow(clippy::must_use_candidate)]
|
||||
#[corresponds(ERR_get_error_line_data)]
|
||||
#[must_use = "Use ErrorStack::clear() to drop the error stack"]
|
||||
pub fn get() -> ErrorStack {
|
||||
let mut vec = vec![];
|
||||
while let Some(err) = Error::get() {
|
||||
@ -44,6 +49,7 @@ impl ErrorStack {
|
||||
}
|
||||
|
||||
/// Pushes the errors back onto the OpenSSL error stack.
|
||||
#[corresponds(ERR_put_error)]
|
||||
pub fn put(&self) {
|
||||
for error in self.errors() {
|
||||
error.put();
|
||||
@ -53,7 +59,15 @@ impl ErrorStack {
|
||||
/// Used to report errors from the Rust crate
|
||||
#[cold]
|
||||
pub(crate) fn internal_error(err: impl error::Error) -> Self {
|
||||
Self(vec![Error::new_internal(err.to_string())])
|
||||
Self(vec![Error::new_internal(Data::String(err.to_string()))])
|
||||
}
|
||||
|
||||
/// Empties the current thread's error queue.
|
||||
#[corresponds(ERR_clear_error)]
|
||||
pub(crate) fn clear() {
|
||||
unsafe {
|
||||
ffi::ERR_clear_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,7 +94,9 @@ impl fmt::Display for ErrorStack {
|
||||
write!(
|
||||
fmt,
|
||||
"[{}]",
|
||||
err.reason_internal().unwrap_or("unknown reason")
|
||||
err.reason_internal()
|
||||
.or_else(|| err.library())
|
||||
.unwrap_or("unknown reason")
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
@ -101,13 +117,20 @@ impl From<ErrorStack> for fmt::Error {
|
||||
}
|
||||
}
|
||||
|
||||
/// An error reported from OpenSSL.
|
||||
/// A detailed error reported as part of an [`ErrorStack`].
|
||||
#[derive(Clone)]
|
||||
pub struct Error {
|
||||
code: c_uint,
|
||||
file: *const c_char,
|
||||
line: c_uint,
|
||||
data: Option<Cow<'static, str>>,
|
||||
data: Data,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum Data {
|
||||
None,
|
||||
CString(CString),
|
||||
String(String),
|
||||
}
|
||||
|
||||
unsafe impl Sync for Error {}
|
||||
@ -117,7 +140,8 @@ static BORING_INTERNAL: &CStr = c"boring-rust";
|
||||
|
||||
impl Error {
|
||||
/// Pops the first error off the OpenSSL error stack.
|
||||
#[allow(clippy::must_use_candidate)]
|
||||
#[must_use = "Use ErrorStack::clear() to drop the error stack"]
|
||||
#[corresponds(ERR_get_error_line_data)]
|
||||
pub fn get() -> Option<Error> {
|
||||
unsafe {
|
||||
ffi::init();
|
||||
@ -132,11 +156,9 @@ impl Error {
|
||||
// The memory referenced by data is only valid until that slot is overwritten
|
||||
// in the error stack, so we'll need to copy it off if it's dynamic
|
||||
let data = if flags & ffi::ERR_FLAG_STRING != 0 {
|
||||
let bytes = CStr::from_ptr(data as *const _).to_bytes();
|
||||
let data = String::from_utf8_lossy(bytes).into_owned();
|
||||
Some(data.into())
|
||||
Data::CString(CStr::from_ptr(data.cast()).to_owned())
|
||||
} else {
|
||||
None
|
||||
Data::None
|
||||
};
|
||||
Some(Error {
|
||||
code,
|
||||
@ -150,6 +172,7 @@ impl Error {
|
||||
}
|
||||
|
||||
/// Pushes the error back onto the OpenSSL error stack.
|
||||
#[corresponds(ERR_put_error)]
|
||||
pub fn put(&self) {
|
||||
unsafe {
|
||||
ffi::ERR_put_error(
|
||||
@ -159,28 +182,29 @@ impl Error {
|
||||
self.file,
|
||||
self.line,
|
||||
);
|
||||
let ptr = match self.data {
|
||||
Some(Cow::Borrowed(data)) => Some(data.as_ptr() as *mut c_char),
|
||||
Some(Cow::Owned(ref data)) => {
|
||||
let ptr = ffi::OPENSSL_malloc((data.len() + 1) as _) as *mut c_char;
|
||||
if ptr.is_null() {
|
||||
None
|
||||
} else {
|
||||
ptr::copy_nonoverlapping(data.as_ptr(), ptr as *mut u8, data.len());
|
||||
*ptr.add(data.len()) = 0;
|
||||
Some(ptr)
|
||||
}
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
if let Some(ptr) = ptr {
|
||||
ffi::ERR_add_error_data(1, ptr);
|
||||
if let Some(cstr) = self.data_cstr() {
|
||||
ffi::ERR_add_error_data(1, cstr.as_ptr().cast_mut());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the raw OpenSSL error code for this error.
|
||||
/// Get `{lib}_R_{reason}` reason code for the given library, or `None` if the error is from a different library.
|
||||
///
|
||||
/// Libraries are identified by [`ERR_LIB_{name}`(ffi::ERR_LIB_SSL) constants.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[track_caller]
|
||||
pub fn library_reason(&self, library_code: ErrLib) -> Option<c_int> {
|
||||
debug_assert!(library_code.0 < ffi::ERR_NUM_LIBS.0);
|
||||
(self.library_code() == library_code.0 as c_int).then_some(self.reason_code())
|
||||
}
|
||||
|
||||
/// Returns a raw OpenSSL **packed** error code for this error, which **can't be reliably compared to any error constant**.
|
||||
///
|
||||
/// Use [`Error::library_code()`] and [`Error::library_reason()`] instead.
|
||||
/// Packed error codes are different than [SSL error codes](crate::ssl::ErrorCode).
|
||||
#[must_use]
|
||||
#[deprecated(note = "use library_reason() to compare error codes")]
|
||||
pub fn code(&self) -> c_uint {
|
||||
self.code
|
||||
}
|
||||
@ -196,32 +220,24 @@ impl Error {
|
||||
if cstr.is_null() {
|
||||
return None;
|
||||
}
|
||||
let bytes = CStr::from_ptr(cstr as *const _).to_bytes();
|
||||
str::from_utf8(bytes).ok()
|
||||
CStr::from_ptr(cstr.cast())
|
||||
.to_str()
|
||||
.ok()
|
||||
.filter(|&msg| msg != "unknown library")
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the raw OpenSSL error constant for the library reporting the
|
||||
/// error.
|
||||
/// Returns the raw OpenSSL error constant for the library reporting the error (`ERR_LIB_{name}`).
|
||||
///
|
||||
/// Error [reason codes](Error::library_reason) are not globally unique, but scoped to each library.
|
||||
#[must_use]
|
||||
pub fn library_code(&self) -> libc::c_int {
|
||||
pub fn library_code(&self) -> c_int {
|
||||
ffi::ERR_GET_LIB(self.code)
|
||||
}
|
||||
|
||||
/// Returns the name of the function reporting the error.
|
||||
#[must_use]
|
||||
/// Returns `None`. Boring doesn't use function codes.
|
||||
pub fn function(&self) -> Option<&'static str> {
|
||||
if self.is_internal() {
|
||||
return None;
|
||||
}
|
||||
unsafe {
|
||||
let cstr = ffi::ERR_func_error_string(self.code);
|
||||
if cstr.is_null() {
|
||||
return None;
|
||||
}
|
||||
let bytes = CStr::from_ptr(cstr as *const _).to_bytes();
|
||||
str::from_utf8(bytes).ok()
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns the reason for the error.
|
||||
@ -232,14 +248,19 @@ impl Error {
|
||||
if cstr.is_null() {
|
||||
return None;
|
||||
}
|
||||
let bytes = CStr::from_ptr(cstr as *const _).to_bytes();
|
||||
str::from_utf8(bytes).ok()
|
||||
CStr::from_ptr(cstr.cast()).to_str().ok()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the raw OpenSSL error constant for the reason for the error.
|
||||
/// Returns [library-specific](Error::library_code) reason code corresponding to some of the `{lib}_R_{reason}` constants.
|
||||
///
|
||||
/// Reason codes are ambiguous, and different libraries reuse the same numeric values for different errors.
|
||||
/// Use [`Error::library_reason`] to compare error codes.
|
||||
///
|
||||
/// For `ERR_LIB_SYS` the reason code is `errno`. `ERR_LIB_USER` can use any values.
|
||||
/// Other libraries may use [`ERR_R_*`](ffi::ERR_R_FATAL) or their own codes.
|
||||
#[must_use]
|
||||
pub fn reason_code(&self) -> libc::c_int {
|
||||
pub fn reason_code(&self) -> c_int {
|
||||
ffi::ERR_GET_REASON(self.code)
|
||||
}
|
||||
|
||||
@ -250,12 +271,15 @@ impl Error {
|
||||
if self.file.is_null() {
|
||||
return "";
|
||||
}
|
||||
let bytes = CStr::from_ptr(self.file as *const _).to_bytes();
|
||||
str::from_utf8(bytes).unwrap_or_default()
|
||||
CStr::from_ptr(self.file.cast())
|
||||
.to_str()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the line in the source file which encountered the error.
|
||||
///
|
||||
/// 0 if unknown
|
||||
#[allow(clippy::unnecessary_cast)]
|
||||
#[must_use]
|
||||
pub fn line(&self) -> u32 {
|
||||
@ -265,15 +289,29 @@ impl Error {
|
||||
/// Returns additional data describing the error.
|
||||
#[must_use]
|
||||
pub fn data(&self) -> Option<&str> {
|
||||
self.data.as_deref()
|
||||
match &self.data {
|
||||
Data::None => None,
|
||||
Data::CString(cstring) => cstring.to_str().ok(),
|
||||
Data::String(s) => Some(s),
|
||||
}
|
||||
}
|
||||
|
||||
fn new_internal(msg: String) -> Self {
|
||||
#[must_use]
|
||||
fn data_cstr(&self) -> Option<Cow<'_, CStr>> {
|
||||
let s = match &self.data {
|
||||
Data::None => return None,
|
||||
Data::CString(cstr) => return Some(Cow::Borrowed(cstr)),
|
||||
Data::String(s) => s.as_str(),
|
||||
};
|
||||
CString::new(s).ok().map(Cow::Owned)
|
||||
}
|
||||
|
||||
fn new_internal(msg: Data) -> Self {
|
||||
Self {
|
||||
code: ffi::ERR_PACK(ffi::ERR_LIB_NONE.0 as _, 0, 0) as _,
|
||||
file: BORING_INTERNAL.as_ptr(),
|
||||
line: 0,
|
||||
data: Some(msg.into()),
|
||||
data: msg,
|
||||
}
|
||||
}
|
||||
|
||||
@ -294,20 +332,19 @@ impl Error {
|
||||
impl fmt::Debug for Error {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
let mut builder = fmt.debug_struct("Error");
|
||||
builder.field("code", &self.code());
|
||||
if let Some(library) = self.library() {
|
||||
builder.field("library", &library);
|
||||
builder.field("code", &self.code);
|
||||
if !self.is_internal() {
|
||||
if let Some(library) = self.library() {
|
||||
builder.field("library", &library);
|
||||
}
|
||||
builder.field("library_code", &self.library_code());
|
||||
if let Some(reason) = self.reason() {
|
||||
builder.field("reason", &reason);
|
||||
}
|
||||
builder.field("reason_code", &self.reason_code());
|
||||
builder.field("file", &self.file());
|
||||
builder.field("line", &self.line());
|
||||
}
|
||||
builder.field("library_code", &self.library_code());
|
||||
if let Some(function) = self.function() {
|
||||
builder.field("function", &function);
|
||||
}
|
||||
if let Some(reason) = self.reason() {
|
||||
builder.field("reason", &reason);
|
||||
}
|
||||
builder.field("reason_code", &self.reason_code());
|
||||
builder.field("file", &self.file());
|
||||
builder.field("line", &self.line());
|
||||
if let Some(data) = self.data() {
|
||||
builder.field("data", &data);
|
||||
}
|
||||
@ -321,7 +358,7 @@ impl fmt::Display for Error {
|
||||
fmt,
|
||||
"{}\n\nCode: {:08X}\nLoc: {}:{}",
|
||||
self.reason_internal().unwrap_or("unknown TLS error"),
|
||||
self.code(),
|
||||
&self.code,
|
||||
self.file(),
|
||||
self.line()
|
||||
)
|
||||
|
||||
@ -96,8 +96,6 @@
|
||||
//! Presently all these key agreements are deployed by Cloudflare, but we do not guarantee continued
|
||||
//! support for them.
|
||||
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
#[macro_use]
|
||||
|
||||
@ -88,7 +88,9 @@ impl Nid {
|
||||
pub fn long_name(&self) -> Result<&'static str, ErrorStack> {
|
||||
unsafe {
|
||||
let nameptr = cvt_p(ffi::OBJ_nid2ln(self.0) as *mut c_char)?;
|
||||
str::from_utf8(CStr::from_ptr(nameptr).to_bytes()).map_err(ErrorStack::internal_error)
|
||||
CStr::from_ptr(nameptr)
|
||||
.to_str()
|
||||
.map_err(ErrorStack::internal_error)
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,7 +100,9 @@ impl Nid {
|
||||
pub fn short_name(&self) -> Result<&'static str, ErrorStack> {
|
||||
unsafe {
|
||||
let nameptr = cvt_p(ffi::OBJ_nid2sn(self.0) as *mut c_char)?;
|
||||
str::from_utf8(CStr::from_ptr(nameptr).to_bytes()).map_err(ErrorStack::internal_error)
|
||||
CStr::from_ptr(nameptr)
|
||||
.to_str()
|
||||
.map_err(ErrorStack::internal_error)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -413,7 +413,6 @@ impl Rsa<Public> {
|
||||
/// `n` is the modulus common to both public and private key.
|
||||
/// `e` is the public exponent.
|
||||
#[corresponds(RSA_new)]
|
||||
/// [`RSA_set0_key`]: https://www.openssl.org/docs/man1.1.0/crypto/RSA_set0_key.html
|
||||
pub fn from_public_components(n: BigNum, e: BigNum) -> Result<Rsa<Public>, ErrorStack> {
|
||||
unsafe {
|
||||
let rsa = cvt_p(ffi::RSA_new())?;
|
||||
@ -472,7 +471,6 @@ impl RsaPrivateKeyBuilder {
|
||||
/// `n` is the modulus common to both public and private key.
|
||||
/// `e` is the public exponent and `d` is the private exponent.
|
||||
#[corresponds(RSA_new)]
|
||||
/// [`RSA_set0_key`]: https://www.openssl.org/docs/man1.1.0/crypto/RSA_set0_key.html
|
||||
pub fn new(n: BigNum, e: BigNum, d: BigNum) -> Result<RsaPrivateKeyBuilder, ErrorStack> {
|
||||
unsafe {
|
||||
let rsa = cvt_p(ffi::RSA_new())?;
|
||||
|
||||
@ -478,7 +478,7 @@ impl<'a> Verifier<'a> {
|
||||
match r {
|
||||
1 => Ok(true),
|
||||
0 => {
|
||||
ErrorStack::get(); // discard error stack
|
||||
ErrorStack::clear(); // discard error stack
|
||||
Ok(false)
|
||||
}
|
||||
_ => Err(ErrorStack::get()),
|
||||
@ -500,7 +500,7 @@ impl<'a> Verifier<'a> {
|
||||
match r {
|
||||
1 => Ok(true),
|
||||
0 => {
|
||||
ErrorStack::get();
|
||||
ErrorStack::clear();
|
||||
Ok(false)
|
||||
}
|
||||
_ => Err(ErrorStack::get()),
|
||||
|
||||
@ -11,7 +11,7 @@ use std::pin::Pin;
|
||||
use std::sync::LazyLock;
|
||||
use std::task::{ready, Context, Poll, Waker};
|
||||
|
||||
/// The type of futures to pass to [`SslContextBuilderExt::set_async_select_certificate_callback`].
|
||||
/// The type of futures to pass to [`SslContextBuilder::set_async_select_certificate_callback`].
|
||||
pub type BoxSelectCertFuture = ExDataFuture<Result<BoxSelectCertFinish, AsyncSelectCertError>>;
|
||||
|
||||
/// The type of callbacks returned by [`BoxSelectCertFuture`] methods.
|
||||
@ -25,19 +25,19 @@ pub type BoxPrivateKeyMethodFuture =
|
||||
pub type BoxPrivateKeyMethodFinish =
|
||||
Box<dyn FnOnce(&mut SslRef, &mut [u8]) -> Result<usize, AsyncPrivateKeyMethodError>>;
|
||||
|
||||
/// The type of futures to pass to [`SslContextBuilderExt::set_async_get_session_callback`].
|
||||
/// The type of futures to pass to [`SslContextBuilder::set_async_get_session_callback`].
|
||||
pub type BoxGetSessionFuture = ExDataFuture<Option<BoxGetSessionFinish>>;
|
||||
|
||||
/// The type of callbacks returned by [`BoxSelectCertFuture`] methods.
|
||||
pub type BoxGetSessionFinish = Box<dyn FnOnce(&mut SslRef, &[u8]) -> Option<SslSession>>;
|
||||
|
||||
/// The type of futures to pass to [`SslContextBuilderExt::set_async_custom_verify_callback`].
|
||||
/// The type of futures to pass to [`SslContextBuilder::set_async_custom_verify_callback`].
|
||||
pub type BoxCustomVerifyFuture = ExDataFuture<Result<BoxCustomVerifyFinish, SslAlert>>;
|
||||
|
||||
/// The type of callbacks returned by [`BoxCustomVerifyFuture`] methods.
|
||||
pub type BoxCustomVerifyFinish = Box<dyn FnOnce(&mut SslRef) -> Result<(), SslAlert>>;
|
||||
|
||||
/// Convenience alias for futures stored in [`Ssl`] ex data by [`SslContextBuilderExt`] methods.
|
||||
/// Convenience alias for futures stored in [`Ssl`] ex data by [`SslContextBuilder`] methods.
|
||||
///
|
||||
/// Public for documentation purposes.
|
||||
pub type ExDataFuture<T> = Pin<Box<dyn Future<Output = T> + Send>>;
|
||||
@ -95,7 +95,7 @@ impl SslContextBuilder {
|
||||
let finish = fut_result.or(Err(SelectCertError::ERROR))?;
|
||||
|
||||
finish(client_hello).or(Err(SelectCertError::ERROR))
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/// Configures a custom private key method on the context.
|
||||
@ -123,7 +123,7 @@ impl SslContextBuilder {
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The returned [`SslSession`] must not be associated with a different [`SslContext`].
|
||||
/// The returned [`SslSession`] must not be associated with a different [`SslContextBuilder`].
|
||||
pub unsafe fn set_async_get_session_callback<F>(&mut self, callback: F)
|
||||
where
|
||||
F: Fn(&mut SslRef, &[u8]) -> Option<BoxGetSessionFuture> + Send + Sync + 'static,
|
||||
@ -144,7 +144,7 @@ impl SslContextBuilder {
|
||||
}
|
||||
};
|
||||
|
||||
self.set_get_session_callback(async_callback)
|
||||
self.set_get_session_callback(async_callback);
|
||||
}
|
||||
|
||||
/// Configures certificate verification.
|
||||
@ -167,7 +167,7 @@ impl SslContextBuilder {
|
||||
where
|
||||
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
|
||||
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`.
|
||||
|
||||
@ -399,7 +399,7 @@ pub(super) unsafe extern "C" fn raw_remove_session<F>(
|
||||
.ex_data(SslContext::cached_ex_index::<F>())
|
||||
.expect("BUG: remove session callback missing");
|
||||
|
||||
callback(ctx, session)
|
||||
callback(ctx, session);
|
||||
}
|
||||
|
||||
type DataPtr = *const c_uchar;
|
||||
@ -451,14 +451,14 @@ where
|
||||
{
|
||||
// SAFETY: boring provides valid inputs.
|
||||
let ssl = unsafe { SslRef::from_ptr(ssl as *mut _) };
|
||||
let line = unsafe { str::from_utf8_unchecked(CStr::from_ptr(line).to_bytes()) };
|
||||
let line = unsafe { CStr::from_ptr(line).to_string_lossy() };
|
||||
|
||||
let callback = ssl
|
||||
.ssl_context()
|
||||
.ex_data(SslContext::cached_ex_index::<F>())
|
||||
.expect("BUG: get session callback missing");
|
||||
|
||||
callback(ssl, line);
|
||||
callback(ssl, &line);
|
||||
}
|
||||
|
||||
pub(super) unsafe extern "C" fn raw_sign<M>(
|
||||
|
||||
@ -1,16 +1,20 @@
|
||||
use crate::ffi;
|
||||
use crate::x509::X509VerifyError;
|
||||
use libc::c_int;
|
||||
use openssl_macros::corresponds;
|
||||
use std::error;
|
||||
use std::error::Error as StdError;
|
||||
use std::ffi::CStr;
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
|
||||
use crate::error::ErrorStack;
|
||||
use crate::ssl::MidHandshakeSslStream;
|
||||
|
||||
/// An error code returned from SSL functions.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
/// `SSL_ERROR_*` error code returned from SSL functions.
|
||||
///
|
||||
/// This is different than [packed error codes](crate::error::Error).
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub struct ErrorCode(c_int);
|
||||
|
||||
impl ErrorCode {
|
||||
@ -50,16 +54,52 @@ impl ErrorCode {
|
||||
/// An error occurred in the SSL library.
|
||||
pub const SSL: ErrorCode = ErrorCode(ffi::SSL_ERROR_SSL);
|
||||
|
||||
/// Wrap an `SSL_ERROR_*` error code.
|
||||
///
|
||||
/// This is different than [packed error codes](crate::error::Error).
|
||||
#[must_use]
|
||||
#[inline]
|
||||
#[cfg_attr(debug_assertions, track_caller)]
|
||||
pub fn from_raw(raw: c_int) -> ErrorCode {
|
||||
ErrorCode(raw)
|
||||
let code = ErrorCode(raw);
|
||||
debug_assert!(
|
||||
raw < 64 || code.description().is_some(),
|
||||
"{raw} is not an SSL_ERROR_* code"
|
||||
);
|
||||
code
|
||||
}
|
||||
|
||||
/// An `SSL_ERROR_*` error code.
|
||||
///
|
||||
/// This is different than [packed error codes](crate::error::Error).
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
#[must_use]
|
||||
pub fn as_raw(&self) -> c_int {
|
||||
self.0
|
||||
}
|
||||
|
||||
#[corresponds(SSL_error_description)]
|
||||
pub fn description(self) -> Option<&'static str> {
|
||||
unsafe {
|
||||
let msg = ffi::SSL_error_description(self.0);
|
||||
if msg.is_null() {
|
||||
return None;
|
||||
}
|
||||
CStr::from_ptr(msg).to_str().ok()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ErrorCode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{} ({})", self.description().unwrap_or("error"), self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ErrorCode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -68,7 +108,7 @@ pub(crate) enum InnerError {
|
||||
Ssl(ErrorStack),
|
||||
}
|
||||
|
||||
/// An SSL error.
|
||||
/// A general SSL error, based on [`SSL_ERROR_*` error codes](ErrorCode).
|
||||
#[derive(Debug)]
|
||||
pub struct Error {
|
||||
pub(crate) code: ErrorCode,
|
||||
@ -76,6 +116,7 @@ pub struct Error {
|
||||
}
|
||||
|
||||
impl Error {
|
||||
/// An `SSL_ERROR_*` error code.
|
||||
#[must_use]
|
||||
pub fn code(&self) -> ErrorCode {
|
||||
self.code
|
||||
@ -96,6 +137,7 @@ impl Error {
|
||||
}
|
||||
}
|
||||
|
||||
/// Stack of [library-specific errors](crate::error::Error), if available.
|
||||
#[must_use]
|
||||
pub fn ssl_error(&self) -> Option<&ErrorStack> {
|
||||
match self.cause {
|
||||
@ -131,26 +173,27 @@ impl From<ErrorStack> for Error {
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self.code {
|
||||
ErrorCode::ZERO_RETURN => fmt.write_str("the SSL session has been shut down"),
|
||||
let msg = match self.code {
|
||||
ErrorCode::ZERO_RETURN => "the SSL session has been shut down",
|
||||
ErrorCode::WANT_READ => match self.io_error() {
|
||||
Some(_) => fmt.write_str("a nonblocking read call would have blocked"),
|
||||
None => fmt.write_str("the operation should be retried"),
|
||||
Some(_) => "a nonblocking read call would have blocked",
|
||||
None => "the operation should be retried",
|
||||
},
|
||||
ErrorCode::WANT_WRITE => match self.io_error() {
|
||||
Some(_) => fmt.write_str("a nonblocking write call would have blocked"),
|
||||
None => fmt.write_str("the operation should be retried"),
|
||||
Some(_) => "a nonblocking write call would have blocked",
|
||||
None => "the operation should be retried",
|
||||
},
|
||||
ErrorCode::SYSCALL => match self.io_error() {
|
||||
Some(err) => write!(fmt, "{err}"),
|
||||
None => fmt.write_str("unexpected EOF"),
|
||||
Some(err) => return err.fmt(fmt),
|
||||
None => "unexpected EOF",
|
||||
},
|
||||
ErrorCode::SSL => match self.ssl_error() {
|
||||
Some(e) => write!(fmt, "{e}"),
|
||||
None => fmt.write_str("unknown BoringSSL error"),
|
||||
Some(err) => return err.fmt(fmt),
|
||||
None => "unknown BoringSSL error",
|
||||
},
|
||||
ErrorCode(code) => write!(fmt, "unknown error code {code}"),
|
||||
}
|
||||
ErrorCode(code) => return code.fmt(fmt),
|
||||
};
|
||||
fmt.write_str(msg)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -700,13 +700,16 @@ impl From<u16> for SslSignatureAlgorithm {
|
||||
/// Numeric identifier of a TLS curve.
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[deprecated(note = "this is not ABI-stable and will be removed in the next release")]
|
||||
pub struct SslCurveNid(c_int);
|
||||
|
||||
/// A TLS Curve.
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[deprecated(note = "this is not ABI-stable and will be removed in the next release")]
|
||||
pub struct SslCurve(c_int);
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl SslCurve {
|
||||
pub const SECP224R1: SslCurve = SslCurve(ffi::SSL_CURVE_SECP224R1 as _);
|
||||
|
||||
@ -1036,7 +1039,7 @@ impl SslContextBuilder {
|
||||
has_shared_cert_store: false,
|
||||
};
|
||||
|
||||
builder.set_ex_data(*RPK_FLAG_INDEX, is_rpk);
|
||||
builder.replace_ex_data(*RPK_FLAG_INDEX, is_rpk);
|
||||
|
||||
builder
|
||||
}
|
||||
@ -1841,7 +1844,7 @@ impl SslContextBuilder {
|
||||
+ Sync
|
||||
+ 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.
|
||||
@ -1953,17 +1956,22 @@ impl SslContextBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
/// DEPRECATED: use `Self::replace_ex_data()` to set the data.
|
||||
///
|
||||
/// If this method is called more than once with the same index, any previous
|
||||
/// value stored in the `SslContextBuilder` may be leaked. This will change in the future release.
|
||||
///
|
||||
/// Sets the extra data at the specified index.
|
||||
///
|
||||
/// This can be used to provide data to callbacks registered with the context. Use the
|
||||
/// `SslContext::new_ex_index` method to create an `Index`.
|
||||
///
|
||||
/// Note that if this method is called multiple times with the same index, any previous
|
||||
/// value stored in the `SslContextBuilder` will be leaked.
|
||||
#[corresponds(SSL_CTX_set_ex_data)]
|
||||
#[deprecated(
|
||||
note = "This may leak memory. Don't rely on leaking. Use `replace_ex_data()` instead."
|
||||
)]
|
||||
pub fn set_ex_data<T>(&mut self, index: Index<SslContext, T>, data: T) {
|
||||
unsafe {
|
||||
self.ctx.set_ex_data(index, data);
|
||||
self.ctx.write_ex_data(index, data);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1974,6 +1982,7 @@ impl SslContextBuilder {
|
||||
///
|
||||
/// Any previous value will be returned and replaced by the new one.
|
||||
#[corresponds(SSL_CTX_set_ex_data)]
|
||||
#[doc(alias = "set_ex_data")]
|
||||
pub fn replace_ex_data<T>(&mut self, index: Index<SslContext, T>, data: T) -> Option<T> {
|
||||
unsafe { self.ctx.replace_ex_data(index, data) }
|
||||
}
|
||||
@ -2043,11 +2052,6 @@ 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.
|
||||
#[cfg(not(feature = "kx-safe-default"))]
|
||||
#[corresponds(SSL_CTX_set1_curves_list)]
|
||||
pub fn set_curves_list(&mut self, curves: &str) -> Result<(), ErrorStack> {
|
||||
let curves = CString::new(curves).map_err(ErrorStack::internal_error)?;
|
||||
@ -2060,6 +2064,8 @@ impl SslContextBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
/// Use `Self::set_curves_list()` instead.
|
||||
///
|
||||
/// Sets the context's supported curves.
|
||||
//
|
||||
// If the "kx-*" flags are used to set key exchange preference, then don't allow the user to
|
||||
@ -2067,6 +2073,10 @@ impl SslContextBuilder {
|
||||
// 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"))]
|
||||
#[deprecated(
|
||||
note = "Use set_curves_list(). set_curves() and SslCurve will be removed in the next release"
|
||||
)]
|
||||
#[allow(deprecated)]
|
||||
pub fn set_curves(&mut self, curves: &[SslCurve]) -> Result<(), ErrorStack> {
|
||||
let curves: Vec<i32> = curves
|
||||
.iter()
|
||||
@ -2292,10 +2302,11 @@ impl SslContextRef {
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal: does not run destructors for the previous value
|
||||
// Unsafe because SSL contexts are not guaranteed to be unique, we call
|
||||
// this only from SslContextBuilder.
|
||||
#[corresponds(SSL_CTX_set_ex_data)]
|
||||
unsafe fn set_ex_data<T>(&mut self, index: Index<SslContext, T>, data: T) {
|
||||
unsafe fn write_ex_data<T>(&mut self, index: Index<SslContext, T>, data: T) {
|
||||
unsafe {
|
||||
let data = Box::into_raw(Box::new(data)) as *mut c_void;
|
||||
ffi::SSL_CTX_set_ex_data(self.as_ptr(), index.as_raw(), data);
|
||||
@ -2310,7 +2321,7 @@ impl SslContextRef {
|
||||
return Some(mem::replace(old, data));
|
||||
}
|
||||
|
||||
self.set_ex_data(index, data);
|
||||
self.write_ex_data(index, data);
|
||||
|
||||
None
|
||||
}
|
||||
@ -2564,7 +2575,7 @@ impl SslCipherRef {
|
||||
CStr::from_ptr(ptr as *const _)
|
||||
};
|
||||
|
||||
str::from_utf8(version.to_bytes()).unwrap()
|
||||
version.to_str().unwrap()
|
||||
}
|
||||
|
||||
/// Returns the number of bits used for the cipher.
|
||||
@ -2590,7 +2601,7 @@ impl SslCipherRef {
|
||||
// SSL_CIPHER_description requires a buffer of at least 128 bytes.
|
||||
let mut buf = [0; 128];
|
||||
let ptr = ffi::SSL_CIPHER_description(self.as_ptr(), buf.as_mut_ptr(), 128);
|
||||
String::from_utf8(CStr::from_ptr(ptr as *const _).to_bytes().to_vec()).unwrap()
|
||||
CStr::from_ptr(ptr.cast()).to_string_lossy().into_owned()
|
||||
}
|
||||
}
|
||||
|
||||
@ -2952,6 +2963,8 @@ impl SslRef {
|
||||
/// Sets the ongoing session's supported groups by their named identifiers
|
||||
/// (formerly referred to as curves).
|
||||
#[corresponds(SSL_set1_groups)]
|
||||
#[deprecated(note = "SslCurveNid will be removed in the next release. Use set_curves_list")]
|
||||
#[allow(deprecated)]
|
||||
pub fn set_group_nids(&mut self, group_nids: &[SslCurveNid]) -> Result<(), ErrorStack> {
|
||||
unsafe {
|
||||
cvt_0i(ffi::SSL_set1_curves(
|
||||
@ -3000,6 +3013,8 @@ impl SslRef {
|
||||
/// Returns the [`SslCurve`] used for this `SslRef`.
|
||||
#[corresponds(SSL_get_curve_id)]
|
||||
#[must_use]
|
||||
#[deprecated(note = "SslCurve will be removed in the next release")]
|
||||
#[allow(deprecated)]
|
||||
pub fn curve(&self) -> Option<SslCurve> {
|
||||
let curve_id = unsafe { ffi::SSL_get_curve_id(self.as_ptr()) };
|
||||
if curve_id == 0 {
|
||||
@ -3216,6 +3231,8 @@ impl SslRef {
|
||||
}
|
||||
|
||||
/// 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)]
|
||||
#[must_use]
|
||||
pub fn state_string(&self) -> &'static str {
|
||||
@ -3224,10 +3241,12 @@ impl SslRef {
|
||||
CStr::from_ptr(ptr as *const _)
|
||||
};
|
||||
|
||||
str::from_utf8(state.to_bytes()).unwrap()
|
||||
state.to_str().unwrap_or_default()
|
||||
}
|
||||
|
||||
/// 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)]
|
||||
#[must_use]
|
||||
pub fn state_string_long(&self) -> &'static str {
|
||||
@ -3236,7 +3255,7 @@ impl SslRef {
|
||||
CStr::from_ptr(ptr as *const _)
|
||||
};
|
||||
|
||||
str::from_utf8(state.to_bytes()).unwrap()
|
||||
state.to_str().unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Sets the host name to be sent to the server for Server Name Indication (SNI).
|
||||
@ -3348,6 +3367,8 @@ impl SslRef {
|
||||
}
|
||||
|
||||
/// 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)]
|
||||
#[must_use]
|
||||
pub fn version_str(&self) -> &'static str {
|
||||
@ -3356,7 +3377,7 @@ impl SslRef {
|
||||
CStr::from_ptr(ptr as *const _)
|
||||
};
|
||||
|
||||
str::from_utf8(version.to_bytes()).unwrap()
|
||||
version.to_str().unwrap()
|
||||
}
|
||||
|
||||
/// Sets the minimum supported protocol version.
|
||||
@ -4028,10 +4049,11 @@ impl<S> MidHandshakeSslStream<S> {
|
||||
Ok(self.stream)
|
||||
} else {
|
||||
self.error = self.stream.make_error(ret);
|
||||
match self.error.would_block() {
|
||||
true => Err(HandshakeError::WouldBlock(self)),
|
||||
false => Err(HandshakeError::Failure(self)),
|
||||
}
|
||||
Err(if self.error.would_block() {
|
||||
HandshakeError::WouldBlock(self)
|
||||
} else {
|
||||
HandshakeError::Failure(self)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4455,16 +4477,11 @@ where
|
||||
Ok(stream)
|
||||
} else {
|
||||
let error = stream.make_error(ret);
|
||||
match error.would_block() {
|
||||
true => Err(HandshakeError::WouldBlock(MidHandshakeSslStream {
|
||||
stream,
|
||||
error,
|
||||
})),
|
||||
false => Err(HandshakeError::Failure(MidHandshakeSslStream {
|
||||
stream,
|
||||
error,
|
||||
})),
|
||||
}
|
||||
Err(if error.would_block() {
|
||||
HandshakeError::WouldBlock(MidHandshakeSslStream { stream, error })
|
||||
} else {
|
||||
HandshakeError::Failure(MidHandshakeSslStream { stream, error })
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
#![allow(deprecated)] // SslCurve
|
||||
use foreign_types::{ForeignType, ForeignTypeRef};
|
||||
use std::io;
|
||||
use std::io::prelude::*;
|
||||
@ -1166,3 +1167,47 @@ fn test_ssl_set_compliance() {
|
||||
ssl.set_compliance_policy(CompliancePolicy::NONE)
|
||||
.expect_err("Testing expect err if set compliance policy to NONE");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(deprecated)]
|
||||
fn ex_data_drop() {
|
||||
use crate::ssl::SslContextBuilder;
|
||||
use std::sync::atomic::AtomicU32;
|
||||
use std::sync::atomic::Ordering::Relaxed;
|
||||
use std::sync::Arc;
|
||||
|
||||
struct TrackDrop(Arc<AtomicU32>);
|
||||
impl Drop for TrackDrop {
|
||||
fn drop(&mut self) {
|
||||
self.0.fetch_add(1, Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
let mut ctx = SslContextBuilder::new(SslMethod::tls()).unwrap();
|
||||
let index = SslContext::new_ex_index().unwrap();
|
||||
let d1 = Arc::new(AtomicU32::new(100));
|
||||
let d2 = Arc::new(AtomicU32::new(200));
|
||||
let d3 = Arc::new(AtomicU32::new(300));
|
||||
ctx.set_ex_data(index, TrackDrop(d1.clone()));
|
||||
assert_eq!(100, d1.load(Relaxed));
|
||||
assert_eq!(200, d2.load(Relaxed));
|
||||
ctx.replace_ex_data(index, TrackDrop(d2.clone()));
|
||||
assert_eq!(101, d1.load(Relaxed));
|
||||
assert_eq!(200, d2.load(Relaxed));
|
||||
ctx.replace_ex_data(index, TrackDrop(d3.clone()));
|
||||
assert_eq!(101, d1.load(Relaxed));
|
||||
assert_eq!(201, d2.load(Relaxed));
|
||||
assert_eq!(300, d3.load(Relaxed));
|
||||
drop(ctx);
|
||||
assert_eq!(101, d1.load(Relaxed));
|
||||
assert_eq!(201, d2.load(Relaxed));
|
||||
assert_eq!(301, d3.load(Relaxed));
|
||||
|
||||
let mut ctx2 = SslContextBuilder::new(SslMethod::tls()).unwrap();
|
||||
|
||||
ctx2.set_ex_data(index, TrackDrop(d1.clone()));
|
||||
ctx2.set_ex_data(index, TrackDrop(d2.clone()));
|
||||
drop(ctx2);
|
||||
assert_eq!(101, d1.load(Relaxed), "set_ex_data has a leak");
|
||||
assert_eq!(202, d2.load(Relaxed));
|
||||
}
|
||||
|
||||
@ -13,6 +13,9 @@ foreign_type_and_impl_send_sync! {
|
||||
type CType = c_char;
|
||||
fn drop = free;
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// MUST be UTF-8.
|
||||
pub struct OpensslString;
|
||||
}
|
||||
|
||||
|
||||
@ -19,7 +19,6 @@ use std::mem;
|
||||
use std::net::IpAddr;
|
||||
use std::path::Path;
|
||||
use std::ptr;
|
||||
use std::slice;
|
||||
use std::str;
|
||||
use std::sync::{LazyLock, Once};
|
||||
|
||||
@ -135,6 +134,7 @@ impl X509StoreContextRef {
|
||||
/// This can be used to provide data to callbacks registered with the context. Use the
|
||||
/// `Ssl::new_ex_index` method to create an `Index`.
|
||||
#[corresponds(X509_STORE_CTX_set_ex_data)]
|
||||
#[doc(alias = "replace_ex_data")]
|
||||
pub fn set_ex_data<T>(&mut self, index: Index<X509StoreContext, T>, data: T) {
|
||||
if let Some(old) = self.ex_data_mut(index) {
|
||||
*old = data;
|
||||
@ -169,11 +169,10 @@ impl X509StoreContextRef {
|
||||
/// * `cert_chain` - The certificates chain.
|
||||
/// * `with_context` - The closure that is called with the initialized context.
|
||||
///
|
||||
/// This corresponds to [`X509_STORE_CTX_init`] before calling `with_context` and to
|
||||
/// [`X509_STORE_CTX_cleanup`] after calling `with_context`.
|
||||
/// Calls [`X509_STORE_CTX_cleanup`] after calling `with_context`.
|
||||
///
|
||||
/// [`X509_STORE_CTX_init`]: https://www.openssl.org/docs/man1.0.2/crypto/X509_STORE_CTX_init.html
|
||||
/// [`X509_STORE_CTX_cleanup`]: https://www.openssl.org/docs/man1.0.2/crypto/X509_STORE_CTX_cleanup.html
|
||||
#[corresponds(X509_STORE_CTX_init)]
|
||||
pub fn init<F, T>(
|
||||
&mut self,
|
||||
trust: &store::X509StoreRef,
|
||||
@ -789,6 +788,13 @@ impl X509Ref {
|
||||
}
|
||||
}
|
||||
|
||||
#[corresponds(X509_check_ip_asc)]
|
||||
pub fn check_ip_asc(&self, address: &str) -> Result<bool, ErrorStack> {
|
||||
let c_str = CString::new(address).map_err(ErrorStack::internal_error)?;
|
||||
|
||||
unsafe { cvt_n(ffi::X509_check_ip_asc(self.as_ptr(), c_str.as_ptr(), 0)).map(|n| n == 1) }
|
||||
}
|
||||
|
||||
to_pem! {
|
||||
/// Serializes the certificate into a PEM-encoded X509 structure.
|
||||
///
|
||||
@ -859,7 +865,7 @@ impl X509 {
|
||||
if ffi::ERR_GET_LIB(err) == ffi::ERR_LIB_PEM.0.try_into().unwrap()
|
||||
&& ffi::ERR_GET_REASON(err) == ffi::PEM_R_NO_START_LINE
|
||||
{
|
||||
ffi::ERR_clear_error();
|
||||
ErrorStack::clear();
|
||||
break;
|
||||
}
|
||||
|
||||
@ -902,7 +908,7 @@ impl fmt::Debug for X509 {
|
||||
|
||||
if let Ok(public_key) = &self.public_key() {
|
||||
debug_struct.field("public_key", public_key);
|
||||
};
|
||||
}
|
||||
// TODO: Print extensions once they are supported on the X509 struct.
|
||||
|
||||
debug_struct.finish()
|
||||
@ -1371,10 +1377,7 @@ pub struct X509ReqBuilder(X509Req);
|
||||
|
||||
impl X509ReqBuilder {
|
||||
/// Returns a builder for a certificate request.
|
||||
///
|
||||
/// This corresponds to [`X509_REQ_new`].
|
||||
///
|
||||
///[`X509_REQ_new`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_REQ_new.html
|
||||
#[corresponds(X509_REQ_new)]
|
||||
pub fn new() -> Result<X509ReqBuilder, ErrorStack> {
|
||||
unsafe {
|
||||
ffi::init();
|
||||
@ -1383,10 +1386,7 @@ impl X509ReqBuilder {
|
||||
}
|
||||
|
||||
/// Set the numerical value of the version field.
|
||||
///
|
||||
/// This corresponds to [`X509_REQ_set_version`].
|
||||
///
|
||||
///[`X509_REQ_set_version`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_REQ_set_version.html
|
||||
#[corresponds(X509_REQ_set_version)]
|
||||
pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> {
|
||||
unsafe { cvt(ffi::X509_REQ_set_version(self.0.as_ptr(), version.into())).map(|_| ()) }
|
||||
}
|
||||
@ -1544,10 +1544,7 @@ impl X509ReqRef {
|
||||
}
|
||||
|
||||
/// Returns the public key of the certificate request.
|
||||
///
|
||||
/// This corresponds to [`X509_REQ_get_pubkey"]
|
||||
///
|
||||
/// [`X509_REQ_get_pubkey`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_REQ_get_pubkey.html
|
||||
#[corresponds(X509_REQ_get_pubkey)]
|
||||
pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
|
||||
unsafe {
|
||||
let key = cvt_p(ffi::X509_REQ_get_pubkey(self.as_ptr()))?;
|
||||
@ -1558,10 +1555,7 @@ impl X509ReqRef {
|
||||
/// Check if the certificate request is signed using the given public key.
|
||||
///
|
||||
/// Returns `true` if verification succeeds.
|
||||
///
|
||||
/// This corresponds to [`X509_REQ_verify"].
|
||||
///
|
||||
/// [`X509_REQ_verify`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_REQ_verify.html
|
||||
#[corresponds(X509_REQ_verify)]
|
||||
pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
|
||||
where
|
||||
T: HasPublic,
|
||||
@ -1570,8 +1564,7 @@ impl X509ReqRef {
|
||||
}
|
||||
|
||||
/// Returns the extensions of the certificate request.
|
||||
///
|
||||
/// This corresponds to [`X509_REQ_get_extensions"]
|
||||
#[corresponds(X509_REQ_get_extensions)]
|
||||
pub fn extensions(&self) -> Result<Stack<X509Extension>, ErrorStack> {
|
||||
unsafe {
|
||||
let extensions = cvt_p(ffi::X509_REQ_get_extensions(self.as_ptr()))?;
|
||||
@ -1626,6 +1619,8 @@ impl X509VerifyError {
|
||||
}
|
||||
|
||||
/// 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)]
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
#[must_use]
|
||||
@ -1634,7 +1629,7 @@ impl X509VerifyError {
|
||||
|
||||
unsafe {
|
||||
let s = ffi::X509_verify_cert_error_string(c_long::from(self.0));
|
||||
str::from_utf8(CStr::from_ptr(s).to_bytes()).unwrap()
|
||||
CStr::from_ptr(s).to_str().unwrap_or_default()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1786,14 +1781,12 @@ impl GeneralNameRef {
|
||||
return None;
|
||||
}
|
||||
|
||||
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 asn = Asn1BitStringRef::from_ptr((*self.as_ptr()).d.ia5);
|
||||
|
||||
let slice = slice::from_raw_parts(ptr, len as usize);
|
||||
// IA5Strings are stated to be ASCII (specifically IA5). Hopefully
|
||||
// OpenSSL checks that when loading a certificate but if not we'll
|
||||
// use this instead of from_utf8_unchecked just in case.
|
||||
str::from_utf8(slice).ok()
|
||||
asn.to_str()
|
||||
}
|
||||
}
|
||||
|
||||
@ -1823,10 +1816,7 @@ impl GeneralNameRef {
|
||||
return None;
|
||||
}
|
||||
|
||||
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))
|
||||
Some(Asn1BitStringRef::from_ptr((*self.as_ptr()).d.ip).as_slice())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1902,8 +1892,8 @@ impl Stackable for X509Object {
|
||||
use crate::ffi::{X509_get0_signature, X509_getm_notAfter, X509_getm_notBefore, X509_up_ref};
|
||||
|
||||
use crate::ffi::{
|
||||
ASN1_STRING_get0_data, X509_ALGOR_get0, X509_REQ_get_subject_name, X509_REQ_get_version,
|
||||
X509_STORE_CTX_get0_chain, X509_set1_notAfter, X509_set1_notBefore,
|
||||
X509_ALGOR_get0, X509_REQ_get_subject_name, X509_REQ_get_version, X509_STORE_CTX_get0_chain,
|
||||
X509_set1_notAfter, X509_set1_notBefore,
|
||||
};
|
||||
|
||||
use crate::ffi::X509_OBJECT_get0_X509;
|
||||
|
||||
@ -836,3 +836,16 @@ fn test_load_subject_der() {
|
||||
];
|
||||
X509Name::from_der(SUBJECT_DER).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_check_ip_asc() {
|
||||
// Covers 127.0.0.1 and 0:0:0:0:0:0:0:1
|
||||
let cert = include_bytes!("../../../test/alt_name_cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
|
||||
assert!(cert.check_ip_asc("127.0.0.1").unwrap());
|
||||
assert!(!cert.check_ip_asc("127.0.0.2").unwrap());
|
||||
|
||||
assert!(cert.check_ip_asc("0:0:0:0:0:0:0:1").unwrap());
|
||||
assert!(!cert.check_ip_asc("0:0:0:0:0:0:0:2").unwrap());
|
||||
}
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
//! Hyper SSL support via BoringSSL.
|
||||
#![warn(missing_docs)]
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
|
||||
use crate::cache::SessionKey;
|
||||
use boring::error::ErrorStack;
|
||||
|
||||
@ -8,6 +8,7 @@ use boring::ssl::{
|
||||
};
|
||||
use http_old::uri::Scheme;
|
||||
use hyper_old::client::connect::{Connected, Connection};
|
||||
#[cfg(feature = "runtime")]
|
||||
use hyper_old::client::HttpConnector;
|
||||
use hyper_old::service::Service;
|
||||
use hyper_old::Uri;
|
||||
|
||||
@ -22,7 +22,7 @@ impl<S> AsyncStreamBridge<S> {
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@ -11,7 +11,6 @@
|
||||
//! [`boring`] crate, on which this crate is built. Configuration of TLS parameters is still
|
||||
//! primarily done through the [`boring`] crate.
|
||||
#![warn(missing_docs)]
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
|
||||
use boring::ssl::{
|
||||
self, ConnectConfiguration, ErrorCode, MidHandshakeSslStream, ShutdownResult, SslAcceptor,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user