Finish implementing TOFU for Verifier;
TODO: additional checks for certificate validity;
This commit is contained in:
parent
ac5f2c21eb
commit
0d15b8d24b
6 changed files with 73 additions and 36 deletions
36
src/certificate_store.rs
Normal file
36
src/certificate_store.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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> From<T> for Verifier<T> {
|
||||||
|
fn from(value: T) -> Self {
|
||||||
|
Self {
|
||||||
|
store: Arc::new(Mutex::new(value)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: CertificateStore> Verifier<T> {
|
impl<T: CertificateStore> Verifier<T> {
|
||||||
pub fn new(store: T) -> Self {
|
pub fn new(store: T) -> Self {
|
||||||
Self {
|
store.into()
|
||||||
store: Arc::new(Mutex::new(store)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue