TROVE-2024-003: Prepare release

This is the release branch for the fix for TROVE-2024-003 (#1409 (closed))

@Diziet @nickm I think this should be reviewed by both of you

Merge request reports

Loading
+1 −1
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"
+40 −11
Changes for crates/tor-circmgr/src/path/hspath.rs: 40 added lines, 11 removed lines.
Original line number Diff line number Diff line
@@ -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};

@@ -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());

@@ -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))
+57 −10
Changes for crates/tor-circmgr/src/hspool.rs: 57 added lines, 10 removed lines.
Original line number Diff line number Diff line
@@ -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,
@@ -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();
@@ -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, &params)
            .map_err(|error| Error::Protocol {
@@ -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
@@ -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() {
@@ -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)
            }
+1 −1
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"
+40 −0
Changes for CHANGELOG.md: 40 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -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