feat: custom cert verifier to support self-signed, fingerprint generator
This commit is contained in:
parent
86fb310e71
commit
19e1148989
7 changed files with 304 additions and 0 deletions
38
src/certs/fingerprint.rs
Normal file
38
src/certs/fingerprint.rs
Normal 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
59
src/certs/insecure.rs
Normal 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
3
src/certs/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
pub mod fingerprint;
|
||||
pub mod insecure;
|
||||
pub mod verifier;
|
122
src/certs/verifier.rs
Normal file
122
src/certs/verifier.rs
Normal 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
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
pub mod certs;
|
||||
pub mod client;
|
||||
pub mod error;
|
||||
pub mod response;
|
||||
|
|
Loading…
Add table
Reference in a new issue