use alloc::sync::Arc;
use std::time::SystemTime;
use pki_types::{CertificateDer, SignatureVerificationAlgorithm, TrustAnchor};
use super::anchors::RootCertStore;
use super::client_verifier_builder::ClientCertVerifierBuilder;
use super::pki_error;
use crate::client::ServerName;
use crate::enums::SignatureScheme;
use crate::error::{CertRevocationListError, CertificateError, Error, PeerMisbehaved};
#[cfg(feature = "logging")]
use crate::log::trace;
use crate::msgs::handshake::DistinguishedName;
use crate::verify::{
ClientCertVerified, ClientCertVerifier, DigitallySignedStruct, HandshakeSignatureValid,
NoClientAuth, ServerCertVerified, ServerCertVerifier,
};
type SignatureAlgorithms = &'static [&'static dyn SignatureVerificationAlgorithm];
static SUPPORTED_SIG_ALGS: SignatureAlgorithms = &[
webpki::ECDSA_P256_SHA256,
webpki::ECDSA_P256_SHA384,
webpki::ECDSA_P384_SHA256,
webpki::ECDSA_P384_SHA384,
webpki::ED25519,
webpki::RSA_PSS_2048_8192_SHA256_LEGACY_KEY,
webpki::RSA_PSS_2048_8192_SHA384_LEGACY_KEY,
webpki::RSA_PSS_2048_8192_SHA512_LEGACY_KEY,
webpki::RSA_PKCS1_2048_8192_SHA256,
webpki::RSA_PKCS1_2048_8192_SHA384,
webpki::RSA_PKCS1_2048_8192_SHA512,
webpki::RSA_PKCS1_3072_8192_SHA384,
];
#[allow(dead_code)]
#[cfg_attr(not(feature = "dangerous_configuration"), allow(unreachable_pub))]
pub fn verify_server_cert_signed_by_trust_anchor(
cert: &ParsedCertificate,
roots: &RootCertStore,
intermediates: &[CertificateDer<'_>],
now: SystemTime,
) -> Result<(), Error> {
let trust_roots = trust_roots(roots);
let webpki_now = webpki::Time::try_from(now).map_err(|_| Error::FailedToGetCurrentTime)?;
cert.0
.verify_for_usage(
SUPPORTED_SIG_ALGS,
&trust_roots,
intermediates,
webpki_now,
webpki::KeyUsage::server_auth(),
None, )
.map_err(pki_error)
.map(|_| ())
}
#[cfg_attr(not(feature = "dangerous_configuration"), allow(unreachable_pub))]
pub fn verify_server_name(cert: &ParsedCertificate, server_name: &ServerName) -> Result<(), Error> {
match server_name {
ServerName::DnsName(dns_name) => {
let dns_name = webpki::DnsNameRef::try_from_ascii_str(dns_name.as_ref())
.map_err(|_| Error::InvalidCertificate(CertificateError::BadEncoding))?;
let name = webpki::SubjectNameRef::DnsName(dns_name);
cert.0
.verify_is_valid_for_subject_name(name)
.map_err(pki_error)?;
}
ServerName::IpAddress(ip_addr) => {
let ip_addr = webpki::IpAddr::from(*ip_addr);
cert.0
.verify_is_valid_for_subject_name(webpki::SubjectNameRef::IpAddress(
webpki::IpAddrRef::from(&ip_addr),
))
.map_err(pki_error)?;
}
}
Ok(())
}
impl ServerCertVerifier for WebPkiServerVerifier {
fn verify_server_cert(
&self,
end_entity: &CertificateDer<'_>,
intermediates: &[CertificateDer<'_>],
server_name: &ServerName,
ocsp_response: &[u8],
now: SystemTime,
) -> Result<ServerCertVerified, Error> {
let cert = ParsedCertificate::try_from(end_entity)?;
verify_server_cert_signed_by_trust_anchor(&cert, &self.roots, intermediates, now)?;
if !ocsp_response.is_empty() {
trace!("Unvalidated OCSP response: {:?}", ocsp_response.to_vec());
}
verify_server_name(&cert, server_name)?;
Ok(ServerCertVerified::assertion())
}
fn verify_tls12_signature(
&self,
message: &[u8],
cert: &CertificateDer<'_>,
dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, Error> {
Self::default_verify_tls12_signature(message, cert, dss)
}
fn verify_tls13_signature(
&self,
message: &[u8],
cert: &CertificateDer<'_>,
dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, Error> {
Self::default_verify_tls13_signature(message, cert, dss)
}
fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
Self::default_supported_verify_schemes()
}
}
#[allow(unreachable_pub)]
pub struct WebPkiServerVerifier {
roots: Arc<RootCertStore>,
}
#[allow(unreachable_pub)]
impl WebPkiServerVerifier {
pub fn new(roots: impl Into<Arc<RootCertStore>>) -> Self {
Self {
roots: roots.into(),
}
}
pub fn default_supported_verify_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,
]
}
pub fn default_verify_tls12_signature(
message: &[u8],
cert: &CertificateDer<'_>,
dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, Error> {
verify_signed_struct(message, cert, dss)
}
pub fn default_verify_tls13_signature(
message: &[u8],
cert: &CertificateDer<'_>,
dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, Error> {
verify_tls13(message, cert, dss)
}
}
fn trust_roots(roots: &RootCertStore) -> Vec<TrustAnchor<'_>> {
roots
.roots
.iter()
.map(|with_dn| {
let inner = with_dn.inner();
TrustAnchor {
subject: inner.subject.as_ref().into(),
subject_public_key_info: inner
.subject_public_key_info
.as_ref()
.into(),
name_constraints: inner
.name_constraints
.as_ref()
.map(|nc| nc.as_ref().into()),
}
})
.collect()
}
pub struct WebPkiClientVerifier {
roots: Arc<RootCertStore>,
subjects: Vec<DistinguishedName>,
crls: Vec<webpki::OwnedCertRevocationList>,
anonymous_policy: AnonymousClientPolicy,
}
impl WebPkiClientVerifier {
pub fn builder(roots: Arc<RootCertStore>) -> ClientCertVerifierBuilder {
ClientCertVerifierBuilder::new(roots)
}
pub fn no_client_auth() -> Arc<dyn ClientCertVerifier> {
Arc::new(NoClientAuth {})
}
pub(crate) fn new(
roots: Arc<RootCertStore>,
crls: Vec<webpki::OwnedCertRevocationList>,
anonymous_policy: AnonymousClientPolicy,
) -> Self {
Self {
subjects: roots
.roots
.iter()
.map(|r| r.subject().clone())
.collect(),
crls,
roots,
anonymous_policy,
}
}
}
impl ClientCertVerifier for WebPkiClientVerifier {
fn offer_client_auth(&self) -> bool {
true
}
fn client_auth_mandatory(&self) -> bool {
match self.anonymous_policy {
AnonymousClientPolicy::Allow => false,
AnonymousClientPolicy::Deny => true,
}
}
fn client_auth_root_subjects(&self) -> &[DistinguishedName] {
&self.subjects
}
fn verify_client_cert(
&self,
end_entity: &CertificateDer<'_>,
intermediates: &[CertificateDer<'_>],
now: SystemTime,
) -> Result<ClientCertVerified, Error> {
let cert = ParsedCertificate::try_from(end_entity)?;
let trust_roots = trust_roots(&self.roots);
let now = webpki::Time::try_from(now).map_err(|_| Error::FailedToGetCurrentTime)?;
#[allow(trivial_casts)] let crls = self
.crls
.iter()
.map(|crl| crl as &dyn webpki::CertRevocationList)
.collect::<Vec<_>>();
let revocation = if crls.is_empty() {
None
} else {
Some(
webpki::RevocationOptionsBuilder::new(&crls)
.expect("invalid crls")
.allow_unknown_status()
.build(),
)
};
cert.0
.verify_for_usage(
SUPPORTED_SIG_ALGS,
&trust_roots,
intermediates,
now,
webpki::KeyUsage::client_auth(),
revocation,
)
.map_err(pki_error)
.map(|_| ClientCertVerified::assertion())
}
fn verify_tls12_signature(
&self,
message: &[u8],
cert: &CertificateDer<'_>,
dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, Error> {
WebPkiServerVerifier::default_verify_tls12_signature(message, cert, dss)
}
fn verify_tls13_signature(
&self,
message: &[u8],
cert: &CertificateDer<'_>,
dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, Error> {
WebPkiServerVerifier::default_verify_tls13_signature(message, cert, dss)
}
fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
WebPkiServerVerifier::default_supported_verify_schemes()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum AnonymousClientPolicy {
Allow,
Deny,
}
impl From<webpki::Error> for CertRevocationListError {
fn from(e: webpki::Error) -> Self {
use webpki::Error::*;
match e {
InvalidCrlSignatureForPublicKey
| UnsupportedCrlSignatureAlgorithm
| UnsupportedCrlSignatureAlgorithmForPublicKey => Self::BadSignature,
InvalidCrlNumber => Self::InvalidCrlNumber,
InvalidSerialNumber => Self::InvalidRevokedCertSerialNumber,
IssuerNotCrlSigner => Self::IssuerInvalidForCrl,
MalformedExtensions | BadDer | BadDerTime => Self::ParseError,
UnsupportedCriticalExtension => Self::UnsupportedCriticalExtension,
UnsupportedCrlVersion => Self::UnsupportedCrlVersion,
UnsupportedDeltaCrl => Self::UnsupportedDeltaCrl,
UnsupportedIndirectCrl => Self::UnsupportedIndirectCrl,
UnsupportedRevocationReason => Self::UnsupportedRevocationReason,
_ => Self::Other(Arc::new(e)),
}
}
}
static ECDSA_SHA256: SignatureAlgorithms = &[webpki::ECDSA_P256_SHA256, webpki::ECDSA_P384_SHA256];
static ECDSA_SHA384: SignatureAlgorithms = &[webpki::ECDSA_P256_SHA384, webpki::ECDSA_P384_SHA384];
static ED25519: SignatureAlgorithms = &[webpki::ED25519];
static RSA_SHA256: SignatureAlgorithms = &[webpki::RSA_PKCS1_2048_8192_SHA256];
static RSA_SHA384: SignatureAlgorithms = &[webpki::RSA_PKCS1_2048_8192_SHA384];
static RSA_SHA512: SignatureAlgorithms = &[webpki::RSA_PKCS1_2048_8192_SHA512];
static RSA_PSS_SHA256: SignatureAlgorithms = &[webpki::RSA_PSS_2048_8192_SHA256_LEGACY_KEY];
static RSA_PSS_SHA384: SignatureAlgorithms = &[webpki::RSA_PSS_2048_8192_SHA384_LEGACY_KEY];
static RSA_PSS_SHA512: SignatureAlgorithms = &[webpki::RSA_PSS_2048_8192_SHA512_LEGACY_KEY];
fn convert_scheme(scheme: SignatureScheme) -> Result<SignatureAlgorithms, Error> {
match scheme {
SignatureScheme::ECDSA_NISTP256_SHA256 => Ok(ECDSA_SHA256),
SignatureScheme::ECDSA_NISTP384_SHA384 => Ok(ECDSA_SHA384),
SignatureScheme::ED25519 => Ok(ED25519),
SignatureScheme::RSA_PKCS1_SHA256 => Ok(RSA_SHA256),
SignatureScheme::RSA_PKCS1_SHA384 => Ok(RSA_SHA384),
SignatureScheme::RSA_PKCS1_SHA512 => Ok(RSA_SHA512),
SignatureScheme::RSA_PSS_SHA256 => Ok(RSA_PSS_SHA256),
SignatureScheme::RSA_PSS_SHA384 => Ok(RSA_PSS_SHA384),
SignatureScheme::RSA_PSS_SHA512 => Ok(RSA_PSS_SHA512),
_ => Err(PeerMisbehaved::SignedHandshakeWithUnadvertisedSigScheme.into()),
}
}
fn verify_sig_using_any_alg(
cert: &webpki::EndEntityCert,
algs: SignatureAlgorithms,
message: &[u8],
sig: &[u8],
) -> Result<(), webpki::Error> {
for alg in algs {
match cert.verify_signature(*alg, message, sig) {
Err(webpki::Error::UnsupportedSignatureAlgorithmForPublicKey) => continue,
res => return res,
}
}
Err(webpki::Error::UnsupportedSignatureAlgorithmForPublicKey)
}
fn verify_signed_struct(
message: &[u8],
cert: &CertificateDer<'_>,
dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, Error> {
let possible_algs = convert_scheme(dss.scheme)?;
let cert = webpki::EndEntityCert::try_from(cert).map_err(pki_error)?;
verify_sig_using_any_alg(&cert, possible_algs, message, dss.signature())
.map_err(pki_error)
.map(|_| HandshakeSignatureValid::assertion())
}
fn convert_alg_tls13(
scheme: SignatureScheme,
) -> Result<&'static dyn SignatureVerificationAlgorithm, Error> {
use crate::enums::SignatureScheme::*;
match scheme {
ECDSA_NISTP256_SHA256 => Ok(webpki::ECDSA_P256_SHA256),
ECDSA_NISTP384_SHA384 => Ok(webpki::ECDSA_P384_SHA384),
ED25519 => Ok(webpki::ED25519),
RSA_PSS_SHA256 => Ok(webpki::RSA_PSS_2048_8192_SHA256_LEGACY_KEY),
RSA_PSS_SHA384 => Ok(webpki::RSA_PSS_2048_8192_SHA384_LEGACY_KEY),
RSA_PSS_SHA512 => Ok(webpki::RSA_PSS_2048_8192_SHA512_LEGACY_KEY),
_ => Err(PeerMisbehaved::SignedHandshakeWithUnadvertisedSigScheme.into()),
}
}
fn verify_tls13(
msg: &[u8],
cert: &CertificateDer<'_>,
dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, Error> {
let alg = convert_alg_tls13(dss.scheme)?;
let cert = webpki::EndEntityCert::try_from(cert).map_err(pki_error)?;
cert.verify_signature(alg, msg, dss.signature())
.map_err(pki_error)
.map(|_| HandshakeSignatureValid::assertion())
}
#[test]
fn pki_crl_errors() {
assert_eq!(
pki_error(webpki::Error::InvalidCrlSignatureForPublicKey),
Error::InvalidCertRevocationList(CertRevocationListError::BadSignature),
);
assert_eq!(
pki_error(webpki::Error::UnsupportedCrlSignatureAlgorithm),
Error::InvalidCertRevocationList(CertRevocationListError::BadSignature),
);
assert_eq!(
pki_error(webpki::Error::UnsupportedCrlSignatureAlgorithmForPublicKey),
Error::InvalidCertRevocationList(CertRevocationListError::BadSignature),
);
assert_eq!(
pki_error(webpki::Error::CertRevoked),
Error::InvalidCertificate(CertificateError::Revoked),
);
assert_eq!(
pki_error(webpki::Error::IssuerNotCrlSigner),
Error::InvalidCertRevocationList(CertRevocationListError::IssuerInvalidForCrl)
);
}
#[test]
fn crl_error_from_webpki() {
use crate::CertRevocationListError::*;
let testcases = &[
(webpki::Error::InvalidCrlSignatureForPublicKey, BadSignature),
(
webpki::Error::UnsupportedCrlSignatureAlgorithm,
BadSignature,
),
(
webpki::Error::UnsupportedCrlSignatureAlgorithmForPublicKey,
BadSignature,
),
(webpki::Error::InvalidCrlNumber, InvalidCrlNumber),
(
webpki::Error::InvalidSerialNumber,
InvalidRevokedCertSerialNumber,
),
(webpki::Error::IssuerNotCrlSigner, IssuerInvalidForCrl),
(webpki::Error::MalformedExtensions, ParseError),
(webpki::Error::BadDer, ParseError),
(webpki::Error::BadDerTime, ParseError),
(
webpki::Error::UnsupportedCriticalExtension,
UnsupportedCriticalExtension,
),
(webpki::Error::UnsupportedCrlVersion, UnsupportedCrlVersion),
(webpki::Error::UnsupportedDeltaCrl, UnsupportedDeltaCrl),
(
webpki::Error::UnsupportedIndirectCrl,
UnsupportedIndirectCrl,
),
(
webpki::Error::UnsupportedRevocationReason,
UnsupportedRevocationReason,
),
];
for t in testcases {
assert_eq!(
<webpki::Error as Into<CertRevocationListError>>::into(t.0),
t.1
);
}
assert!(matches!(
<webpki::Error as Into<CertRevocationListError>>::into(
webpki::Error::NameConstraintViolation
),
Other(_)
));
}
#[cfg_attr(not(feature = "dangerous_configuration"), allow(unreachable_pub))]
pub struct ParsedCertificate<'a>(pub(crate) webpki::EndEntityCert<'a>);
impl<'a> TryFrom<&'a CertificateDer<'a>> for ParsedCertificate<'a> {
type Error = Error;
fn try_from(value: &'a CertificateDer<'a>) -> Result<ParsedCertificate<'a>, Self::Error> {
webpki::EndEntityCert::try_from(value)
.map_err(pki_error)
.map(ParsedCertificate)
}
}
#[cfg(test)]
mod test {
use super::CertificateDer;
#[test]
fn certificate_debug() {
assert_eq!(
"CertificateDer(Der([97, 98]))",
format!("{:?}", CertificateDer::from(b"ab".to_vec()))
);
}
}