//! Functions and type for implementing the onion service directory ring. //! //! The onion service directory ring is an ordered ring of the all of relays in //! the consensus with the HsDir flag. The HSDirs change their position in this //! index every [`TimePeriod`], and every time that the shared random value in //! the consensus changes. (These events are typically synchronized, for //! reasonable network configurations.) //! //! Each onion service is also (semi-privately) associated with "N" positions on //! the ring based on its blinded ID and the current time period. When upload or //! downloading an onion service descriptor descriptor, we look at the ring at //! each of these positions, and consider the "S" relays that fall at that //! position or later. ("N" is a "number of replicas" parameter, and "S" is a //! "Spread" parameter.) #![allow(unused_variables, dead_code)] //TODO hs: remove use tor_hscrypto::{pk::BlindedOnionId, time::TimePeriod}; use tor_llcrypto::pk::ed25519::Ed25519Identity; use tor_netdoc::doc::netstatus::SharedRandVal; use crate::hsdir_params::HsRingParams; /// A sort key determining a position in the onion service directory ring. /// /// This is either the sort key of a given relay at a given time period, or the /// sort key for a probing position for a given onion service id at a given /// time. /// /// The specification calls this an "index" but `HsDirIndex` is a key-length /// sized, apparently-random, value, which determines the ordering of relays on /// the ring. It is not the position number (ie, not a dense index starting at /// 0). /// /// Note that this is _not_ an index into any array; it is instead an index into /// a space of possible values in a (virtual!) ring of 2^256 elements. #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] pub(crate) struct HsDirIndex([u8; 32]); /// A hash ring as used in `NetDir`. /// /// This type is immutable once constructed: entries cannot be added, changed, /// or removed. It can be interpreted only in the context of a given consensus /// document. #[derive(Clone, Debug)] pub(crate) struct HsDirRing { /// The time period for which the ring is valid. period: TimePeriod, /// The shared random value that applies to the ring. shared_rand: SharedRandVal, /// The ring itself. /// /// The first element of each tuple is a 32-byte hash representing a /// position on the ring; the second is the index for the corresponding /// relay within self.consensus.relays(). /// /// This vector is empty in a partial netdir; it is filled in when we /// convert to a complete netdir. ring: Vec<(HsDirIndex, usize)>, } /// Compute the [`HsDirIndex`] for a given relay. pub(crate) fn relay_index( id: Ed25519Identity, rand: SharedRandVal, period: TimePeriod, ) -> HsDirIndex { // TODO hs implement this. // // hsdir_index(node) = H("node-idx" | node_identity | // shared_random_value | // INT_8(period_num) | // INT_8(period_length) ) // // Note that INT_8 means "u64" and H is sha3-256. todo!() } /// Compute the starting [`HsDirIndex`] for a given descriptor replica. pub(crate) fn service_index( id: BlindedOnionId, replica: u8, rand: SharedRandVal, period: TimePeriod, ) -> HsDirIndex { // TODO hs implement this // // hs_index(replicanum) = H("store-at-idx" | // blinded_public_key | // INT_8(replicanum) | // INT_8(period_length) | // INT_8(period_num) ) // // Note that INT_8 means "u64" and H is sha3-256 todo!() } impl HsDirRing { /// Return a new empty HsDirRing from a given set of parameters. pub(crate) fn empty_from_params(params: &HsRingParams) -> Self { Self { period: params.time_period, shared_rand: params.shared_rand, ring: Vec::new(), } } /// Find the location or (notional) insertion point for `idx` within `ring`. fn find_pos(&self, idx: HsDirIndex) -> usize { // TODO hs implement this todo!() } /// Yield items from `ring` starting with `idx`, wrapping around once when we /// reach the end, and yielding no element more than once. pub(crate) fn ring_items_at( &self, idx: HsDirIndex, ) -> impl Iterator { let idx = self.find_pos(idx); self.ring[idx..].iter().chain(&self.ring[..idx]) } /// Return the time period for which this ring applies. pub(crate) fn time_period(&self) -> TimePeriod { self.period } }