Skip to content

Commit

Permalink
tests: use fixed SystemTime for certificate validation
Browse files Browse the repository at this point in the history
Fixing the `SystemTime` that we pass to the certificate verifier for the
mock and real world verification tests will ensure that the tests don't
start to fail just because the vendored certificates have expired.
  • Loading branch information
cpu authored and complexspaces committed Jan 4, 2024
1 parent 65b2a97 commit 22725b9
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 12 deletions.
14 changes: 14 additions & 0 deletions rustls-platform-verifier/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
pub mod ffi;

use std::error::Error as StdError;
use std::time::{Duration, SystemTime};

mod verification_real_world;

Expand All @@ -19,6 +20,9 @@ struct TestCase<'a, E: StdError> {
/// The stapled OCSP response given to us by Rustls, if any.
pub stapled_ocsp: Option<&'a [u8]>,

/// The time to use as the current time for verification.
pub verification_time: SystemTime,

pub expected_result: Result<(), TlsError>,

/// An error that should be present inside an expected `CertificateError::Other` variant.
Expand Down Expand Up @@ -46,3 +50,13 @@ pub fn assert_cert_error_eq<E: StdError + PartialEq + 'static>(
assert_eq!(result, expected);
}
}

/// Return a fixed [SystemTime] for certificate validation purposes.
///
/// We fix the "now" value used for certificate validation to a fixed point in time at which
/// we know the test certificates are valid. This must be updated if the mock certificates
/// are regenerated.
pub(crate) fn verification_time() -> SystemTime {
// Wednesday, January 3, 2024 6:03:08 PM UTC
SystemTime::UNIX_EPOCH + Duration::from_secs(1_704_304_988)
}
4 changes: 4 additions & 0 deletions rustls-platform-verifier/src/tests/verification_mock/ca.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
// Generates the test data files used in the tests in verification_mock.rs.
//
// After re-generating mock certificates be sure to also update the fixed
// verification timestamp in `mod.rs`'s `verification_time` fn to match
// the current time.
//
// The primary point of this program is to fully automate the creation of the
// test data, with minimal tool dependencies (e.g. no OpenSSL), with low effort.
//
Expand Down
24 changes: 21 additions & 3 deletions rustls-platform-verifier/src/tests/verification_mock/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
))]

use super::TestCase;
use crate::tests::assert_cert_error_eq;
use crate::tests::{assert_cert_error_eq, verification_time};
use crate::verification::{EkuError, Verifier};
use rustls::{client::ServerCertVerifier, CertificateError, Error as TlsError};
use std::convert::TryFrom;
Expand Down Expand Up @@ -95,7 +95,7 @@ pub(super) fn verification_without_mock_root() {
&server_name,
&mut std::iter::empty(),
&[],
std::time::SystemTime::now(),
verification_time(),
);

assert_eq!(
Expand All @@ -120,41 +120,47 @@ mock_root_test_cases! {
reference_id: EXAMPLE_COM,
chain: &[ROOT1_INT1_EXAMPLE_COM_GOOD, ROOT1_INT1],
stapled_ocsp: None,
verification_time: verification_time(),
expected_result: Ok(()),
other_error: no_error!(),
},
valid_no_stapling_ipv4 [ any(windows, target_os = "android", target_os = "macos", target_os = "linux") ] => TestCase {
reference_id: LOCALHOST_IPV4,
chain: &[ROOT1_INT1_LOCALHOST_IPV4_GOOD, ROOT1_INT1],
stapled_ocsp: None,
verification_time: verification_time(),
expected_result: Ok(()),
other_error: no_error!(),
},
valid_no_stapling_ipv6 [ any(windows, target_os = "android", target_os = "macos", target_os = "linux") ] => TestCase {
reference_id: LOCALHOST_IPV6,
chain: &[ROOT1_INT1_LOCALHOST_IPV6_GOOD, ROOT1_INT1],
stapled_ocsp: None,
verification_time: verification_time(),
expected_result: Ok(()),
other_error: no_error!(),
},
valid_stapled_good_dns [ any(windows, target_os = "android", target_os = "android", target_os = "macos", target_os = "linux") ] => TestCase {
reference_id: EXAMPLE_COM,
chain: &[ROOT1_INT1_EXAMPLE_COM_GOOD, ROOT1_INT1],
stapled_ocsp: Some(include_bytes!("root1-int1-ee_example.com-good.ocsp")),
verification_time: verification_time(),
expected_result: Ok(()),
other_error: no_error!(),
},
valid_stapled_good_ipv4 [ any(windows, target_os = "android", target_os = "macos", target_os = "linux") ] => TestCase {
reference_id: LOCALHOST_IPV4,
chain: &[ROOT1_INT1_LOCALHOST_IPV4_GOOD, ROOT1_INT1],
stapled_ocsp: Some(include_bytes!("root1-int1-ee_127.0.0.1-good.ocsp")),
verification_time: verification_time(),
expected_result: Ok(()),
other_error: no_error!(),
},
valid_stapled_good_ipv6 [ any(windows, target_os = "android", target_os = "macos", target_os = "linux") ] => TestCase {
reference_id: LOCALHOST_IPV6,
chain: &[ROOT1_INT1_LOCALHOST_IPV6_GOOD, ROOT1_INT1],
stapled_ocsp: Some(include_bytes!("root1-int1-ee_1-good.ocsp")),
verification_time: verification_time(),
expected_result: Ok(()),
other_error: no_error!(),
},
Expand All @@ -166,20 +172,23 @@ mock_root_test_cases! {
reference_id: EXAMPLE_COM,
chain: &[include_bytes!("root1-int1-ee_example.com-revoked.crt"), ROOT1_INT1],
stapled_ocsp: Some(include_bytes!("root1-int1-ee_example.com-revoked.ocsp")),
verification_time: verification_time(),
expected_result: Err(TlsError::InvalidCertificate(CertificateError::Revoked)),
other_error: no_error!(),
},
stapled_revoked_ipv4 [ any(windows, target_os = "android", target_os = "macos") ] => TestCase {
reference_id: LOCALHOST_IPV4,
chain: &[include_bytes!("root1-int1-ee_127.0.0.1-revoked.crt"), ROOT1_INT1],
stapled_ocsp: Some(include_bytes!("root1-int1-ee_127.0.0.1-revoked.ocsp")),
verification_time: verification_time(),
expected_result: Err(TlsError::InvalidCertificate(CertificateError::Revoked)),
other_error: no_error!(),
},
stapled_revoked_ipv6 [ any(windows, target_os = "android", target_os = "macos") ] => TestCase {
reference_id: LOCALHOST_IPV6,
chain: &[include_bytes!("root1-int1-ee_1-revoked.crt"), ROOT1_INT1],
stapled_ocsp: Some(include_bytes!("root1-int1-ee_1-revoked.ocsp")),
verification_time: verification_time(),
expected_result: Err(TlsError::InvalidCertificate(CertificateError::Revoked)),
other_error: no_error!(),
},
Expand All @@ -192,20 +201,23 @@ mock_root_test_cases! {
reference_id: EXAMPLE_COM,
chain: &[ROOT1_INT1_EXAMPLE_COM_GOOD],
stapled_ocsp: None,
verification_time: verification_time(),
expected_result: Err(TlsError::InvalidCertificate(CertificateError::UnknownIssuer)),
other_error: no_error!(),
},
ee_only_ipv4 [ any(windows, target_os = "android", target_os = "macos", target_os = "linux") ] => TestCase {
reference_id: LOCALHOST_IPV4,
chain: &[ROOT1_INT1_LOCALHOST_IPV4_GOOD],
stapled_ocsp: None,
verification_time: verification_time(),
expected_result: Err(TlsError::InvalidCertificate(CertificateError::UnknownIssuer)),
other_error: no_error!(),
},
ee_only_ipv6 [ any(windows, target_os = "android", target_os = "macos", target_os = "linux") ] => TestCase {
reference_id: LOCALHOST_IPV6,
chain: &[ROOT1_INT1_LOCALHOST_IPV6_GOOD],
stapled_ocsp: None,
verification_time: verification_time(),
expected_result: Err(TlsError::InvalidCertificate(CertificateError::UnknownIssuer)),
other_error: no_error!(),
},
Expand All @@ -214,27 +226,31 @@ mock_root_test_cases! {
reference_id: "example.org",
chain: &[ROOT1_INT1_EXAMPLE_COM_GOOD, ROOT1_INT1],
stapled_ocsp: None,
verification_time: verification_time(),
expected_result: Err(TlsError::InvalidCertificate(CertificateError::NotValidForName)),
other_error: no_error!(),
},
domain_mismatch_ipv4 [ any(windows, target_os = "android", target_os = "macos", target_os = "linux") ] => TestCase {
reference_id: "198.168.0.1",
chain: &[ROOT1_INT1_LOCALHOST_IPV4_GOOD, ROOT1_INT1],
stapled_ocsp: None,
verification_time: verification_time(),
expected_result: Err(TlsError::InvalidCertificate(CertificateError::NotValidForName)),
other_error: no_error!(),
},
domain_mismatch_ipv6 [ any(windows, target_os = "android", target_os = "macos", target_os = "linux") ] => TestCase {
reference_id: "::ffff:c6a8:1",
chain: &[ROOT1_INT1_LOCALHOST_IPV6_GOOD, ROOT1_INT1],
stapled_ocsp: None,
verification_time: verification_time(),
expected_result: Err(TlsError::InvalidCertificate(CertificateError::NotValidForName)),
other_error: no_error!(),
},
wrong_eku_dns [ any(windows, target_os = "android", target_os = "macos", target_os = "linux") ] => TestCase {
reference_id: EXAMPLE_COM,
chain: &[include_bytes!("root1-int1-ee_example.com-wrong_eku.crt"), ROOT1_INT1],
stapled_ocsp: None,
verification_time: verification_time(),
expected_result: Err(TlsError::InvalidCertificate(
CertificateError::Other(Arc::from(EkuError)))),
other_error: Some(EkuError),
Expand All @@ -243,6 +259,7 @@ mock_root_test_cases! {
reference_id: LOCALHOST_IPV4,
chain: &[include_bytes!("root1-int1-ee_127.0.0.1-wrong_eku.crt"), ROOT1_INT1],
stapled_ocsp: None,
verification_time: verification_time(),
expected_result: Err(TlsError::InvalidCertificate(
CertificateError::Other(Arc::from(EkuError)))),
other_error: Some(EkuError),
Expand All @@ -251,6 +268,7 @@ mock_root_test_cases! {
reference_id: LOCALHOST_IPV6,
chain: &[include_bytes!("root1-int1-ee_1-wrong_eku.crt"), ROOT1_INT1],
stapled_ocsp: None,
verification_time: verification_time(),
expected_result: Err(TlsError::InvalidCertificate(
CertificateError::Other(Arc::from(EkuError)))),
other_error: Some(EkuError),
Expand Down Expand Up @@ -289,7 +307,7 @@ fn test_with_mock_root<E: std::error::Error + PartialEq + 'static>(test_case: &T
&server_name,
&mut std::iter::empty(),
test_case.stapled_ocsp.unwrap_or(&[]),
std::time::SystemTime::now(),
test_case.verification_time,
);

assert_cert_error_eq(
Expand Down
19 changes: 10 additions & 9 deletions rustls-platform-verifier/src/tests/verification_real_world/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,6 @@
//! fetching then the trust anchors for these certificates might not be
//! trusted by the operating system's root store.
//!
//! XXX: Currently these tests are a time-bomb because they validate the
//! certificates as of the current system time, because the version of
//! Rustls we use does not support passing in a different time. The newest
//! version of Rustls does have that capability. We need to upgrade to that
//! version of Rustls, and/or otherwise change these tests, before these
//! certificates expire in Fall/Winter 2022.
//!
//! XXX: These tests should be using a stapled OCSP responses so that the
//! (operating-system-based) verifier doesn't try to fetch an OCSP
//! response or CRL certificate. However, until we can fix the validation
Expand All @@ -42,7 +35,8 @@
//! Thus we don't expect these tests to be flaky w.r.t. that, except for
//! potentially poor performance.
use super::TestCase;
use crate::{tests::assert_cert_error_eq, Verifier};
use crate::tests::{assert_cert_error_eq, verification_time};
use crate::Verifier;
use rustls::{client::ServerCertVerifier, CertificateError, Error as TlsError};
use std::convert::TryFrom;

Expand Down Expand Up @@ -145,7 +139,7 @@ fn real_world_test<E: std::error::Error>(test_case: &TestCase<E>) {
&server_name,
&mut std::iter::empty(),
stapled_ocsp,
std::time::SystemTime::now(),
test_case.verification_time,
)
.map(|_| ());

Expand All @@ -165,6 +159,7 @@ real_world_test_cases! {
reference_id: MY_1PASSWORD_COM,
chain: VALID_1PASSWORD_COM_CHAIN,
stapled_ocsp: None,
verification_time: verification_time(),
expected_result: Ok(()),
other_error: no_error!(),
},
Expand All @@ -173,6 +168,7 @@ real_world_test_cases! {
reference_id: MY_1PASSWORD_COM,
chain: VALID_1PASSWORD_COM_CHAIN,
stapled_ocsp: None,
verification_time: verification_time(),
expected_result: Ok(()),
other_error: no_error!(),
},
Expand All @@ -181,6 +177,7 @@ real_world_test_cases! {
reference_id: "1password.com",
chain: VALID_1PASSWORD_COM_CHAIN,
stapled_ocsp: None,
verification_time: verification_time(),
expected_result: Ok(()),
other_error: no_error!(),
},
Expand All @@ -189,6 +186,7 @@ real_world_test_cases! {
reference_id: VALID_UNRELATED_DOMAIN,
chain: VALID_1PASSWORD_COM_CHAIN,
stapled_ocsp: None,
verification_time: verification_time(),
expected_result: Err(TlsError::InvalidCertificate(CertificateError::NotValidForName)),
other_error: no_error!(),
},
Expand All @@ -198,6 +196,7 @@ real_world_test_cases! {
reference_id: VALID_UNRELATED_DOMAIN,
chain: VALID_UNRELATED_CHAIN,
stapled_ocsp: None,
verification_time: verification_time(),
expected_result: Ok(()),
other_error: no_error!(),
},
Expand All @@ -207,13 +206,15 @@ real_world_test_cases! {
reference_id: MY_1PASSWORD_COM,
chain: VALID_UNRELATED_CHAIN,
stapled_ocsp: None,
verification_time: verification_time(),
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!(),
},
Expand Down

0 comments on commit 22725b9

Please sign in to comment.