Delegate choosing webpki algorithms to `CryptoProvider`

This drastically simplifies `provider-example`.  But the
primary goal is ensuring a client configured `with_provider(AWS_LC_RS)`
only uses algorithms from aws-lc-rs, irrespective of crate features.
This commit is contained in:
Joseph Birr-Pixton 2023-10-30 09:59:48 +00:00 committed by Joe Birr-Pixton
parent 6719bc52be
commit c6c792b616
11 changed files with 123 additions and 77 deletions

View File

@ -2,7 +2,7 @@ use std::io::{stdout, Read, Write};
use std::net::TcpStream;
use std::sync::Arc;
use rustls_provider_example::{certificate_verifier, PROVIDER};
use rustls_provider_example::PROVIDER;
fn main() {
env_logger::init();
@ -16,8 +16,7 @@ fn main() {
let config = rustls::ClientConfig::builder_with_provider(PROVIDER)
.with_safe_defaults()
.dangerous()
.with_custom_certificate_verifier(certificate_verifier(root_store))
.with_root_certificates(root_store)
.with_no_client_auth();
let server_name = "www.rust-lang.org".try_into().unwrap();

View File

@ -36,6 +36,10 @@ impl rustls::crypto::CryptoProvider for Provider {
) -> Result<Arc<dyn rustls::sign::SigningKey>, rustls::Error> {
unimplemented!()
}
fn signature_verification_algorithms(&self) -> rustls::WebPkiSupportedAlgorithms {
verify::ALGORITHMS
}
}
static ALL_CIPHER_SUITES: &[rustls::SupportedCipherSuite] = &[
@ -67,12 +71,3 @@ pub static TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: rustls::SupportedCipherS
prf_provider: &rustls::crypto::tls12::PrfUsingHmac(&hmac::Sha256Hmac),
aead_alg: &aead::Chacha20Poly1305,
});
pub fn certificate_verifier(
roots: rustls::RootCertStore,
) -> Arc<dyn rustls::client::danger::ServerCertVerifier> {
rustls::client::WebPkiServerVerifier::builder(roots.into())
.with_signature_verification_algorithms(verify::ALGORITHMS)
.build()
.unwrap()
}

View File

@ -5,7 +5,6 @@ use crate::crypto::{CryptoProvider, SupportedKxGroup};
use crate::error::Error;
use crate::key_log::NoKeyLog;
use crate::suites::SupportedCipherSuite;
#[cfg(feature = "ring")]
use crate::webpki;
use crate::{verify, versions};
@ -15,11 +14,9 @@ use pki_types::{CertificateDer, PrivateKeyDer};
use alloc::sync::Arc;
use alloc::vec::Vec;
#[cfg(any(feature = "dangerous_configuration", feature = "ring"))]
use core::marker::PhantomData;
impl ConfigBuilder<ClientConfig, WantsVerifier> {
#[cfg(feature = "ring")]
/// Choose how to verify server certificates.
pub fn with_root_certificates(
self,
@ -33,6 +30,9 @@ impl ConfigBuilder<ClientConfig, WantsVerifier> {
versions: self.state.versions,
verifier: Arc::new(webpki::WebPkiServerVerifier::new_without_revocation(
root_store,
self.state
.provider
.signature_verification_algorithms(),
)),
},
side: PhantomData,

View File

@ -1,5 +1,6 @@
use crate::sign::SigningKey;
use crate::suites;
use crate::webpki::WebPkiSupportedAlgorithms;
use crate::{Error, NamedGroup};
use alloc::boxed::Box;
@ -54,6 +55,11 @@ pub trait CryptoProvider: Send + Sync + Debug + 'static {
&self,
key_der: PrivateKeyDer<'static>,
) -> Result<Arc<dyn SigningKey>, Error>;
/// Return the signature verification algorithms for use with webpki.
///
/// These are used for both certificate chain verification and handshake signature verification.
fn signature_verification_algorithms(&self) -> WebPkiSupportedAlgorithms;
}
/// A supported key exchange group.

View File

@ -59,6 +59,10 @@ impl CryptoProvider for Ring {
sign::any_supported_type(&key_der)
.map_err(|_| Error::General("invalid private key".to_owned()))
}
fn signature_verification_algorithms(&self) -> WebPkiSupportedAlgorithms {
SUPPORTED_SIG_ALGS
}
}
/// The cipher suite configuration that an application should use by default.
@ -103,7 +107,7 @@ pub mod cipher_suite {
/// A `WebPkiSupportedAlgorithms` value that reflects webpki's capabilities when
/// compiled against *ring*.
pub(crate) static SUPPORTED_SIG_ALGS: WebPkiSupportedAlgorithms = WebPkiSupportedAlgorithms {
static SUPPORTED_SIG_ALGS: WebPkiSupportedAlgorithms = WebPkiSupportedAlgorithms {
all: &[
webpki_algs::ECDSA_P256_SHA256,
webpki_algs::ECDSA_P256_SHA384,

View File

@ -9,6 +9,7 @@
use core::time::Duration;
use std::time::Instant;
use crate::crypto::ring;
use crate::verify::ServerCertVerifier;
use crate::webpki::{RootCertStore, WebPkiServerVerifier};
@ -208,7 +209,10 @@ impl Context {
}
fn bench(&self, count: usize) {
let verifier = WebPkiServerVerifier::new_without_revocation(self.roots.clone());
let verifier = WebPkiServerVerifier::new_without_revocation(
self.roots.clone(),
ring::RING.signature_verification_algorithms(),
);
const OCSP_RESPONSE: &[u8] = &[];
let mut times = Vec::new();

View File

@ -5,6 +5,7 @@ use pki_types::{CertificateDer, CertificateRevocationListDer, UnixTime};
use webpki::{CertRevocationList, RevocationCheckDepth, UnknownStatusPolicy};
use super::{pki_error, VerifierBuilderError};
use crate::crypto::CryptoProvider;
use crate::verify::{
ClientCertVerified, ClientCertVerifier, DigitallySignedStruct, HandshakeSignatureValid,
NoClientAuth,
@ -24,11 +25,14 @@ pub struct ClientCertVerifierBuilder {
revocation_check_depth: RevocationCheckDepth,
unknown_revocation_policy: UnknownStatusPolicy,
anon_policy: AnonymousClientPolicy,
supported_algs: Option<WebPkiSupportedAlgorithms>,
supported_algs: WebPkiSupportedAlgorithms,
}
impl ClientCertVerifierBuilder {
pub(crate) fn new(roots: Arc<RootCertStore>) -> Self {
pub(crate) fn new(
roots: Arc<RootCertStore>,
supported_algs: WebPkiSupportedAlgorithms,
) -> Self {
Self {
root_hint_subjects: roots.subjects(),
roots,
@ -36,7 +40,7 @@ impl ClientCertVerifierBuilder {
anon_policy: AnonymousClientPolicy::Deny,
revocation_check_depth: RevocationCheckDepth::Chain,
unknown_revocation_policy: UnknownStatusPolicy::Deny,
supported_algs: None,
supported_algs,
}
}
@ -135,7 +139,7 @@ impl ClientCertVerifierBuilder {
mut self,
supported_algs: WebPkiSupportedAlgorithms,
) -> Self {
self.supported_algs = Some(supported_algs);
self.supported_algs = supported_algs;
self
}
@ -144,8 +148,8 @@ impl ClientCertVerifierBuilder {
/// and to determine what to do with anonymous clients that do not respond to the client
/// certificate authentication offer with a client certificate.
///
/// If the `ring` crate feature is supplied, and `with_signature_verification_algorithms` was not
/// called on the builder, a default set of signature verification algorithms is used.
/// If `with_signature_verification_algorithms` was not called on the builder, a default set of
/// signature verification algorithms is used, controlled by the selected [`crate::crypto::CryptoProvider`].
///
/// Once built, the provided `Arc<dyn ClientCertVerifier>` can be used with a Rustls
/// [crate::server::ServerConfig] to configure client certificate validation using
@ -155,22 +159,11 @@ impl ClientCertVerifierBuilder {
/// This function will return a `ClientCertVerifierBuilderError` if:
/// 1. No trust anchors have been provided.
/// 2. DER encoded CRLs have been provided that can not be parsed successfully.
/// 3. No signature verification algorithms were set and the `ring` feature is not enabled.
#[cfg_attr(not(feature = "ring"), allow(unused_mut))]
pub fn build(mut self) -> Result<Arc<dyn ClientCertVerifier>, VerifierBuilderError> {
pub fn build(self) -> Result<Arc<dyn ClientCertVerifier>, VerifierBuilderError> {
if self.roots.is_empty() {
return Err(VerifierBuilderError::NoRootAnchors);
}
#[cfg(feature = "ring")]
if self.supported_algs.is_none() {
self.supported_algs = Some(crate::crypto::ring::SUPPORTED_SIG_ALGS);
}
let supported_algs = self
.supported_algs
.ok_or(VerifierBuilderError::NoSupportedAlgorithms)?;
Ok(Arc::new(WebPkiClientVerifier::new(
self.roots,
self.root_hint_subjects,
@ -178,7 +171,7 @@ impl ClientCertVerifierBuilder {
self.revocation_check_depth,
self.unknown_revocation_policy,
self.anon_policy,
supported_algs,
self.supported_algs,
)))
}
}
@ -246,14 +239,37 @@ pub struct WebPkiClientVerifier {
}
impl WebPkiClientVerifier {
/// Create builder to build up the `webpki` client certificate verifier configuration.
/// Create a builder for the `webpki` client certificate verifier configuration using
/// the default [`CryptoProvider`].
///
/// 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.
///
/// The cryptography used comes from the default [`CryptoProvider`]: [`crate::crypto::ring::RING`].
/// Use [`Self::builder_with_provider`] if you wish to customize this.
///
/// For more information, see the [`ClientCertVerifierBuilder`] documentation.
#[cfg(feature = "ring")]
pub fn builder(roots: Arc<RootCertStore>) -> ClientCertVerifierBuilder {
ClientCertVerifierBuilder::new(roots)
Self::builder_with_provider(roots, crate::crypto::ring::RING)
}
/// Create a builder for the `webpki` client certificate verifier configuration using
/// a specified [`CryptoProvider`].
///
/// 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.
///
/// The cryptography used comes from the specified [`CryptoProvider`].
///
/// For more information, see the [`ClientCertVerifierBuilder`] documentation.
pub fn builder_with_provider(
roots: Arc<RootCertStore>,
provider: &'static dyn CryptoProvider,
) -> ClientCertVerifierBuilder {
ClientCertVerifierBuilder::new(roots, provider.signature_verification_algorithms())
}
/// Create a new `WebPkiClientVerifier` that disables client authentication. The server will
@ -563,7 +579,6 @@ mod tests {
let all = vec![
VerifierBuilderError::NoRootAnchors,
VerifierBuilderError::InvalidCrl(crate::CertRevocationListError::ParseError),
VerifierBuilderError::NoSupportedAlgorithms,
];
for err in all {

View File

@ -34,11 +34,6 @@ pub enum VerifierBuilderError {
NoRootAnchors,
/// A provided CRL could not be parsed.
InvalidCrl(CertRevocationListError),
/// No supported signature verification algorithms were provided.
///
/// Call `with_signature_verification_algorithms` on the builder, or compile
/// with the `ring` feature.
NoSupportedAlgorithms,
}
impl From<CertRevocationListError> for VerifierBuilderError {
@ -52,9 +47,6 @@ impl fmt::Display for VerifierBuilderError {
match self {
Self::NoRootAnchors => write!(f, "no root trust anchors were provided"),
Self::InvalidCrl(e) => write!(f, "provided CRL could not be parsed: {:?}", e),
Self::NoSupportedAlgorithms => {
write!(f, "no signature verification algorithms were provided")
}
}
}
}

View File

@ -6,8 +6,7 @@ use alloc::vec::Vec;
use pki_types::{CertificateDer, CertificateRevocationListDer, UnixTime};
use webpki::{CertRevocationList, RevocationCheckDepth, UnknownStatusPolicy};
#[cfg(feature = "ring")]
use crate::crypto::ring::SUPPORTED_SIG_ALGS;
use crate::crypto::CryptoProvider;
use crate::verify::{
DigitallySignedStruct, HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier,
};
@ -27,17 +26,20 @@ pub struct ServerCertVerifierBuilder {
crls: Vec<CertificateRevocationListDer<'static>>,
revocation_check_depth: RevocationCheckDepth,
unknown_revocation_policy: UnknownStatusPolicy,
supported_algs: Option<WebPkiSupportedAlgorithms>,
supported_algs: WebPkiSupportedAlgorithms,
}
impl ServerCertVerifierBuilder {
pub(crate) fn new(roots: Arc<RootCertStore>) -> Self {
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: None,
supported_algs,
}
}
@ -86,15 +88,15 @@ impl ServerCertVerifierBuilder {
mut self,
supported_algs: WebPkiSupportedAlgorithms,
) -> Self {
self.supported_algs = Some(supported_algs);
self.supported_algs = supported_algs;
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 the `ring` crate feature is supplied, and `with_signature_verification_algorithms` was not
/// called on the builder, a default set of signature verification algorithms is used.
/// If `with_signature_verification_algorithms` was not called on the builder, a default set of
/// signature verification algorithms is used, controlled by the selected [`crate::crypto::CryptoProvider`].
///
/// Once built, the provided `Arc<dyn ServerCertVerifier>` can be used with a Rustls
/// [crate::server::ServerConfig] to configure client certificate validation using
@ -104,28 +106,17 @@ impl ServerCertVerifierBuilder {
/// This function will return a `CertVerifierBuilderError` if:
/// 1. No trust anchors have been provided.
/// 2. DER encoded CRLs have been provided that can not be parsed successfully.
/// 3. No signature verification algorithms were set and the `ring` feature is not enabled.
#[cfg_attr(not(feature = "ring"), allow(unused_mut))]
pub fn build(mut self) -> Result<Arc<dyn ServerCertVerifier>, VerifierBuilderError> {
pub fn build(self) -> Result<Arc<dyn ServerCertVerifier>, VerifierBuilderError> {
if self.roots.is_empty() {
return Err(VerifierBuilderError::NoRootAnchors);
}
#[cfg(feature = "ring")]
if self.supported_algs.is_none() {
self.supported_algs = Some(SUPPORTED_SIG_ALGS);
}
let supported_algs = self
.supported_algs
.ok_or(VerifierBuilderError::NoSupportedAlgorithms)?;
Ok(Arc::new(WebPkiServerVerifier::new(
self.roots,
parse_crls(self.crls)?,
self.revocation_check_depth,
self.unknown_revocation_policy,
supported_algs,
self.supported_algs,
)))
}
}
@ -142,24 +133,47 @@ pub struct WebPkiServerVerifier {
#[allow(unreachable_pub)]
impl WebPkiServerVerifier {
/// Create builder to build up the `webpki` server certificate verifier configuration.
/// Create a builder for the `webpki` server certificate verifier configuration using
/// the default [`CryptoProvider`].
///
/// Server certificates will be verified using the trust anchors found in the provided `roots`.
///
/// The cryptography used comes from the default [`CryptoProvider`]: [`crate::crypto::ring::RING`].
/// Use [`Self::builder_with_provider`] if you wish to customize this.
///
/// For more information, see the [`ServerCertVerifierBuilder`] documentation.
#[cfg(feature = "ring")]
pub fn builder(roots: Arc<RootCertStore>) -> ServerCertVerifierBuilder {
ServerCertVerifierBuilder::new(roots)
Self::builder_with_provider(roots, crate::crypto::ring::RING)
}
/// 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: &'static dyn 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.
#[cfg(feature = "ring")]
pub(crate) fn new_without_revocation(roots: impl Into<Arc<RootCertStore>>) -> Self {
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_SIG_ALGS,
supported_algs,
)
}
@ -198,7 +212,12 @@ impl WebPkiServerVerifier {
cert: &CertificateDer<'_>,
dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, Error> {
verify_signed_struct(message, cert, dss, &SUPPORTED_SIG_ALGS)
verify_signed_struct(
message,
cert,
dss,
&crate::crypto::ring::RING.signature_verification_algorithms(),
)
}
/// A full implementation of `ServerCertVerifier::verify_tls13_signature` or
@ -209,14 +228,21 @@ impl WebPkiServerVerifier {
cert: &CertificateDer<'_>,
dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, Error> {
verify_tls13(message, cert, dss, &SUPPORTED_SIG_ALGS)
verify_tls13(
message,
cert,
dss,
&crate::crypto::ring::RING.signature_verification_algorithms(),
)
}
/// A full implementation of `ServerCertVerifier::supported_verify_schemes()` or
/// `ClientCertVerifier::supported_verify_schemes()`.
#[cfg(feature = "ring")]
pub fn default_supported_verify_schemes() -> Vec<SignatureScheme> {
SUPPORTED_SIG_ALGS.supported_schemes()
crate::crypto::ring::RING
.signature_verification_algorithms()
.supported_schemes()
}
}

View File

@ -246,7 +246,7 @@ mod tests {
fn webpki_supported_algorithms_is_debug() {
assert_eq!(
"WebPkiSupportedAlgorithms { all: [ .. ], mapping: [ECDSA_NISTP384_SHA384, ECDSA_NISTP256_SHA256, ED25519, RSA_PSS_SHA512, RSA_PSS_SHA384, RSA_PSS_SHA256, RSA_PKCS1_SHA512, RSA_PKCS1_SHA384, RSA_PKCS1_SHA256] }",
format!("{:?}", crate::crypto::ring::SUPPORTED_SIG_ALGS)
format!("{:?}", crate::crypto::ring::RING.signature_verification_algorithms())
);
}
}

View File

@ -5489,6 +5489,11 @@ impl rustls::crypto::CryptoProvider for FaultyRandomProvider {
) -> Result<Arc<dyn rustls::sign::SigningKey>, Error> {
self.parent.load_private_key(key_der)
}
fn signature_verification_algorithms(&self) -> rustls::WebPkiSupportedAlgorithms {
self.parent
.signature_verification_algorithms()
}
}
#[test]