From 411194a8b2400d3c5e9de8b6ff4a6d2b9ad3c3d5 Mon Sep 17 00:00:00 2001 From: Sirius DevOps Date: Thu, 10 Sep 2026 22:02:02 -0400 Subject: [PATCH] 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. --- crates/onionwire-sdk/Cargo.lock | 37 ++++++++++++++- crates/onionwire-sdk/Cargo.toml | 8 ++++ .../tests/provider_resolution.rs | 45 +++++++++++++++++++ 3 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 crates/onionwire-sdk/tests/provider_resolution.rs diff --git a/crates/onionwire-sdk/Cargo.lock b/crates/onionwire-sdk/Cargo.lock index b132956..6835896 100644 --- a/crates/onionwire-sdk/Cargo.lock +++ b/crates/onionwire-sdk/Cargo.lock @@ -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" diff --git a/crates/onionwire-sdk/Cargo.toml b/crates/onionwire-sdk/Cargo.toml index 27c9cb8..c4bb52e 100644 --- a/crates/onionwire-sdk/Cargo.toml +++ b/crates/onionwire-sdk/Cargo.toml @@ -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" diff --git a/crates/onionwire-sdk/tests/provider_resolution.rs b/crates/onionwire-sdk/tests/provider_resolution.rs new file mode 100644 index 0000000..0b490cb --- /dev/null +++ b/crates/onionwire-sdk/tests/provider_resolution.rs @@ -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" + ); +}