Loading CHANGELOG.md +40 −0 Original line number Diff line number Diff line Loading @@ -3,6 +3,46 @@ This file describes changes in Arti through the current release. Once Arti is more mature, we may switch to using a separate changelog for each crate. # Arti 1.2.3 — 15 May 2024 Arti 1.2.3 fixes a high-severity issue affecting onion services and clients connecting to onion services with 'lite' vanguards (the default) enabled: when building anonymizing circuits to or from an onion service the circuit manager code would build the circuits with one hop too few. This makes users of this code more vulnerable to some kinds of traffic analysis when they run or visit onion services. This release also fixes a medium-severity issue affecting 'full' vanguards. With 'full' vanguards enabled, client HsDir circuits, client introduction circuits and service rendezvous-circuits are extended with an extra hop to minimize the linkability of the guard nodes. In some circumstances, the circuit manager would build circuits with one hop too few, making it easier for an adversary to discover the L2 and L3 guards of the affected clients and services. In Arti 1.2.1 and earlier, vanguards were still an experimental feature, or absent, so those versions are classified as "not affected", even though downgrading does not fix the security problem. ### Major bugfixes - Fix a high-severity issue affecting onion service circuits using 'lite' vanguards. Previously, with 'lite' vanguards enabled, any circuit to or from an onion service was one hop too short, making clients and services vulnerable to certain types of traffic analysis. This is also tracked as [TROVE-2024-003]. ([#1409]) - Fix a medium-severity issue affecting onion service circuits using 'full' vanguards. Previously, with 'full' vanguards enabled, *some* circuits to or from an onion service were one hop too short, making linkability attacks more likely to succeed. [TROVE-2024-004]. ([#1400]) [#1400]: https://gitlab.torproject.org/tpo/core/arti/-/issues/1400 [#1409]: https://gitlab.torproject.org/tpo/core/arti/-/issues/1409 [TROVE-2024-003]: https://gitlab.torproject.org/tpo/core/team/-/wikis/NetworkTeam/TROVE [TROVE-2024-004]: https://gitlab.torproject.org/tpo/core/team/-/wikis/NetworkTeam/TROVE # Arti 1.2.2 — 30 April 2024 Arti 1.2.2 continues improvements on previous releases, Loading Cargo.lock +2 −2 Original line number Diff line number Diff line Loading @@ -195,7 +195,7 @@ checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "arti" version = "1.2.2" version = "1.2.3" dependencies = [ "anyhow", "arti-client", Loading Loading @@ -5046,7 +5046,7 @@ dependencies = [ [[package]] name = "tor-circmgr" version = "0.18.0" version = "0.18.1" dependencies = [ "amplify", "async-trait", Loading crates/arti/Cargo.toml +1 −1 Original line number Diff line number Diff line [package] name = "arti" version = "1.2.2" version = "1.2.3" authors = ["The Tor Project, Inc.", "Nick Mathewson <nickm@torproject.org>"] edition = "2021" rust-version = "1.70" Loading crates/tor-circmgr/Cargo.toml +1 −1 Original line number Diff line number Diff line [package] name = "tor-circmgr" version = "0.18.0" version = "0.18.1" authors = ["The Tor Project, Inc.", "Nick Mathewson <nickm@torproject.org>"] edition = "2021" rust-version = "1.70" Loading crates/tor-circmgr/src/hspool.rs +57 −10 Original line number Diff line number Diff line Loading @@ -15,10 +15,10 @@ use futures::{task::SpawnExt, StreamExt, TryFutureExt}; use once_cell::sync::OnceCell; use tor_error::debug_report; use tor_error::{bad_api_usage, internal}; use tor_linkspec::{CircTarget, OwnedCircTarget}; use tor_linkspec::{CircTarget, HasRelayIds as _, OwnedCircTarget, RelayIdSet}; use tor_netdir::{NetDir, NetDirProvider, Relay}; use tor_proto::circuit::{self, ClientCirc}; use tor_relay_selection::{LowLevelRelayPredicate, RelayExclusion}; use tor_proto::circuit::{self, CircParameters, ClientCirc}; use tor_relay_selection::{LowLevelRelayPredicate, RelayExclusion, RelaySelector, RelayUsage}; use tor_rtcompat::{ scheduler::{TaskHandle, TaskSchedule}, Runtime, SleepProviderExt, Loading Loading @@ -304,6 +304,20 @@ impl<R: Runtime> HsCircPool<R> { .into()); } let params = crate::DirInfo::from(netdir).circ_params(); self.extend_circ(circ, params, target).await } /// Try to extend a circuit to the specified target hop. async fn extend_circ<T>( &self, circ: HsCircStub, params: CircParameters, target: T, ) -> Result<Arc<ClientCirc>> where T: CircTarget, { // Estimate how long it will take to extend it one more hop, and // construct a timeout as appropriate. let n_hops = circ.n_hops(); Loading @@ -315,7 +329,6 @@ impl<R: Runtime> HsCircPool<R> { ); // Make a future to extend the circuit. let params = crate::DirInfo::from(netdir).circ_params(); let extend_future = circ .extend_ntor(&target, ¶ms) .map_err(|error| Error::Protocol { Loading Loading @@ -425,7 +438,7 @@ impl<R: Runtime> HsCircPool<R> { }; // Return the circuit we found before, if any. if let Some(circuit) = found_usable_circ { return self.maybe_extend_stub_circuit(circuit, kind); return self.maybe_extend_stub_circuit(netdir, circuit, kind).await; } // TODO: There is a possible optimization here. Instead of only waiting Loading @@ -443,9 +456,10 @@ impl<R: Runtime> HsCircPool<R> { } /// Return a circuit of the specified `kind`, built from `circuit`. fn maybe_extend_stub_circuit( async fn maybe_extend_stub_circuit( &self, mut circuit: HsCircStub, netdir: &NetDir, circuit: HsCircStub, kind: HsCircStubKind, ) -> Result<HsCircStub> { if !self.vanguards_enabled() { Loading @@ -454,16 +468,49 @@ impl<R: Runtime> HsCircPool<R> { match (circuit.kind, kind) { (HsCircStubKind::Stub, HsCircStubKind::Extended) => { // TODO HS-VANGUARDS: if full vanguards are enabled and the circuit we got is STUB, debug!("Wanted STUB+ circuit, but got STUB; extending by 1 hop..."); let params = CircParameters::default(); let usage = RelayUsage::middle_relay(Some(&RelayUsage::middle_relay(None))); let circ_path = circuit.circ.path_ref(); // A STUB circuit is a 3-hop circuit. debug_assert_eq!(circ_path.hops().len(), 3); // Like in VanguardHsPathBuilder::pick_path, we only want to exclude the L2 and L3 // guards (so we skip over the guard) let skip_n = 1; let mut exclude_ids = RelayIdSet::new(); for hop in circ_path .iter() .skip(skip_n) .flat_map(|hop| hop.as_chan_target()) { exclude_ids.extend(hop.identities().map(|id| id.to_owned())); } let exclusion = RelayExclusion::exclude_identities(exclude_ids); let selector = RelaySelector::new(usage, exclusion); let target = { let mut rng = rand::thread_rng(); let (relay, info) = selector.select_relay(&mut rng, netdir); relay.ok_or_else(|| Error::NoRelay { path_kind: "vanguard STUB+", role: "final hop", problem: info.to_string(), })? }; // If full vanguards are enabled and the circuit we got is STUB, // we need to extend it by another hop to make it STUB+ before returning it circuit.kind = kind; let circ = self.extend_circ(circuit, params, target).await?; Ok(circuit) Ok(HsCircStub { circ, kind }) } (HsCircStubKind::Extended, HsCircStubKind::Stub) => { Err(internal!("wanted a STUB circuit, but got STUB+?!").into()) } _ => { trace!("Wanted {kind} circuit, got {}", circuit.kind); // Nothing to do: the circuit stub we got is of the kind we wanted Ok(circuit) } Loading Loading
CHANGELOG.md +40 −0 Original line number Diff line number Diff line Loading @@ -3,6 +3,46 @@ This file describes changes in Arti through the current release. Once Arti is more mature, we may switch to using a separate changelog for each crate. # Arti 1.2.3 — 15 May 2024 Arti 1.2.3 fixes a high-severity issue affecting onion services and clients connecting to onion services with 'lite' vanguards (the default) enabled: when building anonymizing circuits to or from an onion service the circuit manager code would build the circuits with one hop too few. This makes users of this code more vulnerable to some kinds of traffic analysis when they run or visit onion services. This release also fixes a medium-severity issue affecting 'full' vanguards. With 'full' vanguards enabled, client HsDir circuits, client introduction circuits and service rendezvous-circuits are extended with an extra hop to minimize the linkability of the guard nodes. In some circumstances, the circuit manager would build circuits with one hop too few, making it easier for an adversary to discover the L2 and L3 guards of the affected clients and services. In Arti 1.2.1 and earlier, vanguards were still an experimental feature, or absent, so those versions are classified as "not affected", even though downgrading does not fix the security problem. ### Major bugfixes - Fix a high-severity issue affecting onion service circuits using 'lite' vanguards. Previously, with 'lite' vanguards enabled, any circuit to or from an onion service was one hop too short, making clients and services vulnerable to certain types of traffic analysis. This is also tracked as [TROVE-2024-003]. ([#1409]) - Fix a medium-severity issue affecting onion service circuits using 'full' vanguards. Previously, with 'full' vanguards enabled, *some* circuits to or from an onion service were one hop too short, making linkability attacks more likely to succeed. [TROVE-2024-004]. ([#1400]) [#1400]: https://gitlab.torproject.org/tpo/core/arti/-/issues/1400 [#1409]: https://gitlab.torproject.org/tpo/core/arti/-/issues/1409 [TROVE-2024-003]: https://gitlab.torproject.org/tpo/core/team/-/wikis/NetworkTeam/TROVE [TROVE-2024-004]: https://gitlab.torproject.org/tpo/core/team/-/wikis/NetworkTeam/TROVE # Arti 1.2.2 — 30 April 2024 Arti 1.2.2 continues improvements on previous releases, Loading
Cargo.lock +2 −2 Original line number Diff line number Diff line Loading @@ -195,7 +195,7 @@ checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "arti" version = "1.2.2" version = "1.2.3" dependencies = [ "anyhow", "arti-client", Loading Loading @@ -5046,7 +5046,7 @@ dependencies = [ [[package]] name = "tor-circmgr" version = "0.18.0" version = "0.18.1" dependencies = [ "amplify", "async-trait", Loading
crates/arti/Cargo.toml +1 −1 Original line number Diff line number Diff line [package] name = "arti" version = "1.2.2" version = "1.2.3" authors = ["The Tor Project, Inc.", "Nick Mathewson <nickm@torproject.org>"] edition = "2021" rust-version = "1.70" Loading
crates/tor-circmgr/Cargo.toml +1 −1 Original line number Diff line number Diff line [package] name = "tor-circmgr" version = "0.18.0" version = "0.18.1" authors = ["The Tor Project, Inc.", "Nick Mathewson <nickm@torproject.org>"] edition = "2021" rust-version = "1.70" Loading
crates/tor-circmgr/src/hspool.rs +57 −10 Original line number Diff line number Diff line Loading @@ -15,10 +15,10 @@ use futures::{task::SpawnExt, StreamExt, TryFutureExt}; use once_cell::sync::OnceCell; use tor_error::debug_report; use tor_error::{bad_api_usage, internal}; use tor_linkspec::{CircTarget, OwnedCircTarget}; use tor_linkspec::{CircTarget, HasRelayIds as _, OwnedCircTarget, RelayIdSet}; use tor_netdir::{NetDir, NetDirProvider, Relay}; use tor_proto::circuit::{self, ClientCirc}; use tor_relay_selection::{LowLevelRelayPredicate, RelayExclusion}; use tor_proto::circuit::{self, CircParameters, ClientCirc}; use tor_relay_selection::{LowLevelRelayPredicate, RelayExclusion, RelaySelector, RelayUsage}; use tor_rtcompat::{ scheduler::{TaskHandle, TaskSchedule}, Runtime, SleepProviderExt, Loading Loading @@ -304,6 +304,20 @@ impl<R: Runtime> HsCircPool<R> { .into()); } let params = crate::DirInfo::from(netdir).circ_params(); self.extend_circ(circ, params, target).await } /// Try to extend a circuit to the specified target hop. async fn extend_circ<T>( &self, circ: HsCircStub, params: CircParameters, target: T, ) -> Result<Arc<ClientCirc>> where T: CircTarget, { // Estimate how long it will take to extend it one more hop, and // construct a timeout as appropriate. let n_hops = circ.n_hops(); Loading @@ -315,7 +329,6 @@ impl<R: Runtime> HsCircPool<R> { ); // Make a future to extend the circuit. let params = crate::DirInfo::from(netdir).circ_params(); let extend_future = circ .extend_ntor(&target, ¶ms) .map_err(|error| Error::Protocol { Loading Loading @@ -425,7 +438,7 @@ impl<R: Runtime> HsCircPool<R> { }; // Return the circuit we found before, if any. if let Some(circuit) = found_usable_circ { return self.maybe_extend_stub_circuit(circuit, kind); return self.maybe_extend_stub_circuit(netdir, circuit, kind).await; } // TODO: There is a possible optimization here. Instead of only waiting Loading @@ -443,9 +456,10 @@ impl<R: Runtime> HsCircPool<R> { } /// Return a circuit of the specified `kind`, built from `circuit`. fn maybe_extend_stub_circuit( async fn maybe_extend_stub_circuit( &self, mut circuit: HsCircStub, netdir: &NetDir, circuit: HsCircStub, kind: HsCircStubKind, ) -> Result<HsCircStub> { if !self.vanguards_enabled() { Loading @@ -454,16 +468,49 @@ impl<R: Runtime> HsCircPool<R> { match (circuit.kind, kind) { (HsCircStubKind::Stub, HsCircStubKind::Extended) => { // TODO HS-VANGUARDS: if full vanguards are enabled and the circuit we got is STUB, debug!("Wanted STUB+ circuit, but got STUB; extending by 1 hop..."); let params = CircParameters::default(); let usage = RelayUsage::middle_relay(Some(&RelayUsage::middle_relay(None))); let circ_path = circuit.circ.path_ref(); // A STUB circuit is a 3-hop circuit. debug_assert_eq!(circ_path.hops().len(), 3); // Like in VanguardHsPathBuilder::pick_path, we only want to exclude the L2 and L3 // guards (so we skip over the guard) let skip_n = 1; let mut exclude_ids = RelayIdSet::new(); for hop in circ_path .iter() .skip(skip_n) .flat_map(|hop| hop.as_chan_target()) { exclude_ids.extend(hop.identities().map(|id| id.to_owned())); } let exclusion = RelayExclusion::exclude_identities(exclude_ids); let selector = RelaySelector::new(usage, exclusion); let target = { let mut rng = rand::thread_rng(); let (relay, info) = selector.select_relay(&mut rng, netdir); relay.ok_or_else(|| Error::NoRelay { path_kind: "vanguard STUB+", role: "final hop", problem: info.to_string(), })? }; // If full vanguards are enabled and the circuit we got is STUB, // we need to extend it by another hop to make it STUB+ before returning it circuit.kind = kind; let circ = self.extend_circ(circuit, params, target).await?; Ok(circuit) Ok(HsCircStub { circ, kind }) } (HsCircStubKind::Extended, HsCircStubKind::Stub) => { Err(internal!("wanted a STUB circuit, but got STUB+?!").into()) } _ => { trace!("Wanted {kind} circuit, got {}", circuit.kind); // Nothing to do: the circuit stub we got is of the kind we wanted Ok(circuit) } Loading