From 5b50692b3290380893ac2569451cfe595ffb4ccb Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Wed, 3 Jan 2024 13:06:34 -0500 Subject: [PATCH] tests: use fixed SystemTime for certificate validation 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. --- rustls-platform-verifier/src/tests/mod.rs | 14 +++++++++++ .../src/tests/verification_mock/ca.go | 4 ++++ .../src/tests/verification_mock/mod.rs | 24 ++++++++++++++++--- .../src/tests/verification_real_world/mod.rs | 19 ++++++++------- 4 files changed, 49 insertions(+), 12 deletions(-) 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!(), },