Adjust onion service ring APIs in light of prop#342
* It is the NetDir's responsibility to tell the caller what the time period is. * There can be up to two secondary time periods. * Each time period has a single SRV. * Secondary time periods only apply for onion services, when they publish. * When publishing, the correct input is a time period.
This commit is contained in:
parent
f24f8d295c
commit
5b74ef7af9
|
@ -14,14 +14,16 @@ repository = "https://gitlab.torproject.org/tpo/core/arti.git/"
|
|||
[features]
|
||||
default = []
|
||||
|
||||
experimental = ["experimental-api"]
|
||||
experimental = ["experimental-api", "onion-service", "onion-client"]
|
||||
|
||||
# Enable experimental APIs that are not yet officially supported.
|
||||
#
|
||||
# These APIs are not covered by semantic versioning. Using this
|
||||
# feature voids your "semver warrantee".
|
||||
experimental-api = []
|
||||
hs-common = ["tor-hscrypto"]
|
||||
onion-client = ["onion-common"]
|
||||
onion-service = ["onion-common"]
|
||||
onion-common = ["tor-hscrypto"]
|
||||
|
||||
# Enable testing-only APIs. APIs under this feature are not
|
||||
# covered by semver.
|
||||
|
|
|
@ -32,3 +32,26 @@ impl HasKind for Error {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An error returned when looking up onion service directories.
|
||||
#[derive(Error, Clone, Debug)]
|
||||
#[cfg(feature = "onion-common")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "onion-common")))]
|
||||
#[non_exhaustive]
|
||||
pub enum OnionDirLookupError {
|
||||
/// We tried to look up an onion service directory for a time period that
|
||||
/// did not correspond to one of our hash rings.
|
||||
#[error("Tried to look up an onion service directory for an invalid time period.")]
|
||||
WrongTimePeriod,
|
||||
}
|
||||
|
||||
#[cfg(feature = "onion-common")]
|
||||
impl HasKind for OnionDirLookupError {
|
||||
fn kind(&self) -> tor_error::ErrorKind {
|
||||
use tor_error::ErrorKind as EK;
|
||||
use OnionDirLookupError as E;
|
||||
match self {
|
||||
E::WrongTimePeriod => EK::BadApiUsage,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ pub(crate) struct HsDirRing {
|
|||
/// The time period for which the ring is valid.
|
||||
period: TimePeriod,
|
||||
|
||||
/// The shared random value for which the ring is valid.
|
||||
/// The shared random value that applies to the ring.
|
||||
shared_rand: SharedRandVal,
|
||||
|
||||
/// The ring itself.
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
|
||||
|
||||
mod err;
|
||||
#[cfg(feature = "hs-common")]
|
||||
#[cfg(feature = "onion-common")]
|
||||
mod hsdir_ring;
|
||||
pub mod params;
|
||||
mod weight;
|
||||
|
@ -48,7 +48,7 @@ pub mod testnet;
|
|||
#[cfg(feature = "testing")]
|
||||
pub mod testprovider;
|
||||
|
||||
#[cfg(feature = "hs-common")]
|
||||
#[cfg(feature = "onion-common")]
|
||||
use hsdir_ring::HsDirRing;
|
||||
use static_assertions::const_assert;
|
||||
use tor_linkspec::{
|
||||
|
@ -70,14 +70,17 @@ use std::sync::Arc;
|
|||
use strum::{EnumCount, EnumIter};
|
||||
use tracing::warn;
|
||||
|
||||
#[cfg(feature = "hs-common")]
|
||||
use {std::time::SystemTime, tor_hscrypto::pk::BlindedOnionId};
|
||||
#[cfg(feature = "onion-common")]
|
||||
use tor_hscrypto::{pk::BlindedOnionId, time::TimePeriod};
|
||||
|
||||
pub use err::Error;
|
||||
pub use weight::WeightRole;
|
||||
/// A Result using the Error type from the tor-netdir crate
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
#[cfg(feature = "onion-common")]
|
||||
pub use err::OnionDirLookupError;
|
||||
|
||||
use params::NetParameters;
|
||||
|
||||
/// Configuration for determining when two relays have addresses "too close" in
|
||||
|
@ -214,10 +217,8 @@ impl From<u64> for RelayWeight {
|
|||
#[derive(Copy, Clone, Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum OnionServiceDirOp {
|
||||
/// Uploading an onion service descriptor to the current ring.
|
||||
/// Uploading an onion service descriptor.
|
||||
Upload,
|
||||
/// Uploading an onion service descriptor to the previous ring.
|
||||
UploadPrevious,
|
||||
/// Downloading an onion service descriptor.
|
||||
Download,
|
||||
}
|
||||
|
@ -282,33 +283,49 @@ pub struct NetDir {
|
|||
///
|
||||
/// This is empty in a PartialNetDir, and is filled in before the NetDir is
|
||||
/// built.
|
||||
///
|
||||
/// It corresponds to the time period containing the `valid-after` time in
|
||||
/// the consensus. Its SRV is whatever SRV was most current at the time when
|
||||
/// that time period began.
|
||||
///
|
||||
/// This is the hash ring that we should use whenever we are fetching an
|
||||
/// onion service descriptor.
|
||||
//
|
||||
// TODO hs: It is ugly to have this be Option.
|
||||
#[cfg(feature = "hs-common")]
|
||||
#[cfg(feature = "onion-common")]
|
||||
#[allow(dead_code)]
|
||||
hsdir_ring: Option<HsDirRing>,
|
||||
|
||||
/// A hash ring describing the onion service directory based on the
|
||||
/// parameters for the previous (or next) time period.
|
||||
/// parameters for the previous and next time periods.
|
||||
///
|
||||
/// Onion services upload to positions in this ring as well, based on how
|
||||
/// Onion services upload to positions on these ring as well, based on how
|
||||
/// far into the current time period this directory is, so that
|
||||
/// not-synchronized clients can still find their descriptor.
|
||||
///
|
||||
/// This is None in a PartialNetDir, and None if this ring should not be
|
||||
/// used.
|
||||
/// Each of these rings is None in a PartialNetDir, and None if this ring
|
||||
/// should not be used.
|
||||
///
|
||||
/// Note that with the current (2023) network parameters, with
|
||||
/// `hsdir_interval = SRV lifetime = 24 hours` at most one of these
|
||||
/// secondary rings will be active at a time. We have two here in order
|
||||
/// to conform with a more flexible regime in proposal 342.
|
||||
//
|
||||
// TODO hs: It is ugly to have this be Option.
|
||||
// TODO hs: It is sort of ugly to have these be Option.
|
||||
//
|
||||
// TODO hs: hs clients never need this; maybe it could be optional? Or does
|
||||
// that risk too much?
|
||||
// TODO hs: hs clients never need this; so I've made it not-present for thm.
|
||||
// But does that risk too much with respect to side channels?
|
||||
//
|
||||
// TODO hs spec: Despite the confidence in the above comment, I am not sure
|
||||
// that I really get how this secondary ring works. I need to re-read that
|
||||
// part of the spec carefully; we might find a better way to implement this.
|
||||
#[cfg(feature = "hs-common")]
|
||||
// TODO hs: Perhaps we should refactor this so that there is just one
|
||||
// Vec<HsDirRing> (or SmallVec<>); or so that there are `current`, `next`,
|
||||
// and `previous`.
|
||||
//
|
||||
// TODO hs: Perhaps we should refactor this so that it is clear that these
|
||||
// are immutable? On the other hand, the documentation for this type
|
||||
// declares that it is immutable, so we are likely okay.
|
||||
#[cfg(feature = "onion-service")]
|
||||
#[allow(dead_code)]
|
||||
hsdir_secondary_ring: Option<HsDirRing>,
|
||||
hsdir_secondary_rings: (Option<HsDirRing>, Option<HsDirRing>),
|
||||
|
||||
/// Weight values to apply to a given relay when deciding how frequently
|
||||
/// to choose it for a given role.
|
||||
|
@ -545,10 +562,10 @@ impl PartialNetDir {
|
|||
rs_idx_by_missing,
|
||||
rs_idx_by_rsa: Arc::new(rs_idx_by_rsa),
|
||||
rs_idx_by_ed: HashMap::with_capacity(n_relays),
|
||||
#[cfg(feature = "hs-common")]
|
||||
#[cfg(feature = "onion-common")]
|
||||
hsdir_ring: None,
|
||||
#[cfg(feature = "hs-common")]
|
||||
hsdir_secondary_ring: None,
|
||||
#[cfg(feature = "onion-service")]
|
||||
hsdir_secondary_rings: (None, None),
|
||||
weights,
|
||||
};
|
||||
|
||||
|
@ -582,7 +599,7 @@ impl PartialNetDir {
|
|||
loaded
|
||||
}
|
||||
|
||||
/// Compute the hash ring for this NetDir, if one is not already computed.
|
||||
/// Compute the hash ring(s) for this NetDir, if one is not already computed.
|
||||
#[cfg(feature = "hs-common")]
|
||||
#[allow(clippy::missing_panics_doc)]
|
||||
pub fn compute_ring(&mut self) {
|
||||
|
@ -1080,25 +1097,44 @@ impl NetDir {
|
|||
})
|
||||
}
|
||||
|
||||
/// Return the current onion service directory "time period".
|
||||
///
|
||||
/// Specifically, this returns the time period that contains the beginning
|
||||
/// of the validity period of this `NetDir`'s consensus. That time period
|
||||
/// is the one we use when acting as an onion service client.
|
||||
#[cfg(feature = "onion-common")]
|
||||
#[allow(unused, clippy::missing_panics_doc)] // TODO hs: remove.
|
||||
pub fn onion_service_time_period(&self) -> TimePeriod {
|
||||
todo!() // TODO hs
|
||||
}
|
||||
|
||||
/// Return the secondary onion service directory "time periods".
|
||||
///
|
||||
/// These are additional time periods that we publish descriptors for when we are
|
||||
/// acting as an onion service.
|
||||
#[cfg(feature = "onion-service")]
|
||||
#[allow(unused, clippy::missing_panics_doc)] // TODO hs: remove.
|
||||
pub fn onion_service_secondary_time_periods(&self) -> Vec<TimePeriod> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Return the relays in this network directory that will be used to store a
|
||||
/// given onion service's descriptor at a given time.
|
||||
#[cfg(feature = "hs-common")]
|
||||
/// given onion service's descriptor at a given time period.
|
||||
///
|
||||
/// Return an error if the time period is not one returned by
|
||||
/// `onion_service_time_period` or `onion_service_secondary_time_periods`.
|
||||
#[cfg(feature = "onion-common")]
|
||||
#[allow(unused, clippy::missing_panics_doc)] // TODO hs: remove.
|
||||
pub fn onion_service_dirs(
|
||||
&self,
|
||||
id: BlindedOnionId,
|
||||
op: OnionServiceDirOp,
|
||||
when: SystemTime,
|
||||
) -> Vec<Relay<'_>> {
|
||||
when: TimePeriod,
|
||||
) -> std::result::Result<Vec<Relay<'_>>, OnionDirLookupError> {
|
||||
// Algorithm:
|
||||
//
|
||||
// 1. Use the time period length from our parameters to determine what
|
||||
// TimePeriod contains `when`.
|
||||
// 1b. If fetching, use the current ring.
|
||||
// If uploading, determine whether to use the current or secondary ring
|
||||
// based on the time period.
|
||||
// 2. Use `when` to determine whether to use current or previous shared
|
||||
// random value.
|
||||
// 1. Determine which HsDirRing to use, based on the time period.
|
||||
// 2. Find the shared random value that's associated with that HsDirRing.
|
||||
// 3. Choose spread = the parameter `hsdir_spread_store` or
|
||||
// `hsdir_spread_fetch` based on `op`.
|
||||
// 4. Let n_replicas = the parameter `hsdir_n_replicas`.
|
||||
|
|
Loading…
Reference in New Issue