Skip to content

Commit 91e43c0

Browse files
committed
Stream UnifOMR digests and harden Android FFI/chat sync path.
Adopt chunked GetUnifOmrDigest, ownership/envelope/TLS/Tor hardening, and scroll chat threads to the newest message on open.
1 parent ed20a3d commit 91e43c0

17 files changed

Lines changed: 687 additions & 188 deletions

File tree

app/build.gradle.kts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,15 @@ android {
2727
// Override: -PLIGHTWALLET_TLS_PIN_SHA256=<hex> or gradle.properties.
2828
val tlsPin = (project.findProperty("LIGHTWALLET_TLS_PIN_SHA256") as? String)?.trim().orEmpty()
2929
manifestPlaceholders["LIGHTWALLET_TLS_PIN_SHA256"] = tlsPin
30+
// Release builds must ship a pin (fail closed at configure time).
31+
val isReleaseTask = gradle.startParameter.taskNames.any {
32+
it.contains("Release", ignoreCase = true) && !it.contains("UnitTest", ignoreCase = true)
33+
}
34+
if (isReleaseTask && (tlsPin.isEmpty() || tlsPin == "0".repeat(64) || tlsPin.equals("PLACEHOLDER", true))) {
35+
throw GradleException(
36+
"LIGHTWALLET_TLS_PIN_SHA256 must be set to a real 64-hex leaf pin for release builds"
37+
)
38+
}
3039

3140
if (project.property("IS_USE_TEST_ORCHESTRATOR").toString().toBoolean()) {
3241
testInstrumentationRunnerArguments["clearPackageData"] = "true"

darkfi-android-sdk/src/main/java/com/nighthawkapps/lib/android/sdk/chat/DarkfiChatPreferences.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class DarkfiChatPreferences(
2121
* ([socksHost]:[socksPort], default loopback). Also gates DarkIRC SOCKS for non-loopback hosts.
2222
*/
2323
var routeOutboundThroughTor: Boolean
24-
get() = sp.getBoolean(KEY_TOR, false)
24+
get() = sp.getBoolean(KEY_TOR, true)
2525
set(value) {
2626
sp.edit().putBoolean(KEY_TOR, value).apply()
2727
}

darkfi-android-sdk/src/main/java/com/nighthawkapps/lib/android/sdk/daemon/EmbeddedPackagedExecutable.kt

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import com.nighthawkapps.lib.android.spackle.Twig
88
import java.io.File
99
import java.io.FileOutputStream
1010
import java.io.IOException
11+
import java.security.MessageDigest
1112

1213
/**
1314
* Resolves packaged native executables for Android 10+ (W^X / SELinux).
@@ -17,6 +18,14 @@ import java.io.IOException
1718
* `android:extractNativeLibs="true"` so they land under [android.content.pm.ApplicationInfo.nativeLibraryDir].
1819
*/
1920
internal object EmbeddedPackagedExecutable {
21+
/**
22+
* Optional expected SHA-256 (hex) per ABI for release integrity.
23+
* Empty map / missing ABI = skip check (debug). Release builds should populate
24+
* via BuildConfig from Gradle.
25+
*/
26+
@Volatile
27+
var expectedSha256ByAbi: Map<String, String> = emptyMap()
28+
2029
fun resolve(
2130
context: Context,
2231
jniLibFileName: String,
@@ -27,22 +36,58 @@ internal object EmbeddedPackagedExecutable {
2736
val app = context.applicationContext
2837
val fromJni = File(app.applicationInfo.nativeLibraryDir, jniLibFileName)
2938
if (fromJni.exists() && fromJni.length() > 0L) {
30-
return fromJni
39+
return verifyOrNull(fromJni)
3140
}
3241

3342
val fromCodeCache = extractAssetOnce(app, assetSubdir, assetBinaryName, File(app.codeCacheDir, jniLibFileName))
3443
if (fromCodeCache != null) {
35-
return fromCodeCache
44+
return verifyOrNull(fromCodeCache)
3645
}
3746

3847
return extractAssetOnce(
3948
app,
4049
assetSubdir,
4150
assetBinaryName,
4251
File(app.filesDir, legacyRelativePath),
43-
)
52+
)?.let { verifyOrNull(it) }
4453
}
4554

55+
private fun verifyOrNull(file: File): File? {
56+
val expected = expectedSha256ByAbi[Build.SUPPORTED_ABIS.firstOrNull().orEmpty()]
57+
?: expectedSha256ByAbi[abiFolder()]
58+
if (expected.isNullOrBlank()) {
59+
return file
60+
}
61+
val actual = sha256Hex(file) ?: return null
62+
if (!actual.equals(expected, ignoreCase = true)) {
63+
Twig.error {
64+
"Embedded executable SHA-256 mismatch for ${file.name}: refusing exec"
65+
}
66+
return null
67+
}
68+
return file
69+
}
70+
71+
private fun abiFolder(): String =
72+
mapAbiToAssetFolder(Build.SUPPORTED_ABIS.firstOrNull().orEmpty()).orEmpty()
73+
74+
private fun sha256Hex(file: File): String? =
75+
try {
76+
val md = MessageDigest.getInstance("SHA-256")
77+
file.inputStream().use { input ->
78+
val buf = ByteArray(8192)
79+
while (true) {
80+
val n = input.read(buf)
81+
if (n <= 0) break
82+
md.update(buf, 0, n)
83+
}
84+
}
85+
md.digest().joinToString("") { "%02x".format(it) }
86+
} catch (e: Exception) {
87+
Twig.warn { "SHA-256 failed for ${file.absolutePath}: $e" }
88+
null
89+
}
90+
4691
/**
4792
* argv for [ProcessBuilder] when running a PIE binary packaged as `lib*_embedded.so`.
4893
* Uses the system dynamic linker so Android loads the ELF like a normal executable.

darkfi-android-sdk/src/test/java/com/nighthawkapps/lib/android/sdk/chat/DarkfiChatPreferencesEmbeddedTorTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ class DarkfiChatPreferencesEmbeddedTorTest {
3333
}
3434

3535
@Test
36-
fun routeOutboundThroughTor_defaultsToFalse() {
36+
fun routeOutboundThroughTor_defaultsToTrue() {
3737
val app = RuntimeEnvironment.getApplication() as Application
3838
val prefs = DarkfiChatPreferences(app)
39-
assertFalse(prefs.routeOutboundThroughTor)
39+
assertTrue(prefs.routeOutboundThroughTor)
4040
}
4141

4242
@Test

rust/Cargo.lock

Lines changed: 80 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rust/darkfi-mobile-ffi/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ smol = "2.0.2"
4545
unicode-normalization = "0.1.24"
4646
url = "2.5.8"
4747
zeroize = "1.8"
48+
x509-parser = "0.16"
4849
uniffi = { version = "0.31.1", features = ["scaffolding-ffi-buffer-fns", "cli"] }
4950
thiserror = "2"
5051
log = "0.4"
@@ -53,6 +54,7 @@ tracing-subscriber = { version = "0.3", default-features = false, features = ["f
5354
tonic = { version = "0.12", features = ["tls", "tls-webpki-roots"] }
5455
prost = "0.13"
5556
async-compat = "0.2"
57+
async-stream = "0.3"
5658
rustls = { version = "0.23", default-features = false, features = ["ring", "tls12"] }
5759
rustls-pki-types = "1"
5860
regex-lite = "0.1"

0 commit comments

Comments
 (0)