diff options
Diffstat (limited to 'crates/tor-netdoc')
| -rw-r--r-- | crates/tor-netdoc/src/doc/netstatus.rs | 61 | ||||
| -rw-r--r-- | crates/tor-netdoc/src/doc/netstatus/build.rs | 31 | ||||
| -rw-r--r-- | crates/tor-netdoc/src/types/misc.rs | 38 |
3 files changed, 117 insertions, 13 deletions
diff --git a/crates/tor-netdoc/src/doc/netstatus.rs b/crates/tor-netdoc/src/doc/netstatus.rs index f04f4a25b..835e8e517 100644 --- a/crates/tor-netdoc/src/doc/netstatus.rs +++ b/crates/tor-netdoc/src/doc/netstatus.rs @@ -293,8 +293,11 @@ pub struct SignatureGroup { } /// A shared random value produced by the directory authorities. -#[derive(Debug, Clone, Copy)] -// TODO: needs accessors. +#[derive( + Debug, Clone, Copy, Eq, PartialEq, derive_more::From, derive_more::Into, derive_more::AsRef, +)] +// TODO hs: Use CtBytes for this. I don't think it actually matters, but it +// seems like a good idea. pub struct SharedRandVal([u8; 32]); /// A shared-random value produced by the directory authorities, @@ -308,7 +311,7 @@ pub struct SharedRandVal([u8; 32]); non_exhaustive )] #[derive(Debug, Clone)] -struct SharedRandStatus { +pub 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, @@ -320,6 +323,12 @@ struct SharedRandStatus { /// have any more than a small number of possible random values. #[cfg_attr(docsrs, doc(cfg(feature = "dangerous-expose-struct-fields")))] value: SharedRandVal, + + /// The time when this SharedRandVal becomes (or became) the latest. + /// + /// (This is added per proposal 342, assuming that gets accepted.) + #[cfg_attr(docsrs, doc(cfg(feature = "dangerous-expose-struct-fields")))] + timestamp: Option<time::SystemTime>, } /// Parts of the networkstatus header that are present in every networkstatus. @@ -644,6 +653,18 @@ impl<RS> Consensus<RS> { pub fn params(&self) -> &NetParams<i32> { &self.header.hdr.params } + + /// Return the latest shared random value, if the consensus + /// contains one. + pub fn shared_rand_cur(&self) -> Option<&SharedRandStatus> { + self.header.shared_rand_cur.as_ref() + } + + /// Return the previous shared random value, if the consensus + /// contains one. + pub fn shared_rand_prev(&self) -> Option<&SharedRandStatus> { + self.header.shared_rand_prev.as_ref() + } } decl_keyword! { @@ -1003,7 +1024,25 @@ impl SharedRandStatus { let n_reveals: u8 = item.parse_arg(0)?; let val: B64 = item.parse_arg(1)?; let value = SharedRandVal(val.into_array()?); - Ok(SharedRandStatus { n_reveals, value }) + // Added in proposal 342 + let timestamp = item + .parse_optional_arg::<Iso8601TimeNoSp>(2)? + .map(Into::into); + Ok(SharedRandStatus { + n_reveals, + value, + timestamp, + }) + } + + /// Return the actual shared random value. + pub fn value(&self) -> &SharedRandVal { + &self.value + } + + /// Return the timestamp (if any) associated with this `SharedRandValue`. + pub fn timestamp(&self) -> Option<std::time::SystemTime> { + self.timestamp } } @@ -1948,6 +1987,20 @@ mod test { sr.value.0, hex!("e4ba1d638c96c458532adc6957dc0080d03d37c7e5854087d0da90bf5ff4e72e") ); + assert!(sr.timestamp.is_none()); + + let sr2 = gettok( + "shared-rand-current-value 9 \ + 5LodY4yWxFhTKtxpV9wAgNA9N8flhUCH0NqQv1/05y4 2022-01-20T12:34:56\n", + ) + .unwrap(); + let sr2 = SharedRandStatus::from_item(&sr2).unwrap(); + assert_eq!(sr2.n_reveals, sr.n_reveals); + assert_eq!(sr2.value.0, sr.value.0); + assert_eq!( + sr2.timestamp.unwrap(), + humantime::parse_rfc3339("2022-01-20T12:34:56Z").unwrap() + ); let sr = gettok("foo bar\n").unwrap(); let sr = SharedRandStatus::from_item(&sr); diff --git a/crates/tor-netdoc/src/doc/netstatus/build.rs b/crates/tor-netdoc/src/doc/netstatus/build.rs index 54178e4cf..20966883f 100644 --- a/crates/tor-netdoc/src/doc/netstatus/build.rs +++ b/crates/tor-netdoc/src/doc/netstatus/build.rs @@ -14,6 +14,7 @@ use tor_llcrypto::pk::rsa::RsaIdentity; use tor_protover::Protocols; use std::net::IpAddr; +use std::time::SystemTime; /// A builder object used to construct a consensus. /// @@ -147,15 +148,33 @@ 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: SharedRandVal) -> &mut Self { - self.shared_rand_prev = Some(SharedRandStatus { n_reveals, value }); + pub fn shared_rand_prev( + &mut self, + n_reveals: u8, + value: SharedRandVal, + timestamp: Option<SystemTime>, + ) -> &mut Self { + self.shared_rand_prev = Some(SharedRandStatus { + n_reveals, + value, + timestamp, + }); 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: SharedRandVal) -> &mut Self { - self.shared_rand_cur = Some(SharedRandStatus { n_reveals, value }); + pub fn shared_rand_cur( + &mut self, + n_reveals: u8, + value: SharedRandVal, + timestamp: Option<SystemTime>, + ) -> &mut Self { + self.shared_rand_cur = Some(SharedRandStatus { + n_reveals, + value, + timestamp, + }); self } /// Set a named weight parameter for this consensus. @@ -403,8 +422,8 @@ mod test { .param("knish", 1212) .voting_delay(7, 8) .consensus_method(32) - .shared_rand_prev(1, SharedRandVal([b'x'; 32])) - .shared_rand_cur(1, SharedRandVal([b'y'; 32])) + .shared_rand_prev(1, SharedRandVal([b'x'; 32]), None) + .shared_rand_cur(1, SharedRandVal([b'y'; 32]), None) .weight("Wxy", 303) .weight("Wow", 999); diff --git a/crates/tor-netdoc/src/types/misc.rs b/crates/tor-netdoc/src/types/misc.rs index 5f43a10de..470951bb0 100644 --- a/crates/tor-netdoc/src/types/misc.rs +++ b/crates/tor-netdoc/src/types/misc.rs @@ -206,6 +206,7 @@ mod timeimpl { /// space between the date and time. /// /// (Example: "2020-10-09 17:38:12") + #[derive(derive_more::Into, derive_more::From)] pub(crate) struct Iso8601TimeSp(SystemTime); /// Formatting object for parsing the space-separated Iso8601 format. @@ -224,9 +225,31 @@ mod timeimpl { } } - impl From<Iso8601TimeSp> for SystemTime { - fn from(t: Iso8601TimeSp) -> SystemTime { - t.0 + /// A wall-clock time, encoded in ISO8601 format without an intervening + /// space. + /// + /// This represents a specific UTC instant (ie an instant in global civil time). + /// But it may not be able to represent leap seconds. + /// + /// The timezone is not included in the string representation; `+0000` is implicit. + /// + /// (Example: "2020-10-09T17:38:12") + #[derive(derive_more::Into, derive_more::From)] + pub(crate) struct Iso8601TimeNoSp(SystemTime); + + /// Formatting object for parsing the space-separated Iso8601 format. + const ISO_8601NOSP_FMT: &[FormatItem] = + format_description!("[year]-[month]-[day]T[hour]:[minute]:[second]"); + + impl std::str::FromStr for Iso8601TimeNoSp { + type Err = Error; + fn from_str(s: &str) -> Result<Iso8601TimeNoSp> { + let d = PrimitiveDateTime::parse(s, &ISO_8601NOSP_FMT).map_err(|e| { + EK::BadArgument + .at_pos(Pos::at(s)) + .with_msg(format!("invalid time: {}", e)) + })?; + Ok(Iso8601TimeNoSp(d.assume_utc().into())) } } } @@ -671,6 +694,15 @@ mod test { assert!("2020-09-29".parse::<Iso8601TimeSp>().is_err()); assert!("too bad, waluigi time".parse::<Iso8601TimeSp>().is_err()); + let t = "2020-09-29T13:36:33".parse::<Iso8601TimeNoSp>()?; + let t: SystemTime = t.into(); + assert_eq!(t, parse_rfc3339("2020-09-29T13:36:33Z").unwrap()); + + assert!("2020-09-29 13:36:33".parse::<Iso8601TimeNoSp>().is_err()); + assert!("2020-09-29Q13:99:33".parse::<Iso8601TimeNoSp>().is_err()); + assert!("2020-09-29".parse::<Iso8601TimeNoSp>().is_err()); + assert!("too bad, waluigi time".parse::<Iso8601TimeNoSp>().is_err()); + Ok(()) } |
