use rustls::pki_types::CertificateDer;
use sha2::{Digest, Sha256};
use crate::error::{SrxError, TransportError};
pub type CertPin = [u8; 32];
#[must_use]
pub fn cert_sha256(cert_der: &CertificateDer<'_>) -> CertPin {
let mut hasher = Sha256::new();
hasher.update(cert_der.as_ref());
hasher.finalize().into()
}
pub fn verify_peer_cert_pins(
peer_certs: Option<&[CertificateDer<'_>]>,
pins: &[CertPin],
) -> crate::error::Result<()> {
if pins.is_empty() {
return Ok(());
}
let certs = peer_certs.ok_or_else(|| {
SrxError::Transport(TransportError::ConnectionFailed(
"peer did not present certificates for pin verification".into(),
))
})?;
let matched = certs
.iter()
.map(cert_sha256)
.any(|peer_pin| pins.contains(&peer_pin));
if matched {
return Ok(());
}
Err(SrxError::Transport(TransportError::ConnectionFailed(
"certificate pin verification failed".into(),
)))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn cert_sha256_is_deterministic() {
let cert = CertificateDer::from(vec![1, 2, 3, 4, 5, 6]);
assert_eq!(cert_sha256(&cert), cert_sha256(&cert));
}
#[test]
fn verify_peer_cert_pins_rejects_empty_peer_chain_when_pins_required() {
let pin = [0x11u8; 32];
assert!(verify_peer_cert_pins(None, &[pin]).is_err());
}
}