From 239f0b3c7dc5f5447a5f5acbb4830a2ea64ce3b6 Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Wed, 3 Jan 2024 10:26:31 -0500 Subject: [PATCH] Cargo: update to rustls 0.22, associated updates For the time being, this branch continues to unconditionally use *ring* as the crypto provider. Follow-up work to expose this as a choice (e.g allowing aws-lc-rs as a provider) may be interesting. Deps: * updated rustls 0.21 -> 0.22.1 Linux deps: * rustls-native-certs 0.6 -> 0.7 * webpki 0.101 -> 0.102 Android deps: * webpki 0.101 -> 0.102 WASM32 deps: * webpki-roots 0.25 -> 0.26 Summary of breaking change updates: * We use rustls 0.22.1 in specific to benefit from the `pki_types` re-export, removing the need to add that as our own dep with matching version. * `ServerName`, `Certificate`, and `OwnedTrustAnchor` types are now sourced from `pki_types`, with an associated generic lifetime. The `OwnedTrustAnchor` type is now just `TrustAnchor`. * The 'dangerous' rustls crate feature was removed, and associated items moved into new locations with the import path emphasizing danger. * "Other error" types changed to use a specific `rustls::OtherError` inner variant. * `SystemTime` for verifiers replaced with `pki_types::UnixTime`. * Default fns on `ServerCertVerifier` trait were removed, must be reconstituted with `rustls::verify_tls12_signature`, `rustls::verify_tls13_signature` and `WebPkiSupportedAlgorithms.supported_schemes` using a `CryptoProvider`. * `ServerName` now supports a `to_str` operation, avoiding the need to `match` and handle unsupported name types. * `WebPkiVerifier` was renamed to `WebPkiServerVerifier`, handled as an `Arc` and constructed with a builder. --- Cargo.lock | 163 +++++++----------- rustls-platform-verifier/Cargo.toml | 10 +- rustls-platform-verifier/src/lib.rs | 8 +- rustls-platform-verifier/src/tests/mod.rs | 1 + .../src/tests/verification_mock/mod.rs | 38 ++-- .../src/tests/verification_real_world/mod.rs | 13 +- .../src/verification/android.rs | 107 +++++++----- .../src/verification/apple.rs | 98 +++++++---- .../src/verification/mod.rs | 14 +- .../src/verification/others.rs | 105 +++++++---- .../src/verification/windows.rs | 83 +++++---- 11 files changed, 340 insertions(+), 300 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 60a4df44..ffd0dfaf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -47,12 +47,6 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" -[[package]] -name = "bumpalo" -version = "3.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" - [[package]] name = "bytes" version = "1.4.0" @@ -116,6 +110,17 @@ dependencies = [ "regex", ] +[[package]] +name = "getrandom" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "jni" version = "0.19.0" @@ -136,15 +141,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" -[[package]] -name = "js-sys" -version = "0.3.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" -dependencies = [ - "wasm-bindgen", -] - [[package]] name = "libc" version = "0.2.151" @@ -254,52 +250,61 @@ checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" [[package]] name = "ring" -version = "0.16.20" +version = "0.17.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" dependencies = [ "cc", + "getrandom", "libc", - "once_cell", "spin", "untrusted", - "web-sys", - "winapi", + "windows-sys", ] [[package]] name = "rustls" -version = "0.21.7" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" +checksum = "fe6b63262c9fcac8659abfaa96cac103d28166d3ff3eaf8f412e19f3ae9e5a48" dependencies = [ "log", "ring", + "rustls-pki-types", "rustls-webpki", - "sct", + "subtle", + "zeroize", ] [[package]] name = "rustls-native-certs" -version = "0.6.3" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792" dependencies = [ "openssl-probe", "rustls-pemfile", + "rustls-pki-types", "schannel", "security-framework", ] [[package]] name = "rustls-pemfile" -version = "1.0.3" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" +checksum = "35e4980fa29e4c4b212ffb3db068a564cbf560e51d3944b7c88bd8bf5bec64f4" dependencies = [ "base64", + "rustls-pki-types", ] +[[package]] +name = "rustls-pki-types" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e9d979b3ce68192e42760c7810125eb6cf2ea10efae545a156063e61f314e2a" + [[package]] name = "rustls-platform-verifier" version = "0.1.0" @@ -327,11 +332,12 @@ version = "0.1.0" [[package]] name = "rustls-webpki" -version = "0.101.4" +version = "0.102.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d93931baf2d282fff8d3a532bbfd7653f734643161b87e3e01e59a04439bf0d" +checksum = "de2635c8bc2b88d367767c5de8ea1d8db9af3f6219eba28442242d9ab81d1b89" dependencies = [ "ring", + "rustls-pki-types", "untrusted", ] @@ -353,16 +359,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "sct" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "security-framework" version = "2.9.2" @@ -389,9 +385,15 @@ dependencies = [ [[package]] name = "spin" -version = "0.5.2" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" @@ -432,9 +434,9 @@ checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "untrusted" -version = "0.7.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "walkdir" @@ -447,75 +449,20 @@ dependencies = [ ] [[package]] -name = "wasm-bindgen" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.87" +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] -name = "web-sys" -version = "0.3.64" +name = "webpki-roots" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +checksum = "0de2cfda980f21be5a7ed2eadb3e6fe074d56022bea2cdeb1a62eb220fc04188" dependencies = [ - "js-sys", - "wasm-bindgen", + "rustls-pki-types", ] -[[package]] -name = "webpki-roots" -version = "0.25.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" - [[package]] name = "winapi" version = "0.3.9" @@ -612,3 +559,9 @@ name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/rustls-platform-verifier/Cargo.toml b/rustls-platform-verifier/Cargo.toml index 9403a64b..e0471f96 100644 --- a/rustls-platform-verifier/Cargo.toml +++ b/rustls-platform-verifier/Cargo.toml @@ -29,27 +29,27 @@ cert-logging = ["base64"] docsrs = ["jni", "once_cell"] [dependencies] -rustls = { version = "0.21", features = ["dangerous_configuration", "tls12", "logging"] } +rustls = { version = "0.22.1", features = ["tls12", "logging"] } log = { version = "0.4" } base64 = { version = "0.21", optional = true } # Only used when the `cert-logging` feature is enabled. jni = { version = "0.19", default-features = false, optional = true } # Only used during doc generation once_cell = { version = "1.9", optional = true } # Only used during doc generation. [target.'cfg(target_os = "linux")'.dependencies] -rustls-native-certs = "0.6" +rustls-native-certs = "0.7" once_cell = "1.9" -webpki = { package = "rustls-webpki", version = "0.101", features = ["alloc", "std"] } +webpki = { package = "rustls-webpki", version = "0.102", features = ["ring", "alloc", "std"] } [target.'cfg(target_os = "android")'.dependencies] rustls-platform-verifier-android = { path = "../android-release-support", version = "0.1.0" } jni = { version = "0.19", default-features = false } -webpki = { package = "rustls-webpki", version = "0.101", features = ["alloc", "std"] } +webpki = { package = "rustls-webpki", version = "0.102", features = ["ring", "alloc", "std"] } once_cell = "1.9" android_logger = { version = "0.13", optional = true } # Only used during testing. [target.'cfg(target_arch = "wasm32")'.dependencies] once_cell = "1.9" -webpki-roots = "0.25" +webpki-roots = "0.26" [target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies] core-foundation = "0.9" diff --git a/rustls-platform-verifier/src/lib.rs b/rustls-platform-verifier/src/lib.rs index 42d890f5..804cd75a 100644 --- a/rustls-platform-verifier/src/lib.rs +++ b/rustls-platform-verifier/src/lib.rs @@ -53,13 +53,13 @@ pub use tests::ffi::*; /// /// If you require more control over the rustls `ClientConfig`, you can /// instantiate a [Verifier] with [Verifier::default] and then use it -/// with [rustls::ConfigBuilder::with_custom_certificate_verifier]. +/// with [rustls::ConfigBuilder::dangerous::with_custom_certificate_verifier]. /// /// Refer to the crate level documentation to see what platforms /// are currently supported. pub fn tls_config() -> ClientConfig { - rustls::ClientConfig::builder() - .with_safe_defaults() + ClientConfig::builder() + .dangerous() .with_custom_certificate_verifier(Arc::new(Verifier::new())) .with_no_client_auth() } @@ -68,6 +68,6 @@ pub fn tls_config() -> ClientConfig { /// /// This is not intended for production use, you should use [tls_config] instead. #[cfg(feature = "dbg")] -pub fn verifier_for_dbg(root: &[u8]) -> Arc { +pub fn verifier_for_dbg(root: &[u8]) -> Arc { Arc::new(Verifier::new_with_fake_root(root)) } diff --git a/rustls-platform-verifier/src/tests/mod.rs b/rustls-platform-verifier/src/tests/mod.rs index f42f7f8f..8988f16d 100644 --- a/rustls-platform-verifier/src/tests/mod.rs +++ b/rustls-platform-verifier/src/tests/mod.rs @@ -39,6 +39,7 @@ pub fn assert_cert_error_eq( if let Err(InvalidCertificate(CertificateError::Other(err))) = &expected { let expected_err = expected_err.expect("error not provided for `Other` case handling"); let err: &E = err + .0 .downcast_ref() .expect("incorrect `Other` inner error kind"); assert_eq!(err, expected_err); diff --git a/rustls-platform-verifier/src/tests/verification_mock/mod.rs b/rustls-platform-verifier/src/tests/verification_mock/mod.rs index 9f0748eb..e246c740 100644 --- a/rustls-platform-verifier/src/tests/verification_mock/mod.rs +++ b/rustls-platform-verifier/src/tests/verification_mock/mod.rs @@ -23,7 +23,9 @@ use super::TestCase; use crate::tests::assert_cert_error_eq; use crate::verification::{EkuError, Verifier}; -use rustls::{client::ServerCertVerifier, CertificateError, Error as TlsError}; +use rustls::client::danger::ServerCertVerifier; +use rustls::pki_types; +use rustls::{CertificateError, Error as TlsError, OtherError}; use std::convert::TryFrom; use std::net::IpAddr; use std::sync::Arc; @@ -84,18 +86,17 @@ const LOCALHOST_IPV6: &str = "::1"; pub(super) fn verification_without_mock_root() { let verifier = Verifier::new(); - let server_name = rustls::client::ServerName::try_from(EXAMPLE_COM).unwrap(); - let end_entity = rustls::Certificate(ROOT1_INT1_EXAMPLE_COM_GOOD.to_vec()); - let intermediates = [rustls::Certificate(ROOT1_INT1.to_vec())]; + let server_name = pki_types::ServerName::try_from(EXAMPLE_COM).unwrap(); + let end_entity = pki_types::CertificateDer::from(ROOT1_INT1_EXAMPLE_COM_GOOD.to_vec()); + let intermediates = [pki_types::CertificateDer::from(ROOT1_INT1.to_vec())]; // Fails because the server cert has no trust root in Windows, and can't since it uses a self-signed CA. let result = verifier.verify_server_cert( &end_entity, &intermediates, &server_name, - &mut std::iter::empty(), &[], - std::time::SystemTime::now(), + pki_types::UnixTime::now(), ); assert_eq!( @@ -236,7 +237,7 @@ mock_root_test_cases! { chain: &[include_bytes!("root1-int1-ee_example.com-wrong_eku.crt"), ROOT1_INT1], stapled_ocsp: None, expected_result: Err(TlsError::InvalidCertificate( - CertificateError::Other(Arc::from(EkuError)))), + CertificateError::Other(OtherError(Arc::from(EkuError))))), other_error: Some(EkuError), }, wrong_eku_ipv4 [ any(windows, target_os = "android", target_os = "macos", target_os = "linux") ] => TestCase { @@ -244,7 +245,7 @@ mock_root_test_cases! { chain: &[include_bytes!("root1-int1-ee_127.0.0.1-wrong_eku.crt"), ROOT1_INT1], stapled_ocsp: None, expected_result: Err(TlsError::InvalidCertificate( - CertificateError::Other(Arc::from(EkuError)))), + CertificateError::Other(OtherError(Arc::from(EkuError))))), other_error: Some(EkuError), }, wrong_eku_ipv6 [ any(windows, target_os = "android", target_os = "macos", target_os = "linux") ] => TestCase { @@ -252,7 +253,7 @@ mock_root_test_cases! { chain: &[include_bytes!("root1-int1-ee_1-wrong_eku.crt"), ROOT1_INT1], stapled_ocsp: None, expected_result: Err(TlsError::InvalidCertificate( - CertificateError::Other(Arc::from(EkuError)))), + CertificateError::Other(OtherError(Arc::from(EkuError))))), other_error: Some(EkuError), }, } @@ -264,32 +265,25 @@ fn test_with_mock_root(test_case: &T let mut chain = test_case .chain .iter() - .map(|bytes| rustls::Certificate(bytes.to_vec())); + .map(|bytes| pki_types::CertificateDer::from(bytes.to_vec())); let end_entity = chain.next().unwrap(); - let intermediates: Vec = chain.collect(); + let intermediates: Vec> = chain.collect(); - let server_name = rustls::client::ServerName::try_from(test_case.reference_id).unwrap(); + let server_name = pki_types::ServerName::try_from(test_case.reference_id).unwrap(); if test_case.reference_id.parse::().is_ok() { - assert!(matches!( - server_name, - rustls::client::ServerName::IpAddress(_) - )); + assert!(matches!(server_name, pki_types::ServerName::IpAddress(_))); } else { - assert!(matches!( - server_name, - rustls::client::ServerName::DnsName(_) - )); + assert!(matches!(server_name, pki_types::ServerName::DnsName(_))); } let result = verifier.verify_server_cert( &end_entity, &intermediates, &server_name, - &mut std::iter::empty(), test_case.stapled_ocsp.unwrap_or(&[]), - std::time::SystemTime::now(), + pki_types::UnixTime::now(), ); 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..1c62d268 100644 --- a/rustls-platform-verifier/src/tests/verification_real_world/mod.rs +++ b/rustls-platform-verifier/src/tests/verification_real_world/mod.rs @@ -43,7 +43,9 @@ //! potentially poor performance. use super::TestCase; use crate::{tests::assert_cert_error_eq, Verifier}; -use rustls::{client::ServerCertVerifier, CertificateError, Error as TlsError}; +use rustls::client::danger::ServerCertVerifier; +use rustls::pki_types; +use rustls::{CertificateError, Error as TlsError}; use std::convert::TryFrom; // This is the certificate chain presented by one server for @@ -129,12 +131,12 @@ fn real_world_test(test_case: &TestCase) { let mut chain = test_case .chain .iter() - .map(|bytes| rustls::Certificate(bytes.to_vec())); + .map(|bytes| pki_types::CertificateDer::from(bytes.to_vec())); let end_entity_cert = chain.next().unwrap(); - let intermediates: Vec = chain.collect(); + let intermediates: Vec> = chain.collect(); - let server_name = rustls::client::ServerName::try_from(test_case.reference_id).unwrap(); + let server_name = pki_types::ServerName::try_from(test_case.reference_id).unwrap(); let stapled_ocsp = test_case.stapled_ocsp.unwrap_or(&[]); @@ -143,9 +145,8 @@ fn real_world_test(test_case: &TestCase) { &end_entity_cert, &intermediates, &server_name, - &mut std::iter::empty(), stapled_ocsp, - std::time::SystemTime::now(), + pki_types::UnixTime::now(), ) .map(|_| ()); diff --git a/rustls-platform-verifier/src/verification/android.rs b/rustls-platform-verifier/src/verification/android.rs index 90239095..0bcf21d6 100644 --- a/rustls-platform-verifier/src/verification/android.rs +++ b/rustls-platform-verifier/src/verification/android.rs @@ -3,11 +3,15 @@ use jni::{ strings::JavaStr, JNIEnv, }; +use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerifier}; +use rustls::crypto::{verify_tls12_signature, verify_tls13_signature}; +use rustls::pki_types; use rustls::Error::InvalidCertificate; -use rustls::{client::ServerCertVerifier, CertificateError, Error as TlsError, ServerName}; -use std::time::SystemTime; +use rustls::{ + CertificateError, DigitallySignedStruct, Error as TlsError, OtherError, SignatureScheme, +}; -use super::{log_server_cert, unsupported_server_name, ALLOWED_EKUS}; +use super::{log_server_cert, ALLOWED_EKUS}; use crate::android::{with_context, CachedClass}; static CERT_VERIFIER_CLASS: CachedClass = @@ -35,7 +39,7 @@ enum VerifierStatus { const AUTH_TYPE: &str = "RSA"; /// A TLS certificate verifier that utilizes the Android platform verifier. -#[derive(Default)] +#[derive(Debug, Default)] pub struct Verifier { /// Testing only: The root CA certificate to trust. #[cfg(any(test, feature = "ffi-testing"))] @@ -75,25 +79,23 @@ impl Verifier { fn verify_certificate( &self, - end_entity: &rustls::Certificate, - intermediates: &[rustls::Certificate], - server_name: &rustls::ServerName, - server_name_str: &str, + end_entity: &pki_types::CertificateDer<'_>, + intermediates: &[pki_types::CertificateDer<'_>], + server_name: &pki_types::ServerName<'_>, ocsp_response: Option<&[u8]>, - now: SystemTime, + now: pki_types::UnixTime, ) -> Result<(), TlsError> { let certificate_chain = std::iter::once(end_entity) .chain(intermediates) .map(|cert| cert.as_ref()) .enumerate(); - #[allow(clippy::as_conversions)] - let now: i64 = now - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_millis() + // Convert the unix timestamp into milliseconds, expressed as + // an i64 to later be converted into a Java Long used for a Date + // constructor. + let now: i64 = (now.as_secs() * 1000) .try_into() - .unwrap(); + .map_err(|_| TlsError::FailedToGetCurrentTime)?; let verification_result = with_context(|cx| { let env = cx.env(); @@ -172,7 +174,7 @@ impl Verifier { VERIFIER_CALL, &[ JValue::from(*cx.application_context()), - JValue::from(env.new_string(server_name_str)?), + JValue::from(env.new_string(&server_name.to_str())?), JValue::from(env.new_string(AUTH_TYPE)?), JValue::from(JObject::from(allowed_ekus)), JValue::from(ocsp_response), @@ -215,7 +217,7 @@ impl Verifier { Err(InvalidCertificate(CertificateError::BadEncoding)) } VerifierStatus::InvalidExtension => Err(InvalidCertificate( - CertificateError::Other(std::sync::Arc::new(super::EkuError)), + CertificateError::Other(OtherError(std::sync::Arc::new(super::EkuError))), )), } } @@ -258,45 +260,22 @@ fn extract_result_info(env: &JNIEnv<'_>, result: JObject<'_>) -> (VerifierStatus impl ServerCertVerifier for Verifier { fn verify_server_cert( &self, - end_entity: &rustls::Certificate, - intermediates: &[rustls::Certificate], - server_name: &rustls::ServerName, - // Android has no support for providing SCTs to the verifier, - // but it does consider them internally if the hostname matches a - // system-specified list. - _scts: &mut dyn Iterator, + end_entity: &pki_types::CertificateDer<'_>, + intermediates: &[pki_types::CertificateDer<'_>], + server_name: &pki_types::ServerName, ocsp_response: &[u8], - now: SystemTime, - ) -> Result { + now: pki_types::UnixTime, + ) -> Result { log_server_cert(end_entity); - // Verify the server name is one that we support and extract a string to use - // for the platform verifier call. - let ip_name; - let server_name_str = match server_name { - ServerName::DnsName(dns_name) => dns_name.as_ref(), - ServerName::IpAddress(ip_addr) => { - ip_name = ip_addr.to_string(); - &ip_name - } - _ => return Err(unsupported_server_name()), - }; - let ocsp_data = if !ocsp_response.is_empty() { Some(ocsp_response) } else { None }; - match self.verify_certificate( - end_entity, - intermediates, - server_name, - server_name_str, - ocsp_data, - now, - ) { - Ok(()) => Ok(rustls::client::ServerCertVerified::assertion()), + match self.verify_certificate(end_entity, intermediates, server_name, ocsp_data, now) { + Ok(()) => Ok(rustls::client::danger::ServerCertVerified::assertion()), Err(e) => { // This error only tells us what the system errored with, so it doesn't leak anything // sensitive. @@ -305,4 +284,38 @@ impl ServerCertVerifier for Verifier { } } } + + fn verify_tls12_signature( + &self, + message: &[u8], + cert: &pki_types::CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result { + verify_tls12_signature( + message, + cert, + dss, + &rustls::crypto::ring::default_provider().signature_verification_algorithms, + ) + } + + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &pki_types::CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result { + verify_tls13_signature( + message, + cert, + dss, + &rustls::crypto::ring::default_provider().signature_verification_algorithms, + ) + } + + fn supported_verify_schemes(&self) -> Vec { + rustls::crypto::ring::default_provider() + .signature_verification_algorithms + .supported_schemes() + } } diff --git a/rustls-platform-verifier/src/verification/apple.rs b/rustls-platform-verifier/src/verification/apple.rs index 05e8f5b2..304ba37f 100644 --- a/rustls-platform-verifier/src/verification/apple.rs +++ b/rustls-platform-verifier/src/verification/apple.rs @@ -1,13 +1,17 @@ -use super::{log_server_cert, unsupported_server_name}; +use super::log_server_cert; use crate::verification::invalid_certificate; use core_foundation::date::CFDate; use core_foundation_sys::date::kCFAbsoluteTimeIntervalSince1970; -use rustls::{client::ServerCertVerifier, CertificateError, Error as TlsError}; +use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerifier}; +use rustls::crypto::{verify_tls12_signature, verify_tls13_signature}; +use rustls::pki_types; +use rustls::{ + CertificateError, DigitallySignedStruct, Error as TlsError, OtherError, SignatureScheme, +}; use security_framework::{ certificate::SecCertificate, policy::SecPolicy, secure_transport::SslProtocolSide, trust::SecTrust, }; -use std::time::SystemTime; mod errors { pub(super) use security_framework_sys::base::{ @@ -16,29 +20,25 @@ mod errors { }; } -fn system_time_to_cfdate(time: SystemTime) -> Result { +#[allow(clippy::as_conversions)] +fn system_time_to_cfdate(time: pki_types::UnixTime) -> Result { // SAFETY: The interval is defined by macOS externally, but is always present and never modified at runtime // since its a global variable. // // See https://developer.apple.com/documentation/corefoundation/kcfabsolutetimeintervalsince1970. - let unix_adjustment = unsafe { - #[allow(clippy::as_conversions)] - std::time::Duration::from_secs(kCFAbsoluteTimeIntervalSince1970 as u64) - }; + let unix_adjustment = unsafe { kCFAbsoluteTimeIntervalSince1970 as u64 }; // Convert a system timestamp based off the UNIX epoch into the // Apple epoch used by all `CFAbsoluteTime` values. // Subtracting Durations with sub() will panic on overflow - #[allow(clippy::as_conversions)] - time.duration_since(SystemTime::UNIX_EPOCH) - .map_err(|_| TlsError::FailedToGetCurrentTime)? + time.as_secs() .checked_sub(unix_adjustment) .ok_or(TlsError::FailedToGetCurrentTime) - .map(|epoch| CFDate::new(epoch.as_secs() as f64)) + .map(|epoch| CFDate::new(epoch as f64)) } /// A TLS certificate verifier that utilizes the Apple platform certificate facilities. -#[derive(Default)] +#[derive(Default, Debug)] pub struct Verifier { /// Testing only: The root CA certificate to trust. #[cfg(any(test, feature = "ffi-testing", feature = "dbg"))] @@ -65,14 +65,14 @@ impl Verifier { fn verify_certificate( &self, - end_entity: &rustls::Certificate, - intermediates: &[rustls::Certificate], + end_entity: &pki_types::CertificateDer<'_>, + intermediates: &[pki_types::CertificateDer<'_>], server_name: &str, ocsp_response: Option<&[u8]>, - now: SystemTime, + now: pki_types::UnixTime, ) -> Result<(), TlsError> { - let certificates: Vec = std::iter::once(end_entity.0.as_slice()) - .chain(intermediates.iter().map(|cert| cert.0.as_slice())) + let certificates: Vec = std::iter::once(end_entity.as_ref()) + .chain(intermediates.iter().map(|cert| cert.as_ref())) .map(|cert| { SecCertificate::from_der(cert) .map_err(|_| TlsError::InvalidCertificate(CertificateError::BadEncoding)) @@ -164,7 +164,7 @@ impl Verifier { CertificateError::UnknownIssuer, )), errors::errSecInvalidExtendedKeyUsage => Ok(TlsError::InvalidCertificate( - CertificateError::Other(std::sync::Arc::new(super::EkuError)), + CertificateError::Other(OtherError(std::sync::Arc::new(super::EkuError))), )), errors::errSecCertificateRevoked => { Ok(TlsError::InvalidCertificate(CertificateError::Revoked)) @@ -183,27 +183,17 @@ impl Verifier { impl ServerCertVerifier for Verifier { fn verify_server_cert( &self, - end_entity: &rustls::Certificate, - intermediates: &[rustls::Certificate], - server_name: &rustls::ServerName, - _scts: &mut dyn Iterator, + end_entity: &pki_types::CertificateDer<'_>, + intermediates: &[pki_types::CertificateDer<'_>], + server_name: &pki_types::ServerName, ocsp_response: &[u8], - now: SystemTime, - ) -> Result { + now: pki_types::UnixTime, + ) -> Result { log_server_cert(end_entity); // Convert IP addresses to name strings to ensure match check on leaf certificate. // Ref: https://developer.apple.com/documentation/security/1392592-secpolicycreatessl - let ip_name; - - let server = match server_name { - rustls::ServerName::DnsName(name) => name.as_ref(), - rustls::ServerName::IpAddress(addr) => { - ip_name = addr.to_string(); - &ip_name - } - _ => return Err(unsupported_server_name()), - }; + let server = server_name.to_str(); let ocsp_data = if !ocsp_response.is_empty() { Some(ocsp_response) @@ -211,8 +201,8 @@ impl ServerCertVerifier for Verifier { None }; - match self.verify_certificate(end_entity, intermediates, server, ocsp_data, now) { - Ok(()) => Ok(rustls::client::ServerCertVerified::assertion()), + match self.verify_certificate(end_entity, intermediates, &server, ocsp_data, now) { + Ok(()) => Ok(rustls::client::danger::ServerCertVerified::assertion()), Err(e) => { // This error only tells us what the system errored with, so it doesn't leak anything // sensitive. @@ -221,4 +211,38 @@ impl ServerCertVerifier for Verifier { } } } + + fn verify_tls12_signature( + &self, + message: &[u8], + cert: &pki_types::CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result { + verify_tls12_signature( + message, + cert, + dss, + &rustls::crypto::ring::default_provider().signature_verification_algorithms, + ) + } + + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &pki_types::CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result { + verify_tls13_signature( + message, + cert, + dss, + &rustls::crypto::ring::default_provider().signature_verification_algorithms, + ) + } + + fn supported_verify_schemes(&self) -> Vec { + rustls::crypto::ring::default_provider() + .signature_verification_algorithms + .supported_schemes() + } } diff --git a/rustls-platform-verifier/src/verification/mod.rs b/rustls-platform-verifier/src/verification/mod.rs index 97d74707..4798d8da 100644 --- a/rustls-platform-verifier/src/verification/mod.rs +++ b/rustls-platform-verifier/src/verification/mod.rs @@ -38,29 +38,23 @@ impl std::error::Error for EkuError {} // Log the certificate we are verifying so that we can try and find what may be wrong with it // if we need to debug a user's situation. -fn log_server_cert(_end_entity: &rustls::Certificate) { +fn log_server_cert(_end_entity: &rustls::pki_types::CertificateDer<'_>) { #[cfg(feature = "cert-logging")] { use base64::Engine; log::debug!( "verifying certificate: {}", - base64::engine::general_purpose::STANDARD.encode(&_end_entity.0) + base64::engine::general_purpose::STANDARD.encode(_end_entity.as_ref()) ); } } -#[cfg(any(windows, target_os = "android", target_os = "macos", target_os = "ios"))] -fn unsupported_server_name() -> rustls::Error { - log::error!("TLS error: unsupported name type"); - rustls::Error::UnsupportedNameType -} - // Unknown certificate error shorthand. Used when we need to construct an "Other" certificate // error with a platform specific error message. #[cfg(any(windows, target_os = "macos", target_os = "ios"))] fn invalid_certificate(reason: impl Into) -> rustls::Error { - rustls::Error::InvalidCertificate(rustls::CertificateError::Other(std::sync::Arc::from( - Box::from(reason.into()), + rustls::Error::InvalidCertificate(rustls::CertificateError::Other(rustls::OtherError( + std::sync::Arc::from(Box::from(reason.into())), ))) } diff --git a/rustls-platform-verifier/src/verification/others.rs b/rustls-platform-verifier/src/verification/others.rs index beefab2e..cda90a61 100644 --- a/rustls-platform-verifier/src/verification/others.rs +++ b/rustls-platform-verifier/src/verification/others.rs @@ -1,13 +1,17 @@ use super::log_server_cert; use once_cell::sync::OnceCell; +use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}; +use rustls::client::WebPkiServerVerifier; +use rustls::crypto::{verify_tls12_signature, verify_tls13_signature}; +use rustls::pki_types; use rustls::{ - client::{ServerCertVerifier, WebPkiVerifier}, - CertificateError, Error as TlsError, + CertificateError, DigitallySignedStruct, Error as TlsError, OtherError, SignatureScheme, }; -use std::sync::Mutex; +use std::fmt::Debug; +use std::sync::{Arc, Mutex}; /// A TLS certificate verifier that uses the system's root store and WebPKI. -#[derive(Default)] +#[derive(Debug, Default)] pub struct Verifier { // We use a `OnceCell` so we only need // to try loading native root certs once per verifier. @@ -16,11 +20,11 @@ pub struct Verifier { // locking and unlocking the application will pull fresh root // certificates from disk, picking up on any changes // that might have been made since. - inner: OnceCell, + inner: OnceCell>, // Extra trust anchors to add to the verifier above and beyond those provided by the // platform via rustls-native-certs. - extra_roots: Mutex>, + extra_roots: Mutex>>, /// Testing only: an additional root CA certificate to trust. #[cfg(any(test, feature = "ffi-testing", feature = "dbg"))] @@ -42,7 +46,9 @@ impl Verifier { /// Creates a new verifier whose certificate validation is provided by /// WebPKI, using root certificates provided by the platform and augmented by /// the provided extra root certificates. - pub fn new_with_extra_roots(roots: impl IntoIterator) -> Self { + pub fn new_with_extra_roots( + roots: impl IntoIterator>, + ) -> Self { Self { inner: OnceCell::new(), extra_roots: roots.into_iter().collect::>().into(), @@ -62,18 +68,23 @@ impl Verifier { } // Attempt to load CA root certificates present on system, fallback to WebPKI roots if error - fn init_verifier(&self) -> Result { + fn init_verifier(&self) -> Result, TlsError> { let mut root_store = rustls::RootCertStore::empty(); // For testing only: load fake root cert, instead of native/WebPKI roots #[cfg(any(test, feature = "ffi-testing", feature = "dbg"))] { if let Some(test_root) = &self.test_only_root_ca_override { - let (added, ignored) = root_store.add_parsable_certificates(&[test_root.clone()]); + let (added, ignored) = + root_store.add_parsable_certificates([pki_types::CertificateDer::from( + test_root.clone(), + )]); if (added != 1) || (ignored != 0) { panic!("Failed to insert fake, test-only root trust anchor"); } - return Ok(WebPkiVerifier::new(root_store, None)); + return WebPkiServerVerifier::builder(root_store.into()) + .build() + .map_err(|e| TlsError::Other(OtherError(Arc::new(e)))); } } @@ -82,7 +93,7 @@ impl Verifier { let mut extra_roots = self.extra_roots.try_lock().unwrap(); if !extra_roots.is_empty() { let count = extra_roots.len(); - root_store.add_trust_anchors(&mut extra_roots.drain(..)); + root_store.extend(extra_roots.drain(..)); log::debug!( "Loaded {count} extra CA certificates in addition to possible system roots", ); @@ -91,8 +102,7 @@ impl Verifier { #[cfg(all(target_os = "linux", not(target_arch = "wasm32")))] match rustls_native_certs::load_native_certs() { Ok(certs) => { - let certs: Vec> = certs.into_iter().map(|c| c.0).collect(); - let (added, ignored) = root_store.add_parsable_certificates(&certs); + let (added, ignored) = root_store.add_parsable_certificates(certs); if ignored != 0 { log::warn!("Some CA root certificates were ignored due to errors"); @@ -130,37 +140,27 @@ impl Verifier { })); }; - Ok(WebPkiVerifier::new(root_store, None)) + WebPkiServerVerifier::builder(root_store.into()) + .build() + .map_err(|e| TlsError::Other(OtherError(Arc::new(e)))) } } impl ServerCertVerifier for Verifier { fn verify_server_cert( &self, - end_entity: &rustls::Certificate, - intermediates: &[rustls::Certificate], - server_name: &rustls::ServerName, - _scts: &mut dyn Iterator, + end_entity: &pki_types::CertificateDer<'_>, + intermediates: &[pki_types::CertificateDer<'_>], + server_name: &pki_types::ServerName, ocsp_response: &[u8], - now: std::time::SystemTime, - ) -> Result { + now: pki_types::UnixTime, + ) -> Result { log_server_cert(end_entity); let verifier = self.inner.get_or_try_init(|| self.init_verifier())?; verifier - .verify_server_cert( - end_entity, - intermediates, - server_name, - // We currently ignore certificate transparency data so that - // WebPKI doesn't verify it. Since none of the other platforms currently - // don't want possibly-bad CT data to cause problems on one platform but not - // others. On top of that, rustls's verification of it is currently "best effort." - &mut std::iter::empty(), - ocsp_response, - now, - ) + .verify_server_cert(end_entity, intermediates, server_name, ocsp_response, now) .map_err(map_webpki_errors) // This only contains information from the system or other public // bits of the TLS handshake, so it can't leak anything. @@ -169,15 +169,50 @@ impl ServerCertVerifier for Verifier { e }) } + + fn verify_tls12_signature( + &self, + message: &[u8], + cert: &pki_types::CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result { + verify_tls12_signature( + message, + cert, + dss, + &rustls::crypto::ring::default_provider().signature_verification_algorithms, + ) + } + + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &pki_types::CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result { + verify_tls13_signature( + message, + cert, + dss, + &rustls::crypto::ring::default_provider().signature_verification_algorithms, + ) + } + + fn supported_verify_schemes(&self) -> Vec { + rustls::crypto::ring::default_provider() + .signature_verification_algorithms + .supported_schemes() + } } fn map_webpki_errors(err: TlsError) -> TlsError { if let TlsError::InvalidCertificate(CertificateError::Other(other_err)) = &err { - if let Some(webpki::Error::RequiredEkuNotFound) = other_err.downcast_ref::() + if let Some(webpki::Error::RequiredEkuNotFound) = + other_err.0.downcast_ref::() { - return TlsError::InvalidCertificate(CertificateError::Other(std::sync::Arc::new( + return TlsError::InvalidCertificate(CertificateError::Other(OtherError(Arc::new( super::EkuError, - ))); + )))); } } diff --git a/rustls-platform-verifier/src/verification/windows.rs b/rustls-platform-verifier/src/verification/windows.rs index af35bd02..9b3e9a4f 100644 --- a/rustls-platform-verifier/src/verification/windows.rs +++ b/rustls-platform-verifier/src/verification/windows.rs @@ -18,11 +18,14 @@ //! [Microsoft's Documentation]: //! [Microsoft's Example]: -use super::{log_server_cert, unsupported_server_name, ALLOWED_EKUS}; +use super::{log_server_cert, ALLOWED_EKUS}; use crate::windows::{ c_void_from_ref, c_void_from_ref_mut, nonnull_from_const_ptr, ZeroedWithSize, }; -use rustls::{client::ServerCertVerifier, CertificateError, Error as TlsError}; +use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerifier}; +use rustls::crypto::{verify_tls12_signature, verify_tls13_signature}; +use rustls::pki_types; +use rustls::{CertificateError, DigitallySignedStruct, Error as TlsError, SignatureScheme}; use winapi::{ shared::{ minwindef::{FILETIME, TRUE}, @@ -52,7 +55,6 @@ use std::{ convert::TryInto, mem::{self, MaybeUninit}, ptr::{self, NonNull}, - time::SystemTime, }; use crate::verification::invalid_certificate; @@ -328,7 +330,7 @@ impl CertificateStore { fn new_chain_in( &self, certificate: &Certificate, - now: SystemTime, + now: pki_types::UnixTime, ) -> Result { let mut cert_chain = ptr::null(); @@ -351,15 +353,13 @@ impl CertificateStore { const UNIX_ADJUSTMENT: std::time::Duration = std::time::Duration::from_secs(11_644_473_600); - let since_unix_epoch = now - .duration_since(SystemTime::UNIX_EPOCH) - .map_err(|_| TlsError::FailedToGetCurrentTime)?; + let since_unix_epoch = now.as_secs(); // Convert the duration from the UNIX epoch to the Window one, and then convert // the result into a `FILETIME` structure. - let since_windows_epoch = since_unix_epoch + UNIX_ADJUSTMENT; - let intervals = (since_windows_epoch.as_nanos() / 100) as u64; + let since_windows_epoch = since_unix_epoch + UNIX_ADJUSTMENT.as_secs(); + let intervals = (since_windows_epoch * 1_000_000_000) / 100; FILETIME { dwLowDateTime: (intervals & u32::MAX as u64) as u32, @@ -414,7 +414,7 @@ fn call_with_last_error Option>(mut call: F) -> Result, - now: SystemTime, + now: pki_types::UnixTime, ) -> Result<(), TlsError> { #[cfg(any(test, feature = "ffi-testing", feature = "dbg"))] let mut store = match self.test_only_root_ca_override.as_ref() { @@ -526,26 +526,17 @@ fn size_of_struct(val: &T) -> u32 { impl ServerCertVerifier for Verifier { fn verify_server_cert( &self, - end_entity: &rustls::Certificate, - intermediates: &[rustls::Certificate], - server_name: &rustls::client::ServerName, - _scts: &mut dyn Iterator, + end_entity: &pki_types::CertificateDer<'_>, + intermediates: &[pki_types::CertificateDer<'_>], + server_name: &pki_types::ServerName, ocsp_response: &[u8], - now: SystemTime, - ) -> Result { + now: pki_types::UnixTime, + ) -> Result { log_server_cert(end_entity); - let ip_name; - let name = match server_name { - rustls::ServerName::DnsName(name) => name.as_ref(), - rustls::ServerName::IpAddress(addr) => { - ip_name = addr.to_string(); - &ip_name - } - _ => return Err(unsupported_server_name()), - }; + let name = server_name.to_str(); - let intermediate_certs: Vec<&[u8]> = intermediates.iter().map(|c| c.0.as_slice()).collect(); + let intermediate_certs: Vec<&[u8]> = intermediates.iter().map(|c| c.as_ref()).collect(); let ocsp_data = if !ocsp_response.is_empty() { Some(ocsp_response) @@ -554,13 +545,13 @@ impl ServerCertVerifier for Verifier { }; match self.verify_certificate( - &end_entity.0, + end_entity.as_ref(), &intermediate_certs, name.as_bytes(), ocsp_data, now, ) { - Ok(()) => Ok(rustls::client::ServerCertVerified::assertion()), + Ok(()) => Ok(rustls::client::danger::ServerCertVerified::assertion()), Err(e) => { // SAFETY: // Errors are our own custom errors, WinAPI errors, or static strings. @@ -569,4 +560,38 @@ impl ServerCertVerifier for Verifier { } } } + + fn verify_tls12_signature( + &self, + message: &[u8], + cert: &pki_types::CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result { + verify_tls12_signature( + message, + cert, + dss, + &rustls::crypto::ring::default_provider().signature_verification_algorithms, + ) + } + + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &pki_types::CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result { + verify_tls13_signature( + message, + cert, + dss, + &rustls::crypto::ring::default_provider().signature_verification_algorithms, + ) + } + + fn supported_verify_schemes(&self) -> Vec { + rustls::crypto::ring::default_provider() + .signature_verification_algorithms + .supported_schemes() + } }