core_auth/
backend.rs

1//! Core trait and types for pluggable authentication backends.
2
3use crate::AuthError;
4use core_crypto::SecureBytes;
5use core_types::{AuthFactorId, TrustProfileName};
6use std::collections::BTreeMap;
7use std::path::Path;
8
9/// What kind of user interaction a backend requires.
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum AuthInteraction {
12    /// No interaction -- backend can unlock silently (SSH software key, TPM, keyring).
13    None,
14    /// Password/PIN entry required (keyboard input).
15    PasswordEntry,
16    /// Physical touch on hardware token required (FIDO2, PIV with touch policy).
17    HardwareTouch,
18}
19
20/// How the master key should be sent to daemon-secrets.
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub enum IpcUnlockStrategy {
23    /// Use existing `UnlockRequest` with password field.
24    /// daemon-secrets performs the KDF.
25    PasswordUnlock,
26    /// Use `SshUnlockRequest` with pre-derived master key.
27    /// Used by SSH-agent and future backends that derive/unwrap the key client-side.
28    DirectMasterKey,
29}
30
31/// Which piece of the unlock this backend is providing.
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub enum FactorContribution {
34    /// This backend provides a complete master key (unwrapped independently).
35    /// Used in `Any` and `Policy` modes.
36    CompleteMasterKey,
37    /// This backend provides a factor piece for combined derivation.
38    /// Used in `All` mode where the master key is HKDF'd from all pieces.
39    FactorPiece,
40}
41
42/// Result of a successful backend unlock.
43pub struct UnlockOutcome {
44    /// The 32-byte master key (for `DirectMasterKey`) or password bytes (for `PasswordUnlock`).
45    pub master_key: SecureBytes,
46    /// Backend-specific metadata for audit logging.
47    pub audit_metadata: BTreeMap<String, String>,
48    /// Which IPC message type to use.
49    pub ipc_strategy: IpcUnlockStrategy,
50    /// Which factor this outcome represents.
51    pub factor_id: AuthFactorId,
52}
53
54/// A pluggable authentication backend for vault unlock.
55#[async_trait::async_trait]
56pub trait VaultAuthBackend: Send + Sync {
57    /// Which auth factor this backend provides.
58    fn factor_id(&self) -> AuthFactorId;
59
60    /// Human-readable name for audit logs and overlay display.
61    fn name(&self) -> &str;
62
63    /// Short identifier for IPC messages and config.
64    fn backend_id(&self) -> &str;
65
66    /// Check whether this backend has a valid enrollment for the profile.
67    fn is_enrolled(&self, profile: &TrustProfileName, config_dir: &Path) -> bool;
68
69    /// Check whether this backend can currently perform an unlock.
70    /// Must be fast (< 100ms).
71    async fn can_unlock(&self, profile: &TrustProfileName, config_dir: &Path) -> bool;
72
73    /// What kind of user interaction this backend requires.
74    fn requires_interaction(&self) -> AuthInteraction;
75
76    /// Attempt to derive/unwrap the master key for a profile.
77    async fn unlock(
78        &self,
79        profile: &TrustProfileName,
80        config_dir: &Path,
81        salt: &[u8],
82    ) -> Result<UnlockOutcome, AuthError>;
83
84    /// Enroll this backend for a profile. Requires the master key.
85    ///
86    /// `selected_key_index` is an optional index into the list of eligible keys
87    /// returned by the backend. If `None`, the backend picks the first eligible key.
88    async fn enroll(
89        &self,
90        profile: &TrustProfileName,
91        master_key: &SecureBytes,
92        config_dir: &Path,
93        salt: &[u8],
94        selected_key_index: Option<usize>,
95    ) -> Result<(), AuthError>;
96
97    /// Remove enrollment for this backend.
98    async fn revoke(&self, profile: &TrustProfileName, config_dir: &Path) -> Result<(), AuthError>;
99}