Implement most of client-side channel cert validation.

This commit is contained in:
Nick Mathewson 2020-09-09 16:19:42 -04:00
parent e395ad5801
commit 8d01a0a808
7 changed files with 89 additions and 21 deletions

View File

@ -28,11 +28,15 @@ async fn connect<C: ChanTarget>(target: &C) -> Result<Channel<TlsStream<net::Tcp
info!("Negotiating TLS with {}", addr);
let tlscon = connector.connect("ignored", stream).await?;
info!("TLS negotiated.");
let peer_cert = tlscon
.peer_certificate()?
.ok_or(Error::Misc("Somehow a TLS server didn't show a cert?"))?
.to_der()?;
let chan = OutboundClientHandshake::new(tlscon).connect().await?;
info!("version negotiated and cells read.");
let chan = chan.check(target)?;
info!("Certs validated (only not really)");
let chan = chan.check(target, &peer_cert)?;
info!("Certs validated (except for RSA :/)");
let chan = chan.finish(&addr.ip()).await?;
info!("Channel complete.");

View File

@ -166,8 +166,10 @@ mod ed25519_impls {
impl Readable for ed25519::PublicKey {
fn take_from(b: &mut Reader<'_>) -> Result<Self> {
let bytes = b.take(32)?;
Self::from_bytes(array_ref![bytes, 0, 32])
.map_err(|_| Error::BadMessage("Couldn't decode Ed25519 public key"))
Self::from_bytes(array_ref![bytes, 0, 32]).map_err(|e| {
println!("{}", e);
Error::BadMessage("Couldn't decode Ed25519 public key")
})
}
}
impl Writeable for ed25519::Signature {

View File

@ -323,7 +323,13 @@ impl Ed25519Cert {
}
let cert_type = r.take_u8()?.into();
let exp_hours = r.take_u32()?;
let cert_key_type = r.take_u8()?.into();
let mut cert_key_type = r.take_u8()?.into();
// XXXX This is a workaround for a tor bug: the key type is wrong.
if cert_type == CertType::SIGNING_V_TLS_CERT && cert_key_type == KeyType::ED25519_KEY {
cert_key_type = KeyType::SHA256_OF_X509;
}
let cert_key = CertifiedKey::from_reader(cert_key_type, &mut r)?;
let n_exts = r.take_u8()?;
let mut extensions = Vec::new();

View File

@ -10,7 +10,9 @@ publish = false
caret = { path="../caret" }
tor-llcrypto = { path="../tor-llcrypto" }
tor-bytes = { path="../tor-bytes" }
tor-cert = { path="../tor-cert" }
tor-linkspec = { path="../tor-linkspec" }
tor-checkable = { path="../tor-checkable" }
arrayref = "0.3.6"
digest = "0.9.0"

View File

@ -605,6 +605,29 @@ fn take_one_tor_cert(r: &mut Reader<'_>) -> Result<TorCert> {
pub struct Certs {
certs: Vec<TorCert>,
}
impl Certs {
/// Look for a certificate of type 'tp' in this cell; return it if
/// there is one.
pub fn parse_ed_cert(&self, tp: tor_cert::CertType) -> crate::Result<tor_cert::KeyUnknownCert> {
let cert = self
.certs
.iter()
.find(|c| c.certtype == tp.into())
.ok_or_else(|| crate::Error::ChanProto(format!("Missing {} certificate", tp)))?;
let cert = tor_cert::Ed25519Cert::decode(&cert.cert)?;
if cert.peek_cert_type() != tp {
return Err(crate::Error::ChanProto(format!(
"Found a {} certificate labeled as {}",
cert.peek_cert_type(),
tp
)));
}
Ok(cert)
}
}
impl Body for Certs {
fn as_message(self) -> ChanMsg {
ChanMsg::Certs(self)

View File

@ -13,6 +13,9 @@ use crate::{Error, Result};
use std::net;
use tor_bytes::Reader;
use tor_linkspec::ChanTarget;
use tor_llcrypto as ll;
use digest::Digest;
// We only support version 4 for now, since we don't do padding right
static LINK_PROTOCOLS: &[u16] = &[4];
@ -132,8 +135,48 @@ impl<T: AsyncRead + AsyncWrite + Unpin> OutboundClientHandshake<T> {
}
impl<T: AsyncRead + AsyncWrite + Unpin> UnverifiedChannel<T> {
pub fn check<U: ChanTarget>(self, _peer: &U) -> Result<VerifiedChannel<T>> {
// XXXX need to verify certificates
pub fn check<U: ChanTarget>(self, peer: &U, peer_cert: &[u8]) -> Result<VerifiedChannel<T>> {
use tor_cert::CertType;
use tor_checkable::*;
let c = &self.certs_cell;
let id_sk = c.parse_ed_cert(CertType::IDENTITY_V_SIGNING)?;
let sk_tls = c.parse_ed_cert(CertType::SIGNING_V_TLS_CERT)?;
// XXXX need to verify RSA crosscert.
let id_sk = id_sk
.check_key(&None)?
.check_signature()
.map_err(|_| Error::ChanProto("Bad certificate signature".into()))?
.check_valid_now()
.map_err(|_| Error::ChanProto("Certificate expired or not yet valid".into()))?;
let identity_key = id_sk.get_signing_key().ok_or_else(|| {
Error::ChanProto("Missing identity key in identity->signing cert".into())
})?;
let signing_key = id_sk
.get_subject_key()
.as_ed25519()
.ok_or_else(|| Error::ChanProto("Bad key type in identity->signing cert".into()))?;
let sk_tls = sk_tls
.check_key(&Some(*signing_key))? // this is a bad interface XXXX
.check_signature()
.map_err(|_| Error::ChanProto("Bad certificate signature".into()))?
.check_valid_now()
.map_err(|_| Error::ChanProto("Certificate expired or not yet valid".into()))?;
let cert_sha256 = ll::d::Sha256::digest(peer_cert);
if &cert_sha256[..] != sk_tls.get_subject_key().as_bytes() {
return Err(Error::ChanProto(
"Peer cert did not authenticate TLS cert".into(),
));
}
if identity_key != peer.get_ed_identity() {
return Err(Error::ChanProto("Peer ed25519 id not as expected".into()));
}
Ok(VerifiedChannel {
link_protocol: self.link_protocol,
tls: self.tls,

View File

@ -12,10 +12,10 @@ pub enum Error {
/// An error that occurred in the tor_bytes crate while decoding an
/// object.
#[error("parsing error: {0}")]
BytesErr(#[source] tor_bytes::Error),
BytesErr(#[from] tor_bytes::Error),
/// An error that occurred from the io system.
#[error("io error: {0}")]
IoErr(#[source] std::io::Error),
IoErr(#[from] std::io::Error),
/// Somebody asked for a key that we didn't have.
#[error("specified key was missing")]
MissingKey,
@ -39,15 +39,3 @@ pub enum Error {
#[error("channel protocol violation: {0}")]
ChanProto(String),
}
impl From<tor_bytes::Error> for Error {
fn from(e: tor_bytes::Error) -> Self {
Error::BytesErr(e)
}
}
impl From<std::io::Error> for Error {
fn from(e: std::io::Error) -> Self {
Error::IoErr(e)
}
}