walletkit_db/traits.rs
1//! Interfaces for consumer-supplied platform integrations.
2//!
3//! Argument shapes (`Vec<u8>`, owned `String`) mostly mirror `WalletKit`'s
4//! existing uniffi-annotated traits so consumers can bridge with a thin
5//! newtype that just delegates and maps errors. (A blanket impl across
6//! crates is blocked by Rust's orphan rule, so consumers do need a small
7//! wrapper.) `Keystore::seal` is the one exception: it borrows its
8//! plaintext so the secret is never owned by this crate longer than
9//! necessary; a bridge to an owned-only interface (e.g. a uniffi callback)
10//! still needs one copy at that boundary.
11
12use crate::error::StoreResult;
13
14/// Device keystore for sealing and opening secrets under a device-bound key.
15///
16/// Implementations MUST use an AEAD construction (e.g. AES-GCM or
17/// ChaCha20-Poly1305) so that `aad` (additional authenticated data) is
18/// authenticated as part of the seal: any mismatch when opening must fail.
19pub trait Keystore: Send + Sync {
20 /// Seals plaintext under the device-bound key, authenticating `aad`
21 /// (additional authenticated data).
22 ///
23 /// `plaintext` is borrowed rather than owned so that callers holding it
24 /// in a zeroizing buffer (e.g. `Zeroizing<[u8; 32]>`) never have to
25 /// hand ownership of an un-zeroized copy to this trait.
26 ///
27 /// # Errors
28 ///
29 /// Returns an error if the keystore refuses the operation or the seal
30 /// fails.
31 fn seal(&self, aad: &[u8], plaintext: &[u8]) -> StoreResult<Vec<u8>>;
32
33 /// Opens ciphertext under the device-bound key, verifying `aad`. The
34 /// same `aad` supplied at seal time must be supplied here or the open
35 /// operation must fail.
36 ///
37 /// # Errors
38 ///
39 /// Returns an error if authentication fails or the keystore cannot open.
40 fn open_sealed(&self, aad: Vec<u8>, ciphertext: Vec<u8>) -> StoreResult<Vec<u8>>;
41}
42
43/// Atomic blob store for small binary files (e.g. sealed key envelopes).
44///
45/// Provided by the host rather than calling `std::fs` directly for two
46/// reasons:
47///
48/// - **WASM has no `std::fs`.** On `wasm32-unknown-unknown` the runtime
49/// is a Web Worker; the host backs storage with `OPFS`, `IndexedDB`,
50/// or similar.
51/// - **Hosts own where data lives.** iOS sandboxed app-data containers,
52/// Android per-UID data dirs, iCloud-skip flags, atomic-write
53/// semantics — all platform-specific. walletkit-db stays neutral.
54pub trait AtomicBlobStore: Send + Sync {
55 /// Reads the blob at `path`, if present.
56 ///
57 /// # Errors
58 ///
59 /// Returns an error if the read fails.
60 fn read(&self, path: String) -> StoreResult<Option<Vec<u8>>>;
61
62 /// Writes bytes atomically to `path`.
63 ///
64 /// # Errors
65 ///
66 /// Returns an error if the write fails.
67 fn write_atomic(&self, path: String, bytes: Vec<u8>) -> StoreResult<()>;
68
69 /// Deletes the blob at `path`.
70 ///
71 /// # Errors
72 ///
73 /// Returns an error if the delete fails.
74 fn delete(&self, path: String) -> StoreResult<()>;
75}