diff --git a/rustls-platform-verifier/src/tests/mod.rs b/rustls-platform-verifier/src/tests/mod.rs index f42f7f8f..59e8577f 100644 --- a/rustls-platform-verifier/src/tests/mod.rs +++ b/rustls-platform-verifier/src/tests/mod.rs @@ -2,6 +2,7 @@ pub mod ffi; use std::error::Error as StdError; +use std::time::{Duration, SystemTime}; mod verification_real_world; @@ -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. @@ -46,3 +50,13 @@ pub fn assert_cert_error_eq( 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) +} diff --git a/rustls-platform-verifier/src/tests/verification_mock/ca.go b/rustls-platform-verifier/src/tests/verification_mock/ca.go index a6805b8c..ec3b4de1 100644 --- a/rustls-platform-verifier/src/tests/verification_mock/ca.go +++ b/rustls-platform-verifier/src/tests/verification_mock/ca.go @@ -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. // diff --git a/rustls-platform-verifier/src/tests/verification_mock/mod.rs b/rustls-platform-verifier/src/tests/verification_mock/mod.rs index 9f0748eb..ceb83f5f 100644 --- a/rustls-platform-verifier/src/tests/verification_mock/mod.rs +++ b/rustls-platform-verifier/src/tests/verification_mock/mod.rs @@ -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; @@ -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!( @@ -120,6 +120,7 @@ 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!(), }, @@ -127,6 +128,7 @@ mock_root_test_cases! { 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!(), }, @@ -134,6 +136,7 @@ mock_root_test_cases! { 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!(), }, @@ -141,6 +144,7 @@ mock_root_test_cases! { 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!(), }, @@ -148,6 +152,7 @@ mock_root_test_cases! { 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!(), }, @@ -155,6 +160,7 @@ mock_root_test_cases! { 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!(), }, @@ -166,6 +172,7 @@ 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!(), }, @@ -173,6 +180,7 @@ mock_root_test_cases! { 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!(), }, @@ -180,6 +188,7 @@ mock_root_test_cases! { 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!(), }, @@ -192,6 +201,7 @@ 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!(), }, @@ -199,6 +209,7 @@ mock_root_test_cases! { 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!(), }, @@ -206,6 +217,7 @@ mock_root_test_cases! { 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!(), }, @@ -214,6 +226,7 @@ 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!(), }, @@ -221,6 +234,7 @@ mock_root_test_cases! { 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!(), }, @@ -228,6 +242,7 @@ mock_root_test_cases! { 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!(), }, @@ -235,6 +250,7 @@ mock_root_test_cases! { 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), @@ -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), @@ -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), @@ -289,7 +307,7 @@ fn test_with_mock_root(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( diff --git a/rustls-platform-verifier/src/tests/verification_real_world/mod.rs b/rustls-platform-verifier/src/tests/verification_real_world/mod.rs index 964d2242..e2a28910 100644 --- a/rustls-platform-verifier/src/tests/verification_real_world/mod.rs +++ b/rustls-platform-verifier/src/tests/verification_real_world/mod.rs @@ -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 @@ -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; @@ -145,7 +139,7 @@ fn real_world_test(test_case: &TestCase) { &server_name, &mut std::iter::empty(), stapled_ocsp, - std::time::SystemTime::now(), + test_case.verification_time, ) .map(|_| ()); @@ -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!(), }, @@ -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!(), }, @@ -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!(), }, @@ -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!(), }, @@ -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!(), }, @@ -207,6 +206,7 @@ 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!(), }, @@ -214,6 +214,7 @@ real_world_test_cases! { reference_id: LETSENCRYPT_ORG, chain: VALID_LETSENCRYPT_ORG_CHAIN, stapled_ocsp: None, + verification_time: verification_time(), expected_result: Ok(()), other_error: no_error!(), },