Implement most of client-side channel cert validation.
This commit is contained in:
parent
e395ad5801
commit
8d01a0a808
|
@ -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.");
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue