Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion kt/moq/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ kotlin {
dependsOn(jvmAndAndroidMain)
dependencies {
implementation("net.java.dev.jna:jna:5.18.1@aar")
implementation("rustls:rustls-platform-verifier:0.1.1")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Is a custom maven repo for the verifier configured anywhere in the Gradle setup?
rg -nP -C2 'rustlsPlatformVerifier|findRustlsPlatformVerifier|rustls-platform-verifier-android' kt/ 2>/dev/null
# What android-support version does the Rust crate resolve to?
cargo metadata --format-version 1 --manifest-path rs/moq-ffi/Cargo.toml 2>/dev/null \
  | jq -r '.packages[] | select(.name=="rustls-platform-verifier-android") | .version'

Repository: moq-dev/moq

Length of output: 149


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== build files =='
git ls-files 'kt/**/build.gradle.kts' 'kt/**/settings.gradle.kts' 'kt/**/gradle.properties' 'kt/**/gradle/libs.versions.toml' | sed 's#^`#-` #'

echo
echo '== rustls references =='
rg -n --hidden --glob '!**/build/**' --glob '!**/.gradle/**' 'rustls-platform-verifier|rustlsPlatformVerifier|findRustlsPlatformVerifier|cargo metadata|rustls' kt rs .github . 2>/dev/null | sed -n '1,200p'

echo
echo '== target file excerpt =='
cat -n kt/moq/build.gradle.kts | sed -n '1,180p'

Repository: moq-dev/moq

Length of output: 21397


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== target file size =='
wc -l kt/moq/build.gradle.kts

echo
echo '== target file around dependency block =='
sed -n '70,120p' kt/moq/build.gradle.kts | cat -n

Repository: moq-dev/moq

Length of output: 2135


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== relevant files =='
git ls-files | rg '^(kt/moq/build\.gradle\.kts|kt/.*/build\.gradle\.kts|.*Cargo\.toml|.*settings\.gradle(\.kts)?|.*libs\.versions\.toml)$' | sed -n '1,200p'

echo
echo '== kt/moq/build.gradle.kts excerpt =='
sed -n '1,180p' kt/moq/build.gradle.kts | cat -n

echo
echo '== search for repository declarations =='
rg -n --hidden --glob '!**/build/**' --glob '!**/.gradle/**' 'maven\s*\{|mavenLocal\(|mavenCentral\(|url\s*=\s*uri\(|rustls-platform-verifier' kt . 2>/dev/null | sed -n '1,200p'

Repository: moq-dev/moq

Length of output: 8887


🌐 Web query:

rustls-platform-verifier-android Maven Central published rustls-platform-verifier 0.1.1

💡 Result:

The crate rustls-platform-verifier-android version 0.1.1 is published on crates.io [1], not Maven Central [2][3]. It serves as an internal implementation detail for the rustls-platform-verifier Rust crate to provide necessary Kotlin/JVM components for Android certificate validation [4][1]. Because it is not available on Maven Central, the project uses a mechanism where the Android components are bundled within the Rust crate [5][4]. To integrate this in an Android project, developers typically use a provided Gradle script helper [2][6]. This script works by locating the downloaded crate on the local file system (where Cargo stores it), identifying the internal Maven-compatible directory structure contained within that crate, and registering it as a local Maven repository for the Gradle build [2][5][6]. This allows the build system to treat the local files as a Maven dependency without the need for the artifact to be hosted on a public repository like Maven Central [4][6].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== root search for rustls / custom repo plumbing =='
rg -n --hidden --glob '!**/build/**' --glob '!**/.gradle/**' \
  'rustls-platform-verifier-android|rustls-platform-verifier|cargo metadata|mavenLocal\(|maven \{|url\s*=\s*uri\(' . | sed -n '1,200p'

echo
echo '== build file around dependencies and repositories =='
sed -n '1,180p' kt/moq/build.gradle.kts | cat -n

Repository: moq-dev/moq

Length of output: 8654


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== gradle files =='
git ls-files 'kt/**/build.gradle.kts' 'kt/**/settings.gradle.kts' | sed -n '1,200p'

echo
echo '== kt/moq/build.gradle.kts =='
cat -n kt/moq/build.gradle.kts | sed -n '1,180p'

echo
echo '== kt settings/root build excerpts =='
for f in $(git ls-files 'kt/**/settings.gradle.kts' 'kt/**/build.gradle.kts' | grep -v '^kt/moq/build.gradle.kts$'); do
  echo "--- $f ---"
  cat -n "$f" | sed -n '1,220p'
done

Repository: moq-dev/moq

Length of output: 7118


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== kt tree =='
find kt -maxdepth 2 -type f | sort | sed -n '1,200p'

echo
echo '== rustls / maven repo helpers in kt scripts =='
rg -n --hidden --glob '!**/build/**' --glob '!**/.gradle/**' \
  'rustls-platform-verifier|cargo metadata|mavenRepo|mavenLocal|repositories\s*\{|url\s*=\s*uri\(|artifactId|find.*repo' kt/scripts kt/moq kt/settings.gradle.kts 2>/dev/null | sed -n '1,240p'

echo
echo '== kt/settings.gradle.kts =='
cat -n kt/settings.gradle.kts | sed -n '1,220p'

Repository: moq-dev/moq

Length of output: 1786


Add the rustls Android repository for this dependency.
mavenCentral()/google() don’t provide rustls:rustls-platform-verifier:0.1.1, so this implementation(...) needs the custom on-disk repo wired in somewhere in the Gradle setup.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@kt/moq/build.gradle.kts` at line 101, The moq Gradle module depends on
rustls-platform-verifier, but the current repository setup does not expose that
artifact. Update the Gradle configuration around build.gradle.kts to wire in the
custom on-disk rustls Android repository used for
rustls:rustls-platform-verifier:0.1.1, and ensure the repository is available
before the implementation dependency in the moq module is resolved.

}
}
val androidUnitTest by getting {
Expand Down Expand Up @@ -132,7 +133,7 @@ if (androidEnabled) {

mavenPublishing {
publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL, automaticRelease = true)
signAllPublications()
signAllPublications()
coordinates("dev.moq", "moq", version.toString())

pom {
Expand Down
17 changes: 17 additions & 0 deletions kt/moq/src/androidMain/kotlin/dev/moq/PlatformTLS.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package dev.moq

import android.content.Context

object PlatformTLS {
init {
System.loadLibrary("moq_ffi")
}

@JvmStatic
fun initialize(context: Context) {
initializeNative(context.applicationContext)
}

@JvmStatic
private external fun initializeNative(context: Context)
}
4 changes: 4 additions & 0 deletions rs/moq-ffi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ tracing = "0.1"
uniffi = { version = "0.31", features = ["cli"] }
url = "2"

[target.'cfg(target_os = "android")'.dependencies]
jni = "0.22"
rustls-platform-verifier = "0.7"

[build-dependencies]
uniffi = { version = "0.31", features = ["build"] }

Expand Down
2 changes: 1 addition & 1 deletion rs/moq-ffi/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ build_target() {
if is_android "$target"; then
# Android targets use cargo-ndk
cargo ndk --target "$target" --platform 24 -- \
build --release --package moq-ffi --manifest-path "$WORKSPACE_DIR/Cargo.toml"
build --release --package moq-ffi --features android-logcat --manifest-path "$WORKSPACE_DIR/Cargo.toml"
else
# Set up cross-compilation for Linux ARM64
if [[ "$target" == "aarch64-unknown-linux-gnu" ]]; then
Expand Down
16 changes: 16 additions & 0 deletions rs/moq-ffi/src/android.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use jni::EnvUnowned;
use jni::errors::ThrowRuntimeExAndDefault;
use jni::objects::{JClass, JObject};

#[unsafe(no_mangle)]
pub extern "system" fn Java_dev_moq_PlatformTLS_initializeNative<'local>(
mut env: EnvUnowned<'local>,
_class: JClass<'local>,
context: JObject<'local>,
) {
env.with_env(|env| -> jni::errors::Result<()> {
rustls_platform_verifier::android::init_with_env(env, context)?;
Ok(())
})
.resolve::<ThrowRuntimeExAndDefault>();
}
2 changes: 2 additions & 0 deletions rs/moq-ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
//! Provides a Kotlin/Swift-compatible API for real-time pub/sub over QUIC.
//! Uses async UniFFI objects instead of callbacks for a native async experience.

#[cfg(target_os = "android")]
mod android;
pub mod audio;
pub mod consumer;
pub mod error;
Expand Down
37 changes: 36 additions & 1 deletion rs/moq-ffi/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,30 @@ fn map_connect_error(err: moq_native::Error) -> MoqError {
}
}

/// TLS server certificate verifier exposed through the FFI bindings.
#[derive(uniffi::Enum)]
pub enum MoqTlsVerifier {
/// Use the recommended verifier for the current platform.
Default,
/// Use the platform verifier. On Android, call PlatformTLS.initialize first.
Platform,
/// Use bundled Mozilla roots from webpki-roots.
WebPki,
/// Load roots with rustls-native-certs.
NativeRoots,
}

impl From<MoqTlsVerifier> for moq_native::tls::ClientTlsVerifier {
fn from(verifier: MoqTlsVerifier) -> Self {
match verifier {
MoqTlsVerifier::Default => Self::Default,
MoqTlsVerifier::Platform => Self::Platform,
MoqTlsVerifier::WebPki => Self::WebPki,
MoqTlsVerifier::NativeRoots => Self::NativeRoots,
}
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -83,10 +107,21 @@ impl MoqClient {
}
}

/// Select the TLS server certificate verifier.
///
/// Default uses the recommended verifier for the platform. Android defaults
/// to WebPKI/Mozilla roots; platform verifier mode requires Android
/// PlatformTLS initialization before connecting.
pub fn set_tls_verifier(&self, verifier: MoqTlsVerifier) {
if let Some(mut state) = self.task.lock() {
state.config.tls.verifier = verifier.into();
}
}

/// Trust these PEM root certificate file(s) instead of the system roots.
///
/// Pass the paths to PEM-encoded CA certificates. An empty list restores the
/// default behavior of using the platform's native root store.
/// default behavior of using the configured default verifier roots.
pub fn set_tls_roots(&self, paths: Vec<String>) {
if let Some(mut state) = self.task.lock() {
state.config.tls.root = paths.into_iter().map(Into::into).collect();
Expand Down
4 changes: 4 additions & 0 deletions rs/moq-native/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,12 @@ web-transport-proto = { workspace = true, optional = true }
web-transport-quiche = { workspace = true, optional = true }
web-transport-quinn = { workspace = true, optional = true }
web-transport-trait = { workspace = true }
webpki-roots = "1"
x509-parser = "0.18"

[target.'cfg(any(target_os = "android", target_os = "ios", target_os = "macos"))'.dependencies]
rustls-platform-verifier = "0.7"

[target.'cfg(target_os = "android")'.dependencies]
tracing-android = { version = "0.2", optional = true }

Expand Down
11 changes: 9 additions & 2 deletions rs/moq-native/src/quiche.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,15 @@ impl QuicheClient {
Verification::Fingerprints(hashes) => {
builder = builder.with_server_certificate_hashes(hashes);
}
Verification::Roots(roots) => {
builder = builder.with_root_certificates(roots);
Verification::Roots { certs, .. } => {
if !certs.is_empty() {
builder = builder.with_root_certificates(certs);
}
Comment on lines +181 to +184

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Do not silently drop webpki_roots for quiche.

Verification::Roots { webpki_roots: true, certs: [] } is now a valid policy, but this branch ignores the flag and leaves quiche using its implicit defaults. That means explicit WebPki selection—and Android’s default verifier—may not actually use the configured Mozilla/WebPKI roots in this backend. Either install equivalent roots for quiche or return an unsupported-verifier error instead of proceeding.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rs/moq-native/src/quiche.rs` around lines 181 - 184, The quiche verification
path in quiche.rs is ignoring the webpki_roots policy when certs is empty, so
explicit WebPki selection can fall back to quiche defaults instead of the
configured Mozilla/WebPKI roots. Update the Verification::Roots handling to
either install equivalent roots on the builder even when certs is empty, or fail
fast with an unsupported-verifier error rather than silently proceeding. Use the
Verification::Roots match branch and the builder.with_root_certificates flow as
the primary places to adjust.

}
Verification::Platform => {
return Err(Error::Tls(crate::tls::Error::PlatformVerifierUnsupported(
"quiche backend",
)));
}
}

Expand Down
Loading
Loading