Skip to main content

dig_keystore/
lib.rs

1//! # dig-keystore
2//!
3//! Encrypted secret-key storage for DIG Network binaries.
4//!
5//! ## Feature tiers
6//!
7//! The crate splits into a machine-key **core** and a user-key **custody**
8//! tier. The core seals arbitrary bytes under a password and stores them; it
9//! has no notion of whose key it is. Custody models a *user's* identity key:
10//! the typed `Keystore<K: KeyScheme>`, its schemes, and the `SignerHandle<K>`
11//! you get by unlocking one.
12//!
13//! | Feature | Default | Adds |
14//! |---|---|---|
15//! | `file-backend` | yes | `FileBackend` (filesystem storage) |
16//! | `os-keychain` | no | `OsKeychainBackend` (Windows / macOS credential store) |
17//! | `custody` | **no** | `Keystore`, `SignerHandle`, `scheme::*` |
18//! | `hd-derivation` | **no** | `SignerHandle::expose_secret` (implies `custody`) |
19//! | `password-strength` | no | `Password::strength` |
20//! | `testing` | no | `MemoryBackend` + `TEST_PASSWORD` for dependents' tests |
21//!
22//! `custody` is off by default so that a consumer needing only machine-key
23//! sealing — the DIG node engine, whose identity-agnostic boundary is
24//! dig_ecosystem #908 — cannot name the custody API. See `SPEC.md` §18,
25//! **including the honest limits of that under Cargo feature unification**.
26//!
27//! The core surface is [`opaque`] (seal/open arbitrary-length secrets),
28//! [`backend`], and the format/KDF/cipher types. Storage is abstracted behind
29//! the `KeychainBackend` trait; hardware-signer backends (Ledger / YubiHSM)
30//! plug into the same trait in future releases.
31//!
32//! ## File format
33//!
34//! `DIGVK1` (BLS signing) and `DIGLW1` (L1 wallet BLS) for typed custody
35//! keystores, `DIGOP1` for opaque secrets. See `SPEC.md` §3 for the byte-level
36//! layout. Encryption is AES-256-GCM; key derivation is Argon2id (default 64
37//! MiB / 3 iterations / 4 lanes).
38//!
39//! ## Security properties
40//!
41//! - AES-256-GCM authenticated encryption (tag integrity)
42//! - Argon2id memory-hard KDF
43//! - `Zeroizing<...>` wrappers on passwords, seeds, and derived keys
44//! - Outer CRC32 for fast fail on bit-rot
45//! - Atomic file writes (tmp + rename)
46//!
47//! ## Minimal example
48//!
49//! Requires the `custody` feature:
50//!
51//! ```toml
52//! dig-keystore = { version = "0.8", features = ["custody"] }
53//! ```
54#![cfg_attr(
55    feature = "custody",
56    doc = r#"
57```no_run
58use std::sync::Arc;
59use dig_keystore::{
60    Keystore, Password, KdfParams,
61    scheme::BlsSigning,
62    backend::{FileBackend, BackendKey, KeychainBackend},
63};
64
65# fn main() -> dig_keystore::Result<()> {
66let backend: Arc<dyn KeychainBackend> = Arc::new(FileBackend::new("/var/dig/keys"));
67let key = BackendKey::new("validator_bls");
68let password = Password::from("correct horse battery staple");
69
70// Create
71let ks = Keystore::<BlsSigning>::create(
72    backend.clone(),
73    key.clone(),
74    password.clone(),
75    None,                          // generate a fresh seed
76    KdfParams::default(),
77)?;
78
79// Unlock + sign
80let signer = ks.unlock(password)?;
81let sig = signer.sign(b"message");
82let pk = signer.public_key();
83# Ok(())
84# }
85```
86"#
87)]
88#![deny(unsafe_code)]
89#![warn(missing_docs)]
90
91pub mod backend;
92mod cipher;
93#[cfg(feature = "custody")]
94mod custody;
95mod error;
96mod format;
97pub mod hardware;
98mod kdf;
99pub mod opaque;
100mod password;
101
102// Re-exports — the public surface.
103
104#[cfg(feature = "file-backend")]
105pub use backend::FileBackend;
106pub use backend::MemoryBackend;
107#[cfg(feature = "os-keychain")]
108pub use backend::OsKeychainBackend;
109pub use backend::{BackendKey, KeychainBackend};
110
111pub use error::{KeystoreError, Result};
112pub use format::{CipherId, KdfId, KdfParams, KeystoreHeader, FORMAT_VERSION_V1};
113pub use hardware::{
114    DegradeReason, HardwareBoundBackend, HardwareKind, HardwarePolicy, HardwareProbe,
115    HardwareProvider, KeyCustody, ProtectionTier,
116};
117pub use password::Password;
118
119// The user-custody surface, behind the non-default `custody` feature. A
120// consumer that only needs machine-key sealing cannot name these types at all
121// unless something in its resolved graph turns the feature on — see `SPEC.md`
122// §18 for the feature tiers and the honest limits of that guarantee.
123#[cfg(feature = "custody")]
124pub use custody::keystore::Keystore;
125#[cfg(feature = "custody")]
126pub use custody::scheme;
127#[cfg(feature = "custody")]
128pub use custody::scheme::{BlsSigning, KeyScheme, L1WalletBls};
129#[cfg(feature = "custody")]
130pub use custody::signer::SignerHandle;
131
132// chia-bls re-exports so consumers don't need a direct dependency for simple cases.
133pub mod bls {
134    //! Convenience re-exports of the `chia-bls` types used by the BLS schemes.
135    pub use chia_bls::{sign, verify};
136    pub use chia_bls::{PublicKey, SecretKey, Signature};
137}
138
139#[cfg(feature = "testing")]
140pub mod testing {
141    //! Testing helpers for dependent crates — only compiled under the `testing` feature.
142    //!
143    //! Exports [`MemoryBackend`] and a constant [`TEST_PASSWORD`] so that
144    //! dependent crates can stand up disposable keystores in their own tests
145    //! without re-deriving Argon2 + AES-GCM boilerplate.
146
147    pub use crate::backend::MemoryBackend;
148
149    /// A fixed, well-known password for test fixtures.
150    pub const TEST_PASSWORD: &str = "dig-keystore-test-password";
151}