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

View File

@@ -1,4 +1,6 @@
import com.google.protobuf.gradle.*
import groovy.json.JsonSlurper
plugins {
id "com.google.protobuf" version "0.9.4"
id "com.android.application"
@@ -30,8 +32,37 @@ if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
dependencies {
implementation 'com.google.protobuf:protobuf-javalite:3.20.1'
// Add rustls-platform-verifier Android support
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 {
@@ -67,7 +98,7 @@ android {
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.carriez.flutter_hbb"
minSdkVersion 21
minSdkVersion 22
targetSdkVersion 33
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
@@ -97,8 +128,10 @@ flutter {
}
dependencies {
implementation 'com.google.protobuf:protobuf-javalite:3.20.1'
implementation "androidx.media:media:1.6.0"
implementation 'com.github.getActivity:XXPermissions:18.5'
implementation("org.jetbrains.kotlin:kotlin-stdlib") { version { strictly("1.9.10") } }
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.
-keepclassmembers class * extends com.google.protobuf.GeneratedMessageLite {
<fields>;
}
}
# Keep rustls-platform-verifier classes for JNI
-keep, includedescriptorclasses class org.rustls.platformverifier.** { *; }

View File

@@ -23,6 +23,7 @@
</queries>
<application
android:name=".RustDeskApplication"
android:icon="@mipmap/ic_launcher"
android:label="RustDesk"
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 onAppStart(ctx: Context)
external fun setClipboardManager(clipboardManager: RdClipboardManager)
external fun startServer(app_dir: String, custom_client_config: String)
external fun startService()

View File

@@ -22,6 +22,7 @@ use std::time::{Duration, Instant};
lazy_static! {
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 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 AUDIO_RAW: Mutex<FrameRaw> = Mutex::new(FrameRaw::new("audio", MAX_AUDIO_FRAME_TIMEOUT));
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;
}
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
#[no_mangle]
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()
}
#[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://docs.rs/reqwest/latest/reqwest/struct.ClientBuilder.html#method.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 proxy_result = Proxy::from_conf(&conf, None);
match proxy_result {
Ok(proxy) => {
let proxy_setup = match &proxy.intercept {
ProxyScheme::Http { host, .. } =>{ reqwest::Proxy::all(format!("http://{}", host))},
ProxyScheme::Https { host, .. } => {reqwest::Proxy::all(format!("https://{}", host))},
ProxyScheme::Socks5 { addr, .. } => { reqwest::Proxy::all(&format!("socks5://{}", addr)) }
ProxyScheme::Http { host, .. } => {
reqwest::Proxy::all(format!("http://{}", host))
}
ProxyScheme::Https { host, .. } => {
reqwest::Proxy::all(format!("https://{}", host))
}
ProxyScheme::Socks5 { addr, .. } => {
reqwest::Proxy::all(&format!("socks5://{}", addr))
}
};
match proxy_setup {
@@ -28,12 +43,9 @@ macro_rules! configure_http_client {
format!("Basic {}", auth.get_basic_authorization());
if let Ok(auth) = basic_auth.parse() {
builder = builder.default_headers(
vec![(
reqwest::header::PROXY_AUTHORIZATION,
auth,
)]
.into_iter()
.collect(),
vec![(reqwest::header::PROXY_AUTHORIZATION, auth)]
.into_iter()
.collect(),
);
}
}