feat(android): implement private Rust storage runtime (#90)
Dependency security audit / rustsec (push) Failing after 23s

This commit is contained in:
hugo committed 2026-10-08 14:36:05 +02:00
1 parent 180851087b
commit 874c8139a4
36 files changed
+2848 -39

No files matched your search

+1 -1
View File
@@ -31,7 +31,7 @@ kotlin {
}
dependencies {
implementation("${libs.jna.get().module}:${libs.versions.jna.get()}@aar")
implementation(variantOf(libs.jna) { artifactType("aar") })
implementation(libs.annotation)
testImplementation(libs.junit)
androidTestImplementation(libs.android.test.runner)
@@ -0,0 +1,69 @@
package de.rfc1437.ironstorage.bridge
import androidx.test.platform.app.InstrumentationRegistry
import de.rfc1437.ironstorage.bridge.generated.MobileOnboardingFfiException
import de.rfc1437.ironstorage.bridge.generated.MobilePrivateDirectories
import de.rfc1437.ironstorage.bridge.generated.MobilePrivateStorageFfiException
import de.rfc1437.ironstorage.bridge.generated.MobileShellState
import de.rfc1437.ironstorage.bridge.generated.MobileTab
import de.rfc1437.ironstorage.bridge.generated.mobileLocalOnboardingOperation
import de.rfc1437.ironstorage.bridge.generated.setSelectedMobileTab
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
class PrivateStorageTest {
@Test
fun canonicalSandboxInitializationAndRecreationKeepDurableLayout() {
val context = InstrumentationRegistry.getInstrumentation().targetContext
val directories = MobilePrivateDirectories(
context.filesDir.canonicalPath,
context.noBackupFilesDir.canonicalPath,
context.cacheDir.canonicalPath
)
val native = NativeRuntime(context)
val initialized = try {
native.privateStorage(directories)
} catch (error: MobilePrivateStorageFfiException.Failed) {
throw AssertionError("Private initialization failed: ${error.kind}", error)
}
initialized.use { first ->
assertEquals(1u, first.status().version)
assertEquals(null, first.status().recovery)
val configured = first.status().configured
assertEquals(
if (configured) MobileShellState.READY else MobileShellState.EMPTY,
first.shell().pages.first().state
)
val layout = first.layout()
assertEquals("${context.noBackupFilesDir.canonicalPath}/ironstorage", layout.data)
assertEquals("${layout.data}/totp-catalog.toml", layout.catalog)
assertEquals("${context.cacheDir.canonicalPath}/ironstorage", layout.cache)
first.activate()
if (!configured) {
mobileLocalOnboardingOperation().use { onboarding ->
try {
onboarding.generate(
"Sandbox fixture <fixture@example.test>",
"public-test-passphrase",
false
)
} catch (error: MobileOnboardingFfiException.Failed) {
throw AssertionError("Private onboarding failed: ${error.kind}", error)
}
}
}
assertTrue(first.status().configured)
setSelectedMobileTab(MobileTab.PREFERENCES)
assertEquals(MobileTab.PREFERENCES, first.shell().selectedTab)
assertEquals(MobileShellState.READY, first.shell().pages.first().state)
first.clearDisposableCache()
native.privateStorage(directories).use { recreated ->
assertEquals(layout, recreated.layout())
assertEquals(first.status(), recreated.status())
}
assertFalse(layout.toString().contains(context.noBackupFilesDir.path))
}
}
}
@@ -3,9 +3,13 @@ package de.rfc1437.ironstorage.bridge
import android.content.Context
import android.os.Build
import android.os.Looper
import de.rfc1437.ironstorage.bridge.generated.MobilePrivateDirectories
import de.rfc1437.ironstorage.bridge.generated.MobilePrivateRuntime
import de.rfc1437.ironstorage.bridge.generated.MobilePrivateStorageFfiException
import de.rfc1437.ironstorage.bridge.generated.MobileShell
import de.rfc1437.ironstorage.bridge.generated.MobileShellState
import de.rfc1437.ironstorage.bridge.generated.mobileShellFixture
import de.rfc1437.ironstorage.bridge.generated.openMobilePrivateRuntime
import de.rfc1437.ironstorage.bridge.generated.uniffiEnsureInitialized
import java.io.File
import java.io.IOException
@@ -24,6 +28,11 @@ class NativeRuntime(context: Context) {
mobileShellFixture(state)
}
fun privateStorage(directories: MobilePrivateDirectories): MobilePrivateRuntime = call {
initialize()
openMobilePrivateRuntime(directories)
}
private fun initialize() = synchronized(lock) {
initializationFailure?.let { throw NativeBridgeException(it) }
if (!initialized) {
@@ -51,6 +60,8 @@ class NativeRuntime(context: Context) {
fail(error.kind)
} catch (error: CancellationException) {
throw error
} catch (error: MobilePrivateStorageFfiException) {
throw error
} catch (error: NativeBindingMismatchException) {
fail(error.kind)
} catch (_: IOException) {