use std::sync::Arc;
use rustls::{
client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier},
pki_types::CertificateDer as Certificate,
server::danger::{ClientCertVerified, ClientCertVerifier},
CertificateError, DigitallySignedStruct, DistinguishedName, OtherError, PeerMisbehaved,
SignatureScheme, SupportedProtocolVersion,
};
use super::certificate;
use crate::key::PublicKey;
pub static PROTOCOL_VERSIONS: &[&SupportedProtocolVersion] = &[&rustls::version::TLS13];
#[derive(Debug)]
pub struct Libp2pCertificateVerifier {
remote_peer_id: Option<PublicKey>,
}
impl Libp2pCertificateVerifier {
pub fn new() -> Self {
Self {
remote_peer_id: None,
}
}
pub fn with_remote_peer_id(remote_peer_id: Option<PublicKey>) -> Self {
Self { remote_peer_id }
}
fn verification_schemes() -> Vec<SignatureScheme> {
vec![
SignatureScheme::ECDSA_NISTP384_SHA384,
SignatureScheme::ECDSA_NISTP256_SHA256,
SignatureScheme::ED25519,
]
}
}
impl ServerCertVerifier for Libp2pCertificateVerifier {
fn verify_server_cert(
&self,
end_entity: &Certificate,
intermediates: &[Certificate],
_server_name: &rustls::pki_types::ServerName,
_ocsp_response: &[u8],
_now: rustls::pki_types::UnixTime,
) -> Result<ServerCertVerified, rustls::Error> {
let peer_id = verify_presented_certs(end_entity, intermediates)?;
if let Some(ref remote_peer_id) = self.remote_peer_id {
if remote_peer_id != &peer_id {
return Err(rustls::Error::PeerMisbehaved(
PeerMisbehaved::BadCertChainExtensions,
));
}
}
Ok(ServerCertVerified::assertion())
}
fn verify_tls12_signature(
&self,
_message: &[u8],
_cert: &Certificate,
_dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, rustls::Error> {
unreachable!("`PROTOCOL_VERSIONS` only allows TLS 1.3")
}
fn verify_tls13_signature(
&self,
message: &[u8],
cert: &Certificate,
dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, rustls::Error> {
verify_tls13_signature(cert, dss.scheme, message, dss.signature())
}
fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
Self::verification_schemes()
}
}
impl ClientCertVerifier for Libp2pCertificateVerifier {
fn offer_client_auth(&self) -> bool {
true
}
fn verify_client_cert(
&self,
end_entity: &Certificate,
intermediates: &[Certificate],
_now: rustls::pki_types::UnixTime,
) -> Result<ClientCertVerified, rustls::Error> {
verify_presented_certs(end_entity, intermediates)?;
Ok(ClientCertVerified::assertion())
}
fn verify_tls12_signature(
&self,
_message: &[u8],
_cert: &Certificate,
_dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, rustls::Error> {
unreachable!("`PROTOCOL_VERSIONS` only allows TLS 1.3")
}
fn verify_tls13_signature(
&self,
message: &[u8],
cert: &Certificate,
dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, rustls::Error> {
verify_tls13_signature(cert, dss.scheme, message, dss.signature())
}
fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
Self::verification_schemes()
}
fn root_hint_subjects(&self) -> &[DistinguishedName] {
&[][..]
}
}
fn verify_presented_certs(
end_entity: &Certificate,
intermediates: &[Certificate],
) -> Result<PublicKey, rustls::Error> {
if !intermediates.is_empty() {
return Err(rustls::Error::General(
"libp2p-tls requires exactly one certificate".into(),
));
}
let cert = certificate::parse(end_entity)?;
Ok(cert.peer_id())
}
fn verify_tls13_signature(
cert: &Certificate,
signature_scheme: SignatureScheme,
message: &[u8],
signature: &[u8],
) -> Result<HandshakeSignatureValid, rustls::Error> {
certificate::parse(cert)?.verify_signature(signature_scheme, message, signature)?;
Ok(HandshakeSignatureValid::assertion())
}
impl From<certificate::ParseError> for rustls::Error {
fn from(certificate::ParseError(e): certificate::ParseError) -> Self {
use webpki::Error::*;
match e {
BadDer => rustls::Error::InvalidCertificate(CertificateError::BadEncoding),
e => {
rustls::Error::InvalidCertificate(CertificateError::Other(OtherError(Arc::new(e))))
}
}
}
}
impl From<certificate::VerificationError> for rustls::Error {
fn from(certificate::VerificationError(e): certificate::VerificationError) -> Self {
use webpki::Error::*;
match e {
InvalidSignatureForPublicKey => {
rustls::Error::InvalidCertificate(CertificateError::BadSignature)
}
UnsupportedSignatureAlgorithm | UnsupportedSignatureAlgorithmForPublicKey => {
rustls::Error::InvalidCertificate(CertificateError::BadSignature)
}
e => {
rustls::Error::InvalidCertificate(CertificateError::Other(OtherError(Arc::new(e))))
}
}
}
}