use super::Microdesc;
use crate::types::family::RelayFamily;
use crate::types::policy::PortPolicy;
use crate::{BuildError as Error, BuildResult as Result, Error as ParseError};
use tor_llcrypto::pk::{curve25519, ed25519};
use rand::Rng;
#[cfg_attr(docsrs, doc(cfg(feature = "build_docs")))]
#[derive(Debug, Clone)]
pub struct MicrodescBuilder {
ntor_onion_key: Option<curve25519::PublicKey>,
family: RelayFamily,
ipv4_policy: PortPolicy,
ipv6_policy: PortPolicy,
ed25519_id: Option<ed25519::Ed25519Identity>,
}
impl MicrodescBuilder {
pub(crate) fn new() -> Self {
MicrodescBuilder {
ntor_onion_key: None,
family: RelayFamily::new(),
ipv4_policy: PortPolicy::new_reject_all(),
ipv6_policy: PortPolicy::new_reject_all(),
ed25519_id: None,
}
}
pub fn ntor_key(&mut self, key: curve25519::PublicKey) -> &mut Self {
self.ntor_onion_key = Some(key);
self
}
pub fn ed25519_id(&mut self, key: ed25519::Ed25519Identity) -> &mut Self {
self.ed25519_id = Some(key);
self
}
pub fn family(&mut self, family: RelayFamily) -> &mut Self {
self.family = family;
self
}
pub fn ipv4_policy(&mut self, policy: PortPolicy) -> &mut Self {
self.ipv4_policy = policy;
self
}
pub fn ipv6_policy(&mut self, policy: PortPolicy) -> &mut Self {
self.ipv6_policy = policy;
self
}
pub fn parse_family(&mut self, family: &str) -> Result<&mut Self> {
Ok(self.family(family.parse()?))
}
pub fn parse_ipv4_policy(&mut self, policy: &str) -> Result<&mut Self> {
Ok(self.ipv4_policy(policy.parse().map_err(ParseError::from)?))
}
pub fn parse_ipv6_policy(&mut self, policy: &str) -> Result<&mut Self> {
Ok(self.ipv6_policy(policy.parse().map_err(ParseError::from)?))
}
pub fn testing_md(&self) -> Result<Microdesc> {
let ntor_onion_key = self
.ntor_onion_key
.ok_or(Error::CannotBuild("Missing ntor_key"))?;
let ed25519_id = self
.ed25519_id
.ok_or(Error::CannotBuild("Missing ed25519_id"))?;
let sha256 = rand::thread_rng().gen();
Ok(Microdesc {
sha256,
ntor_onion_key,
family: self.family.clone().intern(),
ipv4_policy: self.ipv4_policy.clone().intern(),
ipv6_policy: self.ipv6_policy.clone().intern(),
ed25519_id,
})
}
}
#[cfg(test)]
mod test {
#![allow(clippy::bool_assert_comparison)]
#![allow(clippy::clone_on_copy)]
#![allow(clippy::dbg_macro)]
#![allow(clippy::mixed_attributes_style)]
#![allow(clippy::print_stderr)]
#![allow(clippy::print_stdout)]
#![allow(clippy::single_char_pattern)]
#![allow(clippy::unwrap_used)]
#![allow(clippy::unchecked_duration_subtraction)]
#![allow(clippy::useless_vec)]
#![allow(clippy::needless_pass_by_value)]
use super::*;
#[test]
fn minimal() {
let ed: ed25519::Ed25519Identity = (*b"this is not much of a public key").into();
let ntor: curve25519::PublicKey = (*b"but fortunately nothing cares...").into();
let md = MicrodescBuilder::new()
.ed25519_id(ed)
.ntor_key(ntor)
.testing_md()
.unwrap();
assert_eq!(md.ed25519_id(), &ed);
assert_eq!(md.ntor_key(), &ntor);
assert_eq!(md.family().members().count(), 0);
}
#[test]
fn maximal() -> Result<()> {
let ed: ed25519::Ed25519Identity = (*b"this is not much of a public key").into();
let ntor: curve25519::PublicKey = (*b"but fortunately nothing cares...").into();
let md = Microdesc::builder()
.ed25519_id(ed)
.ntor_key(ntor)
.parse_ipv4_policy("accept 80,443")?
.parse_ipv6_policy("accept 22-80")?
.parse_family("$aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa $bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")?
.testing_md()
.unwrap();
assert_eq!(md.family().members().count(), 2);
assert!(md.family().contains(&[0xaa; 20].into()));
assert!(md.ipv4_policy().allows_port(443));
assert!(md.ipv4_policy().allows_port(80));
assert!(!md.ipv4_policy().allows_port(55));
assert!(!md.ipv6_policy().allows_port(443));
assert!(md.ipv6_policy().allows_port(80));
assert!(md.ipv6_policy().allows_port(55));
Ok(())
}
#[test]
fn failing() {
let ed: ed25519::Ed25519Identity = (*b"this is not much of a public key").into();
let ntor: curve25519::PublicKey = (*b"but fortunately nothing cares...").into();
{
let mut builder = Microdesc::builder();
builder.ed25519_id(ed);
assert!(builder.testing_md().is_err()); }
{
let mut builder = Microdesc::builder();
builder.ntor_key(ntor);
assert!(builder.testing_md().is_err()); }
}
}