Implement Rust-owned Android Keystore secure storage (#93)
Dependency security audit / rustsec (push) Failing after 23s

This commit is contained in:
hugo committed 2026-10-08 16:22:57 +02:00
1 parent 874c8139a4
commit 1abd75282e
36 files changed
+4380 -37

No files matched your search

+23
View File
@@ -61,6 +61,14 @@ fn harden_source(source: &str) -> Result<String, Box<dyn Error>> {
}
let source = source.replace(internal, "class InternalException(@Suppress(\"UNUSED_PARAMETER\") message: String) : kotlin.Exception(\"Native Rust failure ([REDACTED])\")")
.replace(callback, "val err = \"Native callback failed ([REDACTED])\"");
// Callback results are operation-owned; erase plaintext after copying into the wire buffer.
let returned_secret = "val writeReturn = { value: kotlin.ByteArray -> uniffiOutReturn.setValue(FfiConverterByteArray.lower(value)) }";
if source.contains("internal object uniffiCallbackInterfaceMobileAndroidKeystore")
&& source.matches(returned_secret).count() != 2
{
return Err("UniFFI secret callback template changed; review byte-array lifetime".into());
}
let source = source.replace(returned_secret, "val writeReturn = { value: kotlin.ByteArray -> try { uniffiOutReturn.setValue(FfiConverterByteArray.lower(value)) } finally { value.fill(0) } }");
let records = Regex::new(r"(?ms)^([ \t]*)data class (\w+)\s*\((.*?)\)[^\{]*\{\n")?;
if !records.is_match(&source) {
return Err("UniFFI record template changed; review diagnostic hardening".into());
@@ -128,6 +136,21 @@ mod tests {
assert!(!hardened.contains("kotlin.Exception(message)"));
}
#[test]
fn erases_owned_callback_results_after_serialization_even_on_failure() {
let source = SOURCE.to_owned()
+ "val writeReturn = { value: kotlin.ByteArray -> uniffiOutReturn.setValue(FfiConverterByteArray.lower(value)) }";
let hardened = harden_source(&source).unwrap();
assert!(hardened.contains("finally { value.fill(0) }"));
assert!(hardened.contains("FfiConverterByteArray.lower(value)"));
assert!(
harden_source(
&(SOURCE.to_owned()
+ "internal object uniffiCallbackInterfaceMobileAndroidKeystore")
)
.is_err()
);
}
#[test]
fn fails_closed_if_upstream_templates_or_integrity_checks_change() {
assert!(harden_source("data class Secret(val value: String)").is_err());