feat: custom cert verifier to support self-signed, fingerprint generator

This commit is contained in:
DarkCat09 2024-08-02 21:22:19 +04:00
parent 86fb310e71
commit 19e1148989
Signed by: DarkCat09
GPG key ID: 0A26CD5B3345D6E3
7 changed files with 304 additions and 0 deletions

38
src/certs/fingerprint.rs Normal file
View file

@ -0,0 +1,38 @@
use base64ct::{Base64Unpadded, Encoding};
use sha2::{Digest, Sha256, Sha512};
use super::verifier::CertificateDer;
const SHA256_B64_LEN: usize = 44; // 4 * ((256 / 8) as f64 / 3 as f64).ceil()
const SHA512_B64_LEN: usize = 88; // 4 * ((512 / 8) as f64 / 3 as f64).ceil()
pub enum Algorithm {
Sha256,
Sha512,
}
pub fn generate_fingerprint(
cert: &CertificateDer,
algo: Algorithm,
) -> Result<String, base64ct::InvalidLengthError> {
match algo {
Algorithm::Sha256 => {
let mut hasher = Sha256::new();
for chunk in cert.chunks(128) {
hasher.update(chunk);
}
let bin = hasher.finalize();
let mut buf = [0; SHA256_B64_LEN];
Base64Unpadded::encode(&bin, &mut buf).map(|hash| hash.to_owned())
}
Algorithm::Sha512 => {
let mut hasher = Sha512::new();
for chunk in cert.chunks(128) {
hasher.update(chunk);
}
let bin = hasher.finalize();
let mut buf = [0; SHA512_B64_LEN];
Base64Unpadded::encode(&bin, &mut buf).map(|hash| hash.to_owned())
}
}
}

59
src/certs/insecure.rs Normal file
View file

@ -0,0 +1,59 @@
use tokio_rustls::rustls::{
self,
client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier},
crypto::CryptoProvider,
};
#[derive(Debug)]
pub struct AllowAllCertVerifier(CryptoProvider);
impl AllowAllCertVerifier {
fn yes_i_know_what_i_am_doing(provider: CryptoProvider) -> Self {
AllowAllCertVerifier(provider)
}
}
impl ServerCertVerifier for AllowAllCertVerifier {
fn verify_server_cert(
&self,
_end_entity: &rustls::pki_types::CertificateDer<'_>,
_intermediates: &[rustls::pki_types::CertificateDer<'_>],
_server_name: &rustls::pki_types::ServerName<'_>,
_ocsp_response: &[u8],
_now: rustls::pki_types::UnixTime,
) -> Result<ServerCertVerified, rustls::Error> {
Ok(ServerCertVerified::assertion())
}
fn verify_tls12_signature(
&self,
message: &[u8],
cert: &rustls::pki_types::CertificateDer<'_>,
dss: &rustls::DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, rustls::Error> {
rustls::crypto::verify_tls12_signature(
message,
cert,
dss,
&self.0.signature_verification_algorithms,
)
}
fn verify_tls13_signature(
&self,
message: &[u8],
cert: &rustls::pki_types::CertificateDer<'_>,
dss: &rustls::DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, rustls::Error> {
rustls::crypto::verify_tls13_signature(
message,
cert,
dss,
&self.0.signature_verification_algorithms,
)
}
fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
self.0.signature_verification_algorithms.supported_schemes()
}
}

3
src/certs/mod.rs Normal file
View file

@ -0,0 +1,3 @@
pub mod fingerprint;
pub mod insecure;
pub mod verifier;

122
src/certs/verifier.rs Normal file
View file

@ -0,0 +1,122 @@
pub use tokio_rustls::rustls::pki_types::{CertificateDer, ServerName, UnixTime};
use tokio_rustls::rustls::{
self,
client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier},
};
pub trait SelfsignedCertVerifier: Send + Sync {
fn verify(
&self,
cert: &CertificateDer,
host: &str,
now: UnixTime,
) -> Result<bool, rustls::Error>;
}
pub struct SelfsignedCert {
algo: super::fingerprint::Algorithm,
fingerprint: String,
expires: u64,
}
pub struct CustomCertVerifier {
provider: rustls::crypto::CryptoProvider,
webpki_verifier: Option<rustls::client::WebPkiServerVerifier>,
ss_allowed: bool,
ss_verifier: dyn SelfsignedCertVerifier,
}
impl ServerCertVerifier for CustomCertVerifier {
fn verify_server_cert(
&self,
end_entity: &CertificateDer<'_>,
intermediates: &[CertificateDer<'_>],
server_name: &ServerName<'_>,
ocsp_response: &[u8],
now: UnixTime,
) -> Result<ServerCertVerified, rustls::Error> {
// if webpki CA certs enabled
if let Some(wv) = &self.webpki_verifier {
match wv.verify_server_cert(end_entity, intermediates, server_name, ocsp_response, now)
{
Ok(verified) => {
return Ok(verified);
}
Err(
e @ rustls::Error::InvalidCertificate(rustls::CertificateError::UnknownIssuer),
) => {
if !self.ss_allowed {
return Err(e);
}
// go ahead, verify as self-signed
}
Err(e) => {
// any other error, probably related to invalid cert
return Err(e);
}
}
}
// TODO: certificate validation when webpki_verifier is not used
// if self-signed certs enabled
if self.ss_allowed {
// TODO: check if expired or provide handy API to check it
// (probably with rustls-webpki's webpki::Cert)
if self
.ss_verifier
.verify(end_entity, &server_name.to_str().as_ref(), now)?
{
return Ok(ServerCertVerified::assertion());
}
}
// both disabled (shouldn't happen)
Err(rustls::Error::UnsupportedNameType) // not sure if chosen correct enum item
}
fn verify_tls12_signature(
&self,
message: &[u8],
cert: &CertificateDer<'_>,
dss: &rustls::DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, rustls::Error> {
rustls::crypto::verify_tls12_signature(
message,
cert,
dss,
&self.provider.signature_verification_algorithms,
)
}
fn verify_tls13_signature(
&self,
message: &[u8],
cert: &CertificateDer<'_>,
dss: &rustls::DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, rustls::Error> {
rustls::crypto::verify_tls13_signature(
message,
cert,
dss,
&self.provider.signature_verification_algorithms,
)
}
fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
self.provider
.signature_verification_algorithms
.supported_schemes()
}
}
impl std::fmt::Debug for CustomCertVerifier {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"CustomCertVerifier {{ provider: {:?}, webpki_verifier: {:?} }}",
self.provider, self.webpki_verifier
)
}
}

View file

@ -1,3 +1,4 @@
pub mod certs;
pub mod client;
pub mod error;
pub mod response;