add android-keyring

This commit is contained in:
FabianLars
2025-08-07 14:41:43 +02:00
parent af5500caed
commit 88da3d26ad
18 changed files with 91 additions and 399 deletions
+1
View File
@@ -1,2 +1,3 @@
plugins/*/permissions/autogenerated/
plugins/*/android/.tauri/tauri-api/build/
plugins/*/android/build/intermediates/
Generated
+39 -105
View File
@@ -117,6 +117,20 @@ version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
name = "android-keyring"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b051e1fab4f4c15e384424252c57321173b8fb274d50f30bd46145c35cd0a6a2"
dependencies = [
"base64 0.22.1",
"jni",
"keyring",
"ndk-context",
"thiserror 2.0.12",
"tracing",
]
[[package]]
name = "android-tzdata"
version = "0.1.1"
@@ -301,7 +315,7 @@ dependencies = [
"objc2-foundation 0.3.0",
"parking_lot",
"percent-encoding",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
"wl-clipboard-rs",
"x11rb",
]
@@ -882,15 +896,6 @@ dependencies = [
"toml",
]
[[package]]
name = "cbc"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6"
dependencies = [
"cipher",
]
[[package]]
name = "cc"
version = "1.2.19"
@@ -1054,7 +1059,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
dependencies = [
"lazy_static",
"windows-sys 0.48.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -1419,36 +1424,6 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a"
[[package]]
name = "dbus"
version = "0.9.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b"
dependencies = [
"libc",
"libdbus-sys",
"winapi",
]
[[package]]
name = "dbus-secret-service"
version = "4.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b42a16374481d92aed73ae45b1f120207d8e71d24fb89f357fadbd8f946fd84b"
dependencies = [
"aes",
"block-padding",
"cbc",
"dbus",
"futures-util",
"hkdf",
"num",
"once_cell",
"openssl",
"rand 0.8.5",
"sha2",
]
[[package]]
name = "deep-link-example"
version = "0.0.0"
@@ -1855,7 +1830,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e"
dependencies = [
"libc",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -3282,14 +3257,16 @@ dependencies = [
[[package]]
name = "keyring"
version = "4.0.0-rc.2"
source = "git+https://github.com/open-source-cooperative/keyring-rs?rev=9635a2f53a19eb7f188cdc4e38982dcb19caee00#9635a2f53a19eb7f188cdc4e38982dcb19caee00"
version = "3.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eebcc3aff044e5944a8fbaf69eb277d11986064cba30c468730e8b9909fb551c"
dependencies = [
"byteorder",
"dbus-secret-service",
"linux-keyutils",
"log",
"security-framework 2.11.1",
"security-framework 3.2.0",
"windows-sys 0.59.0",
"windows-sys 0.60.2",
"zeroize",
]
@@ -3364,16 +3341,6 @@ version = "0.2.172"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
[[package]]
name = "libdbus-sys"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72"
dependencies = [
"cc",
"pkg-config",
]
[[package]]
name = "libflate"
version = "2.1.0"
@@ -3453,6 +3420,16 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "linux-keyutils"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "761e49ec5fd8a5a463f9b84e877c373d888935b71c6be78f3767fe2ae6bed18e"
dependencies = [
"bitflags 2.9.0",
"libc",
]
[[package]]
name = "linux-raw-sys"
version = "0.4.15"
@@ -3834,30 +3811,6 @@ dependencies = [
"serde",
]
[[package]]
name = "num"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23"
dependencies = [
"num-bigint",
"num-complex",
"num-integer",
"num-iter",
"num-rational",
"num-traits",
]
[[package]]
name = "num-bigint"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
dependencies = [
"num-integer",
"num-traits",
]
[[package]]
name = "num-bigint-dig"
version = "0.8.4"
@@ -3875,15 +3828,6 @@ dependencies = [
"zeroize",
]
[[package]]
name = "num-complex"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
dependencies = [
"num-traits",
]
[[package]]
name = "num-conv"
version = "0.1.0"
@@ -3910,17 +3854,6 @@ dependencies = [
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
dependencies = [
"num-bigint",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.19"
@@ -4881,7 +4814,7 @@ dependencies = [
"once_cell",
"socket2",
"tracing",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -5345,7 +5278,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys 0.4.15",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -5358,7 +5291,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys 0.9.4",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -6900,6 +6833,7 @@ dependencies = [
name = "tauri-plugin-secure-storage"
version = "2.0.0"
dependencies = [
"android-keyring",
"keyring",
"log",
"serde",
@@ -7191,7 +7125,7 @@ dependencies = [
"getrandom 0.3.2",
"once_cell",
"rustix 1.0.5",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -8263,7 +8197,7 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
"windows-sys 0.48.0",
"windows-sys 0.59.0",
]
[[package]]
+10 -12
View File
@@ -15,26 +15,24 @@ rustdoc-args = ["--cfg", "docsrs"]
# Support levels are "full", "partial", "none", "unknown"
# Details of the support level are left to plugin maintainer
[package.metadata.platforms]
windows = { level = "unknown", notes = "" }
linux = { level = "unknown", notes = "" }
macos = { level = "unknown", notes = "" }
android = { level = "unknown", notes = "" }
ios = { level = "unknown", notes = "" }
windows = { level = "full", notes = "Windows' credentials storage only protects from other accounts on that system. The main user and their apps can access your app's data." }
linux = { level = "partial", notes = "For this PoC on Linux the storage is in-memory only and will be reset on reboot." }
macos = { level = "full", notes = "May prompt for passwords twice." }
android = { level = "full", notes = "The implementation is very new and therefore not battle-tested nor audited." }
ios = { level = "full", notes = "" }
[build-dependencies]
tauri-plugin = { workspace = true, features = ["build"] }
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
serde = { workspace = true }
serde_json = { workspace = true }
tauri = { workspace = true }
log = { workspace = true }
thiserror = { workspace = true }
# TODO: Non-android only:
keyring = { git = "https://github.com/open-source-cooperative/keyring-rs", rev = "9635a2f53a19eb7f188cdc4e38982dcb19caee00" }
# When updating to v4 we likely won't use linux-native aka keyutils but we need to look into which backend to use.
# Also, `linux-native` is non persistent.
keyring = { version = "3.6", features = ["apple-native", "windows-native", "linux-native"]}
[features]
vendored = ["keyring/vendored"]
[target."cfg(target_os = \"android\")".dependencies]
android-keyring = "0.2.0"
@@ -1,2 +0,0 @@
/build
/.tauri
@@ -1,44 +0,0 @@
plugins {
id("com.android.library")
id("org.jetbrains.kotlin.android")
}
android {
namespace = "{{android_package_id}}"
compileSdk = 34
defaultConfig {
minSdk = 24
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
}
dependencies {
implementation("androidx.core:core-ktx:1.9.0")
implementation("androidx.appcompat:appcompat:1.6.0")
implementation("com.google.android.material:material:1.7.0")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
implementation(project(":tauri-android"))
}
-21
View File
@@ -1,21 +0,0 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
@@ -1,2 +0,0 @@
include ':tauri-android'
project(':tauri-android').projectDir = new File('./.tauri/tauri-api')
@@ -1,28 +0,0 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
package {{android_package_id}}
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("{{android_package_id}}", appContext.packageName)
}
}
@@ -1,3 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>
@@ -1,32 +0,0 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
package {{android_package_id}}
import android.app.Activity
import app.tauri.annotation.Command
import app.tauri.annotation.InvokeArg
import app.tauri.annotation.TauriPlugin
import app.tauri.plugin.JSObject
import app.tauri.plugin.Plugin
import app.tauri.plugin.Invoke
@InvokeArg
class PingArgs {
var value: String? = null
}
@TauriPlugin
class ExamplePlugin(private val activity: Activity): Plugin(activity) {
private val implementation = Example()
@Command
fun ping(invoke: Invoke) {
val args = invoke.parseArgs(PingArgs::class.java)
val ret = JSObject()
ret.put("value", implementation.pong(args.value ?: "default value :("))
invoke.resolve(ret)
}
}
@@ -1,21 +0,0 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
package {{android_package_id}}
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}
+1 -1
View File
@@ -7,7 +7,7 @@ const COMMANDS: &[&str] = &["set_string", "get_string", "set_binary", "get_binar
fn main() {
let result = tauri_plugin::Builder::new(COMMANDS)
.global_api_script_path("./api-iife.js")
.android_path("android")
//.android_path("android")
//.ios_path("ios")
.try_build();
-37
View File
@@ -1,37 +0,0 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use keyring::Entry;
use serde::de::DeserializeOwned;
use tauri::{plugin::PluginApi, AppHandle, Runtime};
use crate::Result;
pub fn init<R: Runtime, C: DeserializeOwned>(
app: &AppHandle<R>,
_api: PluginApi<R, C>,
) -> crate::Result<SecureStorage<R>> {
Ok(SecureStorage(app.clone()))
}
/// Access to the secure-storage APIs.
pub struct SecureStorage<R: Runtime>(AppHandle<R>);
impl<R: Runtime> SecureStorage<R> {
pub fn set_string(&self, key: &str, value: &str) -> Result<()> {
Ok(Entry::new(&self.0.config().identifier, key)?.set_password(value)?)
}
pub fn get_string(&self, key: &str) -> Result<String> {
Ok(Entry::new(&self.0.config().identifier, key)?.get_password()?)
}
pub fn set_binary(&self, key: &str, value: &[u8]) -> Result<()> {
Ok(Entry::new(&self.0.config().identifier, key)?.set_secret(value)?)
}
pub fn get_binary(&self, key: &str) -> Result<Vec<u8>> {
Ok(Entry::new(&self.0.config().identifier, key)?.get_secret()?)
}
}
-4
View File
@@ -8,12 +8,8 @@ pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[cfg(not(target_os = "android"))]
#[error(transparent)]
Keyring(#[from] keyring::Error),
#[cfg(target_os = "android")]
#[error(transparent)]
PluginInvoke(#[from] tauri::plugin::mobile::PluginInvokeError),
}
impl Serialize for Error {
+27 -19
View File
@@ -2,29 +2,17 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use keyring::Entry;
use tauri::{
plugin::{Builder, TauriPlugin},
Manager, Runtime,
AppHandle, Manager, Runtime,
};
pub use models::*;
#[cfg(not(target_os = "android"))]
mod desktop;
#[cfg(target_os = "android")]
mod mobile;
mod commands;
mod error;
mod models;
pub use error::{Error, Result};
#[cfg(not(target_os = "android"))]
pub use desktop::SecureStorage;
#[cfg(target_os = "android")]
pub use mobile::SecureStorage;
// TODO: Consider using a worker thread to handle caveats mentioned by keyring-rs
/// Extensions to [`tauri::App`], [`tauri::AppHandle`], [`tauri::WebviewWindow`], [`tauri::Webview`] and [`tauri::Window`] to access the secure-storage APIs.
@@ -47,13 +35,33 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
commands::set_binary,
commands::get_binary
])
.setup(|app, api| {
.setup(|app, _api| {
#[cfg(target_os = "android")]
let secure_storage = mobile::init(app, api)?;
#[cfg(not(target_os = "android"))]
let secure_storage = desktop::init(app, api)?;
app.manage(secure_storage);
android_keyring::set_android_keyring_credential_builder()?;
app.manage(SecureStorage(app.clone()));
Ok(())
})
.build()
}
/// Access to the secure-storage APIs.
pub struct SecureStorage<R: Runtime>(AppHandle<R>);
impl<R: Runtime> SecureStorage<R> {
pub fn set_string(&self, key: &str, value: &str) -> Result<()> {
Ok(Entry::new(&self.0.config().identifier, key)?.set_password(value)?)
}
pub fn get_string(&self, key: &str) -> Result<String> {
Ok(Entry::new(&self.0.config().identifier, key)?.get_password()?)
}
pub fn set_binary(&self, key: &str, value: &[u8]) -> Result<()> {
Ok(Entry::new(&self.0.config().identifier, key)?.set_secret(value)?)
}
pub fn get_binary(&self, key: &str) -> Result<Vec<u8>> {
Ok(Entry::new(&self.0.config().identifier, key)?.get_secret()?)
}
}
-40
View File
@@ -1,40 +0,0 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use serde::de::DeserializeOwned;
use tauri::{
plugin::{PluginApi, PluginHandle},
AppHandle, Runtime,
};
use crate::models::*;
#[cfg(target_os = "android")]
const PLUGIN_IDENTIFIER: &str = "app.tauri.secure_storage";
//#[cfg(target_os = "ios")]
//tauri::ios_plugin_binding!(init_plugin_secure_storage);
// initializes the Kotlin or Swift plugin classes
pub fn init<R: Runtime, C: DeserializeOwned>(
_app: &AppHandle<R>,
api: PluginApi<R, C>,
) -> crate::Result<SecureStorage<R>> {
#[cfg(target_os = "android")]
let handle = api.register_android_plugin(PLUGIN_IDENTIFIER, "ExamplePlugin")?;
//#[cfg(target_os = "ios")]
//let handle = api.register_ios_plugin(init_plugin_secure_storage)?;
Ok(SecureStorage(handle))
}
/// Access to the secure_storage APIs.
pub struct SecureStorage<R: Runtime>(PluginHandle<R>);
impl<R: Runtime> SecureStorage<R> {
pub fn ping(&self, payload: PingRequest) -> crate::Result<PingResponse> {
self.0
.run_mobile_plugin("ping", payload)
.map_err(Into::into)
}
}
-17
View File
@@ -1,17 +0,0 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PingRequest {
pub value: Option<String>,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PingResponse {
pub value: Option<String>,
}
+13 -11
View File
@@ -2351,9 +2351,9 @@ snapshots:
- encoding
- mocha
'@covector/assemble@0.12.0':
'@covector/assemble@0.12.0(mocha@10.8.2)':
dependencies:
'@covector/command': 0.8.0
'@covector/command': 0.8.0(mocha@10.8.2)
'@covector/files': 0.8.0
effection: 2.0.8(mocha@10.8.2)
js-yaml: 4.1.0
@@ -2364,9 +2364,10 @@ snapshots:
unified: 9.2.2
transitivePeerDependencies:
- encoding
- mocha
- supports-color
'@covector/changelog@0.12.0':
'@covector/changelog@0.12.0(mocha@10.8.2)':
dependencies:
'@covector/files': 0.8.0
effection: 2.0.8(mocha@10.8.2)
@@ -2376,14 +2377,16 @@ snapshots:
unified: 9.2.2
transitivePeerDependencies:
- encoding
- mocha
- supports-color
'@covector/command@0.8.0':
'@covector/command@0.8.0(mocha@10.8.2)':
dependencies:
'@effection/process': 2.1.4
'@effection/process': 2.1.4(mocha@10.8.2)
effection: 2.0.8(mocha@10.8.2)
transitivePeerDependencies:
- encoding
- mocha
'@covector/files@0.8.0':
dependencies:
@@ -2430,10 +2433,8 @@ snapshots:
dependencies:
effection: 2.0.8(mocha@10.8.2)
mocha: 10.8.2
transitivePeerDependencies:
- encoding
'@effection/process@2.1.4':
'@effection/process@2.1.4(mocha@10.8.2)':
dependencies:
cross-spawn: 7.0.6
ctrlc-windows: 2.2.0
@@ -2441,6 +2442,7 @@ snapshots:
shellwords: 0.1.1
transitivePeerDependencies:
- encoding
- mocha
'@effection/stream@2.0.6':
dependencies:
@@ -3271,9 +3273,9 @@ snapshots:
dependencies:
'@clack/prompts': 0.7.0
'@covector/apply': 0.10.0(mocha@10.8.2)
'@covector/assemble': 0.12.0
'@covector/changelog': 0.12.0
'@covector/command': 0.8.0
'@covector/assemble': 0.12.0(mocha@10.8.2)
'@covector/changelog': 0.12.0(mocha@10.8.2)
'@covector/command': 0.8.0(mocha@10.8.2)
'@covector/files': 0.8.0
effection: 2.0.8(mocha@10.8.2)
globby: 11.1.0