use std::fmt;
use std::sync::{Arc, Mutex};
use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier};
use rustls::crypto::{CryptoProvider, verify_tls12_signature, verify_tls13_signature};
use rustls::pki_types::{CertificateDer, PrivateKeyDer, ServerName, UnixTime};
use rustls::{ClientConfig, DigitallySignedStruct, SignatureScheme};
use sha1::{Digest, Sha1};
#[derive(Clone, PartialEq, Eq)]
pub struct ServerCert {
pub chain: Vec<CertificateDer<'static>>,
pub sha1_hex: String,
pub sha256_hex: String,
}
impl fmt::Debug for ServerCert {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ServerCert")
.field("sha1_hex", &self.sha1_hex)
.field("sha256_hex", &self.sha256_hex)
.finish_non_exhaustive()
}
}
impl ServerCert {
fn new(end_entity: &CertificateDer<'_>, intermediates: &[CertificateDer<'_>]) -> Self {
let mut chain = vec![end_entity.clone().into_owned()];
chain.extend(intermediates.iter().map(|c| c.clone().into_owned()));
Self {
chain,
sha1_hex: hex(&Sha1::digest(end_entity.as_ref())),
sha256_hex: hex(&sha256(end_entity.as_ref())),
}
}
}
pub type CertVerifier = Arc<dyn Fn(&ServerCert) -> bool + Send + Sync>;
pub struct ClientIdentity {
pub chain: Vec<CertificateDer<'static>>,
pub key: PrivateKeyDer<'static>,
}
impl fmt::Debug for ClientIdentity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ClientIdentity")
.field("chain_len", &self.chain.len())
.finish_non_exhaustive()
}
}
pub fn cert_hash(der: &[u8]) -> String {
hex(&Sha1::digest(der))
}
fn hex(bytes: &[u8]) -> String {
bytes.iter().map(|b| format!("{b:02x}")).collect()
}
fn sha256(data: &[u8]) -> Vec<u8> {
let suite = rustls::crypto::ring::cipher_suite::TLS13_AES_128_GCM_SHA256;
match suite.tls13() {
Some(tls13) => tls13.common.hash_provider.hash(data).as_ref().to_vec(),
None => unreachable!("TLS13_AES_128_GCM_SHA256 is a TLS 1.3 suite"),
}
}
#[derive(Debug)]
struct Verifier {
decide: CertVerifierDebug,
provider: Arc<CryptoProvider>,
seen: Mutex<Option<ServerCert>>,
}
struct CertVerifierDebug(CertVerifier);
impl fmt::Debug for CertVerifierDebug {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("CertVerifier")
}
}
impl ServerCertVerifier for Verifier {
fn verify_server_cert(
&self,
end_entity: &CertificateDer<'_>,
intermediates: &[CertificateDer<'_>],
_server_name: &ServerName<'_>,
_ocsp_response: &[u8],
_now: UnixTime,
) -> Result<ServerCertVerified, rustls::Error> {
let cert = ServerCert::new(end_entity, intermediates);
let accepted = (self.decide.0)(&cert);
if let Ok(mut seen) = self.seen.lock() {
*seen = Some(cert);
}
if accepted {
Ok(ServerCertVerified::assertion())
} else {
Err(rustls::Error::InvalidCertificate(
rustls::CertificateError::ApplicationVerificationFailure,
))
}
}
fn verify_tls12_signature(
&self,
message: &[u8],
cert: &CertificateDer<'_>,
dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, rustls::Error> {
verify_tls12_signature(
message,
cert,
dss,
&self.provider.signature_verification_algorithms,
)
}
fn verify_tls13_signature(
&self,
message: &[u8],
cert: &CertificateDer<'_>,
dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, rustls::Error> {
verify_tls13_signature(
message,
cert,
dss,
&self.provider.signature_verification_algorithms,
)
}
fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
self.provider
.signature_verification_algorithms
.supported_schemes()
}
}
pub(crate) struct SeenCert(Arc<Verifier>);
impl SeenCert {
pub(crate) fn take(&self) -> Option<ServerCert> {
self.0.seen.lock().ok().and_then(|mut s| s.take())
}
}
pub(crate) fn client_config(
identity: Option<ClientIdentity>,
decide: CertVerifier,
) -> Result<(Arc<ClientConfig>, SeenCert), rustls::Error> {
let provider = Arc::new(rustls::crypto::ring::default_provider());
let verifier = Arc::new(Verifier {
decide: CertVerifierDebug(decide),
provider: provider.clone(),
seen: Mutex::new(None),
});
let builder = ClientConfig::builder_with_provider(provider)
.with_safe_default_protocol_versions()?
.dangerous()
.with_custom_certificate_verifier(verifier.clone());
let config = match identity {
Some(id) => builder.with_client_auth_cert(id.chain, id.key)?,
None => builder.with_no_client_auth(),
};
Ok((Arc::new(config), SeenCert(verifier)))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn hashes_are_hex() {
assert_eq!(
cert_hash(b"abc"),
"a9993e364706816aba3e25717850c26c9cd0d89d"
);
assert_eq!(
hex(&sha256(b"abc")),
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
);
}
}