netdoc: use improved API for parsing ed25519 certificates.

This commit is contained in:
Nick Mathewson 2020-05-15 11:49:49 -04:00
parent 332b9de542
commit 60adff1fd7
3 changed files with 80 additions and 34 deletions

View File

@ -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<Self>;
fn from_vec(v: Vec<u8>) -> crate::Result<Self> {
Self::from_bytes(&v[..])
fn from_bytes(b: &[u8], p: crate::Pos) -> crate::Result<Self>;
fn from_vec(v: Vec<u8>, p: crate::Pos) -> crate::Result<Self> {
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<RSAPublic> for PublicKey {
fn from(k: RSAPublic) -> PublicKey {
@ -113,11 +114,11 @@ mod rsa {
}
}
impl super::FromBytes for RSAPublic {
fn from_bytes(b: &[u8]) -> Result<Self> {
fn from_bytes(b: &[u8], pos: Pos) -> Result<Self> {
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<Self> {
@ -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<u8>, Pos);
/// An ed25519 certificate as parsed from a directory object, with
/// checked signature.
pub struct ValidatedEdCert(Ed25519Cert, Pos);
impl From<ValidatedEdCert> for Ed25519Cert {
fn from(c: ValidatedEdCert) -> Self {
c.0
}
}
impl super::FromBytes for UnvalidatedEdCert {
fn from_bytes(b: &[u8], p: Pos) -> Result<Self> {
Self::from_vec(b.into(), p)
}
fn from_vec(v: Vec<u8>, p: Pos) -> Result<Self> {
Ok(Self(v, p))
}
}
impl UnvalidatedEdCert {
pub fn validate(self, signing_key: Option<&ed25519::PublicKey>) -> Result<ValidatedEdCert> {
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<Self> {
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<Self> {
if self.0.get_subject_key().as_ed25519() != Some(pk) {
return Err(Error::BadObjectVal(self.1, "incorrect subject key".into()));
}
Ok(self)
}
}
}

View File

@ -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::<UnvalidatedEdCert>("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::<UnvalidatedEdCert>("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());
}

View File

@ -326,7 +326,8 @@ impl<'a> Item<'a> {
/// as a given type that implements FromBytes.
pub fn parse_obj<V: FromBytes>(&self, want_tag: &str) -> Result<V> {
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.
///