From b1601fb17e93c52f144220539fdbcaadb8f14716 Mon Sep 17 00:00:00 2001 From: Christian Visintin Date: Mon, 8 Jun 2026 19:15:11 +0200 Subject: [PATCH] chore(deps): migrate keyring from 3 to 4 keyring v4 is no longer a library crate; the API moved to keyring-core plus per-platform credential store crates. Replace the keyring dependency with keyring-core and the native store crates, and register the default store at runtime (lazily, once) since v4 no longer selects it at compile time via Cargo features. --- Cargo.lock | 94 +++++++++++++++++++++++++++---- Cargo.toml | 19 +++++-- src/system/keys.rs | 2 +- src/system/keys/keyringstorage.rs | 35 +++++++++++- 4 files changed, 131 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1c447cf..373e54e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -110,6 +110,17 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +[[package]] +name = "apple-native-keyring-store" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7be2f067ccd8d4b4d4a66ddafe0f32a5dff31732f32dbff85fefc40929b1f72" +dependencies = [ + "keyring-core", + "log", + "security-framework 3.7.0", +] + [[package]] name = "approx" version = "0.5.1" @@ -1417,11 +1428,29 @@ version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "708b509edf7889e53d7efb0ffadd994cc6c2345ccb62f55cfd6b0682165e4fa6" dependencies = [ + "aes 0.8.4", + "block-padding 0.3.3", + "cbc 0.1.2", "dbus", + "fastrand", + "hkdf 0.12.4", + "num", + "once_cell", "openssl", + "sha2 0.10.9", "zeroize", ] +[[package]] +name = "dbus-secret-service-keyring-store" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21d8f54da401bb5eb2a4d873ac4b359f4a599df2ca8634bb5b8c045e5ee78757" +dependencies = [ + "dbus-secret-service", + "keyring-core", +] + [[package]] name = "delegate" version = "0.13.5" @@ -2973,19 +3002,12 @@ dependencies = [ ] [[package]] -name = "keyring" -version = "3.6.3" +name = "keyring-core" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eebcc3aff044e5944a8fbaf69eb277d11986064cba30c468730e8b9909fb551c" +checksum = "fb1e621458ca9c51aa110bd0339d4751a056b9576bf1253aee1aa560dda0fc9d" dependencies = [ - "byteorder", - "dbus-secret-service", "log", - "openssl", - "security-framework 2.11.1", - "security-framework 3.7.0", - "windows-sys 0.60.2", - "zeroize", ] [[package]] @@ -3463,6 +3485,20 @@ dependencies = [ "unicode-segmentation", ] +[[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" @@ -3489,6 +3525,15 @@ 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.2.2" @@ -3515,6 +3560,17 @@ 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" @@ -6164,6 +6220,7 @@ version = "1.0.0" dependencies = [ "aes 0.9.1", "aes-gcm 0.10.3", + "apple-native-keyring-store", "argh", "base64 0.22.1", "bitflags 2.13.0", @@ -6172,10 +6229,11 @@ dependencies = [ "cfg_aliases", "chrono", "content_inspector", + "dbus-secret-service-keyring-store", "dirs", "edit", "filetime", - "keyring", + "keyring-core", "lazy-regex", "log", "md-5 0.11.0", @@ -6213,6 +6271,7 @@ dependencies = [ "vergen-git2", "whoami", "wildmatch", + "windows-native-keyring-store", ] [[package]] @@ -7275,6 +7334,19 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-native-keyring-store" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063426e76fdec7438d56bb777f67e318a84a25c707b07e575cb8b78e10c028f8" +dependencies = [ + "byteorder", + "keyring-core", + "regex", + "windows-sys 0.61.2", + "zeroize", +] + [[package]] name = "windows-numerics" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index 42b0e29..80ab2cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,12 +48,7 @@ content_inspector = "0.2" dirs = "6" edit = "0.1" filetime = "0.2" -keyring = { version = "3", features = [ - "apple-native", - "sync-secret-service", - "vendored", - "windows-native", -] } +keyring-core = "1" lazy-regex = "3" log = "0.4" md-5 = "0.11" @@ -95,6 +90,12 @@ unicode-width = "0.2" whoami = "2" wildmatch = "2" +[target."cfg(any(target_os = \"linux\", target_os = \"freebsd\"))".dependencies] +dbus-secret-service-keyring-store = { version = "1", features = [ + "crypto-rust", + "vendored", +] } + [target."cfg(target_family = \"unix\")".dependencies] remotefs-ftp = { version = "0.4", features = [ "native-tls", @@ -105,6 +106,12 @@ uzers = "0.12" [target."cfg(target_family = \"windows\")".dependencies] remotefs-ftp = { version = "0.4", features = ["native-tls"] } +[target."cfg(target_os = \"macos\")".dependencies] +apple-native-keyring-store = { version = "1", features = ["keychain"] } + +[target."cfg(target_os = \"windows\")".dependencies] +windows-native-keyring-store = "1" + [dev-dependencies] pretty_assertions = "1" serial_test = "3" diff --git a/src/system/keys.rs b/src/system/keys.rs index 513cc5a..0ad2ebc 100644 --- a/src/system/keys.rs +++ b/src/system/keys.rs @@ -6,7 +6,7 @@ pub mod filestorage; pub mod keyringstorage; // ext -use keyring::Error as KeyringError; +use keyring_core::Error as KeyringError; use thiserror::Error; /// defines the error type for the `KeyStorage` diff --git a/src/system/keys/keyringstorage.rs b/src/system/keys/keyringstorage.rs index 69293e8..24a7e6b 100644 --- a/src/system/keys/keyringstorage.rs +++ b/src/system/keys/keyringstorage.rs @@ -4,10 +4,40 @@ // Local // Ext -use keyring::{Entry as Keyring, Error as KeyringError}; +use std::sync::Once; + +use keyring_core::{Entry as Keyring, Error as KeyringError}; use super::{KeyStorage, KeyStorageError}; +/// Guards the one-time registration of the process-wide default credential store. +static INIT_STORE: Once = Once::new(); + +/// Registers the native credential store as `keyring-core`'s process-wide default. +/// +/// Unlike `keyring` 3.x, which selected the credential store at compile time via +/// Cargo features, `keyring-core` 4.x requires the application to register a store +/// at runtime *before* creating any [`Keyring`] entry. We do this lazily and +/// exactly once, choosing the native store for the current platform. On platforms +/// with no supported store nothing is registered, so [`Keyring`] operations fail +/// and the caller transparently falls back to file-based storage. +fn ensure_default_store() { + INIT_STORE.call_once(|| { + #[cfg(target_os = "macos")] + if let Ok(store) = apple_native_keyring_store::keychain::Store::new() { + keyring_core::set_default_store(store); + } + #[cfg(target_os = "windows")] + if let Ok(store) = windows_native_keyring_store::Store::new() { + keyring_core::set_default_store(store); + } + #[cfg(any(target_os = "linux", target_os = "freebsd"))] + if let Ok(store) = dbus_secret_service_keyring_store::Store::new() { + keyring_core::set_default_store(store); + } + }); +} + /// provides a `KeyStorage` implementation using the keyring crate pub struct KeyringStorage { username: String, @@ -27,6 +57,7 @@ impl KeyStorage for KeyringStorage { /// The key might be acccess through an identifier, which identifies /// the key in the storage fn get_key(&self, storage_id: &str) -> Result { + ensure_default_store(); let storage: Keyring = Keyring::new(storage_id, self.username.as_str())?; match storage.get_password() { Ok(s) => Ok(s), @@ -46,6 +77,7 @@ impl KeyStorage for KeyringStorage { /// Set the key into the key storage fn set_key(&self, storage_id: &str, key: &str) -> Result<(), KeyStorageError> { + ensure_default_store(); let storage: Keyring = Keyring::new(storage_id, self.username.as_str())?; match storage.set_password(key) { Ok(_) => Ok(()), @@ -57,6 +89,7 @@ impl KeyStorage for KeyringStorage { /// /// Returns whether the key storage is supported on the host system fn is_supported(&self) -> bool { + ensure_default_store(); let dummy: String = String::from("dummy-service"); let storage: Keyring = match Keyring::new(dummy.as_str(), self.username.as_str()) { Ok(s) => s,