Share the platform-neutral Rust mobile contract (#91)
Dependency security audit / rustsec (push) Failing after 22s
Dependency security audit / rustsec (push) Failing after 22s
This commit is contained in:
1 parent
fcb36a0684
commit
180851087b
52 files changed
+8932
-4263
No files matched your search
+138
@@ -0,0 +1,138 @@
|
||||
package de.rfc1437.ironstorage.bridge
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import de.rfc1437.ironstorage.bridge.generated.MobileCameraFrame
|
||||
import de.rfc1437.ironstorage.bridge.generated.MobileClipboard
|
||||
import de.rfc1437.ironstorage.bridge.generated.MobileClipboardContent
|
||||
import de.rfc1437.ironstorage.bridge.generated.MobileClipboardDisposition
|
||||
import de.rfc1437.ironstorage.bridge.generated.MobileClipboardWait
|
||||
import de.rfc1437.ironstorage.bridge.generated.MobileCredentialRequest
|
||||
import de.rfc1437.ironstorage.bridge.generated.MobileEntryCopy
|
||||
import de.rfc1437.ironstorage.bridge.generated.MobilePlatformFfiException
|
||||
import de.rfc1437.ironstorage.bridge.generated.MobilePlatformPaths
|
||||
import de.rfc1437.ironstorage.bridge.generated.MobileSecretLocator
|
||||
import de.rfc1437.ironstorage.bridge.generated.MobileShellState
|
||||
import de.rfc1437.ironstorage.bridge.generated.MobileTotpDiscoveryPhase
|
||||
import de.rfc1437.ironstorage.bridge.generated.copyMobileEntry
|
||||
import de.rfc1437.ironstorage.bridge.generated.decodeMobileCameraFrame
|
||||
import de.rfc1437.ironstorage.bridge.generated.mobilePlatformLayout
|
||||
import de.rfc1437.ironstorage.bridge.generated.mobileTotpOperation
|
||||
import de.rfc1437.ironstorage.bridge.generated.validateMobileCredentialRequest
|
||||
import java.util.concurrent.Executors
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertThrows
|
||||
import org.junit.Test
|
||||
|
||||
class MobileContractTest {
|
||||
private val context = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
|
||||
private fun initialize() {
|
||||
NativeRuntime(context).fixture(MobileShellState.EMPTY)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun privatePathsAndProviderFactsAreValidatedInRust() {
|
||||
initialize()
|
||||
val layout = mobilePlatformLayout(
|
||||
MobilePlatformPaths(context.noBackupFilesDir.path, context.cacheDir.path)
|
||||
)
|
||||
assertEquals("${context.noBackupFilesDir.path}/config.toml", layout.configuration)
|
||||
assertThrows(MobilePlatformFfiException.InvalidPaths::class.java) {
|
||||
mobilePlatformLayout(MobilePlatformPaths("relative", context.cacheDir.path))
|
||||
}
|
||||
val request = validateMobileCredentialRequest(
|
||||
MobileCredentialRequest(
|
||||
"opaque-request",
|
||||
"de.example.app",
|
||||
"A".repeat(64),
|
||||
listOf("https://EXAMPLE.test:443/")
|
||||
)
|
||||
)
|
||||
assertEquals(listOf("https://example.test"), request.origins)
|
||||
assertThrows(MobilePlatformFfiException.InvalidCredentialRequest::class.java) {
|
||||
validateMobileCredentialRequest(request.copy(origins = listOf("file:///tmp")))
|
||||
}
|
||||
assertFalse(request.toString().contains("opaque-request"))
|
||||
assertFalse(
|
||||
MobileSecretLocator.OpenPgpPassphrase("SecretReferenceMarker")
|
||||
.toString().contains("SecretReferenceMarker")
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun foreignClipboardCallbackTransfersLargeSecretsAndPreservesNewerContent() {
|
||||
initialize()
|
||||
val secret = "binding-payload-".repeat(70_000)
|
||||
val clipboard = Clipboard()
|
||||
assertEquals(
|
||||
MobileClipboardDisposition.RESTORED_PREVIOUS,
|
||||
copyMobileEntry(MobileEntryCopy(secret, 45uL), clipboard)
|
||||
)
|
||||
assertEquals("previous", clipboard.text)
|
||||
assertEquals(secret.length, clipboard.writtenBytes)
|
||||
assertEquals(45_000uL, clipboard.timeout)
|
||||
clipboard.newer = "newer content"
|
||||
assertEquals(
|
||||
MobileClipboardDisposition.PRESERVED_NEWER,
|
||||
copyMobileEntry(MobileEntryCopy(secret, 45uL), clipboard)
|
||||
)
|
||||
assertEquals("newer content", clipboard.text)
|
||||
clipboard.newer = null
|
||||
clipboard.cancelled = true
|
||||
assertThrows(MobilePlatformFfiException.Cancelled::class.java) {
|
||||
copyMobileEntry(MobileEntryCopy(secret, 45uL), clipboard)
|
||||
}
|
||||
assertEquals("newer content", clipboard.text)
|
||||
assertFalse(MobileClipboardContent.Text(secret.toByteArray()).toString().contains(secret))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun cameraPlaneBoundsAndConcurrentOperationCancellationSurviveNativeOwnership() {
|
||||
initialize()
|
||||
assertThrows(MobilePlatformFfiException.InvalidCameraFrame::class.java) {
|
||||
decodeMobileCameraFrame(MobileCameraFrame(UInt.MAX_VALUE, 1u, 1u, 1u, byteArrayOf(0)))
|
||||
}
|
||||
assertThrows(MobilePlatformFfiException.QrNotFound::class.java) {
|
||||
decodeMobileCameraFrame(
|
||||
MobileCameraFrame(1024u, 1024u, 2048u, 1u, ByteArray(2 * 1024 * 1024) { -1 })
|
||||
)
|
||||
}
|
||||
val executor = Executors.newFixedThreadPool(4)
|
||||
try {
|
||||
val operations = List(16) { mobileTotpOperation() }
|
||||
val tasks = operations.map { operation ->
|
||||
executor.submit {
|
||||
operation.cancel()
|
||||
assertEquals(MobileTotpDiscoveryPhase.CANCELLED, operation.progress().phase)
|
||||
}
|
||||
}
|
||||
tasks.forEach { it.get() }
|
||||
operations.forEach { it.close() }
|
||||
} finally {
|
||||
executor.shutdownNow()
|
||||
}
|
||||
}
|
||||
|
||||
private class Clipboard : MobileClipboard {
|
||||
var text = "previous"
|
||||
var writtenBytes = 0
|
||||
var timeout = 0uL
|
||||
var newer: String? = null
|
||||
var cancelled = false
|
||||
override fun read(): MobileClipboardContent =
|
||||
MobileClipboardContent.Text(text.toByteArray())
|
||||
override fun write(value: ByteArray) {
|
||||
writtenBytes = maxOf(writtenBytes, value.size)
|
||||
text = value.decodeToString()
|
||||
}
|
||||
override fun clear() {
|
||||
text = ""
|
||||
}
|
||||
override fun waitForTimeout(milliseconds: ULong): MobileClipboardWait {
|
||||
timeout = milliseconds
|
||||
newer?.let { text = it }
|
||||
return if (cancelled) MobileClipboardWait.CANCELLED else MobileClipboardWait.ELAPSED
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -55,9 +55,9 @@ class NativeRuntimeTest {
|
||||
fun wrongNativeContractAndChecksumFailWithTypedRedactedErrors() {
|
||||
NativeRuntime(context).fixture(MobileShellState.EMPTY)
|
||||
val version = IntegrityCheckingUniffiLib
|
||||
.ffi_ironstorage_apple_uniffi_contract_version().toLong()
|
||||
.ffi_ironstorage_mobile_uniffi_contract_version().toLong()
|
||||
val checksum = IntegrityCheckingUniffiLib
|
||||
.uniffi_ironstorage_apple_checksum_func_mobile_shell_fixture().toLong() and 0xffffL
|
||||
.uniffi_ironstorage_mobile_checksum_func_mobile_shell_fixture().toLong() and 0xffffL
|
||||
NativeBindingChecks.requireMatch(-27278, 38258, NativeFailureKind.ChecksumMismatch)
|
||||
for ((actual, kind) in listOf(
|
||||
version to NativeFailureKind.ContractMismatch,
|
||||
|
||||
Reference in new issue
Block a user