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" + ); +}