Implement microdescriptor parsing.

Additionally, move and refactor related types to reduce friction for
microdescriptor parsing implementation.
This commit is contained in:
Nick Mathewson 2020-05-15 19:25:40 -04:00
parent fa064717b3
commit a8be828beb
7 changed files with 200 additions and 1 deletions

View File

@ -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 {

View File

@ -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)
}
}

View File

@ -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> {

View File

@ -33,6 +33,7 @@ mod util;
mod macros;
pub mod family;
pub mod microdesc;
pub mod policy;
pub mod routerdesc;
pub mod version;

145
tor-netdoc/src/microdesc.rs Normal file
View File

@ -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,
})
}
}

View File

@ -237,6 +237,8 @@ impl<T: Keyword> SectionRules<T> {
let mut section = Section::new();
self.parse_unverified(tokens, &mut section)?;
self.validate(&section)?;
// TODO: unrecognized tokens with objects won't actually get their
// objects checked for valid base64
Ok(section)
}
}

View File

@ -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;