mobile verify both webpki and installed CA (#13272)

Signed-off-by: 21pages <sunboeasy@gmail.com>
This commit is contained in:
21pages
2025-10-30 13:59:00 +08:00
committed by GitHub
parent d4410e78e2
commit d106d97b99
9 changed files with 137 additions and 30 deletions

34
Cargo.lock generated
View File

@@ -3345,6 +3345,7 @@ dependencies = [
"protobuf-codegen", "protobuf-codegen",
"rand 0.8.5", "rand 0.8.5",
"regex", "regex",
"rustls-native-certs",
"rustls-pki-types", "rustls-pki-types",
"rustls-platform-verifier", "rustls-platform-verifier",
"serde 1.0.203", "serde 1.0.203",
@@ -3365,6 +3366,7 @@ dependencies = [
"tungstenite", "tungstenite",
"url", "url",
"uuid", "uuid",
"webpki-roots 1.0.0",
"whoami", "whoami",
"winapi 0.3.9", "winapi 0.3.9",
"zstd 0.13.1", "zstd 0.13.1",
@@ -6697,9 +6699,9 @@ dependencies = [
[[package]] [[package]]
name = "rustls" name = "rustls"
version = "0.23.26" version = "0.23.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df51b5869f3a441595eac5e8ff14d486ff285f7b8c0df8770e49c3b56351f0f0" checksum = "7160e3e10bf4535308537f3c4e1641468cd0e485175d6163087c0393c7d46643"
dependencies = [ dependencies = [
"log", "log",
"once_cell", "once_cell",
@@ -6719,7 +6721,7 @@ dependencies = [
"openssl-probe", "openssl-probe",
"rustls-pki-types", "rustls-pki-types",
"schannel", "schannel",
"security-framework 3.2.0", "security-framework 3.5.1",
] ]
[[package]] [[package]]
@@ -6742,9 +6744,9 @@ dependencies = [
[[package]] [[package]]
name = "rustls-platform-verifier" name = "rustls-platform-verifier"
version = "0.5.1" version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a5467026f437b4cb2a533865eaa73eb840019a0916f4b9ec563c6e617e086c9" checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784"
dependencies = [ dependencies = [
"core-foundation 0.10.1", "core-foundation 0.10.1",
"core-foundation-sys 0.8.7", "core-foundation-sys 0.8.7",
@@ -6755,7 +6757,7 @@ dependencies = [
"rustls-native-certs", "rustls-native-certs",
"rustls-platform-verifier-android", "rustls-platform-verifier-android",
"rustls-webpki", "rustls-webpki",
"security-framework 3.2.0", "security-framework 3.5.1",
"security-framework-sys", "security-framework-sys",
"webpki-root-certs", "webpki-root-certs",
"windows-sys 0.52.0", "windows-sys 0.52.0",
@@ -6763,15 +6765,15 @@ dependencies = [
[[package]] [[package]]
name = "rustls-platform-verifier-android" name = "rustls-platform-verifier-android"
version = "0.1.0" version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84e217e7fdc8466b5b35d30f8c0a30febd29173df4a3a0c2115d306b9c4117ad" checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
[[package]] [[package]]
name = "rustls-webpki" name = "rustls-webpki"
version = "0.103.1" version = "0.103.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03" checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435"
dependencies = [ dependencies = [
"ring", "ring",
"rustls-pki-types", "rustls-pki-types",
@@ -6901,9 +6903,9 @@ dependencies = [
[[package]] [[package]]
name = "security-framework" name = "security-framework"
version = "3.2.0" version = "3.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef"
dependencies = [ dependencies = [
"bitflags 2.9.1", "bitflags 2.9.1",
"core-foundation 0.10.1", "core-foundation 0.10.1",
@@ -6914,9 +6916,9 @@ dependencies = [
[[package]] [[package]]
name = "security-framework-sys" name = "security-framework-sys"
version = "2.14.0" version = "2.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0"
dependencies = [ dependencies = [
"core-foundation-sys 0.8.7", "core-foundation-sys 0.8.7",
"libc", "libc",
@@ -8846,9 +8848,9 @@ dependencies = [
[[package]] [[package]]
name = "webpki-root-certs" name = "webpki-root-certs"
version = "0.26.8" version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09aed61f5e8d2c18344b3faa33a4c837855fe56642757754775548fee21386c4" checksum = "05d651ec480de84b762e7be71e6efa7461699c19d9e2c272c8d93455f567786e"
dependencies = [ dependencies = [
"rustls-pki-types", "rustls-pki-types",
] ]

View File

@@ -1,4 +1,6 @@
import com.google.protobuf.gradle.* import com.google.protobuf.gradle.*
import groovy.json.JsonSlurper
plugins { plugins {
id "com.google.protobuf" version "0.9.4" id "com.google.protobuf" version "0.9.4"
id "com.android.application" id "com.android.application"
@@ -30,8 +32,37 @@ if (flutterVersionName == null) {
flutterVersionName = '1.0' flutterVersionName = '1.0'
} }
dependencies { // Add rustls-platform-verifier Android support
implementation 'com.google.protobuf:protobuf-javalite:3.20.1' String findRustlsPlatformVerifierMavenDir() {
def dependencyText = providers.exec {
it.workingDir = new File("../..")
commandLine("cargo", "metadata", "--format-version", "1")
}.standardOutput.asText.get()
def dependencyJson = new JsonSlurper().parseText(dependencyText)
def pkg = dependencyJson.packages.find { it.name == "rustls-platform-verifier-android" }
if (pkg == null) {
throw new GradleException("rustls-platform-verifier-android package not found in cargo metadata!")
}
def manifestPath = file(pkg.manifest_path)
def mavenDir = new File(manifestPath.parentFile, "maven")
if (!mavenDir.exists()) {
throw new GradleException("Maven directory not found at: ${mavenDir.path}")
}
println("✓ Found rustls-platform-verifier maven repo at: ${mavenDir.path}")
return mavenDir.path
}
repositories {
maven {
url = findRustlsPlatformVerifierMavenDir()
metadataSources.artifact()
}
} }
protobuf { protobuf {
@@ -67,7 +98,7 @@ android {
defaultConfig { defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.carriez.flutter_hbb" applicationId "com.carriez.flutter_hbb"
minSdkVersion 21 minSdkVersion 22
targetSdkVersion 33 targetSdkVersion 33
versionCode flutterVersionCode.toInteger() versionCode flutterVersionCode.toInteger()
versionName flutterVersionName versionName flutterVersionName
@@ -97,8 +128,10 @@ flutter {
} }
dependencies { dependencies {
implementation 'com.google.protobuf:protobuf-javalite:3.20.1'
implementation "androidx.media:media:1.6.0" implementation "androidx.media:media:1.6.0"
implementation 'com.github.getActivity:XXPermissions:18.5' implementation 'com.github.getActivity:XXPermissions:18.5'
implementation("org.jetbrains.kotlin:kotlin-stdlib") { version { strictly("1.9.10") } } implementation("org.jetbrains.kotlin:kotlin-stdlib") { version { strictly("1.9.10") } }
implementation 'com.caverock:androidsvg-aar:1.4' implementation 'com.caverock:androidsvg-aar:1.4'
implementation "rustls:rustls-platform-verifier:0.1.1"
} }

View File

@@ -1,4 +1,7 @@
# Keep class members from protobuf generated code. # Keep class members from protobuf generated code.
-keepclassmembers class * extends com.google.protobuf.GeneratedMessageLite { -keepclassmembers class * extends com.google.protobuf.GeneratedMessageLite {
<fields>; <fields>;
} }
# Keep rustls-platform-verifier classes for JNI
-keep, includedescriptorclasses class org.rustls.platformverifier.** { *; }

View File

@@ -23,6 +23,7 @@
</queries> </queries>
<application <application
android:name=".RustDeskApplication"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="RustDesk" android:label="RustDesk"
android:requestLegacyExternalStorage="true" android:requestLegacyExternalStorage="true"

View File

@@ -0,0 +1,17 @@
package com.carriez.flutter_hbb
import android.app.Application
import android.util.Log
import ffi.FFI
class RustDeskApplication : Application() {
companion object {
private const val TAG = "RustDeskApplication"
}
override fun onCreate() {
super.onCreate()
Log.d(TAG, "RustDeskApplication onCreate")
FFI.onAppStart(applicationContext)
}
}

View File

@@ -13,6 +13,7 @@ object FFI {
} }
external fun init(ctx: Context) external fun init(ctx: Context)
external fun onAppStart(ctx: Context)
external fun setClipboardManager(clipboardManager: RdClipboardManager) external fun setClipboardManager(clipboardManager: RdClipboardManager)
external fun startServer(app_dir: String, custom_client_config: String) external fun startServer(app_dir: String, custom_client_config: String)
external fun startService() external fun startService()

View File

@@ -22,6 +22,7 @@ use std::time::{Duration, Instant};
lazy_static! { lazy_static! {
static ref JVM: RwLock<Option<JavaVM>> = RwLock::new(None); static ref JVM: RwLock<Option<JavaVM>> = RwLock::new(None);
static ref MAIN_SERVICE_CTX: RwLock<Option<GlobalRef>> = RwLock::new(None); // MainService -> video service / audio service / info static ref MAIN_SERVICE_CTX: RwLock<Option<GlobalRef>> = RwLock::new(None); // MainService -> video service / audio service / info
static ref APPLICATION_CONTEXT: RwLock<Option<GlobalRef>> = RwLock::new(None);
static ref VIDEO_RAW: Mutex<FrameRaw> = Mutex::new(FrameRaw::new("video", MAX_VIDEO_FRAME_TIMEOUT)); static ref VIDEO_RAW: Mutex<FrameRaw> = Mutex::new(FrameRaw::new("video", MAX_VIDEO_FRAME_TIMEOUT));
static ref AUDIO_RAW: Mutex<FrameRaw> = Mutex::new(FrameRaw::new("audio", MAX_AUDIO_FRAME_TIMEOUT)); static ref AUDIO_RAW: Mutex<FrameRaw> = Mutex::new(FrameRaw::new("audio", MAX_AUDIO_FRAME_TIMEOUT));
static ref NDK_CONTEXT_INITED: Mutex<bool> = Default::default(); static ref NDK_CONTEXT_INITED: Mutex<bool> = Default::default();
@@ -462,6 +463,23 @@ fn init_ndk_context(java_vm: *mut c_void, context_jobject: *mut c_void) {
*lock = true; *lock = true;
} }
fn try_init_rustls_platform_verifier(env: &mut JNIEnv, context_jobject: *mut c_void) {
use hbb_common::config::ANDROID_RUSTLS_PLATFORM_VERIFIER_INITIALIZED as INITIALIZED;
use std::sync::atomic::Ordering;
let initialized = INITIALIZED.load(Ordering::Relaxed);
if !initialized {
let ctx_for_rustls = unsafe { JObject::from_raw(context_jobject as jni::sys::jobject) };
if let Err(e) =
hbb_common::rustls_platform_verifier::android::init_hosted(env, ctx_for_rustls)
{
log::error!("Failed to initialize rustls-platform-verifier: {:?}", e);
} else {
INITIALIZED.store(true, Ordering::Relaxed);
log::info!("rustls-platform-verifier initialized successfully");
}
}
}
// https://cjycode.com/flutter_rust_bridge/guides/how-to/ndk-init // https://cjycode.com/flutter_rust_bridge/guides/how-to/ndk-init
#[no_mangle] #[no_mangle]
pub extern "C" fn JNI_OnLoad(vm: jni::JavaVM, res: *mut std::os::raw::c_void) -> jni::sys::jint { pub extern "C" fn JNI_OnLoad(vm: jni::JavaVM, res: *mut std::os::raw::c_void) -> jni::sys::jint {
@@ -471,3 +489,23 @@ pub extern "C" fn JNI_OnLoad(vm: jni::JavaVM, res: *mut std::os::raw::c_void) ->
} }
jni::JNIVersion::V6.into() jni::JNIVersion::V6.into()
} }
#[no_mangle]
pub extern "system" fn Java_ffi_FFI_onAppStart(mut env: JNIEnv, _class: JClass, ctx: JObject) {
if ctx.is_null() {
log::error!("application context is null");
return;
}
if APPLICATION_CONTEXT.read().unwrap().is_some() {
log::info!("application context already initialized");
return;
}
if let Ok(jvm) = env.get_java_vm() {
if let Ok(context) = env.new_global_ref(ctx) {
let java_vm = jvm.get_java_vm_pointer() as *mut c_void;
let context_jobject = context.as_obj().as_raw() as *mut c_void;
*APPLICATION_CONTEXT.write().unwrap() = Some(context);
try_init_rustls_platform_verifier(&mut env, context_jobject);
}
}
}

View File

@@ -9,15 +9,30 @@ macro_rules! configure_http_client {
// https://github.com/rustdesk/rustdesk/issues/11569 // https://github.com/rustdesk/rustdesk/issues/11569
// https://docs.rs/reqwest/latest/reqwest/struct.ClientBuilder.html#method.no_proxy // https://docs.rs/reqwest/latest/reqwest/struct.ClientBuilder.html#method.no_proxy
let mut builder = $builder.no_proxy(); let mut builder = $builder.no_proxy();
#[cfg(any(target_os = "android", target_os = "ios"))]
match hbb_common::verifier::client_config() {
Ok(client_config) => {
builder = builder.use_preconfigured_tls(client_config);
}
Err(e) => {
hbb_common::log::error!("Failed to get client config: {}", e);
}
}
let client = if let Some(conf) = Config::get_socks() { let client = if let Some(conf) = Config::get_socks() {
let proxy_result = Proxy::from_conf(&conf, None); let proxy_result = Proxy::from_conf(&conf, None);
match proxy_result { match proxy_result {
Ok(proxy) => { Ok(proxy) => {
let proxy_setup = match &proxy.intercept { let proxy_setup = match &proxy.intercept {
ProxyScheme::Http { host, .. } =>{ reqwest::Proxy::all(format!("http://{}", host))}, ProxyScheme::Http { host, .. } => {
ProxyScheme::Https { host, .. } => {reqwest::Proxy::all(format!("https://{}", host))}, reqwest::Proxy::all(format!("http://{}", host))
ProxyScheme::Socks5 { addr, .. } => { reqwest::Proxy::all(&format!("socks5://{}", addr)) } }
ProxyScheme::Https { host, .. } => {
reqwest::Proxy::all(format!("https://{}", host))
}
ProxyScheme::Socks5 { addr, .. } => {
reqwest::Proxy::all(&format!("socks5://{}", addr))
}
}; };
match proxy_setup { match proxy_setup {
@@ -28,12 +43,9 @@ macro_rules! configure_http_client {
format!("Basic {}", auth.get_basic_authorization()); format!("Basic {}", auth.get_basic_authorization());
if let Ok(auth) = basic_auth.parse() { if let Ok(auth) = basic_auth.parse() {
builder = builder.default_headers( builder = builder.default_headers(
vec![( vec![(reqwest::header::PROXY_AUTHORIZATION, auth)]
reqwest::header::PROXY_AUTHORIZATION, .into_iter()
auth, .collect(),
)]
.into_iter()
.collect(),
); );
} }
} }