Finish implementing TOFU for Verifier;

TODO: additional checks for certificate validity;
This commit is contained in:
Nathan Fisher 2023-05-24 17:37:44 -04:00
parent ac5f2c21eb
commit 0d15b8d24b
6 changed files with 73 additions and 36 deletions

36
src/certificate_store.rs Normal file
View file

@ -0,0 +1,36 @@
use std::collections::{BTreeMap, HashMap};
/// An item which stores known certificates
pub trait CertificateStore: Send + Sync {
fn get_certificate(&self, host: &str) -> Option<String>;
fn insert_certificate(&mut self, host: &str, fingerprint: &str) -> Option<String>;
fn contains_certificate(&self, host: &str) -> bool;
}
impl CertificateStore for HashMap<String, String> {
fn get_certificate(&self, host: &str) -> Option<String> {
self.get(host).cloned()
}
fn insert_certificate(&mut self, host: &str, fingerprint: &str) -> Option<String> {
self.insert(host.to_string(), fingerprint.to_string())
}
fn contains_certificate(&self, host: &str) -> bool {
self.contains_key(host)
}
}
impl CertificateStore for BTreeMap<String, String> {
fn get_certificate(&self, host: &str) -> Option<String> {
self.get(host).cloned()
}
fn insert_certificate(&mut self, host: &str, fingerprint: &str) -> Option<String> {
self.insert(host.to_string(), fingerprint.to_string())
}
fn contains_certificate(&self, host: &str) -> bool {
self.contains_key(host)
}
}

View file

@ -1,8 +1,4 @@
use digest::Digest; use {digest::Digest, rustls::Certificate, sha2::Sha256, std::fmt::Write, x509_parser::prelude::*};
use rustls::Certificate;
use sha2::Sha256;
use std::fmt::Write;
use x509_parser::prelude::*;
mod error; mod error;
pub use error::Error; pub use error::Error;

View file

@ -1,8 +1,9 @@
#![warn(clippy::all, clippy::pedantic)] #![warn(clippy::all, clippy::pedantic)]
mod certificate_store;
mod fingerprint; mod fingerprint;
mod host; mod host;
pub mod prelude; pub mod prelude;
pub mod receiver; mod receiver;
mod request; mod request;
mod response; mod response;
mod sender; mod sender;

View file

@ -1,10 +1,11 @@
pub use super::{ pub use super::{
certificate_store::CertificateStore,
fingerprint::{Error as FingerprintError, Fingerprint}, fingerprint::{Error as FingerprintError, Fingerprint},
host::{Error as ParseHostError, Host}, host::{Error as ParseHostError, Host},
receiver, //receiver,
request::{Error as ParseRequestError, Request}, request::{Error as ParseRequestError, Request},
response::{Error as ParseResponseError, Response}, response::{Error as ParseResponseError, Response},
sender::{CertificateStore, Error as SenderError, Sender, Verifier}, sender::{Error as SenderError, Sender, Verifier},
status::{ status::{
AuthenticationFailure, Error as ParseStatusError, PermanentFailure, Redirect, Status, AuthenticationFailure, Error as ParseStatusError, PermanentFailure, Redirect, Status,
TemporaryFailure, TemporaryFailure,

View file

@ -1,9 +1,8 @@
pub use self::{ pub use self::{error::Error, verifier::Verifier};
error::Error, use {
verifier::{CertificateStore, Verifier}, crate::prelude::{CertificateStore, Request, Response},
std::io::{Read, Write},
}; };
use crate::{request::Request, response::Response};
use std::io::{Read, Write};
mod error; mod error;
mod verifier; mod verifier;

View file

@ -1,15 +1,15 @@
use crate::fingerprint::Fingerprint; use {
use rustls::{ crate::prelude::{CertificateStore, Fingerprint},
rustls::{
client::{ServerCertVerified, ServerCertVerifier}, client::{ServerCertVerified, ServerCertVerifier},
Certificate, Certificate,
},
std::{
borrow::BorrowMut,
sync::{Arc, Mutex},
time,
},
}; };
use std::sync::{Arc, Mutex};
/// An item which stores known certificates
pub trait CertificateStore: Send + Sync {
fn get(&self, host: &str) -> Option<String>;
fn insert(&mut self, host: &str, fingerprint: &str);
}
#[derive(Debug)] #[derive(Debug)]
/// A verifier is used to verify certificates sent by the receiving server /// A verifier is used to verify certificates sent by the receiving server
@ -27,7 +27,7 @@ impl<S: CertificateStore> ServerCertVerifier for Verifier<S> {
server_name: &rustls::ServerName, server_name: &rustls::ServerName,
_scts: &mut dyn Iterator<Item = &[u8]>, _scts: &mut dyn Iterator<Item = &[u8]>,
_ocsp_response: &[u8], _ocsp_response: &[u8],
_now: std::time::SystemTime, _now: time::SystemTime,
) -> Result<ServerCertVerified, rustls::Error> { ) -> Result<ServerCertVerified, rustls::Error> {
let fp = end_entity let fp = end_entity
.fingerprint() .fingerprint()
@ -37,18 +37,16 @@ impl<S: CertificateStore> ServerCertVerifier for Verifier<S> {
rustls::ServerName::IpAddress(ip) => ip.to_string(), rustls::ServerName::IpAddress(ip) => ip.to_string(),
_ => todo!(), _ => todo!(),
}; };
if let Some(fingerprint) = match server_name { let mut store = self.store.lock().unwrap();
rustls::ServerName::DnsName(n) => self.store.lock().unwrap().get(n.as_ref()), if let Some(fingerprint) = store.get_certificate(&name) {
rustls::ServerName::IpAddress(ip) => self.store.lock().unwrap().get(&ip.to_string()), // TODO: needs a lot more checking for certificate validity
_ => todo!(),
} {
if fingerprint == fp.1 && name == fp.0 { if fingerprint == fp.1 && name == fp.0 {
return Ok(ServerCertVerified::assertion()); return Ok(ServerCertVerified::assertion());
} }
} else { } else {
// todo: need a way to update `self.store`. Probably will require if !store.contains_certificate(&name) {
// an Arc<Mutex<T>> for interior mutability. let _key = store.borrow_mut().insert_certificate(&name, &fp.1);
// UPDATE: Now wrapped in Arc<Mutex<T>> }
} }
Err(rustls::Error::General( Err(rustls::Error::General(
"Unrecognized certificate".to_string(), "Unrecognized certificate".to_string(),
@ -56,10 +54,16 @@ impl<S: CertificateStore> ServerCertVerifier for Verifier<S> {
} }
} }
impl<T: CertificateStore> Verifier<T> { impl<T: CertificateStore> From<T> for Verifier<T> {
pub fn new(store: T) -> Self { fn from(value: T) -> Self {
Self { Self {
store: Arc::new(Mutex::new(store)), store: Arc::new(Mutex::new(value)),
} }
} }
} }
impl<T: CertificateStore> Verifier<T> {
pub fn new(store: T) -> Self {
store.into()
}
}