use rustls::client::danger::ServerCertVerifier;
use rustls::pki_types;
#[cfg(not(any(target_vendor = "apple", windows)))]
use rustls::pki_types::{DnsName, ServerName};
use rustls::{CertificateError, Error as TlsError};
use super::TestCase;
use crate::tests::{assert_cert_error_eq, test_provider, verification_time};
use crate::Verifier;
const VALID_AWS_AMAZON_COM_CHAIN: &[&[u8]] = &[
include_bytes!("aws_amazon_com_valid_1.crt"),
include_bytes!("aws_amazon_com_valid_2.crt"),
include_bytes!("aws_amazon_com_valid_3.crt"),
include_bytes!("aws_amazon_com_valid_4.crt"),
];
#[cfg(not(any(target_vendor = "apple", windows)))]
fn valid_aws_chain_names() -> Vec<String> {
const VALID_AWS_NAMES: &[&str] = &[
"aws.amazon.com",
"aws-us-west-2.amazon.com",
"www.aws.amazon.com",
"1.aws-lbr.amazonaws.com",
"amazonaws-china.com",
"www.amazonaws-china.com",
"aws-us-east-1.amazon.com",
];
VALID_AWS_NAMES
.iter()
.copied()
.map(|name| format!("DnsName(\"{name}\")"))
.collect()
}
const AWS_AMAZON_COM: &str = "aws.amazon.com";
const VALID_UNRELATED_DOMAIN: &str = "my.1password.com";
const VALID_UNRELATED_SUBDOMAIN: &str = "www.amazon.com";
const LETSENCRYPT_ORG: &str = "letsencrypt.org";
const VALID_LETSENCRYPT_ORG_CHAIN: &[&[u8]] = &[
include_bytes!("letsencrypt_org_valid_1.crt"),
include_bytes!("letsencrypt_org_valid_2.crt"),
];
macro_rules! real_world_test_cases {
{ $( $name:ident => $test_case:expr ),+ , } => {
real_world_test_cases!(@ $($name => $test_case),+,);
#[cfg(test)]
mod tests {
$(
#[test]
pub fn $name() {
super::$name()
}
)+
}
#[cfg(feature = "ffi-testing")]
pub static ALL_TEST_CASES: &'static [fn()] = &[
$($name),+
];
};
{@ $( $name:ident => $test_case:expr ),+ , } => {
$(
pub(super) fn $name() {
real_world_test(&$test_case);
}
)+
}
}
macro_rules! no_error {
() => {
None::<std::convert::Infallible>
};
}
fn real_world_test<E: std::error::Error>(test_case: &TestCase<E>) {
log::info!(
"verifying ref ID {:?} expected {:?}",
test_case.reference_id,
test_case.expected_result
);
let crypto_provider = test_provider();
#[cfg(target_os = "freebsd")]
let verifier = Verifier::new_with_extra_roots(
webpki_root_certs::TLS_SERVER_ROOT_CERTS.iter().cloned(),
crypto_provider,
)
.unwrap();
#[cfg(not(target_os = "freebsd"))]
let verifier = Verifier::new(crypto_provider).unwrap();
let mut chain = test_case
.chain
.iter()
.map(|bytes| pki_types::CertificateDer::from(*bytes));
let end_entity_cert = chain.next().unwrap();
let intermediates: Vec<pki_types::CertificateDer<'_>> = chain.collect();
let server_name = pki_types::ServerName::try_from(test_case.reference_id).unwrap();
let stapled_ocsp = test_case.stapled_ocsp.unwrap_or(&[]);
let result = verifier
.verify_server_cert(
&end_entity_cert,
&intermediates,
&server_name,
stapled_ocsp,
test_case.verification_time,
)
.map(|_| ());
assert_cert_error_eq(
&result.map(|_| ()),
&test_case.expected_result,
None::<&std::convert::Infallible>,
);
}
real_world_test_cases! {
aws_amazon_com_valid => TestCase {
reference_id: AWS_AMAZON_COM,
chain: VALID_AWS_AMAZON_COM_CHAIN,
stapled_ocsp: None,
verification_time: verification_time(),
expected_result: Ok(()),
other_error: no_error!(),
},
aws_amazon_com_valid_no_stapled => TestCase {
reference_id: AWS_AMAZON_COM,
chain: VALID_AWS_AMAZON_COM_CHAIN,
stapled_ocsp: None,
verification_time: verification_time(),
expected_result: Ok(()),
other_error: no_error!(),
},
_aws_amazon_com_valid => TestCase {
reference_id: "www.aws.amazon.com",
chain: VALID_AWS_AMAZON_COM_CHAIN,
stapled_ocsp: None,
verification_time: verification_time(),
expected_result: Ok(()),
other_error: no_error!(),
},
unrelated_domain_invalid => TestCase {
reference_id: VALID_UNRELATED_SUBDOMAIN,
chain: VALID_AWS_AMAZON_COM_CHAIN,
stapled_ocsp: None,
verification_time: verification_time(),
#[cfg(not(any(target_vendor = "apple", windows)))]
expected_result: Err(TlsError::InvalidCertificate(CertificateError::NotValidForNameContext {
expected: ServerName::DnsName(DnsName::try_from(VALID_UNRELATED_SUBDOMAIN).unwrap()),
presented: valid_aws_chain_names(),
})),
#[cfg(any(target_vendor = "apple", windows))]
expected_result: Err(TlsError::InvalidCertificate(CertificateError::NotValidForName)),
other_error: no_error!(),
},
unrelated_chain_not_valid_for_my_1password_com => TestCase {
reference_id: VALID_UNRELATED_DOMAIN,
chain: VALID_AWS_AMAZON_COM_CHAIN,
stapled_ocsp: None,
verification_time: verification_time(),
#[cfg(not(any(target_vendor = "apple", windows)))]
expected_result: Err(TlsError::InvalidCertificate(CertificateError::NotValidForNameContext {
expected: ServerName::DnsName(DnsName::try_from(VALID_UNRELATED_DOMAIN).unwrap()),
presented: valid_aws_chain_names(),
})),
#[cfg(any(target_vendor = "apple", windows))]
expected_result: Err(TlsError::InvalidCertificate(CertificateError::NotValidForName)),
other_error: no_error!(),
},
letsencrypt => TestCase {
reference_id: LETSENCRYPT_ORG,
chain: VALID_LETSENCRYPT_ORG_CHAIN,
stapled_ocsp: None,
verification_time: verification_time(),
expected_result: Ok(()),
other_error: no_error!(),
},
}