-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Mobile platform verifier fallback #410
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
rustdesk
merged 1 commit into
rustdesk:main
from
21pages:mobile_platform_verifier_fallback
Oct 30, 2025
+246
−10
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,190 @@ | ||
| use crate::ResultType; | ||
| #[cfg(any(target_os = "android", target_os = "ios"))] | ||
| use rustls_pki_types::{ServerName, UnixTime}; | ||
| use std::sync::Arc; | ||
| use tokio_rustls::rustls::{self, client::WebPkiServerVerifier, ClientConfig}; | ||
| #[cfg(any(target_os = "android", target_os = "ios"))] | ||
| use tokio_rustls::rustls::{ | ||
| client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}, | ||
| DigitallySignedStruct, Error as TLSError, SignatureScheme, | ||
| }; | ||
|
|
||
| /// A certificate verifier that tries a primary verifier first, | ||
| /// and falls back to a platform verifier if the primary fails. | ||
| #[cfg(any(target_os = "android", target_os = "ios"))] | ||
| #[derive(Debug)] | ||
| struct FallbackPlatformVerifier { | ||
| primary: Arc<dyn ServerCertVerifier>, | ||
| fallback: Arc<dyn ServerCertVerifier>, | ||
| } | ||
|
|
||
| #[cfg(any(target_os = "android", target_os = "ios"))] | ||
| impl FallbackPlatformVerifier { | ||
| fn with_platform_fallback( | ||
| primary: Arc<dyn ServerCertVerifier>, | ||
| provider: Arc<rustls::crypto::CryptoProvider>, | ||
| ) -> Result<Self, TLSError> { | ||
| #[cfg(target_os = "android")] | ||
| if !crate::config::ANDROID_RUSTLS_PLATFORM_VERIFIER_INITIALIZED | ||
| .load(std::sync::atomic::Ordering::Relaxed) | ||
| { | ||
| return Err(TLSError::General( | ||
| "rustls-platform-verifier not initialized".to_string(), | ||
| )); | ||
| } | ||
| let fallback = Arc::new(rustls_platform_verifier::Verifier::new(provider)?); | ||
| Ok(Self { primary, fallback }) | ||
| } | ||
| } | ||
|
|
||
| #[cfg(any(target_os = "android", target_os = "ios"))] | ||
| impl ServerCertVerifier for FallbackPlatformVerifier { | ||
| fn verify_server_cert( | ||
| &self, | ||
| end_entity: &rustls_pki_types::CertificateDer<'_>, | ||
| intermediates: &[rustls_pki_types::CertificateDer<'_>], | ||
| server_name: &ServerName<'_>, | ||
| ocsp_response: &[u8], | ||
| now: UnixTime, | ||
| ) -> Result<ServerCertVerified, TLSError> { | ||
| match self.primary.verify_server_cert( | ||
| end_entity, | ||
| intermediates, | ||
| server_name, | ||
| ocsp_response, | ||
| now, | ||
| ) { | ||
| Ok(verified) => Ok(verified), | ||
| Err(primary_err) => { | ||
| match self.fallback.verify_server_cert( | ||
| end_entity, | ||
| intermediates, | ||
| server_name, | ||
| ocsp_response, | ||
| now, | ||
| ) { | ||
| Ok(verified) => Ok(verified), | ||
| Err(fallback_err) => { | ||
| log::error!( | ||
| "Both primary and fallback verifiers failed to verify server certificate, primary error: {:?}, fallback error: {:?}", | ||
| primary_err, | ||
| fallback_err | ||
| ); | ||
| Err(primary_err) | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| fn verify_tls12_signature( | ||
| &self, | ||
| message: &[u8], | ||
| cert: &rustls_pki_types::CertificateDer<'_>, | ||
| dss: &DigitallySignedStruct, | ||
| ) -> Result<HandshakeSignatureValid, TLSError> { | ||
| // Both WebPkiServerVerifier and rustls_platform_verifier use the same signature verification implementation. | ||
| // https://github.com/rustls/rustls/blob/1ee126adb3352a2dcd72420dcd6040351a6ddc1e/rustls/src/webpki/server_verifier.rs#L278 | ||
| // https://github.com/rustls/rustls/blob/1ee126adb3352a2dcd72420dcd6040351a6ddc1e/rustls/src/crypto/mod.rs#L17 | ||
| // https://github.com/rustls/rustls-platform-verifier/blob/1099f161bfc5e3ac7f90aad88b1bf788e72906cb/rustls-platform-verifier/src/verification/android.rs#L9 | ||
| // https://github.com/rustls/rustls-platform-verifier/blob/1099f161bfc5e3ac7f90aad88b1bf788e72906cb/rustls-platform-verifier/src/verification/apple.rs#L6 | ||
| self.primary.verify_tls12_signature(message, cert, dss) | ||
| } | ||
|
|
||
| fn verify_tls13_signature( | ||
| &self, | ||
| message: &[u8], | ||
| cert: &rustls_pki_types::CertificateDer<'_>, | ||
| dss: &DigitallySignedStruct, | ||
| ) -> Result<HandshakeSignatureValid, TLSError> { | ||
| // Same implementation as verify_tls12_signature. | ||
| self.primary.verify_tls13_signature(message, cert, dss) | ||
| } | ||
|
|
||
| fn supported_verify_schemes(&self) -> Vec<SignatureScheme> { | ||
| // Both WebPkiServerVerifier and rustls_platform_verifier use the same crypto provider, | ||
| // so their supported signature schemes are identical. | ||
| // https://github.com/rustls/rustls/blob/1ee126adb3352a2dcd72420dcd6040351a6ddc1e/rustls/src/webpki/server_verifier.rs#L172C52-L172C85 | ||
| // https://github.com/rustls/rustls-platform-verifier/blob/1099f161bfc5e3ac7f90aad88b1bf788e72906cb/rustls-platform-verifier/src/verification/android.rs#L327 | ||
| // https://github.com/rustls/rustls-platform-verifier/blob/1099f161bfc5e3ac7f90aad88b1bf788e72906cb/rustls-platform-verifier/src/verification/apple.rs#L304 | ||
| self.primary.supported_verify_schemes() | ||
| } | ||
| } | ||
|
|
||
| fn webpki_server_verifier( | ||
| provider: Arc<rustls::crypto::CryptoProvider>, | ||
| ) -> ResultType<Arc<WebPkiServerVerifier>> { | ||
| // Load root certificates from both bundled webpki_roots and system-native certificate stores. | ||
| // This approach is consistent with how reqwest and tokio-tungstenite handle root certificates. | ||
| // https://github.com/snapview/tokio-tungstenite/blob/35d110c24c9d030d1608ec964d70c789dfb27452/src/tls.rs#L95 | ||
| // https://github.com/seanmonstar/reqwest/blob/b126ca49da7897e5d676639cdbf67a0f6838b586/src/async_impl/client.rs#L643 | ||
| let mut root_cert_store = rustls::RootCertStore::empty(); | ||
| root_cert_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned()); | ||
| let rustls_native_certs::CertificateResult { certs, errors, .. } = | ||
| rustls_native_certs::load_native_certs(); | ||
| if !errors.is_empty() { | ||
| log::warn!("native root CA certificate loading errors: {errors:?}"); | ||
| } | ||
| root_cert_store.add_parsable_certificates(certs); | ||
|
|
||
| // Build verifier using with_root_certificates behavior (WebPkiServerVerifier without CRLs). | ||
| // Both reqwest and tokio-tungstenite use this approach. | ||
| // https://github.com/seanmonstar/reqwest/blob/b126ca49da7897e5d676639cdbf67a0f6838b586/src/async_impl/client.rs#L749 | ||
| // https://github.com/snapview/tokio-tungstenite/blob/35d110c24c9d030d1608ec964d70c789dfb27452/src/tls.rs#L127 | ||
| // https://github.com/rustls/rustls/blob/1ee126adb3352a2dcd72420dcd6040351a6ddc1e/rustls/src/client/builder.rs#L47 | ||
| // with_root_certificates creates a WebPkiServerVerifier without revocation checking: | ||
| // https://github.com/rustls/rustls/blob/1ee126adb3352a2dcd72420dcd6040351a6ddc1e/rustls/src/webpki/server_verifier.rs#L177 | ||
| // https://github.com/rustls/rustls/blob/1ee126adb3352a2dcd72420dcd6040351a6ddc1e/rustls/src/webpki/server_verifier.rs#L168 | ||
| // Since no CRL is provided (as is the case here), we must explicitly set allow_unknown_revocation_status() | ||
| // to match the behavior of with_root_certificates, which allows unknown revocation status by default. | ||
| // https://github.com/rustls/rustls/blob/1ee126adb3352a2dcd72420dcd6040351a6ddc1e/rustls/src/webpki/server_verifier.rs#L37 | ||
| // Note: build() only returns an error if the root certificate store is empty, which won't happen here. | ||
| let verifier = rustls::client::WebPkiServerVerifier::builder_with_provider( | ||
| Arc::new(root_cert_store), | ||
| provider.clone(), | ||
| ) | ||
| .allow_unknown_revocation_status() | ||
| .build() | ||
| .map_err(|e| anyhow::anyhow!(e))?; | ||
| Ok(verifier) | ||
| } | ||
|
|
||
| pub fn client_config() -> ResultType<ClientConfig> { | ||
| // Use the default builder which uses the default protocol versions and crypto provider. | ||
| // The with_protocol_versions API has been removed in rustls master branch: | ||
| // https://github.com/rustls/rustls/pull/2599 | ||
| // This approach is consistent with tokio-tungstenite's usage: | ||
| // https://github.com/snapview/tokio-tungstenite/blob/35d110c24c9d030d1608ec964d70c789dfb27452/src/tls.rs#L126 | ||
| let config_builder = rustls::ClientConfig::builder(); | ||
| let provider = config_builder.crypto_provider().clone(); | ||
| let webpki_verifier = webpki_server_verifier(provider.clone())?; | ||
| #[cfg(any(target_os = "android", target_os = "ios"))] | ||
| { | ||
| match FallbackPlatformVerifier::with_platform_fallback(webpki_verifier.clone(), provider) { | ||
| Ok(fallback_verifier) => { | ||
| let config = config_builder | ||
| .dangerous() | ||
| .with_custom_certificate_verifier(Arc::new(fallback_verifier)) | ||
| .with_no_client_auth(); | ||
| Ok(config) | ||
| } | ||
| Err(e) => { | ||
| log::error!( | ||
| "Failed to create fallback verifier: {:?}, use webpki verifier instead", | ||
| e | ||
| ); | ||
| let config = config_builder | ||
| .with_webpki_verifier(webpki_verifier) | ||
| .with_no_client_auth(); | ||
| Ok(config) | ||
| } | ||
| } | ||
| } | ||
| #[cfg(not(any(target_os = "android", target_os = "ios")))] | ||
| { | ||
| let config = config_builder | ||
| .with_webpki_verifier(webpki_verifier) | ||
| .with_no_client_auth(); | ||
| Ok(config) | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.