use crate::certificate;
use libp2p_core::PeerId;
use rustls::{
cipher_suite::{
TLS13_AES_128_GCM_SHA256, TLS13_AES_256_GCM_SHA384, TLS13_CHACHA20_POLY1305_SHA256,
},
client::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier},
internal::msgs::handshake::DigitallySignedStruct,
server::{ClientCertVerified, ClientCertVerifier},
Certificate, DistinguishedNames, SignatureScheme, SupportedCipherSuite,
SupportedProtocolVersion,
};
pub static PROTOCOL_VERSIONS: &[&SupportedProtocolVersion] = &[&rustls::version::TLS13];
pub static CIPHERSUITES: &[SupportedCipherSuite] = &[
TLS13_CHACHA20_POLY1305_SHA256,
TLS13_AES_256_GCM_SHA384,
TLS13_AES_128_GCM_SHA256,
];
pub struct Libp2pCertificateVerifier {
remote_peer_id: Option<PeerId>,
}
impl Libp2pCertificateVerifier {
pub fn new() -> Self {
Self {
remote_peer_id: None,
}
}
pub fn with_remote_peer_id(remote_peer_id: Option<PeerId>) -> Self {
Self { remote_peer_id }
}
fn verification_schemes() -> Vec<SignatureScheme> {
vec![
SignatureScheme::ECDSA_NISTP384_SHA384,
SignatureScheme::ECDSA_NISTP256_SHA256,
SignatureScheme::ED25519,
SignatureScheme::RSA_PSS_SHA512,
SignatureScheme::RSA_PSS_SHA384,
SignatureScheme::RSA_PSS_SHA256,
SignatureScheme::RSA_PKCS1_SHA512,
SignatureScheme::RSA_PKCS1_SHA384,
SignatureScheme::RSA_PKCS1_SHA256,
]
}
}
impl ServerCertVerifier for Libp2pCertificateVerifier {
fn verify_server_cert(
&self,
end_entity: &Certificate,
intermediates: &[Certificate],
_server_name: &rustls::ServerName,
_scts: &mut dyn Iterator<Item = &[u8]>,
_ocsp_response: &[u8],
_now: std::time::SystemTime,
) -> Result<ServerCertVerified, rustls::Error> {
let peer_id = verify_presented_certs(end_entity, intermediates)?;
if let Some(remote_peer_id) = self.remote_peer_id {
if remote_peer_id != peer_id {
return Err(rustls::Error::PeerMisbehavedError(
"Wrong peer ID in p2p extension".to_string(),
));
}
}
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 client_auth_root_subjects(&self) -> Option<DistinguishedNames> {
Some(vec![])
}
fn verify_client_cert(
&self,
end_entity: &Certificate,
intermediates: &[Certificate],
_now: std::time::SystemTime,
) -> 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 verify_presented_certs(
end_entity: &Certificate,
intermediates: &[Certificate],
) -> Result<PeerId, 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::InvalidCertificateEncoding,
e => rustls::Error::InvalidCertificateData(format!("invalid peer certificate: {e}")),
}
}
}
impl From<certificate::VerificationError> for rustls::Error {
fn from(certificate::VerificationError(e): certificate::VerificationError) -> Self {
use webpki::Error::*;
match e {
InvalidSignatureForPublicKey => rustls::Error::InvalidCertificateSignature,
UnsupportedSignatureAlgorithm | UnsupportedSignatureAlgorithmForPublicKey => {
rustls::Error::InvalidCertificateSignatureType
}
e => rustls::Error::InvalidCertificateData(format!("invalid peer certificate: {e}")),
}
}
}