Add handling of certs with multiple common names; Check certs for

validity against current date;
This commit is contained in:
Nathan Fisher 2023-05-25 10:22:26 -04:00
parent 0d15b8d24b
commit 2b7119610c
4 changed files with 39 additions and 16 deletions

View file

@ -4,6 +4,7 @@ use {std::fmt, x509_parser::prelude::X509Error};
/// Errors which can occur when fingerprinting a certificate
pub enum Error {
Fmt,
InvalidForDate,
X509(X509Error),
}
@ -11,6 +12,7 @@ impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Fmt => write!(f, "FingerPrint: format error"),
Self::InvalidForDate => write!(f, "FingerPrint: invalid for date"),
Self::X509(e) => write!(f, "FingerPrint: {e}"),
}
}
@ -19,8 +21,8 @@ impl fmt::Display for Error {
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),
_ => None,
}
}
}

View file

@ -1,21 +1,37 @@
use {digest::Digest, rustls::Certificate, sha2::Sha256, std::fmt::Write, x509_parser::prelude::*};
use {digest::Digest, rustls::Certificate, sha2::Sha256, std::{io::Read, fmt::Write}, x509_parser::prelude::*};
mod error;
pub use error::Error;
/// Creates an sha256 fingerprint for a certificate
pub trait Fingerprint {
pub trait GetFingerprint {
type Error;
fn fingerprint(&self) -> Result<(String, String), Self::Error>;
fn fingerprint(&self) -> Result<Fingerprint, Self::Error>;
}
impl Fingerprint for Certificate {
pub struct Fingerprint {
pub names: Vec<String>,
pub fingerprint: String,
}
impl GetFingerprint for Certificate {
type Error = Error;
fn fingerprint(&self) -> Result<(String, String), Self::Error> {
fn fingerprint(&self) -> Result<Fingerprint, Self::Error> {
let (_, pk) = X509Certificate::from_der(self.as_ref())?;
let subject = pk.subject().to_string();
let subject = pk.subject();
let mut names = vec![];
subject.iter_common_name().for_each(|n| {
let mut val = n.attr_value().data;
let mut name = String::new();
if let Ok(_) = val.read_to_string(&mut name) {
names.push(name);
}
});
if !pk.validity().is_valid() {
return Err(Error::InvalidForDate);
}
let key = pk.public_key().subject_public_key.as_ref();
let mut hasher = Sha256::new();
hasher.update(key);
@ -24,6 +40,6 @@ impl Fingerprint for Certificate {
for c in res {
write!(s, "{c:02x}")?;
}
Ok((subject[3..].to_string(), s))
Ok(Fingerprint { names, fingerprint: s })
}
}

View file

@ -1,6 +1,6 @@
pub use super::{
certificate_store::CertificateStore,
fingerprint::{Error as FingerprintError, Fingerprint},
fingerprint::{Error as FingerprintError, Fingerprint, GetFingerprint},
host::{Error as ParseHostError, Host},
//receiver,
request::{Error as ParseRequestError, Request},

View file

@ -1,5 +1,5 @@
use {
crate::prelude::{CertificateStore, Fingerprint},
crate::prelude::{CertificateStore, Fingerprint, GetFingerprint},
rustls::{
client::{ServerCertVerified, ServerCertVerifier},
Certificate,
@ -40,17 +40,22 @@ impl<S: CertificateStore> ServerCertVerifier for Verifier<S> {
let mut store = self.store.lock().unwrap();
if let Some(fingerprint) = store.get_certificate(&name) {
// TODO: needs a lot more checking for certificate validity
if fingerprint == fp.1 && name == fp.0 {
if fingerprint == fp.fingerprint {
for n in fp.names {
if n == name {
return Ok(ServerCertVerified::assertion());
}
}
Err(rustls::Error::InvalidCertificate(rustls::CertificateError::NotValidForName))
} else {
Err(rustls::Error::InvalidCertificate(rustls::CertificateError::NotValidForName))
}
} else {
if !store.contains_certificate(&name) {
let _key = store.borrow_mut().insert_certificate(&name, &fp.1);
let _key = store.borrow_mut().insert_certificate(&name, &fp.fingerprint);
}
return Ok(ServerCertVerified::assertion());
}
Err(rustls::Error::General(
"Unrecognized certificate".to_string(),
))
}
}