Loading crates/arti/Cargo.toml +1 −1 Viewed Changes for crates/arti/Cargo.toml: 1 added line, 1 removed line. 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/src/path/hspath.rs +40 −11 Viewed Changes for crates/tor-circmgr/src/path/hspath.rs: 40 added lines, 11 removed lines. Original line number Diff line number Diff line Loading @@ -43,9 +43,12 @@ // TODO (#1339): we should be consistent with our terminology. use rand::Rng; use tor_error::internal; use tor_linkspec::OwnedChanTarget; use tor_netdir::{NetDir, Relay}; use tor_relay_selection::{RelayExclusion, RelaySelectionConfig, RelaySelector, RelayUsage}; use tor_relay_selection::{ RelayExclusion, RelaySelectionConfig, RelaySelector, RelayUsage, SelectionInfo, }; use crate::{hspool::HsCircStubKind, Error, Result}; Loading Loading @@ -222,13 +225,20 @@ impl VanguardHsPathBuilder { // * the L2 vanguard, because it cannot be selected again as an L3 vanguard // (a relay won't let you extend the circuit to itself). // * the guard, because relays won't let you extend the circuit to their previous hop let neighbor_exclusion = exclude_identities(&[&l2_guard, &l1_guard]); let l1_l2_exclusion = exclude_identities(&[&l2_guard, &l1_guard]); let mut hops = vec![l1_guard, l2_guard.clone()]; let mode = vanguards.mode(); let extra_hop_err = |info: SelectionInfo| Error::NoRelay { path_kind: self.path_kind(), role: "extra hop", problem: info.to_string(), }; // If needed, select an L3 vanguard too if vanguards.mode() == VanguardMode::Full { if mode == VanguardMode::Full { let l3_guard: MaybeOwnedRelay = vanguards .select_vanguard(rng, netdir, Layer::Layer3, &neighbor_exclusion)? .select_vanguard(rng, netdir, Layer::Layer3, &l1_l2_exclusion)? .into(); hops.push(l3_guard.clone()); Loading @@ -239,22 +249,41 @@ impl VanguardHsPathBuilder { // TODO: this usage has need_stable = true, but we probably // don't necessarily need a stable relay here. let usage = RelayUsage::middle_relay(None); let neighbor_exclusion = exclude_identities(&[&l2_guard, &l3_guard]); let l2_l3_exclusion = exclude_identities(&[&l2_guard, &l3_guard]); // We exclude // * the L3 vanguard, because it cannot be selected again as the following // extra hop (a relay won't let you extend the circuit to itself). // * the L2 vanguard, because relays won't let you extend the circuit to their previous hop let selector = RelaySelector::new(usage, neighbor_exclusion); let selector = RelaySelector::new(usage, l2_l3_exclusion); let (extra_hop, info) = selector.select_relay(rng, netdir); let extra_hop = extra_hop.ok_or_else(|| Error::NoRelay { path_kind: self.path_kind(), role: "extra hop", problem: info.to_string(), })?; let extra_hop = extra_hop.ok_or_else(|| extra_hop_err(info))?; hops.push(MaybeOwnedRelay::from(extra_hop)); } } else { // Extend the circuit to a third, arbitrarily chosen hop, excluding the L1 and L2 // guards as before. let usage = RelayUsage::middle_relay(None); let selector = RelaySelector::new(usage, l1_l2_exclusion); let (extra_hop, info) = selector.select_relay(rng, netdir); let extra_hop = extra_hop.ok_or_else(|| extra_hop_err(info))?; hops.push(MaybeOwnedRelay::from(extra_hop)); } match (mode, self.0) { (VanguardMode::Lite, _) => debug_assert_eq!(hops.len(), 3), (VanguardMode::Full, HsCircStubKind::Stub) => debug_assert_eq!(hops.len(), 3), (VanguardMode::Full, HsCircStubKind::Extended) => debug_assert_eq!(hops.len(), 4), (VanguardMode::Disabled, _) => { return Err(internal!( "Called VanguardHsPathBuilder::pick_path(), but vanguards are disabled?!" ) .into()); } (_, _) => { return Err(internal!("Unsupported vanguard mode {mode}").into()); } } Ok((TorPath::new_multihop_from_maybe_owned(hops), mon, usable)) Loading crates/tor-circmgr/src/hspool.rs +57 −10 Viewed Changes for crates/tor-circmgr/src/hspool.rs: 57 added lines, 10 removed lines. 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 crates/tor-circmgr/Cargo.toml +1 −1 Viewed Changes for crates/tor-circmgr/Cargo.toml: 1 added line, 1 removed line. 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 CHANGELOG.md +40 −0 Viewed Changes for CHANGELOG.md: 40 added lines, 0 removed lines. 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 Loading
crates/arti/Cargo.toml +1 −1 Viewed Changes for crates/arti/Cargo.toml: 1 added line, 1 removed line. 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/src/path/hspath.rs +40 −11 Viewed Changes for crates/tor-circmgr/src/path/hspath.rs: 40 added lines, 11 removed lines. Original line number Diff line number Diff line Loading @@ -43,9 +43,12 @@ // TODO (#1339): we should be consistent with our terminology. use rand::Rng; use tor_error::internal; use tor_linkspec::OwnedChanTarget; use tor_netdir::{NetDir, Relay}; use tor_relay_selection::{RelayExclusion, RelaySelectionConfig, RelaySelector, RelayUsage}; use tor_relay_selection::{ RelayExclusion, RelaySelectionConfig, RelaySelector, RelayUsage, SelectionInfo, }; use crate::{hspool::HsCircStubKind, Error, Result}; Loading Loading @@ -222,13 +225,20 @@ impl VanguardHsPathBuilder { // * the L2 vanguard, because it cannot be selected again as an L3 vanguard // (a relay won't let you extend the circuit to itself). // * the guard, because relays won't let you extend the circuit to their previous hop let neighbor_exclusion = exclude_identities(&[&l2_guard, &l1_guard]); let l1_l2_exclusion = exclude_identities(&[&l2_guard, &l1_guard]); let mut hops = vec![l1_guard, l2_guard.clone()]; let mode = vanguards.mode(); let extra_hop_err = |info: SelectionInfo| Error::NoRelay { path_kind: self.path_kind(), role: "extra hop", problem: info.to_string(), }; // If needed, select an L3 vanguard too if vanguards.mode() == VanguardMode::Full { if mode == VanguardMode::Full { let l3_guard: MaybeOwnedRelay = vanguards .select_vanguard(rng, netdir, Layer::Layer3, &neighbor_exclusion)? .select_vanguard(rng, netdir, Layer::Layer3, &l1_l2_exclusion)? .into(); hops.push(l3_guard.clone()); Loading @@ -239,22 +249,41 @@ impl VanguardHsPathBuilder { // TODO: this usage has need_stable = true, but we probably // don't necessarily need a stable relay here. let usage = RelayUsage::middle_relay(None); let neighbor_exclusion = exclude_identities(&[&l2_guard, &l3_guard]); let l2_l3_exclusion = exclude_identities(&[&l2_guard, &l3_guard]); // We exclude // * the L3 vanguard, because it cannot be selected again as the following // extra hop (a relay won't let you extend the circuit to itself). // * the L2 vanguard, because relays won't let you extend the circuit to their previous hop let selector = RelaySelector::new(usage, neighbor_exclusion); let selector = RelaySelector::new(usage, l2_l3_exclusion); let (extra_hop, info) = selector.select_relay(rng, netdir); let extra_hop = extra_hop.ok_or_else(|| Error::NoRelay { path_kind: self.path_kind(), role: "extra hop", problem: info.to_string(), })?; let extra_hop = extra_hop.ok_or_else(|| extra_hop_err(info))?; hops.push(MaybeOwnedRelay::from(extra_hop)); } } else { // Extend the circuit to a third, arbitrarily chosen hop, excluding the L1 and L2 // guards as before. let usage = RelayUsage::middle_relay(None); let selector = RelaySelector::new(usage, l1_l2_exclusion); let (extra_hop, info) = selector.select_relay(rng, netdir); let extra_hop = extra_hop.ok_or_else(|| extra_hop_err(info))?; hops.push(MaybeOwnedRelay::from(extra_hop)); } match (mode, self.0) { (VanguardMode::Lite, _) => debug_assert_eq!(hops.len(), 3), (VanguardMode::Full, HsCircStubKind::Stub) => debug_assert_eq!(hops.len(), 3), (VanguardMode::Full, HsCircStubKind::Extended) => debug_assert_eq!(hops.len(), 4), (VanguardMode::Disabled, _) => { return Err(internal!( "Called VanguardHsPathBuilder::pick_path(), but vanguards are disabled?!" ) .into()); } (_, _) => { return Err(internal!("Unsupported vanguard mode {mode}").into()); } } Ok((TorPath::new_multihop_from_maybe_owned(hops), mon, usable)) Loading
crates/tor-circmgr/src/hspool.rs +57 −10 Viewed Changes for crates/tor-circmgr/src/hspool.rs: 57 added lines, 10 removed lines. 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
crates/tor-circmgr/Cargo.toml +1 −1 Viewed Changes for crates/tor-circmgr/Cargo.toml: 1 added line, 1 removed line. 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
CHANGELOG.md +40 −0 Viewed Changes for CHANGELOG.md: 40 added lines, 0 removed lines. 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