Implement microdescriptor parsing.
Additionally, move and refactor related types to reduce friction for microdescriptor parsing implementation.
This commit is contained in:
parent
fa064717b3
commit
a8be828beb
|
@ -7,6 +7,7 @@
|
|||
|
||||
pub use b64impl::*;
|
||||
pub use curve25519impl::*;
|
||||
pub use ed25519impl::*;
|
||||
pub use edcert::*;
|
||||
pub use fingerprint::*;
|
||||
pub use rsa::*;
|
||||
|
@ -74,6 +75,32 @@ mod curve25519impl {
|
|||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
mod ed25519impl {
|
||||
use super::B64;
|
||||
use crate::{Error, Pos, Result};
|
||||
use tor_llcrypto::pk::ed25519::PublicKey;
|
||||
|
||||
pub struct Ed25519Public(PublicKey);
|
||||
|
||||
impl std::str::FromStr for Ed25519Public {
|
||||
type Err = Error;
|
||||
fn from_str(s: &str) -> Result<Self> {
|
||||
let b64: B64 = s.parse()?;
|
||||
let key = PublicKey::from_bytes(b64.as_bytes()).map_err(|_| {
|
||||
Error::BadArgument(Pos::at(s), "bad length for ed25519 key.".into())
|
||||
})?;
|
||||
Ok(Ed25519Public(key))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Ed25519Public> for PublicKey {
|
||||
fn from(pk: Ed25519Public) -> PublicKey {
|
||||
pk.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
|
||||
mod timeimpl {
|
||||
|
|
|
@ -282,6 +282,11 @@ macro_rules! derive_from_err{
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
derive_from_err! {std::num::ParseIntError}
|
||||
derive_from_err! {std::net::AddrParseError}
|
||||
|
||||
impl From<crate::policy::PolicyError> for Error {
|
||||
fn from(e: crate::policy::PolicyError) -> Error {
|
||||
Error::BadPolicy(Pos::None, e)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,19 @@ use tor_llcrypto::pk::rsa::RSAIdentity;
|
|||
/// TODO: This type probably belongs in a different crate.
|
||||
pub struct RelayFamily(Vec<RSAIdentity>);
|
||||
|
||||
impl RelayFamily {
|
||||
/// Return a new empty RelayFamily.
|
||||
pub fn new() -> Self {
|
||||
RelayFamily(Vec::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for RelayFamily {
|
||||
fn default() -> Self {
|
||||
RelayFamily::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for RelayFamily {
|
||||
type Err = Error;
|
||||
fn from_str(s: &str) -> Result<Self> {
|
||||
|
|
|
@ -33,6 +33,7 @@ mod util;
|
|||
mod macros;
|
||||
|
||||
pub mod family;
|
||||
pub mod microdesc;
|
||||
pub mod policy;
|
||||
pub mod routerdesc;
|
||||
pub mod version;
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
//! Parsing implementation for Tor microdescriptors.
|
||||
//!
|
||||
//! A "microdescriptor" is an incomplete, infrequently-changing
|
||||
//! summary of a relay's informatino information that is generated by
|
||||
//! the directory authorities.
|
||||
//!
|
||||
//! Microdescriptors are much smaller than router descriptors, and
|
||||
//! change less frequently. For this reason, they're currently used
|
||||
//! for building circuits by all relays and clients.
|
||||
|
||||
use crate::argtype::*;
|
||||
use crate::family::RelayFamily;
|
||||
use crate::parse::SectionRules;
|
||||
use crate::policy::PortPolicy;
|
||||
use crate::rules::Keyword;
|
||||
use crate::util;
|
||||
use crate::{Error, Result};
|
||||
use tor_llcrypto::d;
|
||||
use tor_llcrypto::pk::{curve25519, ed25519, rsa};
|
||||
|
||||
use digest::Digest;
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
/// A single microdescriptor.
|
||||
#[allow(dead_code)]
|
||||
pub struct Microdesc {
|
||||
// TODO: maybe this belongs somewhere else. Once it's used to store
|
||||
// correlate the microdesc to a consensus, it's never used again.
|
||||
sha256: [u8; 32],
|
||||
tap_onion_key: rsa::PublicKey,
|
||||
ntor_onion_key: curve25519::PublicKey,
|
||||
family: RelayFamily,
|
||||
ipv4_policy: PortPolicy,
|
||||
ipv6_policy: PortPolicy,
|
||||
// TODO: this is redundant.
|
||||
ed25519_id: Option<ed25519::PublicKey>,
|
||||
// addr is obsolete and doesn't go here any more
|
||||
// pr is obsolete and doesn't go here any more.
|
||||
}
|
||||
|
||||
decl_keyword! {
|
||||
/// Keyword type for recognized objects in microdescriptors.
|
||||
MicrodescKW {
|
||||
"onion-key" => ONION_KEY,
|
||||
"ntor-onion-key" => NTOR_ONION_KEY,
|
||||
"family" => FAMILY,
|
||||
"p" => P,
|
||||
"p6" => P6,
|
||||
"id" => ID,
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref MICRODESC_RULES: SectionRules<MicrodescKW> = {
|
||||
use MicrodescKW::*;
|
||||
|
||||
let mut rules = SectionRules::new();
|
||||
rules.add(ONION_KEY.rule().required().no_args().obj_required());
|
||||
rules.add(NTOR_ONION_KEY.rule().required().args(1..));
|
||||
rules.add(FAMILY.rule().args(1..));
|
||||
rules.add(P.rule().args(2..));
|
||||
rules.add(P6.rule().args(2..));
|
||||
rules.add(ID.rule().may_repeat().args(2..));
|
||||
rules.add(UNRECOGNIZED.rule().may_repeat().obj_optional());
|
||||
rules
|
||||
};
|
||||
}
|
||||
|
||||
impl Microdesc {
|
||||
/// Parse a string into a new microdescriptor.
|
||||
pub fn parse(s: &str) -> Result<Microdesc> {
|
||||
use MicrodescKW::*;
|
||||
|
||||
let mut items = crate::tokenize::NetDocReader::new(s).peekable();
|
||||
|
||||
// We have to start with onion-key
|
||||
let start_pos = {
|
||||
let first = items.peek();
|
||||
let kwd = match first {
|
||||
Some(Ok(tok)) => tok.get_kwd(),
|
||||
_ => return Err(Error::MissingToken("onion-key")),
|
||||
};
|
||||
if kwd != "onion-key" {
|
||||
return Err(Error::MissingToken("onion-key"));
|
||||
}
|
||||
util::str_offset(s, kwd).unwrap()
|
||||
};
|
||||
|
||||
let body = MICRODESC_RULES.parse(&mut items)?;
|
||||
|
||||
// Legacy (tap) onion key
|
||||
let tap_onion_key: rsa::PublicKey = body
|
||||
.get_required(ONION_KEY)?
|
||||
.parse_obj::<RSAPublic>("RSA PUBLIC KEY")?
|
||||
.check_len_eq(1024)?
|
||||
.check_exponent(65537)?
|
||||
.into();
|
||||
|
||||
// Ntor onion key
|
||||
let ntor_onion_key = body
|
||||
.get_required(NTOR_ONION_KEY)?
|
||||
.parse_arg::<Curve25519Public>(0)?
|
||||
.into();
|
||||
|
||||
// family
|
||||
let family = body
|
||||
.maybe(FAMILY)
|
||||
.parse_args_as_str::<RelayFamily>()?
|
||||
.unwrap_or_else(RelayFamily::new);
|
||||
|
||||
// exit policies.
|
||||
let ipv4_policy = body
|
||||
.maybe(P)
|
||||
.parse_args_as_str::<PortPolicy>()?
|
||||
.unwrap_or_else(PortPolicy::new_reject_all);
|
||||
let ipv6_policy = body
|
||||
.maybe(P6)
|
||||
.parse_args_as_str::<PortPolicy>()?
|
||||
.unwrap_or_else(PortPolicy::new_reject_all);
|
||||
|
||||
// ed25519 identity
|
||||
let ed25519_id = {
|
||||
let id_tok = body
|
||||
.get_slice(ID)
|
||||
.iter()
|
||||
.find(|item| item.get_arg(1) == Some("ed25519"));
|
||||
match id_tok {
|
||||
None => None,
|
||||
Some(tok) => Some(tok.parse_arg::<Ed25519Public>(0)?.into()),
|
||||
}
|
||||
};
|
||||
|
||||
let sha256 = d::Sha256::digest(&s[start_pos..].as_bytes()).into();
|
||||
|
||||
Ok(Microdesc {
|
||||
sha256,
|
||||
tap_onion_key,
|
||||
ntor_onion_key,
|
||||
family,
|
||||
ipv4_policy,
|
||||
ipv6_policy,
|
||||
ed25519_id,
|
||||
})
|
||||
}
|
||||
}
|
|
@ -237,6 +237,8 @@ impl<T: Keyword> SectionRules<T> {
|
|||
let mut section = Section::new();
|
||||
self.parse_unverified(tokens, &mut section)?;
|
||||
self.validate(§ion)?;
|
||||
// TODO: unrecognized tokens with objects won't actually get their
|
||||
// objects checked for valid base64
|
||||
Ok(section)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,6 +47,12 @@ impl Display for PortPolicy {
|
|||
}
|
||||
|
||||
impl PortPolicy {
|
||||
/// Return a new PortPolicy that rejects all ports.
|
||||
pub fn new_reject_all() -> Self {
|
||||
PortPolicy {
|
||||
allowed: Vec::new(),
|
||||
}
|
||||
}
|
||||
/// Helper: replace this policy with its inverse.
|
||||
fn invert(&mut self) {
|
||||
let mut prev_hi = 0;
|
||||
|
|
Loading…
Reference in New Issue