From 5b50692b3290380893ac2569451cfe595ffb4ccb Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Wed, 3 Jan 2024 13:06:34 -0500 Subject: [PATCH 1/2] 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!(), }, From 1c03c9661ac9bb78035a11b49376701be35fa63e Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Wed, 3 Jan 2024 14:56:29 -0500 Subject: [PATCH 2/2] verification_real_world: update 1password test data comments * Fix the date the files were vendored * Fix the validity period * Fix the ref to the tool used to update them --- .../src/tests/verification_real_world/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 e2a28910..4e0e20d6 100644 --- a/rustls-platform-verifier/src/tests/verification_real_world/mod.rs +++ b/rustls-platform-verifier/src/tests/verification_real_world/mod.rs @@ -41,16 +41,16 @@ use rustls::{client::ServerCertVerifier, CertificateError, Error as TlsError}; use std::convert::TryFrom; // This is the certificate chain presented by one server for -// my.1password.com when this test was updated 2022-09-22. It is +// my.1password.com when this test was updated 2023-08-01. It is // valid for *.1password.com and 1password.com from -// "Jul 24 00:00:00 2022 GMT" through "Aug 22 23:59:59 2023 GMT". +// "Jun 24 00:00:00 2023 GMT" through "Jul 22 23:59:59 2024 GMT". // // Use this to template view the certificate using OpenSSL: // ```sh // openssl x509 -inform der -text -in 1password_com_valid_1.crt | less // ``` // -// You can update the cert file with `update_valid_1_cert.bash` +// You can update the cert file with `update_valid_ee_certs.rs` const VALID_1PASSWORD_COM_CHAIN: &[&[u8]] = &[ include_bytes!("1password_com_valid_1.crt"), include_bytes!("1password_com_valid_2.crt"),