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 /// Errors which can occur when fingerprinting a certificate
pub enum Error { pub enum Error {
Fmt, Fmt,
InvalidForDate,
X509(X509Error), X509(X509Error),
} }
@ -11,6 +12,7 @@ impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Self::Fmt => write!(f, "FingerPrint: format error"), Self::Fmt => write!(f, "FingerPrint: format error"),
Self::InvalidForDate => write!(f, "FingerPrint: invalid for date"),
Self::X509(e) => write!(f, "FingerPrint: {e}"), Self::X509(e) => write!(f, "FingerPrint: {e}"),
} }
} }
@ -19,8 +21,8 @@ impl fmt::Display for Error {
impl std::error::Error for Error { impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self { match self {
Self::Fmt => None,
Self::X509(e) => Some(e), 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; mod error;
pub use error::Error; pub use error::Error;
/// Creates an sha256 fingerprint for a certificate /// Creates an sha256 fingerprint for a certificate
pub trait Fingerprint { pub trait GetFingerprint {
type Error; 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; 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 (_, 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 key = pk.public_key().subject_public_key.as_ref();
let mut hasher = Sha256::new(); let mut hasher = Sha256::new();
hasher.update(key); hasher.update(key);
@ -24,6 +40,6 @@ impl Fingerprint for Certificate {
for c in res { for c in res {
write!(s, "{c:02x}")?; write!(s, "{c:02x}")?;
} }
Ok((subject[3..].to_string(), s)) Ok(Fingerprint { names, fingerprint: s })
} }
} }

View file

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

View file

@ -1,5 +1,5 @@
use { use {
crate::prelude::{CertificateStore, Fingerprint}, crate::prelude::{CertificateStore, Fingerprint, GetFingerprint},
rustls::{ rustls::{
client::{ServerCertVerified, ServerCertVerifier}, client::{ServerCertVerified, ServerCertVerifier},
Certificate, Certificate,
@ -40,17 +40,22 @@ impl<S: CertificateStore> ServerCertVerifier for Verifier<S> {
let mut store = self.store.lock().unwrap(); let mut store = self.store.lock().unwrap();
if let Some(fingerprint) = store.get_certificate(&name) { if let Some(fingerprint) = store.get_certificate(&name) {
// TODO: needs a lot more checking for certificate validity // TODO: needs a lot more checking for certificate validity
if fingerprint == fp.1 && name == fp.0 { if fingerprint == fp.fingerprint {
return Ok(ServerCertVerified::assertion()); 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 { } else {
if !store.contains_certificate(&name) { 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(),
))
} }
} }