use std::sync::{Arc, Mutex};
use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier};
use rustls::pki_types::{CertificateDer, ServerName, UnixTime};
use rustls::{DigitallySignedStruct, Error as TlsError, SignatureScheme};
use crate::cert_binding::{evaluate, verify_binding_from_leaf_cert, BindingPolicy};
use crate::identity::{peer_id_from_leaf_cert_der, PeerId};
#[derive(Debug, Default, Clone)]
pub struct CapturedPeerId(pub Arc<Mutex<Option<PeerId>>>);
impl CapturedPeerId {
pub fn get(&self) -> Option<PeerId> {
*self.0.lock().unwrap()
}
}
#[derive(Debug, Default, Clone)]
pub struct CapturedBlsPub(pub Arc<Mutex<Option<[u8; 48]>>>);
impl CapturedBlsPub {
pub fn get(&self) -> Option<[u8; 48]> {
*self.0.lock().unwrap()
}
}
#[derive(Debug)]
pub struct PeerIdPinningVerifier {
expected: Option<PeerId>,
captured: CapturedPeerId,
binding_policy: BindingPolicy,
captured_bls: CapturedBlsPub,
defaults: Vec<SignatureScheme>,
}
impl PeerIdPinningVerifier {
pub fn new(expected: Option<PeerId>, captured: CapturedPeerId) -> Self {
PeerIdPinningVerifier {
expected,
captured,
binding_policy: BindingPolicy::Off,
captured_bls: CapturedBlsPub::default(),
defaults: default_signature_schemes(),
}
}
pub fn with_binding(mut self, policy: BindingPolicy, captured_bls: CapturedBlsPub) -> Self {
self.binding_policy = policy;
self.captured_bls = captured_bls;
self
}
}
impl ServerCertVerifier for PeerIdPinningVerifier {
fn verify_server_cert(
&self,
end_entity: &CertificateDer<'_>,
_intermediates: &[CertificateDer<'_>],
_server_name: &ServerName<'_>,
_ocsp_response: &[u8],
_now: UnixTime,
) -> Result<ServerCertVerified, TlsError> {
let derived = peer_id_from_leaf_cert_der(end_entity.as_ref()).ok_or_else(|| {
TlsError::General("peer leaf certificate could not be parsed as X.509".to_string())
})?;
*self.captured.0.lock().unwrap() = Some(derived);
if let Some(expected) = self.expected {
if derived != expected {
return Err(TlsError::General(format!(
"peer_id mismatch: expected {expected}, got {derived}"
)));
}
}
if self.binding_policy != BindingPolicy::Off {
let outcome = verify_binding_from_leaf_cert(end_entity.as_ref());
match evaluate(&outcome, self.binding_policy) {
Ok(bls_pub) => *self.captured_bls.0.lock().unwrap() = bls_pub,
Err(reason) => {
return Err(TlsError::General(format!(
"peer {derived} rejected by cert BLS binding policy: {reason}"
)))
}
}
}
Ok(ServerCertVerified::assertion())
}
fn verify_tls12_signature(
&self,
message: &[u8],
cert: &CertificateDer<'_>,
dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, TlsError> {
rustls::crypto::verify_tls12_signature(
message,
cert,
dss,
&rustls::crypto::ring::default_provider().signature_verification_algorithms,
)
}
fn verify_tls13_signature(
&self,
message: &[u8],
cert: &CertificateDer<'_>,
dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, TlsError> {
rustls::crypto::verify_tls13_signature(
message,
cert,
dss,
&rustls::crypto::ring::default_provider().signature_verification_algorithms,
)
}
fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
self.defaults.clone()
}
}
fn default_signature_schemes() -> Vec<SignatureScheme> {
rustls::crypto::ring::default_provider()
.signature_verification_algorithms
.supported_schemes()
}