test(sdk): reproduce rustls CryptoProvider panic from the device

RED. The APK builds, installs and opens, but the first rustls config built
from the process default panics one call deep inside Arti's rustls backend:

    Could not automatically determine the process-level CryptoProvider from
    Rustls crate features.
    Call CryptoProvider::install_default() before this point ...

rustls 0.23 picks its provider from its own 'ring' / 'aws-lc-rs' crate
features, not from which crypto crates happen to be linked. Arti reaches
rustls via tor-rtcompat with default-features = false, so neither provider
feature is on and rustls compiles with zero providers. 'ring' does appear in
cargo tree, pulled in by snow for Noise -- unrelated and irrelevant.

This commit adds rustls as an explicit dependency with no provider feature
(no behaviour change: it is already in the graph that way) so the test can
name the type, plus the regression test. On the host it reproduces the
device's panic verbatim.

The SDK lockfile is resynced with the root Cargo.toml on main (onionwire
0.2.1, rpassword, md-5, rtoolbox) in the same commit so the test build is
against a consistent lock.
This commit is contained in:
Sirius DevOps 2026-09-10 22:02:02 -04:00
parent 389d8f16c4
commit 411194a8b2
No known key found for this signature in database
3 changed files with 89 additions and 1 deletions

View file

@ -2507,6 +2507,16 @@ dependencies = [
"regex-automata",
]
[[package]]
name = "md-5"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf"
dependencies = [
"cfg-if",
"digest 0.10.7",
]
[[package]]
name = "memchr"
version = "2.8.3"
@ -2750,18 +2760,21 @@ dependencies = [
[[package]]
name = "onionwire"
version = "0.2.0"
version = "0.2.1"
dependencies = [
"argon2",
"arti-client",
"chacha20poly1305",
"ed25519-dalek",
"futures",
"md-5",
"rand 0.8.8",
"ratatui",
"rpassword",
"rusqlite",
"safelog",
"serde_json",
"sha3 0.10.9",
"snow",
"tokio",
"tor-cell",
@ -2776,6 +2789,7 @@ version = "0.1.0"
dependencies = [
"arti-client",
"onionwire",
"rustls",
"thiserror 2.0.20",
"tokio",
"uniffi",
@ -3472,6 +3486,17 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "rpassword"
version = "7.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2da316a15f47e3d053de9cb2c439650bd8fa4aaeb9365f2e5f27f492ff73c196"
dependencies = [
"libc",
"rtoolbox",
"windows-sys 0.61.2",
]
[[package]]
name = "rsa"
version = "0.9.10"
@ -3493,6 +3518,16 @@ dependencies = [
"zeroize",
]
[[package]]
name = "rtoolbox"
version = "0.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a1efe12a1469752d0e6ff5ebec0b6ef4924cc5c4c71046b0ec730040535819d"
dependencies = [
"libc",
"windows-sys 0.61.2",
]
[[package]]
name = "rusqlite"
version = "0.36.0"

View file

@ -32,6 +32,14 @@ arti-client = { version = "0.46", default-features = false, features = [
"static-sqlite",
] }
tokio = { version = "1", features = ["rt-multi-thread", "time"] }
# rustls 0.23 resolves its provider from its OWN `ring` / `aws-lc-rs` features,
# never from whichever crypto crates happen to be linked in the graph. Arti
# pulls rustls in through `tor-rtcompat` with `default-features = false`, so
# unless this crate turns a provider feature on, rustls compiles with zero
# providers and the first TLS config built from the process default fails at
# runtime. (Currently: no provider feature — see the regression test in
# `tests/provider_resolution.rs`.)
rustls = { version = "0.23", default-features = false }
uniffi = { version = "0.32", features = ["cli", "tokio"] }
thiserror = "2"

View file

@ -0,0 +1,45 @@
//! Regression test for the on-device failure: the APK built, installed and
//! opened, but pressing **Open** on the unlock screen died one call deep inside
//! Arti's rustls backend with
//!
//! ```text
//! Could not automatically determine the process-level CryptoProvider from
//! Rustls crate features.
//! Call CryptoProvider::install_default() before this point to select a
//! provider manually, or make sure exactly one of the 'aws-lc-rs' and 'ring'
//! features is enabled.
//! ```
//!
//! rustls 0.23 chooses its provider from its own `ring` / `aws-lc-rs` **crate
//! features**, not from which crypto crates happen to be linked. Arti reaches
//! rustls through `tor-rtcompat` with `default-features = false`, so neither
//! provider feature is on and rustls is compiled with *no* provider at all:
//! `ring` showing up in `cargo tree` (pulled in by `snow`, for Noise) proves
//! nothing. Everything compiles, the APK ships, and the process-default lookup
//! fails at runtime.
//!
//! This test is the invariant Arti relies on: the process default must be
//! resolvable **without** anyone calling `install_default()` first.
//!
//! Deliberately the only test in this file — a second test that installs a
//! provider would race with it inside the same test binary and could mask the
//! regression.
/// Building a `rustls` config the way Arti does, straight from the process
/// default, must not panic.
#[test]
fn rustls_default_provider_resolves_without_manual_install() {
assert!(
rustls::crypto::CryptoProvider::get_default().is_none(),
"this test must start with no provider installed"
);
// Pre-fix this panics with the device's exact message.
let _ = rustls::ClientConfig::builder();
assert!(
rustls::crypto::CryptoProvider::get_default().is_some(),
"rustls resolved no CryptoProvider — the crate feature that selects a \
provider is not enabled in this workspace"
);
}