Merge branch 'onion-netdir-apis' into 'main'
Add onion service APIs to tor-netdir See merge request tpo/core/arti!966
This commit is contained in:
commit
7030cbe460
|
@ -3995,6 +3995,7 @@ dependencies = [
|
|||
"tor-checkable",
|
||||
"tor-config",
|
||||
"tor-error",
|
||||
"tor-hscrypto",
|
||||
"tor-linkspec",
|
||||
"tor-llcrypto",
|
||||
"tor-netdoc",
|
||||
|
|
|
@ -11,6 +11,7 @@ pub mod time;
|
|||
|
||||
/// The information that a client needs to know about an onion service in
|
||||
/// order to connect to it.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Credential {
|
||||
/// Representation for the onion service's public ID.
|
||||
///
|
||||
|
@ -26,6 +27,7 @@ pub struct Credential {
|
|||
/// the current time period.
|
||||
///
|
||||
/// Given this piece of information, the original credential cannot be re-derived.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Subcredential([u8; 32]);
|
||||
|
||||
/// Counts which revision of an onion service descriptor is which, within a
|
||||
|
@ -33,13 +35,10 @@ pub struct Subcredential([u8; 32]);
|
|||
///
|
||||
/// There can be gaps in this numbering. A descriptor with a higher-valued
|
||||
/// revision counter supersedes one with a lower revision counter.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct RevisionCounter(u64);
|
||||
|
||||
/// An opaque value used by an onion service
|
||||
// TODO hs: these values should only permit constant-time comparison.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct RendCookie([u8; 20]);
|
||||
|
||||
/// A position within the onion service directory hash ring.
|
||||
// TODO: these should move to tor-netdir, I think?
|
||||
pub struct HsRingIndex([u8; 32]);
|
||||
|
|
|
@ -23,6 +23,7 @@ use crate::time::TimePeriod;
|
|||
/// This is the decoded and validated ed25519 public key that is encoded as a
|
||||
/// `${base32}.onion` address. When expanded, it is a public key whose
|
||||
/// corresponding secret key is controlled by the onion service.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct OnionId([u8; 32]);
|
||||
|
||||
/// The identity of a v3 onion service, expanded into a public key.
|
||||
|
@ -38,6 +39,7 @@ pub struct OnionId([u8; 32]);
|
|||
//
|
||||
// NOTE: This is a separate type from OnionId because it is about 6x larger. It
|
||||
// is an expanded form, used for doing actual cryptography.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OnionIdKey(ed25519::PublicKey);
|
||||
|
||||
// TODO hs: implement TryFrom<OnionId> for OnionIdKey, and From<OnionIdKey> for OnionId.
|
||||
|
@ -59,9 +61,11 @@ impl OnionIdKey {
|
|||
///
|
||||
/// It is used for two purposes: first, to compute an index into the HSDir
|
||||
/// ring, and second, to sign a `DescSigningKey`.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BlindedOnionIdKey(ed25519::PublicKey);
|
||||
|
||||
/// A blinded onion service identity, repreesented in a compact format.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct BlindedOnionId([u8; 32]);
|
||||
|
||||
// TODO hs: implement TryFrom<BlindedOnionId> for BlinedOnionIdKey, and
|
||||
|
@ -80,6 +84,7 @@ pub struct BlindedOnionId([u8; 32]);
|
|||
/// Note: we use a separate signing key here, rather than using the
|
||||
/// BlidedOnionIdKey directly, so that the secret key for the BlindedOnionIdKey
|
||||
/// can be kept offline.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DescSigningKey(ed25519::PublicKey);
|
||||
|
||||
/// A key used to identify and authenticate an onion service at a single
|
||||
|
@ -89,6 +94,7 @@ pub struct DescSigningKey(ed25519::PublicKey);
|
|||
/// used at each introduction point. Introduction points don't know the
|
||||
/// relation of this key to the onion service: they only recognize the same key
|
||||
/// when they see it again.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct IntroPtAuthKey(ed25519::PublicKey);
|
||||
|
||||
/// A key used in the HsNtor handshake between the client and the onion service.
|
||||
|
@ -96,12 +102,14 @@ pub struct IntroPtAuthKey(ed25519::PublicKey);
|
|||
/// The onion service chooses a different one of these to use with each
|
||||
/// introduction point, though it does not need to tell the introduction points
|
||||
/// about these keys.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct IntroPtEncKey(curve25519::PublicKey);
|
||||
|
||||
/// First type of client authorization key, used for the introduction protocol.
|
||||
///
|
||||
/// This is used to sign a nonce included in an extension in the encrypted
|
||||
/// portion of an introduce cell.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ClientIntroAuthKey(ed25519::PublicKey);
|
||||
|
||||
/// Second type of client authorization key, used for onion descryptor
|
||||
|
@ -109,6 +117,7 @@ pub struct ClientIntroAuthKey(ed25519::PublicKey);
|
|||
///
|
||||
/// Any client who knows the secret key corresponding to this key can decrypt
|
||||
/// the inner layer of the onion service descriptor.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ClientDescAuthKey(curve25519::PublicKey);
|
||||
|
||||
// TODO hs: For each of the above key types, we should have a correspondingly
|
||||
|
|
|
@ -6,6 +6,7 @@ use std::time::{Duration, SystemTime};
|
|||
///
|
||||
/// These time periods are used to derive a different `BlindedOnionIdKey`
|
||||
/// during each period from each `OnionIdKey`.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct TimePeriod {
|
||||
/// Index of the time periods that have passed since the unix epoch.
|
||||
interval_num: u64,
|
||||
|
|
|
@ -14,13 +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 = []
|
||||
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.
|
||||
|
@ -42,6 +45,7 @@ thiserror = "1"
|
|||
tor-checkable = { path = "../tor-checkable", version = "0.4.0" }
|
||||
tor-config = { path = "../tor-config", version = "0.7.0" }
|
||||
tor-error = { path = "../tor-error", version = "0.4.0" }
|
||||
tor-hscrypto = { path = "../tor-hscrypto", version = "0.1.0", optional = true }
|
||||
tor-linkspec = { path = "../tor-linkspec", version = "0.6.0" }
|
||||
tor-llcrypto = { path = "../tor-llcrypto", version = "0.4.0" }
|
||||
tor-netdoc = { path = "../tor-netdoc", version = "0.6.0" }
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
//! 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;
|
||||
|
||||
/// 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 {
|
||||
/// 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<Item = &(HsDirIndex, usize)> {
|
||||
let idx = self.find_pos(idx);
|
||||
self.ring[idx..].iter().chain(&self.ring[..idx])
|
||||
}
|
||||
}
|
|
@ -38,6 +38,8 @@
|
|||
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
|
||||
|
||||
mod err;
|
||||
#[cfg(feature = "onion-common")]
|
||||
mod hsdir_ring;
|
||||
pub mod params;
|
||||
mod weight;
|
||||
|
||||
|
@ -46,6 +48,8 @@ pub mod testnet;
|
|||
#[cfg(feature = "testing")]
|
||||
pub mod testprovider;
|
||||
|
||||
#[cfg(feature = "onion-common")]
|
||||
use hsdir_ring::HsDirRing;
|
||||
use static_assertions::const_assert;
|
||||
use tor_linkspec::{
|
||||
ChanTarget, DirectChanMethodsHelper, HasAddrs, HasRelayIds, RelayIdRef, RelayIdType,
|
||||
|
@ -66,11 +70,17 @@ use std::sync::Arc;
|
|||
use strum::{EnumCount, EnumIter};
|
||||
use tracing::warn;
|
||||
|
||||
#[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
|
||||
|
@ -203,6 +213,16 @@ impl From<u64> for RelayWeight {
|
|||
}
|
||||
}
|
||||
|
||||
/// An operation for which we might be requesting an onion service directory.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum OnionServiceDirOp {
|
||||
/// Uploading an onion service descriptor.
|
||||
Upload,
|
||||
/// Downloading an onion service descriptor.
|
||||
Download,
|
||||
}
|
||||
|
||||
/// A view of the Tor directory, suitable for use in building circuits.
|
||||
///
|
||||
/// Abstractly, a [`NetDir`] is a set of usable public [`Relay`]s, each of which
|
||||
|
@ -259,6 +279,54 @@ pub struct NetDir {
|
|||
/// can be immutable.
|
||||
rs_idx_by_rsa: Arc<HashMap<RsaIdentity, usize>>,
|
||||
|
||||
/// A hash ring describing the onion service directory.
|
||||
///
|
||||
/// 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 = "onion-common")]
|
||||
#[allow(dead_code)]
|
||||
hsdir_ring: Option<HsDirRing>,
|
||||
|
||||
/// A hash ring describing the onion service directory based on the
|
||||
/// parameters for the previous and next time periods.
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// 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 sort of ugly to have these be Option.
|
||||
//
|
||||
// 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: 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_rings: (Option<HsDirRing>, Option<HsDirRing>),
|
||||
|
||||
/// Weight values to apply to a given relay when deciding how frequently
|
||||
/// to choose it for a given role.
|
||||
weights: weight::WeightSet,
|
||||
|
@ -494,6 +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 = "onion-common")]
|
||||
hsdir_ring: None,
|
||||
#[cfg(feature = "onion-service")]
|
||||
hsdir_secondary_rings: (None, None),
|
||||
weights,
|
||||
};
|
||||
|
||||
|
@ -514,9 +586,32 @@ impl PartialNetDir {
|
|||
loaded.push(md.digest());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "hs-common")]
|
||||
{
|
||||
// TODO hs: cache the values for the hash rings if possible, maybe
|
||||
// in the PartialNetDir, since we will want to use them in computing
|
||||
// hash indices for the new hash ring. This can let us save some
|
||||
// computation? Alternatively, we could compute the new rings at
|
||||
// this point, but that could make this operation a bit expensive.
|
||||
}
|
||||
|
||||
loaded
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
// TODO hs: compute the ring based on the time period and shared random
|
||||
// value of the consensus.
|
||||
//
|
||||
// The ring itself can be a bit expensive to compute, so maybe we should
|
||||
// make sure this happens in a separate task or something, and expose a
|
||||
// way to do that?
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Return true if this are enough information in this directory
|
||||
/// to build multihop paths.
|
||||
pub fn have_enough_paths(&self) -> bool {
|
||||
|
@ -526,6 +621,7 @@ impl PartialNetDir {
|
|||
/// circuits, return it.
|
||||
pub fn unwrap_if_sufficient(self) -> std::result::Result<NetDir, PartialNetDir> {
|
||||
if self.netdir.have_enough_paths() {
|
||||
// self.compute_ring(); // TODO hs
|
||||
Ok(self.netdir)
|
||||
} else {
|
||||
Err(self)
|
||||
|
@ -1000,6 +1096,60 @@ impl NetDir {
|
|||
.filter(|other_relay| other_relay.md.family().contains(relay_rsa_id))
|
||||
})
|
||||
}
|
||||
|
||||
/// 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 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: TimePeriod,
|
||||
) -> std::result::Result<Vec<Relay<'_>>, OnionDirLookupError> {
|
||||
// Algorithm:
|
||||
//
|
||||
// 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`.
|
||||
// 5. Initialize Dirs = []
|
||||
// 6. for idx in 0..n_replicas:
|
||||
// - let H = hsdir_ring::onion_service_index(id, replica, rand,
|
||||
// period).
|
||||
// - Find the position of H within hsdir_ring.
|
||||
// - Take elements from hsdir_ring starting at that position,
|
||||
// adding them to Dirs until we have added `spread` new elements
|
||||
// that were not there before.
|
||||
// 7. return Dirs.
|
||||
|
||||
todo!() // TODO hs
|
||||
}
|
||||
}
|
||||
|
||||
impl MdReceiver for NetDir {
|
||||
|
|
|
@ -292,10 +292,13 @@ pub struct SignatureGroup {
|
|||
signatures: Vec<Signature>,
|
||||
}
|
||||
|
||||
// TODO hs: Lower this type to tor-llcrypto: It is relied upon by various crypto
|
||||
// things in onion services, and may later be used elsewhere too.
|
||||
//
|
||||
/// A shared-random value produced by the directory authorities.
|
||||
/// A shared random value produced by the directory authorities.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
// TODO: needs accessors.
|
||||
pub struct SharedRandVal([u8; 32]);
|
||||
|
||||
/// A shared-random value produced by the directory authorities,
|
||||
/// along with meta-information about that value.
|
||||
#[allow(dead_code)]
|
||||
// TODO hs: This should have real accessors, not this 'visible/visibility' hack.
|
||||
#[cfg_attr(
|
||||
|
@ -305,7 +308,7 @@ pub struct SignatureGroup {
|
|||
non_exhaustive
|
||||
)]
|
||||
#[derive(Debug, Clone)]
|
||||
struct SharedRandVal {
|
||||
struct SharedRandStatus {
|
||||
/// How many authorities revealed shares that contributed to this value.
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "dangerous-expose-struct-fields")))]
|
||||
n_reveals: u8,
|
||||
|
@ -315,10 +318,8 @@ struct SharedRandVal {
|
|||
/// that this value isn't predictable before it first becomes
|
||||
/// live, and that a hostile party could not have forced it to
|
||||
/// have any more than a small number of possible random values.
|
||||
//
|
||||
// TODO hs-client: This should become [u8; 32] if we get approval to nail it down in the spec.
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "dangerous-expose-struct-fields")))]
|
||||
value: Vec<u8>,
|
||||
value: SharedRandVal,
|
||||
}
|
||||
|
||||
/// Parts of the networkstatus header that are present in every networkstatus.
|
||||
|
@ -385,10 +386,10 @@ struct ConsensusHeader {
|
|||
consensus_method: u32,
|
||||
/// Global shared-random value for the previous shared-random period.
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "dangerous-expose-struct-fields")))]
|
||||
shared_rand_prev: Option<SharedRandVal>,
|
||||
shared_rand_prev: Option<SharedRandStatus>,
|
||||
/// Global shared-random value for the current shared-random period.
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "dangerous-expose-struct-fields")))]
|
||||
shared_rand_cur: Option<SharedRandVal>,
|
||||
shared_rand_cur: Option<SharedRandStatus>,
|
||||
}
|
||||
|
||||
/// Description of an authority's identity and address.
|
||||
|
@ -985,7 +986,7 @@ impl CommonHeader {
|
|||
}
|
||||
}
|
||||
|
||||
impl SharedRandVal {
|
||||
impl SharedRandStatus {
|
||||
/// Parse a current or previous shared rand value from a given
|
||||
/// SharedRandPreviousValue or SharedRandCurrentValue.
|
||||
fn from_item(item: &Item<'_, NetstatusKwd>) -> Result<Self> {
|
||||
|
@ -1001,8 +1002,8 @@ impl SharedRandVal {
|
|||
}
|
||||
let n_reveals: u8 = item.parse_arg(0)?;
|
||||
let val: B64 = item.parse_arg(1)?;
|
||||
let value = val.into();
|
||||
Ok(SharedRandVal { n_reveals, value })
|
||||
let value = SharedRandVal(val.into_array()?);
|
||||
Ok(SharedRandStatus { n_reveals, value })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1024,12 +1025,12 @@ impl ConsensusHeader {
|
|||
|
||||
let shared_rand_prev = sec
|
||||
.get(SHARED_RAND_PREVIOUS_VALUE)
|
||||
.map(SharedRandVal::from_item)
|
||||
.map(SharedRandStatus::from_item)
|
||||
.transpose()?;
|
||||
|
||||
let shared_rand_cur = sec
|
||||
.get(SHARED_RAND_CURRENT_VALUE)
|
||||
.map(SharedRandVal::from_item)
|
||||
.map(SharedRandStatus::from_item)
|
||||
.transpose()?;
|
||||
|
||||
Ok(ConsensusHeader {
|
||||
|
@ -1939,16 +1940,16 @@ mod test {
|
|||
let sr =
|
||||
gettok("shared-rand-previous-value 9 5LodY4yWxFhTKtxpV9wAgNA9N8flhUCH0NqQv1/05y4\n")
|
||||
.unwrap();
|
||||
let sr = SharedRandVal::from_item(&sr).unwrap();
|
||||
let sr = SharedRandStatus::from_item(&sr).unwrap();
|
||||
|
||||
assert_eq!(sr.n_reveals, 9);
|
||||
assert_eq!(
|
||||
sr.value,
|
||||
sr.value.0,
|
||||
hex!("e4ba1d638c96c458532adc6957dc0080d03d37c7e5854087d0da90bf5ff4e72e")
|
||||
);
|
||||
|
||||
let sr = gettok("foo bar\n").unwrap();
|
||||
let sr = SharedRandVal::from_item(&sr);
|
||||
let sr = SharedRandStatus::from_item(&sr);
|
||||
assert!(sr.is_err());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
use super::rs::build::RouterStatusBuilder;
|
||||
use super::{
|
||||
CommonHeader, Consensus, ConsensusFlavor, ConsensusHeader, ConsensusVoterInfo, DirSource,
|
||||
Footer, Lifetime, NetParams, ProtoStatus, RouterStatus, SharedRandVal,
|
||||
Footer, Lifetime, NetParams, ProtoStatus, RouterStatus, SharedRandStatus, SharedRandVal,
|
||||
};
|
||||
|
||||
use crate::{BuildError as Error, BuildResult as Result};
|
||||
|
@ -42,9 +42,9 @@ pub struct ConsensusBuilder<RS> {
|
|||
/// See [`ConsensusHeader::consensus_method`]
|
||||
consensus_method: Option<u32>,
|
||||
/// See [`ConsensusHeader::shared_rand_prev`]
|
||||
shared_rand_prev: Option<SharedRandVal>,
|
||||
shared_rand_prev: Option<SharedRandStatus>,
|
||||
/// See [`ConsensusHeader::shared_rand_cur`]
|
||||
shared_rand_cur: Option<SharedRandVal>,
|
||||
shared_rand_cur: Option<SharedRandStatus>,
|
||||
/// See [`Consensus::voters`]
|
||||
voters: Vec<ConsensusVoterInfo>,
|
||||
/// See [`Consensus::relays`]
|
||||
|
@ -147,15 +147,15 @@ impl<RS> ConsensusBuilder<RS> {
|
|||
/// Set the previous day's shared-random value for this consensus.
|
||||
///
|
||||
/// This value is optional.
|
||||
pub fn shared_rand_prev(&mut self, n_reveals: u8, value: Vec<u8>) -> &mut Self {
|
||||
self.shared_rand_prev = Some(SharedRandVal { n_reveals, value });
|
||||
pub fn shared_rand_prev(&mut self, n_reveals: u8, value: SharedRandVal) -> &mut Self {
|
||||
self.shared_rand_prev = Some(SharedRandStatus { n_reveals, value });
|
||||
self
|
||||
}
|
||||
/// Set the current day's shared-random value for this consensus.
|
||||
///
|
||||
/// This value is optional.
|
||||
pub fn shared_rand_cur(&mut self, n_reveals: u8, value: Vec<u8>) -> &mut Self {
|
||||
self.shared_rand_cur = Some(SharedRandVal { n_reveals, value });
|
||||
pub fn shared_rand_cur(&mut self, n_reveals: u8, value: SharedRandVal) -> &mut Self {
|
||||
self.shared_rand_cur = Some(SharedRandStatus { n_reveals, value });
|
||||
self
|
||||
}
|
||||
/// Set a named weight parameter for this consensus.
|
||||
|
@ -402,8 +402,8 @@ mod test {
|
|||
.param("knish", 1212)
|
||||
.voting_delay(7, 8)
|
||||
.consensus_method(32)
|
||||
.shared_rand_prev(1, (*b"").into())
|
||||
.shared_rand_cur(1, (*b"hi there").into())
|
||||
.shared_rand_prev(1, SharedRandVal([b'x'; 32]))
|
||||
.shared_rand_cur(1, SharedRandVal([b'y'; 32]))
|
||||
.weight("Wxy", 303)
|
||||
.weight("Wow", 999);
|
||||
|
||||
|
|
|
@ -72,6 +72,15 @@ mod b64impl {
|
|||
Err(EK::BadObjectVal.with_msg("Invalid length on base64 data"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to convert this object into an array of N bytes.
|
||||
///
|
||||
/// Return an error if the length is wrong.
|
||||
pub(crate) fn into_array<const N: usize>(self) -> Result<[u8; N]> {
|
||||
self.0
|
||||
.try_into()
|
||||
.map_err(|_| EK::BadObjectVal.with_msg("Invalid length on base64 data"))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<B64> for Vec<u8> {
|
||||
|
|
Loading…
Reference in New Issue