mirror of https://github.com/ctz/rustls
669 lines
24 KiB
Rust
669 lines
24 KiB
Rust
use alloc::sync::Arc;
|
|
use std::time::SystemTime;
|
|
|
|
use super::anchors::{OwnedTrustAnchor, RootCertStore};
|
|
use super::client_verifier_builder::ClientCertVerifierBuilder;
|
|
use crate::client::ServerName;
|
|
use crate::enums::SignatureScheme;
|
|
use crate::error::{CertRevocationListError, CertificateError, Error, PeerMisbehaved};
|
|
use crate::key::Certificate;
|
|
#[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 webpki::SignatureVerificationAlgorithm];
|
|
|
|
/// Which signature verification mechanisms we support. No particular
|
|
/// order.
|
|
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,
|
|
];
|
|
|
|
/// Verify that the end-entity certificate `end_entity` is a valid server cert
|
|
/// and chains to at least one of the [OwnedTrustAnchor] in the `roots` [RootCertStore].
|
|
///
|
|
/// `intermediates` contains all certificates other than `end_entity` that
|
|
/// were sent as part of the server's [Certificate] message. It is in the
|
|
/// same order that the server sent them and may be empty.
|
|
#[allow(dead_code)]
|
|
#[cfg_attr(not(feature = "dangerous_configuration"), allow(unreachable_pub))]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "dangerous_configuration")))]
|
|
pub fn verify_server_cert_signed_by_trust_anchor(
|
|
cert: &ParsedCertificate,
|
|
roots: &RootCertStore,
|
|
intermediates: &[Certificate],
|
|
now: SystemTime,
|
|
) -> Result<(), Error> {
|
|
let chain = intermediate_chain(intermediates);
|
|
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,
|
|
&chain,
|
|
webpki_now,
|
|
webpki::KeyUsage::server_auth(),
|
|
None, // no CRLs
|
|
)
|
|
.map_err(pki_error)
|
|
.map(|_| ())
|
|
}
|
|
|
|
/// Verify that the `end_entity` has a name or alternative name matching the `server_name`
|
|
/// note: this only verifies the name and should be used in conjuction with more verification
|
|
/// like [verify_server_cert_signed_by_trust_anchor]
|
|
#[cfg_attr(not(feature = "dangerous_configuration"), allow(unreachable_pub))]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "dangerous_configuration")))]
|
|
pub fn verify_server_name(cert: &ParsedCertificate, server_name: &ServerName) -> Result<(), Error> {
|
|
match server_name {
|
|
ServerName::DnsName(dns_name) => {
|
|
// unlikely error because dns_name::DnsNameRef and webpki::DnsNameRef
|
|
// should have the same encoding rules.
|
|
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 {
|
|
/// Will verify the certificate is valid in the following ways:
|
|
/// - Signed by a trusted `RootCertStore` CA
|
|
/// - Not Expired
|
|
/// - Valid for DNS entry
|
|
fn verify_server_cert(
|
|
&self,
|
|
end_entity: &Certificate,
|
|
intermediates: &[Certificate],
|
|
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: &Certificate,
|
|
dss: &DigitallySignedStruct,
|
|
) -> Result<HandshakeSignatureValid, Error> {
|
|
Self::default_verify_tls12_signature(message, cert, dss)
|
|
}
|
|
|
|
fn verify_tls13_signature(
|
|
&self,
|
|
message: &[u8],
|
|
cert: &Certificate,
|
|
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()
|
|
}
|
|
}
|
|
|
|
/// Default `ServerCertVerifier`, see the trait impl for more information.
|
|
#[allow(unreachable_pub)]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "dangerous_configuration")))]
|
|
pub struct WebPkiServerVerifier {
|
|
roots: RootCertStore,
|
|
}
|
|
|
|
#[allow(unreachable_pub)]
|
|
impl WebPkiServerVerifier {
|
|
/// Constructs a new `WebPkiServerVerifier`.
|
|
///
|
|
/// `roots` is the set of trust anchors to trust for issuing server certs.
|
|
pub fn new(roots: RootCertStore) -> Self {
|
|
Self { roots }
|
|
}
|
|
|
|
/// Which signature verification schemes the `webpki` crate supports.
|
|
pub fn default_supported_verify_schemes() -> Vec<SignatureScheme> {
|
|
vec![
|
|
SignatureScheme::ECDSA_SECP384R1_SHA384,
|
|
SignatureScheme::ECDSA_SECP256R1_SHA256,
|
|
SignatureScheme::ED25519,
|
|
SignatureScheme::RSA_PSS_RSAE_SHA512,
|
|
SignatureScheme::RSA_PSS_RSAE_SHA384,
|
|
SignatureScheme::RSA_PSS_RSAE_SHA256,
|
|
SignatureScheme::RSA_PKCS1_SHA512,
|
|
SignatureScheme::RSA_PKCS1_SHA384,
|
|
SignatureScheme::RSA_PKCS1_SHA256,
|
|
]
|
|
}
|
|
|
|
/// A full implementation of `ServerCertVerifier::verify_tls12_signature` or
|
|
/// `ClientCertVerifier::verify_tls12_signature`.
|
|
pub fn default_verify_tls12_signature(
|
|
message: &[u8],
|
|
cert: &Certificate,
|
|
dss: &DigitallySignedStruct,
|
|
) -> Result<HandshakeSignatureValid, Error> {
|
|
verify_signed_struct(message, cert, dss)
|
|
}
|
|
|
|
/// A full implementation of `ServerCertVerifier::verify_tls13_signature` or
|
|
/// `ClientCertVerifier::verify_tls13_signature`.
|
|
pub fn default_verify_tls13_signature(
|
|
message: &[u8],
|
|
cert: &Certificate,
|
|
dss: &DigitallySignedStruct,
|
|
) -> Result<HandshakeSignatureValid, Error> {
|
|
verify_tls13(message, cert, dss)
|
|
}
|
|
}
|
|
|
|
fn intermediate_chain(intermediates: &[Certificate]) -> Vec<&[u8]> {
|
|
intermediates
|
|
.iter()
|
|
.map(|cert| cert.0.as_ref())
|
|
.collect()
|
|
}
|
|
|
|
fn trust_roots(roots: &RootCertStore) -> Vec<webpki::TrustAnchor> {
|
|
roots
|
|
.roots
|
|
.iter()
|
|
.map(OwnedTrustAnchor::to_trust_anchor)
|
|
.collect()
|
|
}
|
|
|
|
/// An unparsed DER encoded Certificate Revocation List (CRL).
|
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
|
pub struct UnparsedCertRevocationList(pub Vec<u8>);
|
|
|
|
impl UnparsedCertRevocationList {
|
|
/// Parse the CRL DER, yielding a [`webpki::CertRevocationList`] or an error if the CRL
|
|
/// is malformed, or uses unsupported features.
|
|
pub fn parse(&self) -> Result<webpki::OwnedCertRevocationList, CertRevocationListError> {
|
|
webpki::BorrowedCertRevocationList::from_der(&self.0)
|
|
.and_then(|crl| crl.to_owned())
|
|
.map_err(CertRevocationListError::from)
|
|
}
|
|
}
|
|
|
|
/// A client certificate verifier that uses the `webpki` crate[^1] to perform client certificate
|
|
/// validation. It must be created via the [WebPkiClientVerifier::builder()] function.
|
|
///
|
|
/// Once built, the provided `Arc<dyn ClientCertVerifier>` can be used with a Rustls [crate::server::ServerConfig]
|
|
/// to configure client certificate validation using [`with_client_cert_verifier`][crate::ConfigBuilder<ClientConfig, WantsVerifier>::with_client_cert_verifier].
|
|
///
|
|
/// Example:
|
|
///
|
|
/// To require all clients present a client certificate issued by a trusted CA:
|
|
/// ```no_run
|
|
/// # use rustls::RootCertStore;
|
|
/// # use rustls::server::WebPkiClientVerifier;
|
|
/// # let roots = RootCertStore::empty();
|
|
/// let client_verifier = WebPkiClientVerifier::builder(roots.into())
|
|
/// .build()
|
|
/// .unwrap();
|
|
/// ```
|
|
///
|
|
/// Or, to allow clients presenting a client certificate authenticated by a trusted CA, or
|
|
/// anonymous clients that present no client certificate:
|
|
/// ```no_run
|
|
/// # use rustls::RootCertStore;
|
|
/// # use rustls::server::WebPkiClientVerifier;
|
|
/// # let roots = RootCertStore::empty();
|
|
/// let client_verifier = WebPkiClientVerifier::builder(roots.into())
|
|
/// .allow_unauthenticated()
|
|
/// .build()
|
|
/// .unwrap();
|
|
/// ```
|
|
///
|
|
/// If you wish to disable advertising client authentication:
|
|
/// ```no_run
|
|
/// # use rustls::RootCertStore;
|
|
/// # use rustls::server::WebPkiClientVerifier;
|
|
/// # let roots = RootCertStore::empty();
|
|
/// let client_verifier = WebPkiClientVerifier::no_client_auth();
|
|
/// ```
|
|
///
|
|
/// You can also configure the client verifier to check for certificate revocation with
|
|
/// client certificate revocation lists (CRLs):
|
|
/// ```no_run
|
|
/// # use rustls::RootCertStore;
|
|
/// # use rustls::server::{WebPkiClientVerifier};
|
|
/// # let roots = RootCertStore::empty();
|
|
/// # let crls = Vec::new();
|
|
/// let client_verifier = WebPkiClientVerifier::builder(roots.into())
|
|
/// .with_crls(crls)
|
|
/// .build()
|
|
/// .unwrap();
|
|
/// ```
|
|
///
|
|
/// [^1]: <https://github.com/rustls/webpki>
|
|
pub struct WebPkiClientVerifier {
|
|
roots: Arc<RootCertStore>,
|
|
subjects: Vec<DistinguishedName>,
|
|
crls: Vec<webpki::OwnedCertRevocationList>,
|
|
anonymous_policy: AnonymousClientPolicy,
|
|
}
|
|
|
|
impl WebPkiClientVerifier {
|
|
/// Create builder to build up the `webpki` client certificate verifier configuration.
|
|
/// Client certificate authentication will be offered by the server, and client certificates
|
|
/// will be verified using the trust anchors found in the provided `roots`. If you
|
|
/// wish to disable client authentication use [WebPkiClientVerifier::no_client_auth()] instead.
|
|
///
|
|
/// For more information, see the [`ClientCertVerifierBuilder`] documentation.
|
|
pub fn builder(roots: Arc<RootCertStore>) -> ClientCertVerifierBuilder {
|
|
ClientCertVerifierBuilder::new(roots)
|
|
}
|
|
|
|
/// Create a new `WebPkiClientVerifier` that disables client authentication. The server will
|
|
/// not offer client authentication and anonymous clients will be accepted.
|
|
///
|
|
/// This is in contrast to using `WebPkiClientVerifier::builder().allow_unauthenticated().build()`,
|
|
/// which will produce a verifier that will offer client authentication, but not require it.
|
|
pub fn no_client_auth() -> Arc<dyn ClientCertVerifier> {
|
|
Arc::new(NoClientAuth {})
|
|
}
|
|
|
|
/// Construct a new `WebpkiClientVerifier`.
|
|
///
|
|
/// `roots` is the list of trust anchors to use for certificate validation.
|
|
/// `crls` are an iterable of owned certificate revocation lists (CRLs) to use for
|
|
/// client certificate validation.
|
|
/// `anonymous_policy` controls whether client authentication is required, or if anonymous
|
|
/// clients can connect.
|
|
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: &Certificate,
|
|
intermediates: &[Certificate],
|
|
now: SystemTime,
|
|
) -> Result<ClientCertVerified, Error> {
|
|
let cert = ParsedCertificate::try_from(end_entity)?;
|
|
let chain = intermediate_chain(intermediates);
|
|
let trust_roots = trust_roots(&self.roots);
|
|
let now = webpki::Time::try_from(now).map_err(|_| Error::FailedToGetCurrentTime)?;
|
|
|
|
#[allow(trivial_casts)] // Cast to &dyn trait is required.
|
|
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,
|
|
&chain,
|
|
now,
|
|
webpki::KeyUsage::client_auth(),
|
|
revocation,
|
|
)
|
|
.map_err(pki_error)
|
|
.map(|_| ClientCertVerified::assertion())
|
|
}
|
|
|
|
fn verify_tls12_signature(
|
|
&self,
|
|
message: &[u8],
|
|
cert: &Certificate,
|
|
dss: &DigitallySignedStruct,
|
|
) -> Result<HandshakeSignatureValid, Error> {
|
|
WebPkiServerVerifier::default_verify_tls12_signature(message, cert, dss)
|
|
}
|
|
|
|
fn verify_tls13_signature(
|
|
&self,
|
|
message: &[u8],
|
|
cert: &Certificate,
|
|
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()
|
|
}
|
|
}
|
|
|
|
/// Controls how the [WebPkiClientVerifier] handles anonymous clients.
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub(crate) enum AnonymousClientPolicy {
|
|
/// Clients that do not present a client certificate are allowed.
|
|
Allow,
|
|
/// Clients that do not present a client certificate are denied.
|
|
Deny,
|
|
}
|
|
|
|
fn pki_error(error: webpki::Error) -> Error {
|
|
use webpki::Error::*;
|
|
match error {
|
|
BadDer | BadDerTime | TrailingData(_) => CertificateError::BadEncoding.into(),
|
|
CertNotValidYet => CertificateError::NotValidYet.into(),
|
|
CertExpired | InvalidCertValidity => CertificateError::Expired.into(),
|
|
UnknownIssuer => CertificateError::UnknownIssuer.into(),
|
|
CertNotValidForName => CertificateError::NotValidForName.into(),
|
|
CertRevoked => CertificateError::Revoked.into(),
|
|
IssuerNotCrlSigner => CertRevocationListError::IssuerInvalidForCrl.into(),
|
|
|
|
InvalidSignatureForPublicKey
|
|
| UnsupportedSignatureAlgorithm
|
|
| UnsupportedSignatureAlgorithmForPublicKey => CertificateError::BadSignature.into(),
|
|
|
|
InvalidCrlSignatureForPublicKey
|
|
| UnsupportedCrlSignatureAlgorithm
|
|
| UnsupportedCrlSignatureAlgorithmForPublicKey => {
|
|
CertRevocationListError::BadSignature.into()
|
|
}
|
|
|
|
_ => CertificateError::Other(Arc::new(error)).into(),
|
|
}
|
|
}
|
|
|
|
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_RSAE_SHA256: SignatureAlgorithms = &[webpki::RSA_PSS_2048_8192_SHA256_LEGACY_KEY];
|
|
static RSA_PSS_RSAE_SHA384: SignatureAlgorithms = &[webpki::RSA_PSS_2048_8192_SHA384_LEGACY_KEY];
|
|
static RSA_PSS_RSAE_SHA512: SignatureAlgorithms = &[webpki::RSA_PSS_2048_8192_SHA512_LEGACY_KEY];
|
|
|
|
fn convert_scheme(scheme: SignatureScheme) -> Result<SignatureAlgorithms, Error> {
|
|
match scheme {
|
|
// nb. for TLS1.2 the curve is not fixed by SignatureScheme.
|
|
SignatureScheme::ECDSA_SECP256R1_SHA256 => Ok(ECDSA_SHA256),
|
|
SignatureScheme::ECDSA_SECP384R1_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_RSAE_SHA256 => Ok(RSA_PSS_RSAE_SHA256),
|
|
SignatureScheme::RSA_PSS_RSAE_SHA384 => Ok(RSA_PSS_RSAE_SHA384),
|
|
SignatureScheme::RSA_PSS_RSAE_SHA512 => Ok(RSA_PSS_RSAE_SHA512),
|
|
|
|
_ => Err(PeerMisbehaved::SignedHandshakeWithUnadvertisedSigScheme.into()),
|
|
}
|
|
}
|
|
|
|
fn verify_sig_using_any_alg(
|
|
cert: &webpki::EndEntityCert,
|
|
algs: SignatureAlgorithms,
|
|
message: &[u8],
|
|
sig: &[u8],
|
|
) -> Result<(), webpki::Error> {
|
|
// TLS doesn't itself give us enough info to map to a single webpki::SignatureVerificationAlgorithm.
|
|
// Therefore, convert_algs maps to several and we try them all.
|
|
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: &Certificate,
|
|
dss: &DigitallySignedStruct,
|
|
) -> Result<HandshakeSignatureValid, Error> {
|
|
let possible_algs = convert_scheme(dss.scheme)?;
|
|
let cert = webpki::EndEntityCert::try_from(cert.0.as_ref()).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 webpki::SignatureVerificationAlgorithm, Error> {
|
|
use crate::enums::SignatureScheme::*;
|
|
|
|
match scheme {
|
|
ECDSA_SECP256R1_SHA256 => Ok(webpki::ECDSA_P256_SHA256),
|
|
ECDSA_SECP384R1_SHA384 => Ok(webpki::ECDSA_P384_SHA384),
|
|
ED25519 => Ok(webpki::ED25519),
|
|
RSA_PSS_RSAE_SHA256 => Ok(webpki::RSA_PSS_2048_8192_SHA256_LEGACY_KEY),
|
|
RSA_PSS_RSAE_SHA384 => Ok(webpki::RSA_PSS_2048_8192_SHA384_LEGACY_KEY),
|
|
RSA_PSS_RSAE_SHA512 => Ok(webpki::RSA_PSS_2048_8192_SHA512_LEGACY_KEY),
|
|
_ => Err(PeerMisbehaved::SignedHandshakeWithUnadvertisedSigScheme.into()),
|
|
}
|
|
}
|
|
|
|
fn verify_tls13(
|
|
msg: &[u8],
|
|
cert: &Certificate,
|
|
dss: &DigitallySignedStruct,
|
|
) -> Result<HandshakeSignatureValid, Error> {
|
|
let alg = convert_alg_tls13(dss.scheme)?;
|
|
|
|
let cert = webpki::EndEntityCert::try_from(cert.0.as_ref()).map_err(pki_error)?;
|
|
|
|
cert.verify_signature(alg, msg, dss.signature())
|
|
.map_err(pki_error)
|
|
.map(|_| HandshakeSignatureValid::assertion())
|
|
}
|
|
|
|
#[test]
|
|
fn pki_crl_errors() {
|
|
// CRL signature errors should be turned into BadSignature.
|
|
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),
|
|
);
|
|
|
|
// Revoked cert errors should be turned into Revoked.
|
|
assert_eq!(
|
|
pki_error(webpki::Error::CertRevoked),
|
|
Error::InvalidCertificate(CertificateError::Revoked),
|
|
);
|
|
|
|
// Issuer not CRL signer errors should be turned into IssuerInvalidForCrl
|
|
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(_)
|
|
));
|
|
}
|
|
|
|
/// wrapper around internal representation of a parsed certificate. This is used in order to avoid parsing twice when specifying custom verification
|
|
#[cfg_attr(not(feature = "dangerous_configuration"), allow(unreachable_pub))]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "dangerous_configuration")))]
|
|
pub struct ParsedCertificate<'a>(pub(crate) webpki::EndEntityCert<'a>);
|
|
|
|
impl<'a> TryFrom<&'a Certificate> for ParsedCertificate<'a> {
|
|
type Error = Error;
|
|
fn try_from(value: &'a Certificate) -> Result<ParsedCertificate<'a>, Self::Error> {
|
|
webpki::EndEntityCert::try_from(value.0.as_ref())
|
|
.map_err(pki_error)
|
|
.map(ParsedCertificate)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::Certificate;
|
|
|
|
#[test]
|
|
fn certificate_debug() {
|
|
assert_eq!(
|
|
"Certificate(b\"ab\")",
|
|
format!("{:?}", Certificate(b"ab".to_vec()))
|
|
);
|
|
}
|
|
}
|