diff --git a/tor-netdoc/src/argtype.rs b/tor-netdoc/src/argtype.rs index 7eae6ae53..f20554fe1 100644 --- a/tor-netdoc/src/argtype.rs +++ b/tor-netdoc/src/argtype.rs @@ -7,13 +7,14 @@ pub use b64impl::*; pub use curve25519impl::*; +pub use edcert::*; 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[..]) + fn from_bytes(b: &[u8], p: crate::Pos) -> crate::Result; + fn from_vec(v: Vec, p: crate::Pos) -> crate::Result { + Self::from_bytes(&v[..], p) } } @@ -105,7 +106,7 @@ mod rsa { /// An RSA public key, as parsed from a base64-encoded object. #[allow(non_camel_case_types)] - pub struct RSAPublic(PublicKey); + pub struct RSAPublic(PublicKey, Pos); impl From for PublicKey { fn from(k: RSAPublic) -> PublicKey { @@ -113,11 +114,11 @@ mod rsa { } } impl super::FromBytes for RSAPublic { - fn from_bytes(b: &[u8]) -> Result { + fn from_bytes(b: &[u8], pos: Pos) -> Result { let key = PublicKey::from_der(b).ok_or_else(|| { Error::BadObjectVal(Pos::None, "unable to decode RSA public key".into()) })?; - Ok(RSAPublic(key)) + Ok(RSAPublic(key, pos)) } } impl RSAPublic { @@ -126,10 +127,7 @@ mod rsa { if self.0.exponent_is(e) { Ok(self) } else { - Err(Error::BadObjectVal( - Pos::None, - "invalid RSA exponent".into(), - )) + Err(Error::BadObjectVal(self.1, "invalid RSA exponent".into())) } } /// Give an error if the exponent of this key is not contained in 'bounds' @@ -137,7 +135,7 @@ mod rsa { if bounds.contains(&self.0.bits()) { Ok(self) } else { - Err(Error::BadObjectVal(Pos::None, "invalid RSA length".into())) + Err(Error::BadObjectVal(self.1, "invalid RSA length".into())) } } pub fn check_len_eq(self, n: usize) -> Result { @@ -145,3 +143,60 @@ mod rsa { } } } + +mod edcert { + use crate::{Error, Pos, Result}; + use tor_cert::Ed25519Cert; + use tor_llcrypto::pk::ed25519; + + /// An ed25519 certificate as parsed from a directory object, with + /// signature not validated. + pub struct UnvalidatedEdCert(Vec, Pos); + + /// An ed25519 certificate as parsed from a directory object, with + /// checked signature. + pub struct ValidatedEdCert(Ed25519Cert, Pos); + impl From for Ed25519Cert { + fn from(c: ValidatedEdCert) -> Self { + c.0 + } + } + impl super::FromBytes for UnvalidatedEdCert { + fn from_bytes(b: &[u8], p: Pos) -> Result { + Self::from_vec(b.into(), p) + } + fn from_vec(v: Vec, p: Pos) -> Result { + Ok(Self(v, p)) + } + } + impl UnvalidatedEdCert { + pub fn validate(self, signing_key: Option<&ed25519::PublicKey>) -> Result { + let cert = Ed25519Cert::decode_and_check(&self.0, signing_key) + .map_err(|e| Error::BadObjectVal(self.1, e.to_string()))?; + Ok(ValidatedEdCert(cert, self.1)) + } + } + impl ValidatedEdCert { + /// Give an error if this certificate's type is not `desired_type`. + pub fn check_cert_type(self, desired_type: u8) -> Result { + if self.0.get_cert_type() != desired_type { + return Err(Error::BadObjectVal( + self.1, + format!( + "bad certificate type {} (wanted {})", + self.0.get_cert_type(), + desired_type + ), + )); + } + Ok(self) + } + /// Give an error if this certificate's subject_key is not `pk` + pub fn check_subject_key_is(self, pk: &ed25519::PublicKey) -> Result { + if self.0.get_subject_key().as_ed25519() != Some(pk) { + return Err(Error::BadObjectVal(self.1, "incorrect subject key".into())); + } + Ok(self) + } + } +} diff --git a/tor-netdoc/src/routerdesc.rs b/tor-netdoc/src/routerdesc.rs index 9453e54c0..bc391dfc7 100644 --- a/tor-netdoc/src/routerdesc.rs +++ b/tor-netdoc/src/routerdesc.rs @@ -295,15 +295,11 @@ impl RouterDesc { if cert_tok.offset_in(s).unwrap() < start_offset { return Err(Error::MisplacedToken("identity-ed25519", cert_tok.pos())); } - let cert = cert_tok.get_obj("ED25519 CERT")?; - let cert = tor_cert::Ed25519Cert::decode_and_check(&cert[..], None) - .map_err(|_| Error::BadSignature(cert_tok.pos()))?; - if cert.get_cert_type() != tor_cert::certtype::IDENTITY_V_SIGNING { - return Err(Error::BadObjectVal( - cert_tok.pos(), - "wrong certificate type".to_string(), - )); - } + let cert: tor_cert::Ed25519Cert = cert_tok + .parse_obj::("ED25519 CERT")? + .validate(None)? + .check_cert_type(tor_cert::certtype::IDENTITY_V_SIGNING)? + .into(); let sk = cert.get_subject_key().as_ed25519().ok_or_else(|| { Error::BadObjectVal(cert_tok.pos(), "no ed25519 signing key".to_string()) })?; @@ -403,24 +399,18 @@ impl RouterDesc { // Technically required? XXXX let cc = body.get_required(NTOR_ONION_KEY_CROSSCERT)?; let sign: u8 = cc.parse_arg(0)?; - let cert = cc.get_obj("ED25519 CERT")?; if sign != 0 && sign != 1 { - return Err(Error::BadArgument(cc.pos(), "not 0 or 1".to_string())); + return Err(Error::BadArgument(cc.arg_pos(0), "not 0 or 1".to_string())); } let ntor_as_ed = ll::pk::keymanip::convert_curve25519_to_ed25519_public(&ntor_onion_key, sign) .ok_or_else(|| Error::Internal(cc.pos()))?; // XXX not really 'internal' - let crosscert = tor_cert::Ed25519Cert::decode_and_check(&cert[..], Some(&ntor_as_ed)) - .map_err(|_| Error::BadSignature(cc.pos()))?; - if crosscert.get_cert_type() != tor_cert::certtype::NTOR_CC_IDENTITY { - return Err(Error::BadObjectVal( - cc.pos(), - "wrong certificate type".into(), - )); - } - if crosscert.get_subject_key().as_ed25519() != Some(identity_cert.get_signing_key()) { - return Err(Error::BadSignature(cc.pos())); - } + let crosscert: tor_cert::Ed25519Cert = cc + .parse_obj::("ED25519 CERT")? + .validate(Some(&ntor_as_ed))? + .check_cert_type(tor_cert::certtype::NTOR_CC_IDENTITY)? + .check_subject_key_is(identity_cert.get_signing_key())? + .into(); expiry = std::cmp::min(expiry, crosscert.get_expiry()); } diff --git a/tor-netdoc/src/tokenize.rs b/tor-netdoc/src/tokenize.rs index 03cd291e6..0da5f7461 100644 --- a/tor-netdoc/src/tokenize.rs +++ b/tor-netdoc/src/tokenize.rs @@ -326,7 +326,8 @@ impl<'a> Item<'a> { /// 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))) + let p = Pos::at(self.object.unwrap().data); + V::from_vec(bytes, p).map_err(|e| e.at_pos(p)) } /// Return the position of this item. ///