use crate::ProtocolError; use crate::config::{Certificate, TlsCipherSuite, TlsClock, Verifier}; use crate::extensions::extension_data::signature_algorithms::SignatureScheme; use crate::handshake::{ certificate::{ Certificate as OwnedCertificate, CertificateEntryRef, CertificateRef as ServerCertificate, }, certificate_verify::HandshakeVerifyRef, }; use core::marker::PhantomData; use digest::Digest; use heapless::Vec; #[cfg(all(not(feature = "alloc"), feature = "webpki"))] impl TryInto<&'static webpki::SignatureAlgorithm> for SignatureScheme { type Error = ProtocolError; fn try_into(self) -> Result<&'static webpki::SignatureAlgorithm, Self::Error> { #[allow(clippy::match_same_arms)] match self { SignatureScheme::RsaPkcs1Sha256 | SignatureScheme::RsaPkcs1Sha384 | SignatureScheme::RsaPkcs1Sha512 => Err(ProtocolError::InvalidSignatureScheme), SignatureScheme::EcdsaSecp256r1Sha256 => Ok(&webpki::ECDSA_P256_SHA256), SignatureScheme::EcdsaSecp384r1Sha384 => Ok(&webpki::ECDSA_P384_SHA384), SignatureScheme::EcdsaSecp521r1Sha512 => Err(ProtocolError::InvalidSignatureScheme), SignatureScheme::RsaPssRsaeSha256 | SignatureScheme::RsaPssRsaeSha384 | SignatureScheme::RsaPssRsaeSha512 => Err(ProtocolError::InvalidSignatureScheme), SignatureScheme::Ed25519 => Ok(&webpki::ED25519), SignatureScheme::Ed448 | SignatureScheme::Sha224Ecdsa | SignatureScheme::Sha224Rsa | SignatureScheme::Sha224Dsa => Err(ProtocolError::InvalidSignatureScheme), SignatureScheme::RsaPssPssSha256 | SignatureScheme::RsaPssPssSha384 | SignatureScheme::RsaPssPssSha512 => Err(ProtocolError::InvalidSignatureScheme), SignatureScheme::RsaPkcs1Sha1 | SignatureScheme::EcdsaSha1 => { Err(ProtocolError::InvalidSignatureScheme) } SignatureScheme::MlDsa44 | SignatureScheme::MlDsa65 | SignatureScheme::MlDsa87 => { Err(ProtocolError::InvalidSignatureScheme) } SignatureScheme::Sha256BrainpoolP256r1 | SignatureScheme::Sha384BrainpoolP384r1 | SignatureScheme::Sha512BrainpoolP512r1 => Err(ProtocolError::InvalidSignatureScheme), } } } #[cfg(all(feature = "alloc", feature = "webpki"))] impl TryInto<&'static webpki::SignatureAlgorithm> for SignatureScheme { type Error = ProtocolError; fn try_into(self) -> Result<&'static webpki::SignatureAlgorithm, Self::Error> { match self { SignatureScheme::RsaPkcs1Sha256 => Ok(&webpki::RSA_PKCS1_2048_8192_SHA256), SignatureScheme::RsaPkcs1Sha384 => Ok(&webpki::RSA_PKCS1_2048_8192_SHA384), SignatureScheme::RsaPkcs1Sha512 => Ok(&webpki::RSA_PKCS1_2048_8192_SHA512), SignatureScheme::EcdsaSecp256r1Sha256 => Ok(&webpki::ECDSA_P256_SHA256), SignatureScheme::EcdsaSecp384r1Sha384 => Ok(&webpki::ECDSA_P384_SHA384), SignatureScheme::EcdsaSecp521r1Sha512 => Err(ProtocolError::InvalidSignatureScheme), SignatureScheme::RsaPssRsaeSha256 => Ok(&webpki::RSA_PSS_2048_8192_SHA256_LEGACY_KEY), SignatureScheme::RsaPssRsaeSha384 => Ok(&webpki::RSA_PSS_2048_8192_SHA384_LEGACY_KEY), SignatureScheme::RsaPssRsaeSha512 => Ok(&webpki::RSA_PSS_2048_8192_SHA512_LEGACY_KEY), SignatureScheme::Ed25519 => Ok(&webpki::ED25519), SignatureScheme::Ed448 => Err(ProtocolError::InvalidSignatureScheme), SignatureScheme::Sha224Ecdsa => Err(ProtocolError::InvalidSignatureScheme), SignatureScheme::Sha224Rsa => Err(ProtocolError::InvalidSignatureScheme), SignatureScheme::Sha224Dsa => Err(ProtocolError::InvalidSignatureScheme), SignatureScheme::RsaPssPssSha256 => Err(ProtocolError::InvalidSignatureScheme), SignatureScheme::RsaPssPssSha384 => Err(ProtocolError::InvalidSignatureScheme), SignatureScheme::RsaPssPssSha512 => Err(ProtocolError::InvalidSignatureScheme), SignatureScheme::RsaPkcs1Sha1 => Err(ProtocolError::InvalidSignatureScheme), SignatureScheme::EcdsaSha1 => Err(ProtocolError::InvalidSignatureScheme), SignatureScheme::MlDsa44 => Err(ProtocolError::InvalidSignatureScheme), SignatureScheme::MlDsa65 => Err(ProtocolError::InvalidSignatureScheme), SignatureScheme::MlDsa87 => Err(ProtocolError::InvalidSignatureScheme), SignatureScheme::Sha256BrainpoolP256r1 => Err(ProtocolError::InvalidSignatureScheme), SignatureScheme::Sha384BrainpoolP384r1 => Err(ProtocolError::InvalidSignatureScheme), SignatureScheme::Sha512BrainpoolP512r1 => Err(ProtocolError::InvalidSignatureScheme), } } } static ALL_SIGALGS: &[&webpki::SignatureAlgorithm] = &[ &webpki::ECDSA_P256_SHA256, &webpki::ECDSA_P256_SHA384, &webpki::ECDSA_P384_SHA256, &webpki::ECDSA_P384_SHA384, &webpki::ED25519, ]; pub struct CertVerifier<'a, CipherSuite, Clock, const CERT_SIZE: usize> where Clock: TlsClock, CipherSuite: TlsCipherSuite, { ca: Certificate<&'a [u8]>, host: Option>, certificate_transcript: Option, certificate: Option>, _clock: PhantomData, } impl<'a, CipherSuite, Clock, const CERT_SIZE: usize> CertVerifier<'a, CipherSuite, Clock, CERT_SIZE> where Clock: TlsClock, CipherSuite: TlsCipherSuite, { #[must_use] pub fn new(ca: Certificate<&'a [u8]>) -> Self { Self { ca, host: None, certificate_transcript: None, certificate: None, _clock: PhantomData, } } } impl Verifier for CertVerifier<'_, CipherSuite, Clock, CERT_SIZE> where CipherSuite: TlsCipherSuite, Clock: TlsClock, { fn set_hostname_verification(&mut self, hostname: &str) -> Result<(), ProtocolError> { self.host.replace( heapless::String::try_from(hostname).map_err(|_| ProtocolError::InsufficientSpace)?, ); Ok(()) } fn verify_certificate( &mut self, transcript: &CipherSuite::Hash, cert: ServerCertificate, ) -> Result<(), ProtocolError> { verify_certificate(self.host.as_deref(), &self.ca, &cert, Clock::now())?; self.certificate.replace(cert.try_into()?); self.certificate_transcript.replace(transcript.clone()); Ok(()) } fn verify_signature(&mut self, verify: HandshakeVerifyRef) -> Result<(), ProtocolError> { let handshake_hash = unwrap!(self.certificate_transcript.take()); let ctx_str = b"TLS 1.3, server CertificateVerify\x00"; let mut msg: Vec = Vec::new(); msg.resize(64, 0x20) .map_err(|_| ProtocolError::EncodeError)?; msg.extend_from_slice(ctx_str) .map_err(|_| ProtocolError::EncodeError)?; msg.extend_from_slice(&handshake_hash.finalize()) .map_err(|_| ProtocolError::EncodeError)?; let certificate = unwrap!(self.certificate.as_ref()).try_into()?; verify_signature(&msg[..], &certificate, &verify)?; Ok(()) } } fn verify_signature( message: &[u8], certificate: &ServerCertificate, verify: &HandshakeVerifyRef, ) -> Result<(), ProtocolError> { let mut verified = false; if !certificate.entries.is_empty() { if let CertificateEntryRef::X509(certificate) = certificate.entries[0] { let cert = webpki::EndEntityCert::try_from(certificate).map_err(|e| { warn!("ProtocolError loading cert: {:?}", e); ProtocolError::DecodeError })?; trace!( "Verifying with signature scheme {:?}", verify.signature_scheme ); info!("Signature: {:x?}", verify.signature); let pkisig = verify.signature_scheme.try_into()?; match cert.verify_signature(pkisig, message, verify.signature) { Ok(()) => { verified = true; } Err(e) => { info!("ProtocolError verifying signature: {:?}", e); } } } } if !verified { return Err(ProtocolError::InvalidSignature); } Ok(()) } fn verify_certificate( verify_host: Option<&str>, ca: &Certificate<&[u8]>, certificate: &ServerCertificate, now: Option, ) -> Result<(), ProtocolError> { let mut verified = false; let mut host_verified = false; if let Certificate::X509(ca) = ca { let trust = webpki::TrustAnchor::try_from_cert_der(ca).map_err(|e| { warn!("ProtocolError loading CA: {:?}", e); ProtocolError::DecodeError })?; trace!("We got {} certificate entries", certificate.entries.len()); if !certificate.entries.is_empty() { if let CertificateEntryRef::X509(certificate) = certificate.entries[0] { let cert = webpki::EndEntityCert::try_from(certificate).map_err(|e| { warn!("ProtocolError loading cert: {:?}", e); ProtocolError::DecodeError })?; let time = if let Some(now) = now { webpki::Time::from_seconds_since_unix_epoch(now) } else { webpki::Time::from_seconds_since_unix_epoch(0) }; info!("Certificate is loaded!"); match cert.verify_for_usage( ALL_SIGALGS, &[trust], &[], time, webpki::KeyUsage::server_auth(), &[], ) { Ok(()) => verified = true, Err(e) => { warn!("ProtocolError verifying certificate: {:?}", e); } } if let Some(server_name) = verify_host { match webpki::SubjectNameRef::try_from_ascii(server_name.as_bytes()) { Ok(subject) => match cert.verify_is_valid_for_subject_name(subject) { Ok(()) => host_verified = true, Err(e) => { warn!("ProtocolError verifying host: {:?}", e); } }, Err(e) => { warn!("ProtocolError verifying host: {:?}", e); } } } } } } if !verified { return Err(ProtocolError::InvalidCertificate); } if !host_verified && verify_host.is_some() { return Err(ProtocolError::InvalidCertificate); } Ok(()) }