use anyhow::Result;
use rustls::{
client::danger::{ServerCertVerified, ServerCertVerifier},
pki_types::{CertificateDer, PrivateKeyDer, ServerName, UnixTime},
};
use std::{fs::File, io::BufReader, path::Path, sync::Arc};
pub fn load_certs(path: &Path) -> Result<Vec<CertificateDer<'static>>> {
if !path.exists() {
return Err(anyhow::anyhow!("Cert not found in path: {path:?}"));
}
rustls_pemfile::certs(&mut BufReader::new(File::open(path)?))
.collect::<std::io::Result<_>>()
.map_err(anyhow::Error::from)
}
pub fn load_keys(path: &Path) -> Result<PrivateKeyDer<'static>> {
if !path.exists() {
return Err(anyhow::anyhow!("Private key not found in path: {path:?}"));
}
rustls_pemfile::private_key(&mut BufReader::new(File::open(path)?))?
.ok_or_else(|| anyhow::anyhow!("Private key returned None"))
}
pub fn cert_from_str(cert: &str) -> Result<Vec<CertificateDer<'static>>> {
rustls_pemfile::certs(&mut cert.as_bytes())
.collect::<std::io::Result<_>>()
.map_err(anyhow::Error::from)
}
pub fn key_from_str(key: &str) -> Result<PrivateKeyDer<'static>> {
rustls_pemfile::private_key(&mut key.as_bytes())?
.ok_or_else(|| anyhow::anyhow!("Private ket returned None"))
}
pub fn compute_fingerprint(cert: &CertificateDer<'_>) -> String {
let hash = ring::digest::digest(&ring::digest::SHA256, cert.as_ref());
hash.as_ref().iter().map(|b| format!("{b:02x}")).collect()
}
#[derive(Debug)]
pub struct FingerprintVerifier {
expected: String,
provider: Arc<rustls::crypto::CryptoProvider>,
}
impl FingerprintVerifier {
pub fn new(fingerprint: &str) -> Result<Arc<Self>> {
let normalized = fingerprint.to_lowercase().replace([':', ' '], "");
if normalized.len() != 64 || !normalized.chars().all(|c| c.is_ascii_hexdigit()) {
return Err(anyhow::anyhow!(
"Invalid server fingerprint: must be a 64-character hex SHA-256 digest \
(colons/spaces are accepted). Got {normalized:?}"
));
}
Ok(Arc::new(Self {
expected: normalized,
provider: Arc::new(rustls::crypto::ring::default_provider()),
}))
}
fn check(&self, cert: &CertificateDer<'_>) -> Result<(), rustls::Error> {
let got = compute_fingerprint(cert);
if got == self.expected {
Ok(())
} else {
Err(rustls::Error::General(format!(
"TLS certificate fingerprint mismatch: expected {}, got {got}",
self.expected,
)))
}
}
}
impl rustls::client::danger::ServerCertVerifier for FingerprintVerifier {
fn verify_server_cert(
&self,
end_entity: &CertificateDer<'_>,
_intermediates: &[CertificateDer<'_>],
_server_name: &ServerName<'_>,
_ocsp_response: &[u8],
_now: UnixTime,
) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
self.check(end_entity)?;
Ok(rustls::client::danger::ServerCertVerified::assertion())
}
fn verify_tls12_signature(
&self,
message: &[u8],
cert: &CertificateDer<'_>,
dss: &rustls::DigitallySignedStruct,
) -> Result<rustls::client::danger::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<rustls::client::danger::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()
}
}
#[derive(Debug)]
pub struct NoCertVerification;
impl ServerCertVerifier for NoCertVerification {
fn verify_server_cert(
&self,
_end_entity: &tokio_rustls::rustls::pki_types::CertificateDer<'_>,
_intermediates: &[tokio_rustls::rustls::pki_types::CertificateDer<'_>],
_server_name: &tokio_rustls::rustls::pki_types::ServerName<'_>,
_ocsp_response: &[u8],
_now: tokio_rustls::rustls::pki_types::UnixTime,
) -> Result<tokio_rustls::rustls::client::danger::ServerCertVerified, tokio_rustls::rustls::Error>
{
Ok(ServerCertVerified::assertion())
}
fn verify_tls12_signature(
&self,
_message: &[u8],
_cert: &tokio_rustls::rustls::pki_types::CertificateDer<'_>,
_dss: &tokio_rustls::rustls::DigitallySignedStruct,
) -> Result<
tokio_rustls::rustls::client::danger::HandshakeSignatureValid,
tokio_rustls::rustls::Error,
> {
Ok(tokio_rustls::rustls::client::danger::HandshakeSignatureValid::assertion())
}
fn verify_tls13_signature(
&self,
_message: &[u8],
_cert: &tokio_rustls::rustls::pki_types::CertificateDer<'_>,
_dss: &tokio_rustls::rustls::DigitallySignedStruct,
) -> Result<
tokio_rustls::rustls::client::danger::HandshakeSignatureValid,
tokio_rustls::rustls::Error,
> {
Ok(tokio_rustls::rustls::client::danger::HandshakeSignatureValid::assertion())
}
fn supported_verify_schemes(&self) -> Vec<tokio_rustls::rustls::SignatureScheme> {
vec![
tokio_rustls::rustls::SignatureScheme::RSA_PKCS1_SHA1,
tokio_rustls::rustls::SignatureScheme::RSA_PKCS1_SHA256,
tokio_rustls::rustls::SignatureScheme::ECDSA_NISTP256_SHA256,
tokio_rustls::rustls::SignatureScheme::RSA_PKCS1_SHA384,
tokio_rustls::rustls::SignatureScheme::ECDSA_NISTP384_SHA384,
tokio_rustls::rustls::SignatureScheme::RSA_PKCS1_SHA512,
tokio_rustls::rustls::SignatureScheme::ECDSA_NISTP521_SHA512,
tokio_rustls::rustls::SignatureScheme::RSA_PSS_SHA256,
tokio_rustls::rustls::SignatureScheme::RSA_PSS_SHA384,
tokio_rustls::rustls::SignatureScheme::RSA_PSS_SHA512,
tokio_rustls::rustls::SignatureScheme::ED25519,
tokio_rustls::rustls::SignatureScheme::ED448,
]
}
}
#[derive(Debug)]
pub struct SkipQuicServerVerification(Arc<rustls::crypto::CryptoProvider>);
impl SkipQuicServerVerification {
pub fn new() -> Arc<Self> {
Arc::new(Self(Arc::new(rustls::crypto::ring::default_provider())))
}
}
impl rustls::client::danger::ServerCertVerifier for SkipQuicServerVerification {
fn verify_server_cert(
&self,
_end_entity: &CertificateDer<'_>,
_intermediates: &[CertificateDer<'_>],
_server_name: &ServerName<'_>,
_ocsp: &[u8],
_now: UnixTime,
) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
Ok(rustls::client::danger::ServerCertVerified::assertion())
}
fn verify_tls12_signature(
&self,
message: &[u8],
cert: &CertificateDer<'_>,
dss: &rustls::DigitallySignedStruct,
) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
rustls::crypto::verify_tls12_signature(
message,
cert,
dss,
&self.0.signature_verification_algorithms,
)
}
fn verify_tls13_signature(
&self,
message: &[u8],
cert: &CertificateDer<'_>,
dss: &rustls::DigitallySignedStruct,
) -> Result<rustls::client::danger::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()
}
}