diff --git a/Cargo.lock b/Cargo.lock index 267e42a..d75e2ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3265,6 +3265,7 @@ dependencies = [ "nostr-relay-pool", "nostr-sdk", "num-bigint 0.4.6", + "openssl", "parking_lot 0.12.5", "pin-project", "qrcode", @@ -6118,6 +6119,15 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" +[[package]] +name = "openssl-src" +version = "300.6.1+3.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46eb8fb9fb3b61ce1c0f8a026c4c1a0714d3a9e138e7fbde78753ce2babc3846" +dependencies = [ + "cc", +] + [[package]] name = "openssl-sys" version = "0.9.111" @@ -6126,6 +6136,7 @@ checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" dependencies = [ "cc", "libc", + "openssl-src", "pkg-config", "vcpkg", ] diff --git a/Cargo.toml b/Cargo.toml index 56879cb..d4ae4b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -87,6 +87,13 @@ bytes = "1.11.0" hyper-socks2 = "0.9.1" hyper-proxy2 = "0.1.0" hyper-tls = "0.6.0" +## native-tls (via hyper-tls) uses OpenSSL on Linux/Android. Upstream Grim got a +## vendored, statically-linked OpenSSL for free through arti's `static` feature; +## dropping arti for Nym took that with it, breaking Android/cross builds (no +## system OpenSSL for the target) and leaving desktop dynamically linked to +## libssl. Restore the vendored build so every target is self-contained. Inert on +## Windows/macOS, which use SChannel / Security.framework instead of OpenSSL. +openssl = { version = "0.10", features = ["vendored"] } async-std = "1.13.2" uuid = { version = "0.8.2", features = ["v4"] } num-bigint = "0.4.6" diff --git a/scripts/android.sh b/scripts/android.sh index 098c60e..cf17873 100755 --- a/scripts/android.sh +++ b/scripts/android.sh @@ -48,6 +48,22 @@ function build_lib() { sed -i -e 's/"cdylib","rlib"]/"rlib"]/g' Cargo.toml rm -f Cargo.toml-e + + # Bundle the Nym SOCKS5 sidecar beside libgrim.so. Named lib*.so so Android + # ships it in the APK's jniLibs and extracts it to the native-library dir — + # the only exec-allowed location for a child process (manifest needs + # extractNativeLibs=true). Built from the Nym workspace; see scripts/nym-android.sh. + [[ $1 == "v7" ]] && nym_target=armv7-linux-androideabi + [[ $1 == "v8" ]] && nym_target=aarch64-linux-android + [[ $1 == "x86" ]] && nym_target=x86_64-linux-android + nym_bin="${NYM_DIR:-../nym/target}/${nym_target}/release/nym-socks5-client" + if [ -f "${nym_bin}" ]; then + cp "${nym_bin}" "android/app/src/main/jniLibs/${arch}/libnym_socks5_client.so" + echo "bundled Nym sidecar: jniLibs/${arch}/libnym_socks5_client.so" + else + echo "WARN: Nym sidecar missing at ${nym_bin} — APK will have NO mixnet sidecar" + success=0 + fi } ### Build application diff --git a/scripts/nym-android.sh b/scripts/nym-android.sh new file mode 100755 index 0000000..452e13c --- /dev/null +++ b/scripts/nym-android.sh @@ -0,0 +1,31 @@ +#!/bin/bash +# Cross-compile the bundled Nym SOCKS5 sidecar (nym-socks5-client) for Android. +# scripts/android.sh copies the result into the APK's jniLibs as +# libnym_socks5_client.so so Goblin can launch the mixnet client on-device. +# +# Usage: NYM_SRC=../nym scripts/nym-android.sh [v7|v8|x86|all] +# NYM_SRC path to the Nym workspace checkout (default: ../nym) +# Requires: ANDROID_NDK_HOME, rustup android targets, cargo-ndk. +# +# Note: the sidecar is patched to use preconfigured webpki roots on Android +# (common/http-api-client/src/registry.rs) — the default rustls platform +# verifier needs the app's JNI context, which a standalone process lacks. +set -e + +NYM_SRC="${NYM_SRC:-../nym}" +WHICH="${1:-all}" + +build() { + local abi="$1" + echo ">> building nym-socks5-client for ${abi}" + ( cd "${NYM_SRC}" && cargo ndk -t "${abi}" build --release -p nym-socks5-client ) +} + +case "${WHICH}" in + v7) build armeabi-v7a ;; + v8) build arm64-v8a ;; + x86) build x86_64 ;; + all) build arm64-v8a; build x86_64; build armeabi-v7a ;; + *) echo "usage: $0 [v7|v8|x86|all]"; exit 1 ;; +esac +echo "done — sidecars in ${NYM_SRC}/target//release/nym-socks5-client" diff --git a/src/gui/views/goblin/onboarding.rs b/src/gui/views/goblin/onboarding.rs index a2acc68..74702aa 100644 --- a/src/gui/views/goblin/onboarding.rs +++ b/src/gui/views/goblin/onboarding.rs @@ -194,8 +194,8 @@ impl OnboardingContent { ( "Send like a message", "Pay a @username or npub and it arrives as an end-to-end \ - encrypted message over nostr and Tor — no one in between can \ - see the amount or who's involved.", + encrypted message over nostr and the Nym mixnet — no one in \ + between can see the amount or who's involved.", ), ( "Yours alone", diff --git a/src/nym/sidecar.rs b/src/nym/sidecar.rs index f9a917f..68a4814 100644 --- a/src/nym/sidecar.rs +++ b/src/nym/sidecar.rs @@ -31,7 +31,16 @@ use log::{error, info, warn}; use super::{SOCKS5_HOST, SOCKS5_PORT}; -/// Bundled SOCKS5 client binary name. +/// Bundled SOCKS5 client binary name. Windows release archives ship the `.exe`; +/// `Command`/`current_exe().parent().join(..)` need the suffix to find it. On +/// Android the sidecar is shipped inside the APK's `jniLibs` as a `lib*.so` (the +/// only files extracted to the exec-allowed native-library dir) — same trick +/// upstream Grim used for Tor's webtunnel binary. +#[cfg(target_os = "windows")] +const BIN_NAME: &str = "nym-socks5-client.exe"; +#[cfg(target_os = "android")] +const BIN_NAME: &str = "libnym_socks5_client.so"; +#[cfg(not(any(target_os = "windows", target_os = "android")))] const BIN_NAME: &str = "nym-socks5-client"; /// Per-app client id; namespaces the config/keys under the Nym data root. @@ -84,6 +93,17 @@ fn binary_path() -> PathBuf { return PathBuf::from(p); } } + // Android: `current_exe()` is the zygote/app_process, not us — the sidecar + // rides in the APK's jniLibs and is extracted to the native-library dir + // (the one exec-allowed location). MainActivity exports it as + // `NATIVE_LIBS_DIR` (see android/.../MainActivity.java). + #[cfg(target_os = "android")] + if let Ok(dir) = std::env::var("NATIVE_LIBS_DIR") { + let p = PathBuf::from(dir).join(BIN_NAME); + if p.exists() { + return p; + } + } if let Ok(exe) = std::env::current_exe() { if let Some(dir) = exe.parent() { let sibling = dir.join(BIN_NAME);