use super::super::*;
const TOPLEVEL_DOCTYPE_FOR_ERROR: &str =
ns_expr!("NetworkStatusVote", "NetworkStatusNs", "NetworkStatusMd",);
pub type Router = ns_type!(
crate::doc::netstatus::VoteRouterStatus,
crate::doc::netstatus::PlainRouterStatus,
crate::doc::netstatus::MdRouterStatus,
);
#[derive(Deftly, Clone, Debug)]
#[derive_deftly(NetdocParseableUnverified)]
#[deftly(netdoc(doctype_for_error = TOPLEVEL_DOCTYPE_FOR_ERROR))]
#[non_exhaustive]
pub struct NetworkStatus {
pub network_status_version: (NdaNetworkStatusVersion, NdaNetworkStatusVersionFlavour),
pub vote_status: NdiVoteStatus,
pub published: ns_type!((NdaSystemTimeDeprecatedSyntax,), Option<Void>,),
pub valid_after: (NdaSystemTimeDeprecatedSyntax,),
pub valid_until: (NdaSystemTimeDeprecatedSyntax,),
pub voting_delay: NdiVotingDelay,
#[deftly(netdoc(default))]
pub params: NdiParams,
#[deftly(netdoc(subdoc))]
pub authority: NddAuthoritySection,
#[deftly(netdoc(subdoc))]
pub r: Vec<Router>,
#[deftly(netdoc(subdoc))]
pub directory_footer: Option<NddDirectoryFooter>,
}
#[derive(Deftly, Clone, Debug)]
#[derive_deftly(NetdocParseableSignatures)]
#[deftly(netdoc(signatures(hashes_accu = "DirectorySignaturesHashesAccu")))]
#[non_exhaustive]
pub struct NetworkStatusSignatures {
pub directory_signature: ns_type!(NdiDirectorySignature, Vec<NdiDirectorySignature>),
}
#[derive(Deftly, Clone, Debug, Hash, Eq, PartialEq)]
#[derive_deftly(ItemValueParseable)]
#[non_exhaustive]
pub struct NdiVoteStatus {
pub status: NdaVoteStatus,
}
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
#[non_exhaustive]
pub struct NdaVoteStatus {}
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
#[non_exhaustive]
pub struct NdaNetworkStatusVersionFlavour {}
const NDA_NETWORK_STATUS_VERSION_FLAVOUR: Option<&str> = ns_expr!(None, None, Some("microdesc"));
impl ItemArgumentParseable for NdaNetworkStatusVersionFlavour {
fn from_args<'s>(args: &mut ArgumentStream<'s>) -> Result<Self, AE> {
let exp: Option<&str> = NDA_NETWORK_STATUS_VERSION_FLAVOUR;
if let Some(exp) = exp {
let got = args.next().ok_or(AE::Missing)?;
if got != exp {
return Err(AE::Invalid);
};
} else {
args.reject_extra_args()?;
}
Ok(Self {})
}
}
const NDA_VOTE_STATUS: &str = ns_expr!("vote", "consensus", "consensus");
impl FromStr for NdaVoteStatus {
type Err = InvalidNetworkStatusVoteStatus;
fn from_str(s: &str) -> Result<Self, InvalidNetworkStatusVoteStatus> {
if s == NDA_VOTE_STATUS {
Ok(Self {})
} else {
Err(InvalidNetworkStatusVoteStatus {})
}
}
}
impl Display for NdaVoteStatus {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt(NDA_VOTE_STATUS, f)
}
}
impl NormalItemArgument for NdaVoteStatus {}
#[derive(Deftly, Clone, Debug, Hash, Eq, PartialEq)]
#[derive_deftly(ItemValueParseable)]
#[non_exhaustive]
pub struct NdiVotingDelay {
pub vote_seconds: u32,
pub dist_seconds: u32,
}
#[derive(Deftly, Clone, Debug)]
#[derive_deftly(NetdocParseable)]
#[non_exhaustive]
pub struct NddDirectoryFooter {
pub directory_footer: (),
}
#[derive(Deftly, Clone, Debug)]
#[derive_deftly(ItemValueParseable)]
#[non_exhaustive]
pub struct NdiAuthorityDirSource {
pub nickname: types::Nickname,
pub h_p_auth_id_rsa: types::Fingerprint,
}
ns_choose! { (
use VoteAuthoritySection as NddAuthoritySection;
)(
use ConsensusAuthoritySection as NddAuthoritySection;
)}
ns_choose! { (
impl NetworkStatusUnverified {
pub fn verify_selfcert(
self,
now: SystemTime,
) -> Result<(NetworkStatus, SignaturesData<NetworkStatusUnverified>), VF> {
let validity = *self.body.published.0 ..= *self.body.valid_until.0;
check_validity_time(now, validity)?;
let cert = self.body.parse_authcert()?.verify_selfcert(now)?;
netstatus::verify_general_timeless(
&self.sigs.hashes,
slice::from_ref(&self.sigs.sigs.directory_signature),
&[*cert.fingerprint],
&[&cert],
1,
)?;
Ok(self.unwrap_unverified())
}
}
impl NetworkStatus {
fn parse_authcert(&self) -> Result<crate::doc::authcert::AuthCertUnverified, EP> {
let cert_input = ParseInput::new(
self.authority.cert.as_str(),
"<embedded auth cert>",
);
parse_netdoc(&cert_input).map_err(|e| e.problem)
}
pub fn h_kp_auth_id_rsa(&self) -> pk::rsa::RsaIdentity {
*self.parse_authcert()
.expect("was verified already!")
.inspect_unverified()
.0
.fingerprint
}
}
) (
impl NetworkStatusUnverified {
pub fn verify(
self,
now: SystemTime,
authorities: &[pk::rsa::RsaIdentity],
certs: &[&DirAuthKeyCert],
) -> Result<(NetworkStatus, SignaturesData<NetworkStatusUnverified>), VF> {
let threshold = authorities.len() / 2 + 1; let validity_start = self.body.valid_after.0
.checked_sub(Duration::from_secs(self.body.voting_delay.dist_seconds.into()))
.ok_or(VF::Other)?;
check_validity_time(now, validity_start..= *self.body.valid_until.0)?;
netstatus::verify_general_timeless(
&self.sigs.hashes,
&self.sigs.sigs.directory_signature,
authorities,
certs,
threshold,
)?;
Ok(self.unwrap_unverified())
}
}
)}