Skip to content
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
6341199
Add feature gated boringssl harness
kaukabrizvi Dec 1, 2025
57a190a
Add boringssl harness
kaukabrizvi Dec 8, 2025
fad110f
Type changes since rebase
kaukabrizvi Dec 8, 2025
feeff69
Add mtls tests cases for boringssl
kaukabrizvi Dec 9, 2025
b34de83
Remove unused imports
kaukabrizvi Dec 9, 2025
49ae2a1
cleanup PR
kaukabrizvi Dec 9, 2025
8eac659
additional cleanup
kaukabrizvi Dec 9, 2025
dd658cc
Add comment for cargo config
kaukabrizvi Dec 9, 2025
d6a7e49
Apply formatting rules
kaukabrizvi Dec 9, 2025
bee0027
clippy fix and macOS CI debug
kaukabrizvi Dec 9, 2025
b17e48c
install binutils in macOS runner
kaukabrizvi Dec 10, 2025
59895bc
set objocpy env var for macOS build
kaukabrizvi Dec 10, 2025
5636273
try LLVM instead of bin utils
kaukabrizvi Dec 10, 2025
96ce5cf
Add go to nix packages
kaukabrizvi Dec 10, 2025
c8e52ca
Add protocol versions to boring mTLS tests
kaukabrizvi Dec 10, 2025
689b5b0
Apply cargo format
kaukabrizvi Dec 10, 2025
429db60
Debug message to print macOS symbols
kaukabrizvi Dec 10, 2025
a082354
Feature gate boringssl to not build on macOS
kaukabrizvi Dec 10, 2025
24aff57
Apply cargo fmt
kaukabrizvi Dec 10, 2025
df0ed60
adjust cargo.toml for OS gating
kaukabrizvi Dec 10, 2025
638d454
Add comments to explain broginssl gating
kaukabrizvi Dec 10, 2025
24cce5f
Apply cargo fmt
kaukabrizvi Dec 10, 2025
cc22199
More closely match openssl harness
kaukabrizvi Dec 12, 2025
5f5cf65
Feature gate boringssl
kaukabrizvi Dec 18, 2025
8760ec8
Merge branch 'main' into add-boringssl-harness
kaukabrizvi Dec 18, 2025
3c78451
Point to fork's main branch
kaukabrizvi Dec 31, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions bindings/rust/standard/integration/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ no-sensitive-tests = []
# can be disabled by turning off this feature.
pq = [ "s2n-tls/pq" ]

boringssl = ["tls-harness/boringssl"]

[dependencies]
s2n-tls = { path = "../../extended/s2n-tls", features = ["unstable-testing", "unstable-crl"]}
s2n-tls-hyper = { path = "../s2n-tls-hyper" }
Expand Down Expand Up @@ -48,6 +50,13 @@ hyper-util = "0.1"
dhat = "0.3.3"
tabled = "0.20.0"

# NOTE: BoringSSL is disabled on macOS to avoid symbol collisions with
# OpenSSL; see https://github.com/aws/s2n-tls/pull/5659 for details.
[target.'cfg(not(target_os = "macos"))'.dev-dependencies.boring]
git = "https://github.com/kaukabrizvi/boring.git"
branch = "symbol-prefixing"
features = ["prefix-symbols"]

[build-dependencies]
# The ML-DSA tests require the ML-DSA support added in Openssl-3.5
# Since this overrides the dependency from the openssl-src crate,
Expand Down
255 changes: 249 additions & 6 deletions bindings/rust/standard/integration/src/mtls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ use std::{
},
};

// NOTE: BoringSSL tests are disabled on macOS to avoid symbol collisions with
// OpenSSL; see https://github.com/aws/s2n-tls/pull/5659 for details.
#[cfg(all(feature = "boringssl", not(target_os = "macos")))]
use boring::ssl::SslVersion;
use rustls::ClientConfig;

use s2n_tls::{
Expand All @@ -46,6 +50,9 @@ use tls_harness::{
PemType, SigType, TlsConnPair, TlsConnection,
};

#[cfg(all(feature = "boringssl", not(target_os = "macos")))]
use tls_harness::cohort::{BoringSslConfig, BoringSslConnection};

const APP_DATA_SIZE: usize = 100_000;

/// A wrapper around a raw pointer to `s2n_cert_validation_info` that can be sent across threads.
Expand Down Expand Up @@ -210,6 +217,58 @@ fn rustls_mtls_server(
server.into()
}

#[cfg(all(feature = "boringssl", not(target_os = "macos")))]
fn boringssl_mtls_client(sig_type: SigType, version: SslVersion) -> BoringSslConfig {
use tls_harness::harness::{Mode, TlsConfigBuilder};

let mut builder = boring::ssl::SslContextBuilder::new_test_config(Mode::Client);
builder.set_trust(sig_type);

builder
.set_certificate_chain_file(tls_harness::get_cert_path(
PemType::ClientCertChain,
sig_type,
))
.unwrap();
builder
.set_private_key_file(
tls_harness::get_cert_path(PemType::ClientKey, sig_type),
boring::ssl::SslFiletype::PEM,
)
.unwrap();
builder.set_verify(boring::ssl::SslVerifyMode::PEER);

// Pin the protocol version
builder.set_min_proto_version(Some(version)).unwrap();
builder.set_max_proto_version(Some(version)).unwrap();

BoringSslConfig {
config: builder.build(),
session_ticket_storage: Default::default(),
}
}

#[cfg(all(feature = "boringssl", not(target_os = "macos")))]
fn boringssl_mtls_server(sig_type: SigType, version: SslVersion) -> BoringSslConfig {
use tls_harness::harness::{Mode, TlsConfigBuilder};

let mut builder = boring::ssl::SslContextBuilder::new_test_config(Mode::Server);
builder.set_chain(sig_type);
builder.set_trust(sig_type);
builder.set_verify(
boring::ssl::SslVerifyMode::PEER | boring::ssl::SslVerifyMode::FAIL_IF_NO_PEER_CERT,
);

// Pin the protocol version
builder.set_min_proto_version(Some(version)).unwrap();
builder.set_max_proto_version(Some(version)).unwrap();

BoringSslConfig {
config: builder.build(),
session_ticket_storage: Default::default(),
}
}

// ============================================================================
// Basic mTLS tests
// ============================================================================
Expand All @@ -227,7 +286,7 @@ where

// s2n client, rustls server
#[test]
fn s2n_client_basic() {
fn rustls_server_basic() {
// TLS 1.2
let client = {
let builder = s2n_mtls_base_builder(SigType::Rsa2048);
Expand All @@ -252,7 +311,7 @@ fn s2n_client_basic() {

// rustls client, s2n server
#[test]
fn s2n_server_basic() {
fn rustls_client_basic() {
// TLS 1.2
let client = rustls_mtls_client(SigType::Rsa2048, &rustls::version::TLS12);
let server = {
Expand All @@ -275,6 +334,58 @@ fn s2n_server_basic() {
);
}

// s2n client, boringssl server
#[cfg(all(feature = "boringssl", not(target_os = "macos")))]
#[test]
fn boringssl_server_basic() {
// TLS 1.2
let client = {
let builder = s2n_mtls_base_builder(SigType::Rsa2048);
S2NConfig::from(builder.build().unwrap())
};
let server = boringssl_mtls_server(SigType::Rsa2048, SslVersion::TLS1_2);
test_basic::<S2NConnection, BoringSslConnection>(&client, &server);

// TLS 1.3
crate::capability_check::required_capability(
&[crate::capability_check::Capability::Tls13],
|| {
let client = {
let builder = s2n_mtls_base_builder(SigType::Rsa2048);
S2NConfig::from(builder.build().unwrap())
};
let server = boringssl_mtls_server(SigType::Rsa2048, SslVersion::TLS1_3);
test_basic::<S2NConnection, BoringSslConnection>(&client, &server);
},
);
}

// boringssl client, s2n server
#[cfg(all(feature = "boringssl", not(target_os = "macos")))]
#[test]
fn boringssl_client_basic() {
// TLS 1.2
let client = boringssl_mtls_client(SigType::Rsa2048, SslVersion::TLS1_2);
let server = {
let builder = s2n_mtls_base_builder(SigType::Rsa2048);
S2NConfig::from(builder.build().unwrap())
};
test_basic::<BoringSslConnection, S2NConnection>(&client, &server);

// TLS 1.3
crate::capability_check::required_capability(
&[crate::capability_check::Capability::Tls13],
|| {
let client = boringssl_mtls_client(SigType::Rsa2048, SslVersion::TLS1_3);
let server = {
let builder = s2n_mtls_base_builder(SigType::Rsa2048);
S2NConfig::from(builder.build().unwrap())
};
test_basic::<BoringSslConnection, S2NConnection>(&client, &server);
},
);
}

// ============================================================================
// Sync callback tests
// ============================================================================
Expand All @@ -294,7 +405,7 @@ where

// s2n client with sync callback, rustls server
#[test]
fn s2n_client_sync_callback() {
fn rustls_server_sync_callback() {
// TLS 1.2
let (client, handle) = {
let mut builder = s2n_mtls_base_builder(SigType::Rsa2048);
Expand Down Expand Up @@ -326,7 +437,7 @@ fn s2n_client_sync_callback() {

// rustls client, s2n server with sync callback
#[test]
fn s2n_server_sync_callback() {
fn rustls_client_sync_callback() {
// TLS 1.2
let client = rustls_mtls_client(SigType::Rsa2048, &rustls::version::TLS12);
let (server, handle) = {
Expand Down Expand Up @@ -357,6 +468,70 @@ fn s2n_server_sync_callback() {
);
}

// s2n client with sync callback, boringssl server
#[cfg(all(feature = "boringssl", not(target_os = "macos")))]
#[test]
fn boringssl_server_sync_callback() {
// TLS 1.2
let (client, handle) = {
let mut builder = s2n_mtls_base_builder(SigType::Rsa2048);
let cb = TestCertValidationCallback::new_sync();
let invoked = Arc::clone(cb.invoked_count());
builder.set_cert_validation_callback_sync(cb).unwrap();
(S2NConfig::from(builder.build().unwrap()), invoked)
};
let server = boringssl_mtls_server(SigType::Rsa2048, SslVersion::TLS1_2);
test_sync_callback::<S2NConnection, BoringSslConnection>(&client, &server, handle);

// TLS 1.3
crate::capability_check::required_capability(
&[crate::capability_check::Capability::Tls13],
|| {
let (client, handle) = {
let mut builder = s2n_mtls_base_builder(SigType::Rsa2048);
let cb = TestCertValidationCallback::new_sync();
let invoked = Arc::clone(cb.invoked_count());
builder.set_cert_validation_callback_sync(cb).unwrap();
(S2NConfig::from(builder.build().unwrap()), invoked)
};
let server = boringssl_mtls_server(SigType::Rsa2048, SslVersion::TLS1_3);
test_sync_callback::<S2NConnection, BoringSslConnection>(&client, &server, handle);
},
);
}

// boringssl client, s2n server with sync callback
#[cfg(all(feature = "boringssl", not(target_os = "macos")))]
#[test]
fn boringssl_client_sync_callback() {
// TLS 1.2
let client = boringssl_mtls_client(SigType::Rsa2048, SslVersion::TLS1_2);
let (server, handle) = {
let mut builder = s2n_mtls_base_builder(SigType::Rsa2048);
let cb = TestCertValidationCallback::new_sync();
let invoked = Arc::clone(cb.invoked_count());
builder.set_cert_validation_callback_sync(cb).unwrap();
(S2NConfig::from(builder.build().unwrap()), invoked)
};
test_sync_callback::<BoringSslConnection, S2NConnection>(&client, &server, handle);

// TLS 1.3
crate::capability_check::required_capability(
&[crate::capability_check::Capability::Tls13],
|| {
let client = boringssl_mtls_client(SigType::Rsa2048, SslVersion::TLS1_3);
let (server, handle) = {
let mut builder = s2n_mtls_base_builder(SigType::Rsa2048);
let cb = TestCertValidationCallback::new_sync();
let invoked = Arc::clone(cb.invoked_count());
builder.set_cert_validation_callback_sync(cb).unwrap();
(S2NConfig::from(builder.build().unwrap()), invoked)
};
test_sync_callback::<BoringSslConnection, S2NConnection>(&client, &server, handle);
},
);
}

// ============================================================================
// Async callback tests
// ============================================================================
Expand Down Expand Up @@ -430,7 +605,7 @@ where

// s2n client with async callback, rustls server
#[test]
fn s2n_client_async_callback() {
fn rustls_server_async_callback() {
// TLS 1.2
let (client, handle, rx) = {
let builder = s2n_mtls_base_builder(SigType::Rsa2048);
Expand Down Expand Up @@ -462,7 +637,7 @@ fn s2n_client_async_callback() {

// rustls client, s2n server with async callback
#[test]
fn s2n_server_async_callback() {
fn rustls_client_async_callback() {
// TLS 1.2
let client = rustls_mtls_client(SigType::Rsa2048, &rustls::version::TLS12);
let (server, handle, rx) = {
Expand Down Expand Up @@ -493,6 +668,74 @@ fn s2n_server_async_callback() {
);
}

// s2n client with async callback, boringssl server
#[cfg(all(feature = "boringssl", not(target_os = "macos")))]
#[test]
fn boringssl_server_async_callback() {
// TLS 1.2
let (client, handle, rx) = {
let builder = s2n_mtls_base_builder(SigType::Rsa2048);
let mut s2n_cfg = S2NConfig::from(builder.build().unwrap());
let (invoked, rx) = register_async_cert_callback(&mut s2n_cfg);
(s2n_cfg, invoked, rx)
};
let server = boringssl_mtls_server(SigType::Rsa2048, SslVersion::TLS1_2);
let _pair = test_async_client_callback::<S2NConnection, BoringSslConnection>(
&client, &server, handle, rx,
);

// TLS 1.3
crate::capability_check::required_capability(
&[crate::capability_check::Capability::Tls13],
|| {
let (client, handle, rx) = {
let builder = s2n_mtls_base_builder(SigType::Rsa2048);
let mut s2n_cfg = S2NConfig::from(builder.build().unwrap());
let (invoked, rx) = register_async_cert_callback(&mut s2n_cfg);
(s2n_cfg, invoked, rx)
};
let server = boringssl_mtls_server(SigType::Rsa2048, SslVersion::TLS1_3);
let _pair = test_async_client_callback::<S2NConnection, BoringSslConnection>(
&client, &server, handle, rx,
);
},
);
}

// boringssl client, s2n server with async callback
#[cfg(all(feature = "boringssl", not(target_os = "macos")))]
#[test]
fn boringssl_client_async_callback() {
// TLS 1.2
let client = boringssl_mtls_client(SigType::Rsa2048, SslVersion::TLS1_2);
let (server, handle, rx) = {
let builder = s2n_mtls_base_builder(SigType::Rsa2048);
let mut s2n_cfg = S2NConfig::from(builder.build().unwrap());
let (invoked, rx) = register_async_cert_callback(&mut s2n_cfg);
(s2n_cfg, invoked, rx)
};
let _pair = test_async_server_callback::<BoringSslConnection, S2NConnection>(
&client, &server, handle, rx,
);

// TLS 1.3
crate::capability_check::required_capability(
&[crate::capability_check::Capability::Tls13],
|| {
let client = boringssl_mtls_client(SigType::Rsa2048, SslVersion::TLS1_3);
let (server, handle, rx) = {
let builder = s2n_mtls_base_builder(SigType::Rsa2048);
let mut s2n_cfg = S2NConfig::from(builder.build().unwrap());
let (invoked, rx) = register_async_cert_callback(&mut s2n_cfg);
(s2n_cfg, invoked, rx)
};
let _pair = test_async_server_callback::<BoringSslConnection, S2NConnection>(
&client, &server, handle, rx,
);
},
);
}

// s2n client, s2n server with async callback
#[test]
fn s2n_s2n_mtls_async_callback() {
Expand Down
12 changes: 12 additions & 0 deletions bindings/rust/standard/tls-harness/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
[features]
default = []
boringssl = ["dep:boring"]

[package]
name = "tls-harness"
version = "0.1.0"
Expand All @@ -14,10 +18,18 @@ rustls-pemfile = "2.2.0"
openssl = { version = "0.10.73", features = ["vendored"] }
openssl-sys = "0.9.109"
byteorder = "1.5.0"
boring = { git = "https://github.com/kaukabrizvi/boring.git", branch = "symbol-prefixing", features = ["prefix-symbols"], optional = true }

brass-aphid-wire-decryption = "0.0.1"
brass-aphid-wire-messages = "0.0.1"

# NOTE: BoringSSL is disabled on macOS to avoid symbol collisions with
# OpenSSL; see https://github.com/aws/s2n-tls/pull/5659 for details.
[target.'cfg(not(target_os = "macos"))'.dependencies.boring]
git = "https://github.com/kaukabrizvi/boring.git"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with this, but can we just open an issue to find a way to "time bomb" ourselves? I'd like our CI to fail if your fork goes more than e.g. 6 months without a commit. "trust but verify" 😉

Copy link
Contributor Author

@kaukabrizvi kaukabrizvi Dec 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’ve installed https://wei.github.io/pull/ on the fork, which should help keep it in check. That said, an additional guardrail would be useful - I’ll open a follow-up issue after this lands to add a CI “time-bomb” so the fork doesn’t silently go stale.

branch = "symbol-prefixing"
features = ["prefix-symbols"]

[dev-dependencies]
# env_logger and log are used to enable logging for rustls, which can help with
# debugging interop failures
Expand Down
Loading
Loading