use std::sync::Arc;
use rcgen::{CertificateParams, KeyPair};
use rustls::pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer, ServerName, UnixTime};
use sha2::{Digest, Sha256};
fn ensure_crypto_provider() {
let _ = rustls::crypto::ring::default_provider().install_default();
}
pub fn generate_self_signed_cert()
-> Result<(rustls::ServerConfig, Vec<u8>, String), Box<dyn std::error::Error + Send + Sync>> {
ensure_crypto_provider();
let mut params = CertificateParams::new(vec!["oaat-endpoint".into()])?;
params.distinguished_name.push(
rcgen::DnType::CommonName,
rcgen::DnValue::Utf8String("OAAT Endpoint".into()),
);
let key_pair = KeyPair::generate()?;
let cert = params.self_signed(&key_pair)?;
let cert_der = cert.der().to_vec();
let fingerprint = cert_fingerprint(&cert_der);
let key_der = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from(key_pair.serialize_der()));
let cert_chain = vec![CertificateDer::from(cert_der.clone())];
let server_config = rustls::ServerConfig::builder()
.with_no_client_auth()
.with_single_cert(cert_chain, key_der)?;
Ok((server_config, cert_der, fingerprint))
}
pub fn make_client_config_tofu() -> rustls::ClientConfig {
ensure_crypto_provider();
rustls::ClientConfig::builder()
.dangerous()
.with_custom_certificate_verifier(Arc::new(TofuVerifier))
.with_no_client_auth()
}
pub fn cert_fingerprint(cert_der: &[u8]) -> String {
let hash = Sha256::digest(cert_der);
hash.iter()
.map(|b| format!("{b:02x}"))
.collect::<Vec<_>>()
.join(":")
}
#[derive(Debug)]
struct TofuVerifier;
impl rustls::client::danger::ServerCertVerifier for TofuVerifier {
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> {
let fp = cert_fingerprint(end_entity.as_ref());
let _ = fp;
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> {
Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
}
fn verify_tls13_signature(
&self,
_message: &[u8],
_cert: &CertificateDer<'_>,
_dss: &rustls::DigitallySignedStruct,
) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
}
fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
rustls::crypto::ring::default_provider()
.signature_verification_algorithms
.supported_schemes()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn generate_cert_and_fingerprint() {
let (config, cert_der, fingerprint) = generate_self_signed_cert().unwrap();
assert!(!cert_der.is_empty());
assert_eq!(fingerprint.len(), 95);
assert!(fingerprint.contains(':'));
let _ = config;
}
#[test]
fn fingerprint_deterministic() {
let data = b"test certificate data";
let fp1 = cert_fingerprint(data);
let fp2 = cert_fingerprint(data);
assert_eq!(fp1, fp2);
}
#[test]
fn tofu_client_config_builds() {
let config = make_client_config_tofu();
let _ = config;
}
}