Skip to main content

dig_keystore/backend/
mod.rs

1//! Storage backend abstraction.
2//!
3//! A `KeychainBackend` is any byte-blob KV store. The shipped `FileBackend`
4//! persists to the local filesystem with atomic (tmp + rename) writes. Planned
5//! future backends: `OsKeyringBackend` (macOS Keychain / Windows Credential
6//! Store / Secret Service), and hardware-signer backends (`LedgerBackend`,
7//! `YubiHsmBackend`) that proxy `sign` to an external device.
8
9use crate::error::Result;
10
11#[cfg(feature = "file-backend")]
12mod file;
13#[cfg(feature = "testing")]
14mod memory;
15
16#[cfg(feature = "file-backend")]
17pub use file::FileBackend;
18#[cfg(feature = "testing")]
19pub use memory::MemoryBackend;
20
21/// An opaque key identifying a single encrypted blob within a backend.
22///
23/// For `FileBackend`, the key maps to `<root>/<key>.dks`; for an OS-keyring
24/// backend it maps to a service / account pair; for a hardware signer it maps
25/// to a slot identifier.
26#[derive(Clone, Debug, PartialEq, Eq, Hash)]
27pub struct BackendKey(pub String);
28
29impl BackendKey {
30    /// Construct from any string-like value.
31    pub fn new(name: impl Into<String>) -> Self {
32        Self(name.into())
33    }
34
35    /// Borrow as `&str`.
36    pub fn as_str(&self) -> &str {
37        &self.0
38    }
39}
40
41impl<T: Into<String>> From<T> for BackendKey {
42    fn from(v: T) -> Self {
43        Self(v.into())
44    }
45}
46
47impl std::fmt::Display for BackendKey {
48    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49        f.write_str(&self.0)
50    }
51}
52
53/// Storage backend trait. Implementations must be `Send + Sync + 'static` so
54/// they can be held behind `Arc<dyn KeychainBackend>`.
55pub trait KeychainBackend: Send + Sync + 'static {
56    /// Read the full contents of the blob at `key`.
57    ///
58    /// Returns a backend I/O error if the blob does not exist.
59    fn read(&self, key: &BackendKey) -> Result<Vec<u8>>;
60
61    /// Write `data` to `key`. Implementations should be atomic — a reader
62    /// seeing the key after this call must see either the old bytes or the
63    /// new bytes in full, never a torn mix.
64    fn write(&self, key: &BackendKey, data: &[u8]) -> Result<()>;
65
66    /// Remove the blob at `key`. Implementations should best-effort overwrite
67    /// the storage before removing so residual disk sectors do not retain the
68    /// ciphertext.
69    fn delete(&self, key: &BackendKey) -> Result<()>;
70
71    /// List keys that start with `prefix`. Order is unspecified.
72    fn list(&self, prefix: &str) -> Result<Vec<BackendKey>>;
73
74    /// Whether a blob exists at `key`. Default impl delegates to `read`;
75    /// backends with cheaper existence checks should override.
76    fn exists(&self, key: &BackendKey) -> Result<bool> {
77        match self.read(key) {
78            Ok(_) => Ok(true),
79            Err(crate::error::KeystoreError::Backend(e))
80                if e.kind() == std::io::ErrorKind::NotFound =>
81            {
82                Ok(false)
83            }
84            Err(e) => Err(e),
85        }
86    }
87}