sync upstream (#74)

* Add fips-precompiled feature to support newer versions of FIPS (#338)

Newer versions of FIPS don't need any special casing in our bindings,
unlike the submoduled boringssl-fips. In addition, many users currently
use FIPS by precompiling BoringSSL with the proper build tools and
passing that in to the bindings.

Until we adopt the Update Stream pattern for FIPS, there are two main
use cases:

1. Passing an unmodified, precompiled FIPS validated version of
   boringssl (fips-precompiled)

2. Passing a custom source directory of boringssl meant to be linked
   with a FIPS validated bcm.o. This is mainly useful if you carry
   custom patches but still want to use a FIPS validated BoringCrypto.
   (fips-link-precompiled)

This commit introduces the `fips-precompiled` feature and removes the
`fips-no-compat` feature.

* Release 4.16.0 (#341)

* feat(x509): Implement `Clone` for `X509Store` (#339)


* boring(x509): impl Clone of X509Store

* expose SSL_set_compliance_policy

* fix clippy error

* Use ubuntu-latest for all ci jobs

ubuntu 20.04 is now deprecated:
https://github.com/actions/runner-images/issues/11101

* add SslCurve::X25519_MLKEM768 constant

* Clippy

* Fix linking SystemFunction036 from advapi32 in Rust 1.87

* rustfmt ;(

* build: Fix the build for 32-bit Linux platform

* Update Cargo.toml

* boring(ssl): use `corresponds` macro in `add_certificate_compression_algorithm`

* Add `X509_STORE_CTX_get0_cert` interface

This method reliably retrieves the certificate the `X509_STORE_CTX` is
verifying, unlike `X509_STORE_CTX_get_current_cert`, which may return
the "problematic" cert when verification fails.

* Update bindgen from 0.70.1 -> 0.71.1.

* Revert "feat(x509): Implement `Clone` for `X509Store` (#339)" (#353)

* Revert "feat(x509): Implement `Clone` for `X509Store` (#339)"

This reverts commit 49a8d0906a.

See <https://github.com/cloudflare/boring/pull/120>.

* Ensure Clone is not added to X509Store

* Add comment about why X509Store must not implement Clone

---------

Co-authored-by: Kornel <kornel@cloudflare.com>

* Release 4.17.0 (#354)

* Add set_verify_param

* clippy fix

---------

Co-authored-by: Rushil Mehra <84047965+rushilmehra@users.noreply.github.com>
Co-authored-by: Shih-Chiang Chien <shih-chiang@cloudflare.com>
Co-authored-by: Rushil Mehra <rmehra@cloudflare.com>
Co-authored-by: Eric Rosenberg <eric_rosenberg@apple.com>
Co-authored-by: Kornel <kornel@cloudflare.com>
Co-authored-by: James Larisch <jlarisch@cloudflare.com>
Co-authored-by: Yury Yarashevich <yura.yaroshevich@gmail.com>
Co-authored-by: Anthony Ramine <123095+nox@users.noreply.github.com>
This commit is contained in:
0x676e67 2025-05-30 11:15:39 +08:00 committed by GitHub
commit 0f2461ad00
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 247 additions and 58 deletions

View File

@ -22,7 +22,7 @@ boring-sys = { package = "boring-sys2", version = "4.15.13", path = "./boring-sy
boring = { package = "boring2", version = "4.15.13", path = "./boring" } boring = { package = "boring2", version = "4.15.13", path = "./boring" }
tokio-boring = { package = "tokio-boring2", version = "4.15.13", path = "./tokio-boring" } tokio-boring = { package = "tokio-boring2", version = "4.15.13", path = "./tokio-boring" }
bindgen = { version = "0.70.1", default-features = false, features = ["runtime"] } bindgen = { version = "0.71.1", default-features = false, features = ["runtime"] }
bytes = "1" bytes = "1"
cmake = "0.1.18" cmake = "0.1.18"
fs_extra = "1.3.0" fs_extra = "1.3.0"

View File

@ -1,4 +1,32 @@
4.17.0
- 2025-05-27 Revert "feat(x509): Implement `Clone` for `X509Store` (#339)" (#353)
- 2025-05-14 Update bindgen from 0.70.1 -> 0.71.1.
- 2025-05-19 Add `X509_STORE_CTX_get0_cert` interface
- 2025-05-18 boring(ssl): use `corresponds` macro in `add_certificate_compression_algorithm`
- 2025-02-14 Update Cargo.toml
- 2025-02-13 build: Fix the build for 32-bit Linux platform
- 2025-05-20 rustfmt ;(
- 2025-05-20 Fix linking SystemFunction036 from advapi32 in Rust 1.87
- 2025-05-20 Clippy
- 2025-05-01 add SslCurve::X25519_MLKEM768 constant
- 2025-04-17 Use ubuntu-latest for all ci jobs
- 2025-04-16 fix clippy error
- 2025-04-15 expose SSL_set_compliance_policy
- 2025-04-07 feat(x509): Implement `Clone` for `X509Store` (#339)
4.16.0
- 2025-03-31 Add fips-precompiled feature to support newer versions of FIPS (#338)
- 2025-03-18 Document linking to C++ standard library (#335)
- 2025-03-18 Revert "Remove "fips-no-compat", decouple "fips-compat" from "fips"" (#334)
- 2025-03-11 boring: Disable `SslCurve` API with "fips" feature
- 2025-03-11 boring-sys: Ignore patches when boringSSL is precompiled
- 2025-03-13 Remove "fips-no-compat", decouple "fips-compat" from "fips"
- 2025-03-14 Add feature "fips-no-compat"
- 2025-03-10 Advertise X25519MLKEM768 with "kx-client-pq-preferred" (#329)
- 2025-03-10 Update to actions/cache@v4 (#328)
- 2025-02-28 Add missing release notes entry (#324)
4.15.0 4.15.0
- 2025-02-27 Expose API to enable certificate compression. (#241) - 2025-02-27 Expose API to enable certificate compression. (#241)
- 2025-02-23 Fix lifetimes in ssl::select_next_proto - 2025-02-23 Fix lifetimes in ssl::select_next_proto

View File

@ -58,9 +58,19 @@ features = ["pq-experimental", "underscore-wildcards"]
rustdoc-args = ["--cfg", "docsrs"] rustdoc-args = ["--cfg", "docsrs"]
[features] [features]
# Use a FIPS-validated version of boringssl. # Compile boringssl using the FIPS build flag if building boringssl from
# scratch.
#
# See
# https://boringssl.googlesource.com/boringssl/+/master/crypto/fipsmodule/FIPS.md
# for instructions and more details on the boringssl FIPS flag.
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. # Link with precompiled FIPS-validated `bcm.o` module.
fips-link-precompiled = [] fips-link-precompiled = []

View File

@ -16,6 +16,7 @@ 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) fips_link_precompiled: bool,
pub(crate) pq_experimental: bool, pub(crate) pq_experimental: bool,
pub(crate) rpk: bool, pub(crate) rpk: bool,
@ -47,11 +48,7 @@ impl Config {
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap(); let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
let features = Features::from_env(); let features = Features::from_env();
let env = Env::from_env( let env = Env::from_env(&host, &target, features.is_fips_like());
&host,
&target,
features.fips || features.fips_link_precompiled,
);
let mut is_bazel = false; let mut is_bazel = false;
if let Some(src_path) = &env.source_path { if let Some(src_path) = &env.source_path {
@ -80,6 +77,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();
@ -103,15 +104,22 @@ impl Config {
); );
} }
// 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 { if is_precompiled_native_lib && self.features.fips_link_precompiled {
panic!("precompiled BoringSSL was provided, so FIPS configuration can't be applied"); 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 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 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();
@ -119,12 +127,17 @@ impl Features {
Self { Self {
fips, fips,
fips_precompiled,
fips_link_precompiled, fips_link_precompiled,
pq_experimental, pq_experimental,
rpk, rpk,
underscore_wildcards, underscore_wildcards,
} }
} }
pub(crate) fn is_fips_like(&self) -> bool {
self.fips || self.fips_precompiled || self.fips_link_precompiled
}
} }
impl Env { impl Env {
@ -138,6 +151,7 @@ impl Env {
let target_var = |name: &str| { let target_var = |name: &str| {
let kind = if host == target { "HOST" } else { "TARGET" }; 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!("{}_{}", kind, name))) .or_else(|| var(&format!("{}_{}", kind, name)))

View File

@ -661,13 +661,14 @@ fn main() {
let bssl_dir = built_boring_source_path(&config); let bssl_dir = built_boring_source_path(&config);
let build_path = get_boringssl_platform_output_path(&config); let build_path = get_boringssl_platform_output_path(&config);
if config.is_bazel || (config.features.fips && config.env.path.is_some()) { if config.is_bazel || (config.features.is_fips_like() && config.env.path.is_some()) {
println!( println!(
"cargo:rustc-link-search=native={}/lib/{}", "cargo:rustc-link-search=native={}/lib/{}",
bssl_dir.display(), bssl_dir.display(),
build_path build_path
); );
} else { } else {
// todo(rmehra): clean this up, I think these are pretty redundant
println!( println!(
"cargo:rustc-link-search=native={}/build/crypto/{}", "cargo:rustc-link-search=native={}/build/crypto/{}",
bssl_dir.display(), bssl_dir.display(),
@ -699,6 +700,11 @@ fn main() {
println!("cargo:rustc-link-lib=static=crypto"); println!("cargo:rustc-link-lib=static=crypto");
println!("cargo:rustc-link-lib=static=ssl"); println!("cargo:rustc-link-lib=static=ssl");
if config.target_os == "windows" {
// Rust 1.87.0 compat - https://github.com/rust-lang/rust/pull/138233
println!("cargo:rustc-link-lib=advapi32");
}
let include_path = config.env.include_path.clone().unwrap_or_else(|| { let include_path = config.env.include_path.clone().unwrap_or_else(|| {
if let Some(bssl_path) = &config.env.path { if let Some(bssl_path) = &config.env.path {
return bssl_path.join("include"); return bssl_path.join("include");
@ -717,9 +723,12 @@ fn main() {
// bindgen 0.70 replaced the run-time layout tests with compile-time ones, // 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. // but they depend on std::mem::offset_of, stabilized in 1.77.
let supports_layout_tests = autocfg::new().probe_rustc_version(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(bindgen::RustTarget::Stable_1_68) // bindgen MSRV is 1.70, so this is enough .rust_target(target_rust_version) // bindgen MSRV is 1.70, so this is enough
.derive_copy(true) .derive_copy(true)
.derive_debug(true) .derive_debug(true)
.derive_default(true) .derive_default(true)
@ -759,7 +768,7 @@ fn main() {
"des.h", "des.h",
"dtls1.h", "dtls1.h",
"hkdf.h", "hkdf.h",
#[cfg(not(any(feature = "fips", feature = "fips-no-compat")))] #[cfg(not(feature = "fips"))]
"hpke.h", "hpke.h",
"hmac.h", "hmac.h",
"hrss.h", "hrss.h",

View File

@ -19,6 +19,7 @@ use std::os::raw::{c_char, c_int, c_uint, c_ulong};
#[allow( #[allow(
clippy::useless_transmute, clippy::useless_transmute,
clippy::derive_partial_eq_without_eq, clippy::derive_partial_eq_without_eq,
clippy::ptr_offset_with_cast,
dead_code dead_code
)] )]
mod generated { mod generated {

View File

@ -19,24 +19,27 @@ rustdoc-args = ["--cfg", "docsrs"]
[features] [features]
# Controlling the build # Controlling the build
# Use a FIPS-validated version of BoringSSL. This feature sets "fips-compat". # 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.
fips = ["fips-compat", "boring-sys/fips"] fips = ["fips-compat", "boring-sys/fips"]
# Use a FIPS build of BoringSSL, but don't set "fips-compat". # Build with compatibility for the submoduled boringssl-fips, without enabling
# # the `fips` feature itself (useful e.g. if `fips-link-precompiled` is used
# As of boringSSL commit a430310d6563c0734ddafca7731570dfb683dc19, we no longer # with an older BoringSSL version).
# 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-no-compat = ["boring-sys/fips"]
# Build with compatibility for the BoringSSL FIPS version, without enabling the
# `fips` feature itself (useful e.g. if `fips-link-precompiled` is used with an
# older BoringSSL version).
fips-compat = [] fips-compat = []
# 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 = ["boring-sys/fips-precompiled"]
# Link with precompiled FIPS-validated `bcm.o` module. # Link with precompiled FIPS-validated `bcm.o` module.
fips-link-precompiled = ["boring-sys/fips-link-precompiled"] fips-link-precompiled = ["boring-sys/fips-link-precompiled"]

View File

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

View File

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

View File

@ -112,7 +112,7 @@ pub use self::cert_compression::CertCompressionAlgorithm;
pub use self::connector::{ pub use self::connector::{
ConnectConfiguration, SslAcceptor, SslAcceptorBuilder, SslConnector, SslConnectorBuilder, ConnectConfiguration, SslAcceptor, SslAcceptorBuilder, SslConnector, SslConnectorBuilder,
}; };
#[cfg(not(any(feature = "fips", feature = "fips-no-compat")))] #[cfg(not(feature = "fips"))]
pub use self::ech::SslEchKeysRef; pub use self::ech::SslEchKeysRef;
pub use self::error::{Error, ErrorCode, HandshakeError}; pub use self::error::{Error, ErrorCode, HandshakeError};
@ -122,7 +122,7 @@ mod callbacks;
#[cfg(feature = "cert-compression")] #[cfg(feature = "cert-compression")]
mod cert_compression; mod cert_compression;
mod connector; mod connector;
#[cfg(not(any(feature = "fips", feature = "fips-no-compat")))] #[cfg(not(feature = "fips"))]
mod ech; mod ech;
mod error; mod error;
mod mut_only; mod mut_only;
@ -770,24 +770,36 @@ impl SslCurve {
pub const FFDHE3072: SslCurve = SslCurve(ffi::SSL_CURVE_DHE3072 as _); pub const FFDHE3072: SslCurve = SslCurve(ffi::SSL_CURVE_DHE3072 as _);
#[cfg(feature = "pq-experimental")] #[cfg(feature = "pq-experimental")]
pub const X25519_MLKEM768: SslCurve = SslCurve(ffi::SSL_CURVE_X25519_MLKEM768 as _); #[cfg(not(any(feature = "fips", feature = "fips-precompiled")))]
#[cfg(feature = "pq-experimental")]
#[cfg(not(any(feature = "fips", feature = "fips-no-compat")))]
pub const X25519_KYBER768_DRAFT00: SslCurve = pub const X25519_KYBER768_DRAFT00: SslCurve =
SslCurve(ffi::SSL_CURVE_X25519_KYBER768_DRAFT00 as _); SslCurve(ffi::SSL_CURVE_X25519_KYBER768_DRAFT00 as _);
#[cfg(all(not(feature = "fips"), feature = "pq-experimental"))] #[cfg(all(
not(any(feature = "fips", feature = "fips-precompiled")),
feature = "pq-experimental"
))]
pub const X25519_KYBER768_DRAFT00_OLD: SslCurve = pub const X25519_KYBER768_DRAFT00_OLD: SslCurve =
SslCurve(ffi::SSL_CURVE_X25519_KYBER768_DRAFT00_OLD as _); SslCurve(ffi::SSL_CURVE_X25519_KYBER768_DRAFT00_OLD as _);
#[cfg(all(not(feature = "fips"), feature = "pq-experimental"))] #[cfg(all(
not(any(feature = "fips", feature = "fips-precompiled")),
feature = "pq-experimental"
))]
pub const X25519_KYBER512_DRAFT00: SslCurve = pub const X25519_KYBER512_DRAFT00: SslCurve =
SslCurve(ffi::SSL_CURVE_X25519_KYBER512_DRAFT00 as _); SslCurve(ffi::SSL_CURVE_X25519_KYBER512_DRAFT00 as _);
#[cfg(all(not(feature = "fips"), feature = "pq-experimental"))] #[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 _); 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 /// Returns the curve name
#[corresponds(SSL_get_curve_name)] #[corresponds(SSL_get_curve_name)]
pub fn name(&self) -> Option<&'static str> { pub fn name(&self) -> Option<&'static str> {
@ -818,15 +830,27 @@ impl SslCurve {
ffi::SSL_CURVE_SECP384R1 => Some(ffi::NID_secp384r1), ffi::SSL_CURVE_SECP384R1 => Some(ffi::NID_secp384r1),
ffi::SSL_CURVE_SECP521R1 => Some(ffi::NID_secp521r1), ffi::SSL_CURVE_SECP521R1 => Some(ffi::NID_secp521r1),
ffi::SSL_CURVE_X25519 => Some(ffi::NID_X25519), ffi::SSL_CURVE_X25519 => Some(ffi::NID_X25519),
#[cfg(not(any(feature = "fips", feature = "fips-no-compat")))] #[cfg(not(any(feature = "fips", feature = "fips-precompiled")))]
ffi::SSL_CURVE_X25519_KYBER768_DRAFT00 => Some(ffi::NID_X25519Kyber768Draft00), ffi::SSL_CURVE_X25519_KYBER768_DRAFT00 => Some(ffi::NID_X25519Kyber768Draft00),
#[cfg(all(not(feature = "fips"), feature = "pq-experimental"))] #[cfg(all(
not(any(feature = "fips", feature = "fips-precompiled")),
feature = "pq-experimental"
))]
ffi::SSL_CURVE_X25519_KYBER768_DRAFT00_OLD => Some(ffi::NID_X25519Kyber768Draft00Old), ffi::SSL_CURVE_X25519_KYBER768_DRAFT00_OLD => Some(ffi::NID_X25519Kyber768Draft00Old),
#[cfg(all(not(feature = "fips"), feature = "pq-experimental"))] #[cfg(all(
not(any(feature = "fips", feature = "fips-precompiled")),
feature = "pq-experimental"
))]
ffi::SSL_CURVE_X25519_KYBER512_DRAFT00 => Some(ffi::NID_X25519Kyber512Draft00), ffi::SSL_CURVE_X25519_KYBER512_DRAFT00 => Some(ffi::NID_X25519Kyber512Draft00),
#[cfg(all(not(feature = "fips"), feature = "pq-experimental"))] #[cfg(all(
not(any(feature = "fips", feature = "fips-precompiled")),
feature = "pq-experimental"
))]
ffi::SSL_CURVE_P256_KYBER768_DRAFT00 => Some(ffi::NID_P256Kyber768Draft00), ffi::SSL_CURVE_P256_KYBER768_DRAFT00 => Some(ffi::NID_P256Kyber768Draft00),
#[cfg(all(not(feature = "fips"), feature = "pq-experimental"))] #[cfg(all(
not(any(feature = "fips", feature = "fips-precompiled")),
feature = "pq-experimental"
))]
ffi::SSL_CURVE_X25519_MLKEM768 => Some(ffi::NID_X25519MLKEM768), ffi::SSL_CURVE_X25519_MLKEM768 => Some(ffi::NID_X25519MLKEM768),
ffi::SSL_CURVE_DHE2048 => Some(ffi::NID_ffdhe2048), ffi::SSL_CURVE_DHE2048 => Some(ffi::NID_ffdhe2048),
ffi::SSL_CURVE_DHE3072 => Some(ffi::NID_ffdhe3072), ffi::SSL_CURVE_DHE3072 => Some(ffi::NID_ffdhe3072),
@ -1663,12 +1687,6 @@ impl SslContextBuilder {
unsafe { X509StoreBuilderRef::from_ptr(ffi::SSL_CTX_get_cert_store(self.as_ptr())) } unsafe { X509StoreBuilderRef::from_ptr(ffi::SSL_CTX_get_cert_store(self.as_ptr())) }
} }
/// Returns a mutable reference to the context's certificate store.
#[corresponds(SSL_CTX_get_cert_store)]
pub fn cert_store_mut(&mut self) -> &mut X509StoreBuilderRef {
unsafe { X509StoreBuilderRef::from_ptr_mut(ffi::SSL_CTX_get_cert_store(self.as_ptr())) }
}
/// Sets the callback dealing with OCSP stapling. /// Sets the callback dealing with OCSP stapling.
/// ///
/// On the client side, this callback is responsible for validating the OCSP status response /// On the client side, this callback is responsible for validating the OCSP status response
@ -2070,7 +2088,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(any(feature = "fips", feature = "fips-no-compat")))] #[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(|_| ()) }
@ -2312,7 +2330,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(any(feature = "fips", feature = "fips-no-compat")))] #[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(|_| ()) }
@ -3715,6 +3733,13 @@ impl SslRef {
ffi::SSL_set_enable_ech_grease(self.as_ptr(), enable); ffi::SSL_set_enable_ech_grease(self.as_ptr(), enable);
} }
} }
/// Sets the compliance policy on `SSL`.
#[cfg(not(feature = "fips-compat"))]
#[corresponds(SSL_set_compliance_policy)]
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(|_| ()) }
}
} }
/// An SSL stream midway through the handshake process. /// An SSL stream midway through the handshake process.

View File

@ -56,17 +56,29 @@ fn no_error_when_trusted_and_callback_returns_true() {
#[test] #[test]
fn callback_receives_correct_certificate() { fn callback_receives_correct_certificate() {
let server = Server::builder().build(); // Server sends the full chain (leaf + root)...
let server = Server::builder_full_chain().build();
// but client doesn't load the root as trusted.
// So we expect an error.
let mut client = server.client(); let mut client = server.client();
let expected = "59172d9313e84459bcff27f967e79e6e9217e584"; let leaf_sha1 = "59172d9313e84459bcff27f967e79e6e9217e584";
let root_sha1 = "c0cbdf7cdd03c9773e5468e1f6d2da7d5cbb1875";
client.ctx().set_verify(SslVerifyMode::PEER); client.ctx().set_verify(SslVerifyMode::PEER);
client.ctx().set_cert_verify_callback(move |x509| { client.ctx().set_cert_verify_callback(move |x509| {
assert!(!x509.verify_cert().unwrap()); assert!(!x509.verify_cert().unwrap());
// This is set to the root, since that's the problematic cert.
assert!(x509.current_cert().is_some()); assert!(x509.current_cert().is_some());
// This is set to the leaf, since that's the cert we're verifying.
assert!(x509.cert().is_some());
assert!(x509.verify_result().is_err()); assert!(x509.verify_result().is_err());
let cert = x509.current_cert().unwrap();
let digest = cert.digest(MessageDigest::sha1()).unwrap(); let root = x509.current_cert().unwrap();
assert_eq!(hex::encode(digest), expected); let digest = root.digest(MessageDigest::sha1()).unwrap();
assert_eq!(hex::encode(digest), root_sha1);
let leaf = x509.cert().unwrap();
let digest = leaf.digest(MessageDigest::sha1()).unwrap();
assert_eq!(hex::encode(digest), leaf_sha1);
true true
}); });

View File

@ -21,13 +21,13 @@ use crate::ssl::{
use crate::x509::verify::X509CheckFlags; use crate::x509::verify::X509CheckFlags;
use crate::x509::{X509Name, X509}; use crate::x509::{X509Name, X509};
#[cfg(not(any(feature = "fips", feature = "fips-no-compat")))] #[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(any(feature = "fips", feature = "fips-no-compat")))] #[cfg(not(feature = "fips"))]
mod ech; mod ech;
mod extensions; mod extensions;
mod private_key_method; mod private_key_method;
@ -991,7 +991,7 @@ fn test_get_ciphers() {
} }
#[test] #[test]
#[cfg(not(any(feature = "fips", feature = "fips-no-compat")))] #[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,3 +1071,52 @@ fn test_info_callback() {
client.connect(); client.connect();
assert!(CALLED_BACK.load(Ordering::Relaxed)); assert!(CALLED_BACK.load(Ordering::Relaxed));
} }
#[cfg(not(feature = "fips-compat"))]
#[test]
fn test_ssl_set_compliance() {
let ctx = SslContext::builder(SslMethod::tls()).unwrap().build();
let mut ssl = Ssl::new(&ctx).unwrap();
ssl.set_compliance_policy(CompliancePolicy::FIPS_202205)
.unwrap();
assert_eq!(ssl.max_proto_version().unwrap(), SslVersion::TLS1_3);
assert_eq!(ssl.min_proto_version().unwrap(), SslVersion::TLS1_2);
const FIPS_CIPHERS: [&str; 4] = [
"ECDHE-ECDSA-AES128-GCM-SHA256",
"ECDHE-RSA-AES128-GCM-SHA256",
"ECDHE-ECDSA-AES256-GCM-SHA384",
"ECDHE-RSA-AES256-GCM-SHA384",
];
let ciphers = ssl.ciphers();
assert_eq!(ciphers.len(), FIPS_CIPHERS.len());
for cipher in ciphers.into_iter().zip(FIPS_CIPHERS) {
assert_eq!(cipher.0.name(), cipher.1)
}
let ctx = SslContext::builder(SslMethod::tls()).unwrap().build();
let mut ssl = Ssl::new(&ctx).unwrap();
ssl.set_compliance_policy(CompliancePolicy::WPA3_192_202304)
.unwrap();
assert_eq!(ssl.max_proto_version().unwrap(), SslVersion::TLS1_3);
assert_eq!(ssl.min_proto_version().unwrap(), SslVersion::TLS1_2);
const WPA3_192_CIPHERS: [&str; 2] = [
"ECDHE-ECDSA-AES256-GCM-SHA384",
"ECDHE-RSA-AES256-GCM-SHA384",
];
let ciphers = ssl.ciphers();
assert_eq!(ciphers.len(), WPA3_192_CIPHERS.len());
for cipher in ciphers.into_iter().zip(WPA3_192_CIPHERS) {
assert_eq!(cipher.0.name(), cipher.1)
}
ssl.set_compliance_policy(CompliancePolicy::NONE)
.expect_err("Testing expect err if set compliance policy to NONE");
}

View File

@ -36,6 +36,24 @@ impl Server {
} }
} }
/// Serves the leaf and the root together.
pub fn builder_full_chain() -> Builder {
let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
// Uses certs.pem instead of cert.pem.
ctx.set_certificate_chain_file("test/certs.pem").unwrap();
ctx.set_private_key_file("test/key.pem", SslFiletype::PEM)
.unwrap();
Builder {
ctx,
ssl_cb: Box::new(|_| {}),
io_cb: Box::new(|_| {}),
err_cb: Box::new(|_| {}),
should_error: false,
expected_connections_count: 1,
}
}
pub fn client(&self) -> ClientBuilder { pub fn client(&self) -> ClientBuilder {
ClientBuilder { ClientBuilder {
ctx: SslContext::builder(SslMethod::tls()).unwrap(), ctx: SslContext::builder(SslMethod::tls()).unwrap(),

View File

@ -148,6 +148,12 @@ impl X509StoreContextRef {
unsafe { X509VerifyParamRef::from_ptr_mut(ffi::X509_STORE_CTX_get0_param(self.as_ptr())) } unsafe { X509VerifyParamRef::from_ptr_mut(ffi::X509_STORE_CTX_get0_param(self.as_ptr())) }
} }
/// Sets the X509 verifification configuration on the X509_STORE_CTX.
#[corresponds(X509_STORE_CTX_set0_param)]
pub fn set_verify_param(&mut self, param: &mut X509VerifyParamRef) {
unsafe { ffi::X509_STORE_CTX_set0_param(self.as_ptr(), param.as_ptr()) }
}
/// Verifies the stored certificate. /// Verifies the stored certificate.
/// ///
/// Returns `true` if verification succeeds. The `error` method will return the specific /// Returns `true` if verification succeeds. The `error` method will return the specific
@ -209,6 +215,20 @@ impl X509StoreContextRef {
} }
} }
} }
/// Returns a reference to the certificate being verified.
/// May return None if a raw public key is being verified.
#[corresponds(X509_STORE_CTX_get0_cert)]
pub fn cert(&self) -> Option<&X509Ref> {
unsafe {
let ptr = ffi::X509_STORE_CTX_get0_cert(self.as_ptr());
if ptr.is_null() {
None
} else {
Some(X509Ref::from_ptr(ptr))
}
}
}
} }
/// A builder used to construct an `X509`. /// A builder used to construct an `X509`.

View File

@ -27,7 +27,7 @@ fips = ["boring/fips", "boring-sys/fips"]
# #
# TODO(cjpatton) Delete this feature and modify "fips" so that it doesn't imply # TODO(cjpatton) Delete this feature and modify "fips" so that it doesn't imply
# "fips-compat". # "fips-compat".
fips-no-compat = ["boring/fips-no-compat"] fips-precompiled = ["boring/fips-precompiled"]
# Link with precompiled FIPS-validated `bcm.o` module. # Link with precompiled FIPS-validated `bcm.o` module.
fips-link-precompiled = ["boring/fips-link-precompiled", "boring-sys/fips-link-precompiled"] fips-link-precompiled = ["boring/fips-link-precompiled", "boring-sys/fips-link-precompiled"]