diff --git a/tor-netdoc/src/argtype.rs b/tor-netdoc/src/argtype.rs index d0136de70..7eae6ae53 100644 --- a/tor-netdoc/src/argtype.rs +++ b/tor-netdoc/src/argtype.rs @@ -7,8 +7,16 @@ pub use b64impl::*; pub use curve25519impl::*; +pub use rsa::*; pub use timeimpl::*; +pub trait FromBytes: Sized { + fn from_bytes(b: &[u8]) -> crate::Result; + fn from_vec(v: Vec) -> crate::Result { + Self::from_bytes(&v[..]) + } +} + mod b64impl { use crate::{Error, Pos, Result}; @@ -89,3 +97,51 @@ mod timeimpl { } } } + +mod rsa { + use crate::{Error, Pos, Result}; + use std::ops::RangeBounds; + use tor_llcrypto::pk::rsa::PublicKey; + + /// An RSA public key, as parsed from a base64-encoded object. + #[allow(non_camel_case_types)] + pub struct RSAPublic(PublicKey); + + impl From for PublicKey { + fn from(k: RSAPublic) -> PublicKey { + k.0 + } + } + impl super::FromBytes for RSAPublic { + fn from_bytes(b: &[u8]) -> Result { + let key = PublicKey::from_der(b).ok_or_else(|| { + Error::BadObjectVal(Pos::None, "unable to decode RSA public key".into()) + })?; + Ok(RSAPublic(key)) + } + } + impl RSAPublic { + /// Give an error if the exponent of this key is not 'e' + pub fn check_exponent(self, e: u32) -> Result { + if self.0.exponent_is(e) { + Ok(self) + } else { + Err(Error::BadObjectVal( + Pos::None, + "invalid RSA exponent".into(), + )) + } + } + /// Give an error if the exponent of this key is not contained in 'bounds' + pub fn check_len>(self, bounds: B) -> Result { + if bounds.contains(&self.0.bits()) { + Ok(self) + } else { + Err(Error::BadObjectVal(Pos::None, "invalid RSA length".into())) + } + } + pub fn check_len_eq(self, n: usize) -> Result { + self.check_len(n..=n) + } + } +} diff --git a/tor-netdoc/src/tokenize.rs b/tor-netdoc/src/tokenize.rs index 9d1c4069b..03cd291e6 100644 --- a/tor-netdoc/src/tokenize.rs +++ b/tor-netdoc/src/tokenize.rs @@ -4,6 +4,7 @@ //! directory document, and NetDocReader, which is used to break a //! string into Items. +use crate::argtype::FromBytes; use crate::{Error, Pos, Result}; use std::cell::{Ref, RefCell}; use std::str::FromStr; @@ -321,6 +322,12 @@ impl<'a> Item<'a> { } } } + /// Try to decode the base64 contents of this item's associated object + /// as a given type that implements FromBytes. + pub fn parse_obj(&self, want_tag: &str) -> Result { + let bytes = self.get_obj(want_tag)?; + V::from_vec(bytes).map_err(|e| e.at_pos(Pos::at(self.object.unwrap().data))) + } /// Return the position of this item. /// /// This position won't be useful unless it is later contextualized