Skip to main content

zvault_core/
error.rs

1//! Error types for `zvault-core`.
2//!
3//! Each error variant carries enough context to diagnose the problem without
4//! a debugger. Crypto errors never include key material — only key identifiers
5//! or operation descriptions.
6
7use zvault_storage::StorageError;
8
9/// Errors from cryptographic operations.
10#[derive(Debug, thiserror::Error)]
11pub enum CryptoError {
12    /// AES-256-GCM encryption failed.
13    #[error("encryption failed: {reason}")]
14    Encryption { reason: String },
15
16    /// AES-256-GCM decryption failed (wrong key, corrupted ciphertext, or tampered tag).
17    #[error("decryption failed: {reason}")]
18    Decryption { reason: String },
19
20    /// HKDF key derivation failed.
21    #[error("key derivation failed for context '{context}': {reason}")]
22    KeyDerivation { context: String, reason: String },
23
24    /// Ciphertext is too short to contain a valid nonce + tag.
25    #[error("ciphertext too short: expected at least {expected} bytes, got {actual}")]
26    CiphertextTooShort { expected: usize, actual: usize },
27}
28
29/// Errors from the encryption barrier.
30#[derive(Debug, thiserror::Error)]
31pub enum BarrierError {
32    /// The vault is sealed — no operations are possible until unseal.
33    #[error("vault is sealed")]
34    Sealed,
35
36    /// A cryptographic operation within the barrier failed.
37    #[error("barrier crypto error: {0}")]
38    Crypto(#[from] CryptoError),
39
40    /// The underlying storage backend returned an error.
41    #[error("barrier storage error: {0}")]
42    Storage(#[from] StorageError),
43}
44
45/// Errors from seal/unseal operations.
46#[derive(Debug, thiserror::Error)]
47pub enum SealError {
48    /// The vault has already been initialized.
49    #[error("vault is already initialized")]
50    AlreadyInitialized,
51
52    /// The vault has not been initialized yet.
53    #[error("vault is not initialized")]
54    NotInitialized,
55
56    /// The vault is already unsealed.
57    #[error("vault is already unsealed")]
58    AlreadyUnsealed,
59
60    /// The vault is already sealed.
61    #[error("vault is already sealed")]
62    AlreadySealed,
63
64    /// Invalid Shamir configuration parameters.
65    #[error("invalid seal config: {reason}")]
66    InvalidConfig { reason: String },
67
68    /// A submitted unseal share was invalid or corrupted.
69    #[error("invalid unseal share: {reason}")]
70    InvalidShare { reason: String },
71
72    /// Shamir secret recovery failed (not enough shares or corrupted shares).
73    #[error("share recovery failed: {reason}")]
74    RecoveryFailed { reason: String },
75
76    /// Failed to decrypt the root key with the reconstructed unseal key.
77    #[error("root key decryption failed: {reason}")]
78    RootKeyDecryption { reason: String },
79
80    /// A cryptographic operation failed during seal/unseal.
81    #[error("seal crypto error: {0}")]
82    Crypto(#[from] CryptoError),
83
84    /// The encryption barrier returned an error during raw storage access.
85    #[error("seal barrier error: {0}")]
86    Barrier(#[from] BarrierError),
87
88    /// The underlying storage backend returned an error.
89    #[error("seal storage error: {0}")]
90    Storage(#[from] StorageError),
91}
92
93/// Errors from token operations.
94#[derive(Debug, thiserror::Error)]
95pub enum TokenError {
96    /// The token was not found in storage.
97    #[error("token not found")]
98    NotFound,
99
100    /// The token has expired.
101    #[error("token expired at {expired_at}")]
102    Expired { expired_at: String },
103
104    /// The token is not renewable.
105    #[error("token is not renewable")]
106    NotRenewable,
107
108    /// The token has exceeded its maximum TTL.
109    #[error("token has exceeded max TTL of {max_ttl_secs}s")]
110    MaxTtlExceeded { max_ttl_secs: i64 },
111
112    /// The barrier returned an error.
113    #[error("token barrier error: {0}")]
114    Barrier(#[from] BarrierError),
115}
116
117/// Errors from policy operations.
118#[derive(Debug, thiserror::Error)]
119pub enum PolicyError {
120    /// The requested policy was not found.
121    #[error("policy not found: {name}")]
122    NotFound { name: String },
123
124    /// The policy document is invalid.
125    #[error("invalid policy: {reason}")]
126    Invalid { reason: String },
127
128    /// Cannot modify a built-in policy.
129    #[error("cannot modify built-in policy: {name}")]
130    BuiltIn { name: String },
131
132    /// Access denied by policy evaluation.
133    #[error("permission denied on path '{path}' for capability '{capability}'")]
134    Denied { path: String, capability: String },
135
136    /// The barrier returned an error.
137    #[error("policy barrier error: {0}")]
138    Barrier(#[from] BarrierError),
139}
140
141/// Errors from audit operations.
142#[derive(Debug, thiserror::Error)]
143pub enum AuditError {
144    /// All audit backends failed to write — request must be denied.
145    #[error("all audit backends failed (fail-closed)")]
146    AllBackendsFailed,
147
148    /// A specific audit backend failed.
149    #[error("audit backend '{name}' failed: {reason}")]
150    BackendFailure { name: String, reason: String },
151
152    /// Serialization of the audit entry failed.
153    #[error("audit serialization failed: {reason}")]
154    Serialization { reason: String },
155}
156
157/// Errors from mount table operations.
158#[derive(Debug, thiserror::Error)]
159pub enum MountError {
160    /// The mount path is already in use.
161    #[error("mount path already in use: {path}")]
162    AlreadyMounted { path: String },
163
164    /// The mount path was not found.
165    #[error("mount not found: {path}")]
166    NotFound { path: String },
167
168    /// Invalid mount path.
169    #[error("invalid mount path: {reason}")]
170    InvalidPath { reason: String },
171
172    /// Unknown engine type.
173    #[error("unknown engine type: {engine_type}")]
174    UnknownEngineType { engine_type: String },
175
176    /// The barrier returned an error.
177    #[error("mount barrier error: {0}")]
178    Barrier(#[from] BarrierError),
179}
180
181/// Errors from secrets engine operations.
182#[derive(Debug, thiserror::Error)]
183pub enum EngineError {
184    /// The requested secret was not found.
185    #[error("secret not found at path '{path}'")]
186    NotFound { path: String },
187
188    /// Invalid request to the engine.
189    #[error("invalid engine request: {reason}")]
190    InvalidRequest { reason: String },
191
192    /// The barrier returned an error.
193    #[error("engine barrier error: {0}")]
194    Barrier(#[from] BarrierError),
195
196    /// Internal engine error.
197    #[error("engine internal error: {reason}")]
198    Internal { reason: String },
199}
200
201/// Errors from lease operations.
202#[derive(Debug, thiserror::Error)]
203pub enum LeaseError {
204    /// The lease was not found.
205    #[error("lease not found: {lease_id}")]
206    NotFound { lease_id: String },
207
208    /// The lease has already expired.
209    #[error("lease already expired: {lease_id}")]
210    Expired { lease_id: String },
211
212    /// The lease is not renewable.
213    #[error("lease is not renewable: {lease_id}")]
214    NotRenewable { lease_id: String },
215
216    /// The barrier returned an error.
217    #[error("lease barrier error: {0}")]
218    Barrier(#[from] BarrierError),
219}
220
221/// Errors from the database secrets engine.
222#[derive(Debug, thiserror::Error)]
223pub enum DatabaseError {
224    /// Database connection config not found.
225    #[error("database config not found: {name}")]
226    NotFound { name: String },
227
228    /// Database role not found.
229    #[error("database role not found: {name}")]
230    RoleNotFound { name: String },
231
232    /// Invalid configuration parameters.
233    #[error("invalid database config: {reason}")]
234    InvalidConfig { reason: String },
235
236    /// Internal engine error.
237    #[error("database engine error: {reason}")]
238    Internal { reason: String },
239
240    /// The barrier returned an error.
241    #[error("database barrier error: {0}")]
242    Barrier(#[from] BarrierError),
243}
244
245/// Errors from the PKI secrets engine.
246#[derive(Debug, thiserror::Error)]
247pub enum PkiError {
248    /// No root CA has been generated yet.
249    #[error("no root CA configured — generate one first")]
250    NoRootCa,
251
252    /// PKI role not found.
253    #[error("PKI role not found: {name}")]
254    RoleNotFound { name: String },
255
256    /// Invalid configuration or request.
257    #[error("invalid PKI request: {reason}")]
258    InvalidRequest { reason: String },
259
260    /// Certificate generation failed.
261    #[error("certificate generation failed: {reason}")]
262    CertGeneration { reason: String },
263
264    /// Internal engine error.
265    #[error("PKI engine error: {reason}")]
266    Internal { reason: String },
267
268    /// The barrier returned an error.
269    #[error("PKI barrier error: {0}")]
270    Barrier(#[from] BarrierError),
271}
272
273/// Errors from the AppRole auth method.
274#[derive(Debug, thiserror::Error)]
275pub enum AppRoleError {
276    /// AppRole role not found.
277    #[error("approle role not found: {name}")]
278    RoleNotFound { name: String },
279
280    /// Invalid secret ID.
281    #[error("invalid secret ID for role '{role_name}'")]
282    InvalidSecretId { role_name: String },
283
284    /// Invalid configuration.
285    #[error("invalid approle config: {reason}")]
286    InvalidConfig { reason: String },
287
288    /// Internal error.
289    #[error("approle error: {reason}")]
290    Internal { reason: String },
291
292    /// The barrier returned an error.
293    #[error("approle barrier error: {0}")]
294    Barrier(#[from] BarrierError),
295}