Skip to main content

azul_core/
keyring.rs

1//! POD types for the system-keyring surface
2//! (SUPER_PLAN_2 §4 P4.2 + research/02 §0 "hardware-bound" storage).
3//!
4//! A biometry-bindable secret key/value store backed by the OS keyring:
5//! iOS/macOS Keychain (`SecItem*`, optionally `kSecAttrAccessControl =
6//! biometryCurrentSet`), Android `KeyStore` (`setUserAuthenticationRequired`),
7//! Linux libsecret, Windows `CredentialLocker`. Defined here in `azul-core`
8//! so the request/result types cross the FFI without `azul-layout` being a
9//! dependency; the stateful side lives in
10//! `azul_layout::managers::keyring::KeyringManager`.
11//!
12//! Request-driven and channel-delivered, mirroring biometric ([`crate::
13//! biometric`]): a `Get` of a biometry-bound item shows the OS prompt and
14//! resolves asynchronously, so *every* op resolves through the result
15//! channel for a uniform, engine-agnostic surface. One op is in flight at
16//! a time (the demo reveals one entry at a time); request↔result
17//! correlation by id is a future refinement.
18
19use azul_css::AzString;
20
21/// A keyring operation queued by a callback
22/// (`CallbackInfo::keyring_store` / `keyring_get` / `keyring_delete`) and
23/// dispatched to the platform backend by the layout pass.
24///
25/// `secret` is an [`AzString`] — the common case is a password / token;
26/// binary blobs are base64-encoded by the caller. `key` is the lookup
27/// name, scoped to the app's keyring service.
28#[repr(C, u8)]
29#[derive(Debug, Clone, PartialEq, Eq)]
30pub enum KeyringRequest {
31    /// Write `secret` under `key`, overwriting any existing value. When
32    /// `require_biometry` is set the item is stored access-controlled so a
33    /// later `Get` triggers the OS biometric prompt (Keychain
34    /// `biometryCurrentSet` / `KeyStore` `setUserAuthenticationRequired`).
35    Store {
36        key: AzString,
37        secret: AzString,
38        require_biometry: bool,
39    },
40    /// Read the secret stored under `key`. For a biometry-bound item the
41    /// OS shows its auth prompt first; the result arrives asynchronously.
42    Get { key: AzString },
43    /// Remove the item stored under `key` (no-op if absent).
44    Delete { key: AzString },
45}
46#[allow(variant_size_differences)]
47// repr(C,u8) FFI enum: boxing the large variant would change the C ABI (api.json bindings); size disparity accepted
48/// The outcome of a [`KeyringRequest`], delivered to the result channel
49/// and read by callbacks via `CallbackInfo::get_keyring_result()`.
50#[repr(C, u8)]
51#[derive(Debug, Clone, PartialEq, Eq)]
52pub enum KeyringResult {
53    /// A `Store` succeeded.
54    Stored,
55    /// A `Get` returned the secret.
56    Retrieved(AzString),
57    /// A `Delete` succeeded (the key is now absent).
58    Deleted,
59    /// The requested key was not present in the keyring.
60    NotFound,
61    /// A biometry-bound read was refused — the user failed or cancelled
62    /// the OS auth prompt.
63    Denied,
64    /// No keyring backend is available on this platform / it isn't
65    /// configured (e.g. Linux without a running secret service).
66    Unavailable,
67    /// A platform error occurred (locked keychain, I/O, unmapped code).
68    Error,
69}
70
71impl KeyringResult {
72    /// The retrieved secret, if this is a successful `Get`.
73    #[must_use]
74    pub const fn secret(&self) -> Option<&AzString> {
75        match self {
76            Self::Retrieved(s) => Some(s),
77            _ => None,
78        }
79    }
80
81    /// `true` for the success outcomes (`Stored` / `Retrieved` / `Deleted`).
82    #[must_use]
83    pub const fn is_ok(&self) -> bool {
84        matches!(self, Self::Stored | Self::Retrieved(_) | Self::Deleted)
85    }
86}
87
88// FFI Option wrapper for `CallbackInfo::get_keyring_result() ->
89// Option<KeyringResult>` — `None` until the first op completes. Not Copy
90// (carries an `AzString` in `Retrieved`), so `copy = false` (mirrors
91// `OptionNodeType`).
92impl_option!(
93    KeyringResult,
94    OptionKeyringResult,
95    copy = false,
96    [Debug, Clone, PartialEq, Eq]
97);
98
99#[cfg(test)]
100#[path = "keyring_test.rs"]
101mod keyring_test;