diff --git a/crates/dbx-core/Cargo.toml b/crates/dbx-core/Cargo.toml index eb56a1d3c7..eea1376940 100644 --- a/crates/dbx-core/Cargo.toml +++ b/crates/dbx-core/Cargo.toml @@ -15,6 +15,9 @@ sqlite-multiple-ciphers = ["dbx-drivers/sqlite-multiple-ciphers"] # (dbx-cli, dbx-mcp); do not combine with the cipher features. sqlite-bundled = ["rusqlite/bundled", "dbx-drivers/sqlite-bundled"] openapi = ["dbx-types/openapi", "dbx-sql/openapi"] +# OS keyring storage for the secret-store key material (macOS/Windows keychain, +# Linux dbus Secret Service with persistent fallback). Desktop only. +os-keyring = ["dep:keyring", "keyring/linux-native-sync-persistent"] system-fonts = ["font-kit"] [dependencies] @@ -64,7 +67,11 @@ aes-gcm = "0.10" aes = "0.8" cbc = "0.1" argon2 = "0.5" -keyring = { version = "3.6.3", default-features = false, features = ["apple-native", "windows-native", "linux-native-sync-persistent", "crypto-rust"] } +# The Linux dbus Secret Service backend (linux-native-sync-persistent) pulls +# libdbus-sys, whose pkg-config build script cannot cross-compile (musl static +# browser, docker zigbuild). It stays opt-in so server/cross builds skip it and +# fall back to the managed .dbx/secret.key file. +keyring = { version = "3.6.3", default-features = false, optional = true, features = ["apple-native", "windows-native", "crypto-rust"] } sha2 = "0.10" async-trait = "0.1" bytes = "1" diff --git a/crates/dbx-core/src/persistence/secret_codec.rs b/crates/dbx-core/src/persistence/secret_codec.rs index 1f033d44ec..daad28b1ca 100644 --- a/crates/dbx-core/src/persistence/secret_codec.rs +++ b/crates/dbx-core/src/persistence/secret_codec.rs @@ -16,7 +16,9 @@ use argon2::{Algorithm, Argon2, Params, Version}; use base64::Engine as _; const PREFIX: &str = "dbxenc1"; +#[cfg(feature = "os-keyring")] const KEYRING_SERVICE: &str = "com.dbx.app.secret-store.v1"; +#[cfg(feature = "os-keyring")] const KEYRING_USER: &str = "local-data-encryption-key"; #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -246,6 +248,7 @@ impl SecretCodec { } } +#[cfg(feature = "os-keyring")] fn platform_keyring_codec(allow_create: bool) -> Option { let entry = keyring::Entry::new(KEYRING_SERVICE, KEYRING_USER).ok()?; match entry.get_password() { @@ -263,6 +266,13 @@ fn platform_keyring_codec(allow_create: bool) -> Option { Some(SecretCodec::new(key)) } +#[cfg(not(feature = "os-keyring"))] +fn platform_keyring_codec(_allow_create: bool) -> Option { + // Cross/server builds compile without the OS keyring backend; the managed + // .dbx/secret.key file remains the key material source. + None +} + fn create_managed_key(path: &std::path::Path) -> Result { let parent = path.parent().ok_or_else(|| "KEY_FILE_UNAVAILABLE".to_string())?; std::fs::create_dir_all(parent).map_err(|_| "KEY_FILE_UNAVAILABLE".to_string())?; diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 8e161b7363..4d580c6c22 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -13,9 +13,10 @@ name = "dbx_lib" crate-type = ["rlib"] [features] -default = ["duckdb-sidecar", "dynamodb", "mq-admin", "sqlite-multiple-ciphers", "system-fonts"] +default = ["duckdb-sidecar", "dynamodb", "mq-admin", "sqlite-multiple-ciphers", "system-fonts", "os-keyring"] custom-protocol = ["tauri/custom-protocol"] duckdb-sidecar = ["dbx-core/duckdb-sidecar"] +os-keyring = ["dbx-core/os-keyring"] dynamodb = ["dbx-core/dynamodb"] mq-admin = ["dbx-core/mq-admin"] sqlite-sqlcipher = ["dbx-core/sqlite-sqlcipher"]