feat: integrate keyring and stronghold plugin (#39)

* feat: add keyring plugin to handle DB encryption

* feat: integrate stronghold plugin

* feat: use new get_or_set APIs added to keyring plugin

* chore: update keyring submodule

* feat: add stronghold integration

Stronghold loading is too slow for now, waiting for a fix https://github.com/tauri-apps/plugins-workspace/issues/2048
This commit is contained in:
Huakun Shen 2025-01-04 13:00:16 -05:00 committed by GitHub
parent caa252b4dd
commit 6995e0b8d9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 1024 additions and 38 deletions

5
.gitmodules vendored
View File

@ -6,4 +6,7 @@
url = https://github.com/HuakunShen/tauri-plugin-network.git
[submodule "vendors/tauri-plugin-system-info"]
path = vendors/tauri-plugin-system-info
url = https://github.com/HuakunShen/tauri-plugin-system-info.git
url = https://github.com/HuakunShen/tauri-plugin-system-info.git
[submodule "vendors/tauri-plugin-keyring"]
path = vendors/tauri-plugin-keyring
url = https://github.com/HuakunShen/tauri-plugin-keyring.git

853
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -19,6 +19,7 @@ tokio = { version = "1.0", features = ["macros", "rt-multi-thread", "signal"] }
tokio-util = "0.7.12"
mdns-sd = "0.11.1"
tauri-plugin-network = { path = "./vendors/tauri-plugin-network" }
tauri-plugin-keyring = { path = "./vendors/tauri-plugin-keyring" }
tauri-plugin-clipboard = "2.1.8"
mac-security-rs = { path = "./packages/mac-security-rs" }
log = "0.4.22"

View File

@ -23,6 +23,7 @@
"@tanstack/table-core": "^8.20.5",
"@tauri-apps/api": "^2.1.1",
"@tauri-apps/plugin-shell": "^2.2.0",
"@tauri-apps/plugin-stronghold": "^2.2.0",
"dompurify": "^3.2.3",
"gsap": "^3.12.5",
"kkrpc": "^0.0.13",

View File

@ -43,6 +43,7 @@ tauri-plugin-http = "2.2.0"
tauri-plugin-upload = { workspace = true }
# tauri-plugin-upload = "2.2.1"
tauri-plugin-jarvis = { workspace = true }
tauri-plugin-keyring = { workspace = true }
tauri-plugin-network = { workspace = true }
tauri-plugin-system-info = { workspace = true }
tauri-plugin-clipboard = { workspace = true }
@ -55,6 +56,7 @@ uuid = "1.11.0"
# tauri-plugin-devtools = "2.0.0"
obfstr = { workspace = true }
base64 = { workspace = true }
tauri-plugin-stronghold = "2.2.0"
[target."cfg(target_os = \"macos\")".dependencies]
cocoa = "0.24.1"

View File

@ -147,6 +147,8 @@
"process:default",
"system-info:allow-all",
"shell:default",
"keyring:default",
"stronghold:default",
{
"identifier": "shell:allow-spawn",
"allow": [

View File

@ -0,0 +1,13 @@
use tauri::Runtime;
use tauri_plugin_keyring::KeyringExt;
#[tauri::command]
pub async fn get_stronghold_key<R: Runtime>(app: tauri::AppHandle<R>) -> Result<String, String> {
app.keyring()
.get_or_set_password(
"kunkun",
"stronghold_key",
uuid::Uuid::new_v4().to_string().as_str(),
)
.map_err(|err| err.to_string())
}

View File

@ -0,0 +1 @@
pub mod keyring;

View File

@ -1,4 +1,5 @@
use std::{path::PathBuf, sync::Mutex};
pub mod commands;
mod setup;
pub mod utils;
use base64::prelude::*;
@ -15,6 +16,7 @@ use tauri_plugin_jarvis::{
settings::AppSettings,
},
};
use tauri_plugin_keyring::KeyringExt;
pub use tauri_plugin_log::fern::colors::ColoredLevelConfig;
use tauri_plugin_store::{StoreBuilder, StoreExt};
use utils::server::tauri_file_server;
@ -24,15 +26,15 @@ pub fn run() {
let context = tauri::generate_context!();
let mut builder = tauri::Builder::default();
let db_key = if cfg!(debug_assertions) {
None
} else {
let db_enc_key_env = obfstr::obfstr!(env!("DB_ENCRYPTION_KEY")).to_string();
match db_enc_key_env == "none" {
true => None,
false => Some(db_enc_key_env),
}
};
// let db_key = if cfg!(debug_assertions) {
// None
// } else {
// let db_enc_key_env = obfstr::obfstr!(env!("DB_ENCRYPTION_KEY")).to_string();
// match db_enc_key_env == "none" {
// true => None,
// false => Some(db_enc_key_env),
// }
// };
#[cfg(debug_assertions)]
{
@ -102,10 +104,12 @@ pub fn run() {
.plugin(tauri_plugin_notification::init())
.plugin(tauri_plugin_fs::init())
.plugin(tauri_plugin_shellx::init(shell_unlocked))
.plugin(tauri_plugin_jarvis::init(db_key.clone()))
.plugin(tauri_plugin_jarvis::init())
.plugin(tauri_plugin_clipboard::init())
.plugin(tauri_plugin_keyring::init())
.plugin(tauri_plugin_network::init())
.plugin(tauri_plugin_system_info::init());
.plugin(tauri_plugin_system_info::init())
.invoke_handler(tauri::generate_handler![commands::keyring::get_stronghold_key]);
let app = builder
.register_uri_scheme_protocol("appicon", |_app, request| {
@ -196,6 +200,7 @@ pub fn run() {
.setup(move |app| {
setup::window::setup_window(app.handle());
setup::tray::create_tray(app.handle())?;
setup::stronghold::setup_stronghold(app.handle())?;
#[cfg(all(not(target_os = "macos"), debug_assertions))]
{
app.deep_link().register("kunkun")?;
@ -241,8 +246,14 @@ pub fn run() {
/* ----------------------------- Database Setup ----------------------------- */
// setup::db::setup_db(app)?;
/* ------------------------- Clipboard History Setup ------------------------ */
let db_key = setup::keyring::setup_keyring(app.handle())?;
let db_path = get_kunkun_db_path(app.app_handle())?;
app.manage(tauri_plugin_jarvis::commands::db::DBState::new(
db_path.clone(),
db_key.clone(),
)?);
tauri_plugin_jarvis::setup::db::setup_db(app.app_handle())?;
/* ------------------------- Clipboard History Setup ------------------------ */
// println!("DB_ENCRYPTION_KEY: {:?}", db_key);
// let jarvis_db = JarvisDB::new(db_path.clone(), db_key.clone())?;

View File

@ -0,0 +1,14 @@
use tauri::AppHandle;
use tauri_plugin_keyring::KeyringExt;
pub fn setup_keyring(app: &AppHandle) -> anyhow::Result<Option<String>> {
Ok(if cfg!(debug_assertions) {
None
} else {
Some(app.keyring().get_or_set_password(
"kunkun",
"db_key",
uuid::Uuid::new_v4().to_string().as_str(),
)?)
})
}

View File

@ -1,4 +1,6 @@
pub mod clipboard;
pub mod deeplink;
pub mod keyring;
pub mod stronghold;
pub mod tray;
pub mod window;

View File

@ -0,0 +1,12 @@
use tauri::{AppHandle, Manager};
pub fn setup_stronghold(app: &AppHandle) -> tauri::Result<()> {
let salt_path = app
.path()
.app_local_data_dir()
.expect("could not resolve app local data path")
.join("salt.txt");
app.plugin(tauri_plugin_stronghold::Builder::with_argon2(&salt_path).build())?;
Ok(())
}

View File

@ -0,0 +1,56 @@
import { invoke } from "@tauri-apps/api/core"
import { Client, Store, Stronghold } from "@tauri-apps/plugin-stronghold"
/**
* @returns Stronghold Encryption Key Stored in Keyring
*/
export function getStrongholdKey() {
return invoke<string>("get_stronghold_key")
}
export class KunkunStronghold {
private stronghold: Stronghold | undefined
private client: Client | undefined
private store: Store | undefined
private vaultPath: string
private clientName: string
constructor(vaultPath: string, clientName: string) {
this.vaultPath = vaultPath
this.clientName = clientName
}
async init() {
console.log("init debug 1")
const key = await getStrongholdKey()
console.log("init debug 2", this.vaultPath, "vault password")
this.stronghold = await Stronghold.load(this.vaultPath, "vault password")
console.log("init debug 3")
try {
this.client = await this.stronghold.loadClient(this.clientName)
} catch (error) {
this.client = await this.stronghold.createClient(this.clientName)
}
console.log("init debug 4")
this.store = this.client?.getStore()
}
insertRecord(key: string, value: string) {
const data = Array.from(new TextEncoder().encode(value))
return this.store?.insert(key, data)
}
async getRecord(key: string) {
const data = await this.store?.get(key)
if (!data) return null
return new TextDecoder().decode(new Uint8Array(data))
}
removeRecord(key: string) {
return this.store?.remove(key)
}
save() {
return this.stronghold?.save()
}
}

View File

@ -46,6 +46,7 @@
"@tauri-apps/plugin-upload": "https://gitpkg.vercel.app/HuakunShen/tauri-plugins-workspace/plugins/upload?69b198b0ccba269fe7622a95ec6a33ae392bff03",
"supabase": "^2.1.1",
"tauri-plugin-network-api": "workspace:*",
"tauri-plugin-keyring-api": "workspace:*",
"tauri-plugin-shellx-api": "^2.0.14",
"tauri-plugin-system-info-api": "workspace:*",
"valibot": "^1.0.0-beta.9",

View File

@ -14,9 +14,7 @@ import { upsertExtension } from "./db"
export function loadExtensionManifestFromDisk(manifestPath: string): Promise<ExtPackageJsonExtra> {
debug(`loadExtensionManifestFromDisk: ${manifestPath}`)
return readTextFile(manifestPath).then(async (content) => {
console.log("content", content)
const json = JSON.parse(content)
console.log("manifest json", json)
const parse = v.safeParse(ExtPackageJson, json)
if (parse.issues) {
error(`Fail to load extension from ${manifestPath}. See console for parse error.`)

View File

@ -53,7 +53,7 @@ impl JarvisState {
}
/// Initializes the plugin.
pub fn init<R: Runtime>(db_key: Option<String>) -> TauriPlugin<R> {
pub fn init<R: Runtime>() -> TauriPlugin<R> {
Builder::new("jarvis")
.invoke_handler(tauri::generate_handler![
/* ------------------------------ dev commands ------------------------------ */
@ -185,9 +185,6 @@ pub fn init<R: Runtime>(db_key: Option<String>) -> TauriPlugin<R> {
app.manage(JarvisState::new());
app.manage(FileTransferState::default());
app.manage(commands::apps::ApplicationsState::default());
let db_path = get_kunkun_db_path(app)?;
app.manage(commands::db::DBState::new(db_path.clone(), db_key.clone())?);
setup::db::setup_db(app)?;
println!("Jarvis Plugin Initialized");
app.manage(Peers::default());

53
pnpm-lock.yaml generated
View File

@ -65,6 +65,9 @@ importers:
supabase:
specifier: ^2.1.1
version: 2.1.1
tauri-plugin-keyring-api:
specifier: workspace:*
version: link:vendors/tauri-plugin-keyring
tauri-plugin-network-api:
specifier: workspace:*
version: link:vendors/tauri-plugin-network
@ -230,6 +233,9 @@ importers:
'@tauri-apps/plugin-shell':
specifier: ^2.2.0
version: 2.2.0
'@tauri-apps/plugin-stronghold':
specifier: ^2.2.0
version: 2.2.0
dompurify:
specifier: ^3.2.3
version: 3.2.3
@ -1160,6 +1166,28 @@ importers:
specifier: latest
version: 1.1.14
vendors/tauri-plugin-keyring:
dependencies:
'@tauri-apps/api':
specifier: ^2.1.1
version: 2.1.1
devDependencies:
'@rollup/plugin-typescript':
specifier: ^11.1.6
version: 11.1.6(rollup@4.28.1)(tslib@2.8.1)(typescript@5.6.3)
rollup:
specifier: ^4.9.6
version: 4.28.1
tslib:
specifier: ^2.6.2
version: 2.8.1
typedoc:
specifier: ^0.27.5
version: 0.27.5(typescript@5.6.3)
typescript:
specifier: ^5.3.3
version: 5.6.3
vendors/tauri-plugin-network:
dependencies:
'@tauri-apps/api':
@ -4111,6 +4139,9 @@ packages:
'@tauri-apps/plugin-store@2.2.0':
resolution: {integrity: sha512-hJTRtuJis4w5fW1dkcgftsYxKXK0+DbAqurZ3CURHG5WkAyyZgbxpeYctw12bbzF9ZbZREXZklPq8mocCC3Sgg==}
'@tauri-apps/plugin-stronghold@2.2.0':
resolution: {integrity: sha512-N0SxfcNifvlXQ3ZHGxx9ecWNZGXtYJ6PBL0Ac8bGs90I2e2a0+JJcsAnMxQA5XQcIQfpYIXPgf0hNCliz828fw==}
'@tauri-apps/plugin-updater@2.3.0':
resolution: {integrity: sha512-qdzyZEUN69FZQ/nRx51fBub10tT6wffJl3DLVo9q922Gvw8Wk++rZhoD9eethPlZYbog/7RGgT8JkrfLh5BKAg==}
@ -14208,6 +14239,10 @@ snapshots:
dependencies:
'@tauri-apps/api': 2.1.1
'@tauri-apps/plugin-stronghold@2.2.0':
dependencies:
'@tauri-apps/api': 2.1.1
'@tauri-apps/plugin-updater@2.3.0':
dependencies:
'@tauri-apps/api': 2.1.1
@ -16768,8 +16803,8 @@ snapshots:
'@typescript-eslint/parser': 8.15.0(eslint@8.57.1)(typescript@5.6.3)
eslint: 8.57.1
eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1)
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1)
eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1)
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.1)
eslint-plugin-react: 7.37.2(eslint@8.57.1)
eslint-plugin-react-hooks: 5.0.0(eslint@8.57.1)
@ -16797,37 +16832,37 @@ snapshots:
transitivePeerDependencies:
- supports-color
eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1):
eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1):
dependencies:
'@nolyfill/is-core-module': 1.0.39
debug: 4.3.7(supports-color@9.4.0)
enhanced-resolve: 5.17.1
eslint: 8.57.1
eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1)
eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
fast-glob: 3.3.2
get-tsconfig: 4.8.1
is-bun-module: 1.2.1
is-glob: 4.0.3
optionalDependencies:
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1)
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
transitivePeerDependencies:
- '@typescript-eslint/parser'
- eslint-import-resolver-node
- eslint-import-resolver-webpack
- supports-color
eslint-module-utils@2.12.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1):
eslint-module-utils@2.12.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1):
dependencies:
debug: 3.2.7
optionalDependencies:
'@typescript-eslint/parser': 8.15.0(eslint@8.57.1)(typescript@5.6.3)
eslint: 8.57.1
eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1)
eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1)
transitivePeerDependencies:
- supports-color
eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1):
eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1):
dependencies:
'@rtsao/scc': 1.1.0
array-includes: 3.1.8
@ -16838,7 +16873,7 @@ snapshots:
doctrine: 2.1.0
eslint: 8.57.1
eslint-import-resolver-node: 0.3.9
eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1)
eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
hasown: 2.0.2
is-core-module: 2.15.1
is-glob: 4.0.3

View File

@ -5,6 +5,7 @@ packages:
- "packages/templates/*"
- "packages/tauri-plugins/*"
- "vendors/tauri-plugin-network"
- "vendors/tauri-plugin-keyring"
- "vendors/tauri-plugin-system-info"
- "vendors/kkrpc/packages/kkrpc"

1
vendors/tauri-plugin-keyring vendored Submodule

@ -0,0 +1 @@
Subproject commit 9444a75884a4a7ce1f22ebf3e65ea36de49acd8c

@ -1 +1 @@
Subproject commit b755ff494e0b58e46459dc1484b8b9f65e9c1a9a
Subproject commit 990320d9068b832c6291bccb1e252cf7175bd4a6