Add Store, Verifier and Fingerprint types for handling

certificates
This commit is contained in:
Nathan Fisher 2023-05-22 20:47:20 -04:00
parent 8d6c21325f
commit 7bbdb1381d
7 changed files with 125 additions and 2 deletions

View file

@ -3,7 +3,14 @@ name = "dory"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
[dependencies]
digest = "0.10.7"
sha2 = "0.10.6"
x509-parser = "0.15.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies.rustls]
rustls = "0.21.1" version = "0.21.1"
features = [ "dangerous_configuration" ]

3
src/client.rs Normal file
View file

@ -0,0 +1,3 @@
pub mod store;
pub mod verifier;

5
src/client/store.rs Normal file
View file

@ -0,0 +1,5 @@
pub trait CertificateStore {
fn get(&self, host: &str) -> Option<String>;
fn insert(&mut self, host: &str, fingerprint: &str);
}

39
src/client/verifier.rs Normal file
View file

@ -0,0 +1,39 @@
use crate::fingerprint::Fingerprint;
use rustls::{client::{ServerCertVerified, ServerCertVerifier}, Certificate};
use super::store::CertificateStore;
pub struct Verifier<'a, T: CertificateStore> {
store: &'a T,
}
impl<'a, T: CertificateStore + Sync> ServerCertVerifier for Verifier<'a, T> {
fn verify_server_cert(
&self,
end_entity: &Certificate,
_intermediates: &[Certificate],
server_name: &rustls::ServerName,
_scts: &mut dyn Iterator<Item = &[u8]>,
_ocsp_response: &[u8],
_now: std::time::SystemTime,
) -> Result<ServerCertVerified, rustls::Error> {
let fp = end_entity.fingerprint().map_err(|e| rustls::Error::General(e.to_string()))?;
let name = match server_name {
rustls::ServerName::DnsName(n) => n.as_ref().to_string(),
rustls::ServerName::IpAddress(ip) => ip.to_string(),
_ => todo!()
};
if let Some(fingerprint) = match server_name {
rustls::ServerName::DnsName(n) => self.store.get(n.as_ref()),
rustls::ServerName::IpAddress(ip) => self.store.get(&ip.to_string()),
_ => todo!(),
} {
if fingerprint == fp.1 && name == fp.0 {
return Ok(ServerCertVerified::assertion());
}
} else {
// todo: need a way to update `self.store`. Probably will require
// an Arc<Mutex<T>> for interior mutability
}
return Err(rustls::Error::General("Unrecognized certificate".to_string()));
}
}

66
src/fingerprint.rs Normal file
View file

@ -0,0 +1,66 @@
use digest::Digest;
use rustls::Certificate;
use sha2::Sha256;
use std::fmt::{Write, self};
use x509_parser::prelude::*;
pub trait Fingerprint {
type Error;
fn fingerprint(&self) -> Result<(String, String), Self::Error>;
}
#[derive(Clone, Debug, PartialEq)]
pub enum Error {
Fmt,
X509(X509Error),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Fmt => write!(f, "FingerPrint: format error"),
Self::X509(e) => write!(f, "FingerPrint: {e}"),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Fmt => None,
Self::X509(e) => Some(e),
}
}
}
impl From<fmt::Error> for Error {
fn from(_value: fmt::Error) -> Self {
Self::Fmt
}
}
impl From<x509_parser::nom::Err<x509_parser::error::X509Error>> for Error {
fn from(value: x509_parser::nom::Err<x509_parser::error::X509Error>) -> Self {
Self::X509(value.into())
}
}
impl Fingerprint for Certificate {
type Error = Error;
fn fingerprint(&self) -> Result<(String, String), Self::Error> {
let (_, pk) = X509Certificate::from_der(self.as_ref())?;
let subject = pk.subject().to_string();
let key = pk.public_key().subject_public_key.as_ref();
let mut hasher = Sha256::new();
hasher.update(key);
let res = hasher.finalize();
let mut s = String::with_capacity(res.len());
for c in res {
write!(s, "{c:02x}")?;
}
Ok((subject[3..].to_string(), s))
}
}

View file

@ -1,5 +1,8 @@
#![warn(clippy::all, clippy::pedantic)] #![warn(clippy::all, clippy::pedantic)]
pub mod client;
pub mod fingerprint;
pub mod host; pub mod host;
pub mod request; pub mod request;
pub mod response; pub mod response;
pub mod server;
pub mod status; pub mod status;

0
src/server.rs Normal file
View file