mirror of https://github.com/ctz/rustls
417 lines
15 KiB
Rust
417 lines
15 KiB
Rust
use alloc::sync::Arc;
|
|
use alloc::vec::Vec;
|
|
|
|
use pki_types::{CertificateDer, CertificateRevocationListDer, ServerName, UnixTime};
|
|
use webpki::{CertRevocationList, RevocationCheckDepth, UnknownStatusPolicy};
|
|
|
|
use crate::crypto::{CryptoProvider, WebPkiSupportedAlgorithms};
|
|
#[cfg(feature = "logging")]
|
|
use crate::log::trace;
|
|
use crate::verify::{
|
|
DigitallySignedStruct, HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier,
|
|
};
|
|
use crate::webpki::verify::{
|
|
verify_server_cert_signed_by_trust_anchor_impl, verify_tls12_signature, verify_tls13_signature,
|
|
ParsedCertificate,
|
|
};
|
|
use crate::webpki::{parse_crls, verify_server_name, VerifierBuilderError};
|
|
#[cfg(doc)]
|
|
use crate::{crypto, ConfigBuilder, ServerConfig};
|
|
use crate::{Error, RootCertStore, SignatureScheme};
|
|
|
|
/// A builder for configuring a `webpki` server certificate verifier.
|
|
///
|
|
/// For more information, see the [`WebPkiServerVerifier`] documentation.
|
|
#[derive(Debug, Clone)]
|
|
pub struct ServerCertVerifierBuilder {
|
|
roots: Arc<RootCertStore>,
|
|
crls: Vec<CertificateRevocationListDer<'static>>,
|
|
revocation_check_depth: RevocationCheckDepth,
|
|
unknown_revocation_policy: UnknownStatusPolicy,
|
|
supported_algs: WebPkiSupportedAlgorithms,
|
|
}
|
|
|
|
impl ServerCertVerifierBuilder {
|
|
pub(crate) fn new(
|
|
roots: Arc<RootCertStore>,
|
|
supported_algs: WebPkiSupportedAlgorithms,
|
|
) -> Self {
|
|
Self {
|
|
roots,
|
|
crls: Vec::new(),
|
|
revocation_check_depth: RevocationCheckDepth::Chain,
|
|
unknown_revocation_policy: UnknownStatusPolicy::Deny,
|
|
supported_algs,
|
|
}
|
|
}
|
|
|
|
/// Verify the revocation state of presented client certificates against the provided
|
|
/// certificate revocation lists (CRLs). Calling `with_crls` multiple times appends the
|
|
/// given CRLs to the existing collection.
|
|
pub fn with_crls(
|
|
mut self,
|
|
crls: impl IntoIterator<Item = CertificateRevocationListDer<'static>>,
|
|
) -> Self {
|
|
self.crls.extend(crls);
|
|
self
|
|
}
|
|
|
|
/// Only check the end entity certificate revocation status when using CRLs.
|
|
///
|
|
/// If CRLs are provided using [`with_crls`][Self::with_crls] only check the end entity
|
|
/// certificate's revocation status. Overrides the default behavior of checking revocation
|
|
/// status for each certificate in the verified chain built to a trust anchor
|
|
/// (excluding the trust anchor itself).
|
|
///
|
|
/// If no CRLs are provided then this setting has no effect. Neither the end entity certificate
|
|
/// or any intermediates will have revocation status checked.
|
|
pub fn only_check_end_entity_revocation(mut self) -> Self {
|
|
self.revocation_check_depth = RevocationCheckDepth::EndEntity;
|
|
self
|
|
}
|
|
|
|
/// Allow unknown certificate revocation status when using CRLs.
|
|
///
|
|
/// If CRLs are provided with [`with_crls`][Self::with_crls] and it isn't possible to
|
|
/// determine the revocation status of a certificate, do not treat it as an error condition.
|
|
/// Overrides the default behavior where unknown revocation status is considered an error.
|
|
///
|
|
/// If no CRLs are provided then this setting has no effect as revocation status checks
|
|
/// are not performed.
|
|
pub fn allow_unknown_revocation_status(mut self) -> Self {
|
|
self.unknown_revocation_policy = UnknownStatusPolicy::Allow;
|
|
self
|
|
}
|
|
|
|
/// Build a server certificate verifier, allowing control over the root certificates to use as
|
|
/// trust anchors, and to control how server certificate revocation checking is performed.
|
|
///
|
|
/// If `with_signature_verification_algorithms` was not called on the builder, a default set of
|
|
/// signature verification algorithms is used, controlled by the selected [`crypto::CryptoProvider`].
|
|
///
|
|
/// Once built, the provided `Arc<dyn ServerCertVerifier>` can be used with a Rustls
|
|
/// [`ServerConfig`] to configure client certificate validation using
|
|
/// [`with_client_cert_verifier`][ConfigBuilder<ClientConfig, WantsVerifier>::with_client_cert_verifier].
|
|
///
|
|
/// # Errors
|
|
/// This function will return a [`VerifierBuilderError`] if:
|
|
/// 1. No trust anchors have been provided.
|
|
/// 2. DER encoded CRLs have been provided that can not be parsed successfully.
|
|
pub fn build(self) -> Result<Arc<WebPkiServerVerifier>, VerifierBuilderError> {
|
|
if self.roots.is_empty() {
|
|
return Err(VerifierBuilderError::NoRootAnchors);
|
|
}
|
|
|
|
Ok(WebPkiServerVerifier::new(
|
|
self.roots,
|
|
parse_crls(self.crls)?,
|
|
self.revocation_check_depth,
|
|
self.unknown_revocation_policy,
|
|
self.supported_algs,
|
|
)
|
|
.into())
|
|
}
|
|
}
|
|
|
|
/// Default `ServerCertVerifier`, see the trait impl for more information.
|
|
#[allow(unreachable_pub)]
|
|
#[derive(Debug)]
|
|
pub struct WebPkiServerVerifier {
|
|
roots: Arc<RootCertStore>,
|
|
crls: Vec<CertRevocationList<'static>>,
|
|
revocation_check_depth: RevocationCheckDepth,
|
|
unknown_revocation_policy: UnknownStatusPolicy,
|
|
supported: WebPkiSupportedAlgorithms,
|
|
}
|
|
|
|
#[allow(unreachable_pub)]
|
|
impl WebPkiServerVerifier {
|
|
/// Create a builder for the `webpki` server certificate verifier configuration using
|
|
/// the [process-default `CryptoProvider`][CryptoProvider#using-the-per-process-default-cryptoprovider].
|
|
///
|
|
/// Server certificates will be verified using the trust anchors found in the provided `roots`.
|
|
///
|
|
/// Use [`Self::builder_with_provider`] if you wish to specify an explicit provider.
|
|
///
|
|
/// For more information, see the [`ServerCertVerifierBuilder`] documentation.
|
|
pub fn builder(roots: Arc<RootCertStore>) -> ServerCertVerifierBuilder {
|
|
Self::builder_with_provider(
|
|
roots,
|
|
Arc::clone(CryptoProvider::get_default_or_install_from_crate_features()),
|
|
)
|
|
}
|
|
|
|
/// Create a builder for the `webpki` server certificate verifier configuration using
|
|
/// a specified [`CryptoProvider`].
|
|
///
|
|
/// Server certificates will be verified using the trust anchors found in the provided `roots`.
|
|
///
|
|
/// The cryptography used comes from the specified [`CryptoProvider`].
|
|
///
|
|
/// For more information, see the [`ServerCertVerifierBuilder`] documentation.
|
|
pub fn builder_with_provider(
|
|
roots: Arc<RootCertStore>,
|
|
provider: Arc<CryptoProvider>,
|
|
) -> ServerCertVerifierBuilder {
|
|
ServerCertVerifierBuilder::new(roots, provider.signature_verification_algorithms)
|
|
}
|
|
|
|
/// Short-cut for creating a `WebPkiServerVerifier` that does not perform certificate revocation
|
|
/// checking, avoiding the need to use a builder.
|
|
pub(crate) fn new_without_revocation(
|
|
roots: impl Into<Arc<RootCertStore>>,
|
|
supported_algs: WebPkiSupportedAlgorithms,
|
|
) -> Self {
|
|
Self::new(
|
|
roots,
|
|
Vec::default(),
|
|
RevocationCheckDepth::Chain,
|
|
UnknownStatusPolicy::Allow,
|
|
supported_algs,
|
|
)
|
|
}
|
|
|
|
/// Constructs a new `WebPkiServerVerifier`.
|
|
///
|
|
/// * `roots` is the set of trust anchors to trust for issuing server certs.
|
|
/// * `crls` are a vec of owned certificate revocation lists (CRLs) to use for
|
|
/// client certificate validation.
|
|
/// * `revocation_check_depth` controls which certificates have their revocation status checked
|
|
/// when `crls` are provided.
|
|
/// * `unknown_revocation_policy` controls how certificates with an unknown revocation status
|
|
/// are handled when `crls` are provided.
|
|
/// * `supported` is the set of supported algorithms that will be used for
|
|
/// certificate verification and TLS handshake signature verification.
|
|
pub(crate) fn new(
|
|
roots: impl Into<Arc<RootCertStore>>,
|
|
crls: Vec<CertRevocationList<'static>>,
|
|
revocation_check_depth: RevocationCheckDepth,
|
|
unknown_revocation_policy: UnknownStatusPolicy,
|
|
supported: WebPkiSupportedAlgorithms,
|
|
) -> Self {
|
|
Self {
|
|
roots: roots.into(),
|
|
crls,
|
|
revocation_check_depth,
|
|
unknown_revocation_policy,
|
|
supported,
|
|
}
|
|
}
|
|
}
|
|
|
|
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
|
|
/// - Valid revocation status (if applicable).
|
|
///
|
|
/// Depending on the verifier's configuration revocation status checking may be performed for
|
|
/// each certificate in the chain to a root CA (excluding the root itself), or only the
|
|
/// end entity certificate. Similarly, unknown revocation status may be treated as an error
|
|
/// or allowed based on configuration.
|
|
fn verify_server_cert(
|
|
&self,
|
|
end_entity: &CertificateDer<'_>,
|
|
intermediates: &[CertificateDer<'_>],
|
|
server_name: &ServerName<'_>,
|
|
ocsp_response: &[u8],
|
|
now: UnixTime,
|
|
) -> Result<ServerCertVerified, Error> {
|
|
let cert = ParsedCertificate::try_from(end_entity)?;
|
|
|
|
let crl_refs = self.crls.iter().collect::<Vec<_>>();
|
|
|
|
let revocation = if self.crls.is_empty() {
|
|
None
|
|
} else {
|
|
// Note: unwrap here is safe because RevocationOptionsBuilder only errors when given
|
|
// empty CRLs.
|
|
Some(
|
|
webpki::RevocationOptionsBuilder::new(crl_refs.as_slice())
|
|
// Note: safe to unwrap here - new is only fallible if no CRLs are provided
|
|
// and we verify this above.
|
|
.unwrap()
|
|
.with_depth(self.revocation_check_depth)
|
|
.with_status_policy(self.unknown_revocation_policy)
|
|
.build(),
|
|
)
|
|
};
|
|
|
|
// Note: we use the crate-internal `_impl` fn here in order to provide revocation
|
|
// checking information, if applicable.
|
|
verify_server_cert_signed_by_trust_anchor_impl(
|
|
&cert,
|
|
&self.roots,
|
|
intermediates,
|
|
revocation,
|
|
now,
|
|
self.supported.all,
|
|
)?;
|
|
|
|
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> {
|
|
verify_tls12_signature(message, cert, dss, &self.supported)
|
|
}
|
|
|
|
fn verify_tls13_signature(
|
|
&self,
|
|
message: &[u8],
|
|
cert: &CertificateDer<'_>,
|
|
dss: &DigitallySignedStruct,
|
|
) -> Result<HandshakeSignatureValid, Error> {
|
|
verify_tls13_signature(message, cert, dss, &self.supported)
|
|
}
|
|
|
|
fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
|
|
self.supported.supported_schemes()
|
|
}
|
|
}
|
|
|
|
test_for_each_provider! {
|
|
use std::sync::Arc;
|
|
use std::{vec, println};
|
|
use std::prelude::v1::*;
|
|
|
|
use pki_types::{CertificateDer, CertificateRevocationListDer};
|
|
|
|
use super::{VerifierBuilderError, WebPkiServerVerifier};
|
|
use crate::RootCertStore;
|
|
|
|
fn load_crls(crls_der: &[&[u8]]) -> Vec<CertificateRevocationListDer<'static>> {
|
|
crls_der
|
|
.iter()
|
|
.map(|pem_bytes| {
|
|
rustls_pemfile::crls(&mut &pem_bytes[..])
|
|
.next()
|
|
.unwrap()
|
|
.unwrap()
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
fn test_crls() -> Vec<CertificateRevocationListDer<'static>> {
|
|
load_crls(&[
|
|
include_bytes!("../../../test-ca/ecdsa-p256/client.revoked.crl.pem").as_slice(),
|
|
include_bytes!("../../../test-ca/rsa-2048/client.revoked.crl.pem").as_slice(),
|
|
])
|
|
}
|
|
|
|
fn load_roots(roots_der: &[&[u8]]) -> Arc<RootCertStore> {
|
|
let mut roots = RootCertStore::empty();
|
|
roots_der.iter().for_each(|der| {
|
|
roots
|
|
.add(CertificateDer::from(der.to_vec()))
|
|
.unwrap()
|
|
});
|
|
roots.into()
|
|
}
|
|
|
|
fn test_roots() -> Arc<RootCertStore> {
|
|
load_roots(&[
|
|
include_bytes!("../../../test-ca/ecdsa-p256/ca.der").as_slice(),
|
|
include_bytes!("../../../test-ca/rsa-2048/ca.der").as_slice(),
|
|
])
|
|
}
|
|
|
|
#[test]
|
|
fn test_with_invalid_crls() {
|
|
// Trying to build a server verifier with invalid CRLs should error at build time.
|
|
let result = WebPkiServerVerifier::builder_with_provider(
|
|
test_roots(),
|
|
provider::default_provider().into(),
|
|
)
|
|
.with_crls(vec![CertificateRevocationListDer::from(vec![0xFF])])
|
|
.build();
|
|
assert!(matches!(result, Err(VerifierBuilderError::InvalidCrl(_))));
|
|
}
|
|
|
|
#[test]
|
|
fn test_with_crls_multiple_calls() {
|
|
// We should be able to call `with_crls` on a server verifier multiple times.
|
|
let initial_crls = test_crls();
|
|
let extra_crls =
|
|
load_crls(&[
|
|
include_bytes!("../../../test-ca/eddsa/client.revoked.crl.pem").as_slice(),
|
|
]);
|
|
|
|
let builder = WebPkiServerVerifier::builder_with_provider(
|
|
test_roots(),
|
|
provider::default_provider().into(),
|
|
)
|
|
.with_crls(initial_crls.clone())
|
|
.with_crls(extra_crls.clone());
|
|
|
|
// There should be the expected number of crls.
|
|
assert_eq!(builder.crls.len(), initial_crls.len() + extra_crls.len());
|
|
// The builder should be Debug.
|
|
println!("{:?}", builder);
|
|
builder.build().unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn test_builder_no_roots() {
|
|
// Trying to create a server verifier builder with no trust anchors should fail at build time
|
|
let result = WebPkiServerVerifier::builder_with_provider(
|
|
RootCertStore::empty().into(),
|
|
provider::default_provider().into(),
|
|
)
|
|
.build();
|
|
assert!(matches!(result, Err(VerifierBuilderError::NoRootAnchors)));
|
|
}
|
|
|
|
#[test]
|
|
fn test_server_verifier_ee_only() {
|
|
// We should be able to build a server cert. verifier that only checks the EE cert.
|
|
let builder = WebPkiServerVerifier::builder_with_provider(
|
|
test_roots(),
|
|
provider::default_provider().into(),
|
|
)
|
|
.only_check_end_entity_revocation();
|
|
// The builder should be Debug.
|
|
println!("{:?}", builder);
|
|
builder.build().unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn test_server_verifier_allow_unknown() {
|
|
// We should be able to build a server cert. verifier that allows unknown revocation
|
|
// status.
|
|
let builder = WebPkiServerVerifier::builder_with_provider(
|
|
test_roots(),
|
|
provider::default_provider().into(),
|
|
)
|
|
.allow_unknown_revocation_status();
|
|
// The builder should be Debug.
|
|
println!("{:?}", builder);
|
|
builder.build().unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn test_server_verifier_allow_unknown_ee_only() {
|
|
// We should be able to build a server cert. verifier that allows unknown revocation
|
|
// status and only checks the EE cert.
|
|
let builder = WebPkiServerVerifier::builder_with_provider(
|
|
test_roots(),
|
|
provider::default_provider().into(),
|
|
)
|
|
.allow_unknown_revocation_status()
|
|
.only_check_end_entity_revocation();
|
|
// The builder should be Debug.
|
|
println!("{:?}", builder);
|
|
builder.build().unwrap();
|
|
}
|
|
}
|