Skip to main content

keepass_ng/
error.rs

1//! Error types that this crate can return
2
3#[derive(thiserror::Error, Debug)]
4pub enum Error {
5    #[error("std::io::Error {0}")]
6    Io(#[from] std::io::Error),
7
8    #[error("DatabaseError::RecycleBinDisabled")]
9    RecycleBinDisabled,
10
11    #[error("DatabaseError::RecycleBinAlreadyExists")]
12    RecycleBinAlreadyExists,
13
14    #[error("DatabaseOpenError {0}")]
15    DatabaseOpenError(#[from] DatabaseOpenError),
16
17    #[error("DatabaseSaveError {0}")]
18    DatabaseSaveError(#[from] DatabaseSaveError),
19
20    #[cfg(feature = "totp")]
21    #[error("DbOtpError {0}")]
22    DbOtpError(#[from] crate::db::otp::TOTPError),
23
24    #[error("OuterCipherConfigError {0}")]
25    OuterCipherConfigError(#[from] OuterCipherConfigError),
26
27    #[error("InnerCipherConfigError {0}")]
28    InnerCipherConfigError(#[from] InnerCipherConfigError),
29
30    #[error("CompressionConfigError {0}")]
31    CompressionConfigError(#[from] CompressionConfigError),
32
33    #[error("KdfConfigError {0}")]
34    KdfConfigError(#[from] KdfConfigError),
35
36    #[error("CryptographyError {0}")]
37    CryptographyError(#[from] CryptographyError),
38
39    #[error("BlockStreamError {0}")]
40    BlockStreamError(#[from] BlockStreamError),
41
42    #[error("VariantDictionaryError {0}")]
43    VariantDictionaryError(#[from] VariantDictionaryError),
44
45    #[error("XmlParseError {0}")]
46    XmlParseError(#[from] XmlParseError),
47
48    #[error("ParseColorError {0}")]
49    ParseColorError(#[from] ParseColorError),
50
51    #[error("ParseIconIdError {}", icon_id)]
52    ParseIconIdError { icon_id: usize },
53
54    #[cfg(feature = "merge")]
55    #[error("MergeError {0}")]
56    MergeError(#[from] crate::db::merge::MergeError),
57
58    #[error("String error: {0}")]
59    String(String),
60}
61
62impl From<&str> for Error {
63    fn from(s: &str) -> Self {
64        Error::String(s.to_string())
65    }
66}
67
68impl From<String> for Error {
69    fn from(s: String) -> Self {
70        Error::String(s)
71    }
72}
73
74impl From<&String> for Error {
75    fn from(s: &String) -> Self {
76        Error::String(s.clone())
77    }
78}
79
80pub type Result<T, E = Error> = std::result::Result<T, E>;
81
82pub type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>;
83
84/// Errors upon reading a Database
85#[derive(Debug, thiserror::Error)]
86pub enum DatabaseOpenError {
87    /// An I/O error has occurred while reading the database
88    #[error(transparent)]
89    Io(#[from] std::io::Error),
90
91    /// An error with the database's key has occurred
92    #[error(transparent)]
93    Key(#[from] DatabaseKeyError),
94
95    /// The database is corrupted
96    #[error(transparent)]
97    DatabaseIntegrity(#[from] DatabaseIntegrityError),
98
99    /// The database version cannot be read by this library
100    #[error("Opening this database version is not supported")]
101    UnsupportedVersion,
102}
103
104/// Errors stemming from corrupted databases
105#[derive(Debug, thiserror::Error)]
106pub enum DatabaseIntegrityError {
107    /// The database does not have a valid KDBX identifier
108    #[error("Invalid KDBX identifier")]
109    InvalidKDBXIdentifier,
110
111    /// The version of the KDBX file is invalid
112    #[error("Invalid KDBX version: {}.{}.{}", version, file_major_version, file_minor_version)]
113    InvalidKDBXVersion {
114        version: u32,
115        file_major_version: u32,
116        file_minor_version: u32,
117    },
118
119    /// The fixed header has an invalid size
120    #[error("Invalid header size: {}", size)]
121    InvalidFixedHeader { size: usize },
122
123    #[error("Invalid field length for type {}: {} (expected {})", field_type, field_size, expected_field_size)]
124    InvalidKDBFieldLength {
125        field_type: u16,
126        field_size: u32,
127        expected_field_size: u32,
128    },
129
130    #[error("Missing group level")]
131    MissingKDBGroupLevel,
132
133    #[error("Invalid KDBX header field ID: {}", field_id)]
134    InvalidKDBXHeaderFieldID { field_id: u8 },
135
136    #[error("Invalid group level {} (current level {})", group_level, current_level)]
137    InvalidKDBGroupLevel { group_level: u16, current_level: u16 },
138
139    #[error("Missing group ID")]
140    MissingKDBGroupId,
141
142    #[error("Invalid group ID {}", group_id)]
143    InvalidKDBGroupId { group_id: u32 },
144
145    #[error("Invalid group field type: {}", field_type)]
146    InvalidKDBGroupFieldType { field_type: u16 },
147
148    #[error("Invalid entry field type: {}", field_type)]
149    InvalidKDBEntryFieldType { field_type: u16 },
150
151    #[error("Incomplete group")]
152    IncompleteKDBGroup,
153
154    #[error("Incomplete entry")]
155    IncompleteKDBEntry,
156
157    #[error("Invalid fixed cipher ID: {}", cid)]
158    InvalidFixedCipherID { cid: u32 },
159
160    #[error("Header hash masmatch")]
161    HeaderHashMismatch,
162
163    #[error("Invalid outer header entry: {}", entry_type)]
164    InvalidOuterHeaderEntry { entry_type: u8 },
165
166    #[error("Incomplete outer header: Missing {}", missing_field)]
167    IncompleteOuterHeader { missing_field: String },
168
169    #[error("Invalid inner header entry: {}", entry_type)]
170    InvalidInnerHeaderEntry { entry_type: u8 },
171
172    #[error("Incomplete outer header: Missing {}", missing_field)]
173    IncompleteInnerHeader { missing_field: String },
174
175    #[error(transparent)]
176    Cryptography(#[from] CryptographyError),
177
178    #[error(transparent)]
179    Xml(#[from] XmlParseError),
180
181    #[error(transparent)]
182    OuterCipher(#[from] OuterCipherConfigError),
183
184    #[error(transparent)]
185    InnerCipher(#[from] InnerCipherConfigError),
186
187    #[error(transparent)]
188    Compression(#[from] CompressionConfigError),
189
190    #[error(transparent)]
191    BlockStream(#[from] BlockStreamError),
192
193    #[error(transparent)]
194    VariantDictionary(#[from] VariantDictionaryError),
195
196    #[error(transparent)]
197    KdfSettings(#[from] KdfConfigError),
198
199    #[error(transparent)]
200    Io(#[from] std::io::Error),
201}
202
203/// Errors occurring when saving a Database
204#[derive(Debug, thiserror::Error)]
205pub enum DatabaseSaveError {
206    /// The current database version cannot be saved by this library
207    #[error("Saving this database version is not supported")]
208    UnsupportedVersion,
209
210    /// Error while writing out the inner XML database
211    #[error("Error while generating XML")]
212    Xml(#[from] xml::writer::Error),
213
214    /// General I/O issues while writing the database
215    #[error(transparent)]
216    Io(#[from] std::io::Error),
217
218    /// An error with the key occurred while writing the database
219    #[error(transparent)]
220    Key(#[from] DatabaseKeyError),
221
222    /// A cryptography error occurred while writing the database
223    #[error(transparent)]
224    Cryptography(#[from] CryptographyError),
225
226    /// An error getting randomness for keys occurred
227    #[error(transparent)]
228    Random(#[from] getrandom::Error),
229}
230
231/// Errors related to the database key
232#[derive(Debug, thiserror::Error)]
233pub enum DatabaseKeyError {
234    /// The key specified was incorrect, e.g. because of a wrong password
235    #[error("Incorrect key")]
236    IncorrectKey,
237
238    /// An error occurred in an underlying cryptographic operation while computing the key
239    #[error(transparent)]
240    Cryptography(#[from] CryptographyError),
241
242    /// An I/O error occurred while loading the keyfile
243    #[error(transparent)]
244    Io(#[from] std::io::Error),
245
246    /// An XML error occurred while loading the keyfile
247    #[error(transparent)]
248    Xml(#[from] xml::reader::Error),
249
250    /// The keyfile is invalid and did not contain a key
251    #[error("Could not obtain a key from the keyfile")]
252    InvalidKeyFile,
253
254    /// Could not get challenge response key.
255    #[cfg(feature = "challenge_response")]
256    #[error("Error with the challenge-response key: {0}")]
257    ChallengeResponseKeyError(String),
258}
259
260/// Errors with the configuration of the outer encryption
261#[derive(Debug, thiserror::Error)]
262pub enum OuterCipherConfigError {
263    #[error(transparent)]
264    Cryptography(#[from] CryptographyError),
265
266    #[error("Invalid outer cipher ID: {:?}", cid)]
267    InvalidOuterCipherID { cid: Vec<u8> },
268}
269
270/// Errors with the configuration of the inner encryption
271#[derive(Debug, thiserror::Error)]
272pub enum InnerCipherConfigError {
273    #[error(transparent)]
274    Cryptography(#[from] CryptographyError),
275
276    #[error("Invalid inner cipher ID: {}", cid)]
277    InvalidInnerCipherID { cid: u32 },
278}
279
280/// Errors with the configuration of the compression algorithm
281#[derive(Debug, thiserror::Error)]
282pub enum CompressionConfigError {
283    /// The identifier for the compression algorithm specified in the database is invalid
284    #[error("Invalid compression algorithm: {}", cid)]
285    InvalidCompressionSuite { cid: u32 },
286}
287
288/// Errors with the configuration of the Key Derivation Function
289#[derive(Debug, thiserror::Error)]
290pub enum KdfConfigError {
291    #[error("Invalid KDF version: {}", version)]
292    InvalidKDFVersion { version: u32 },
293
294    #[error("Invalid KDF UUID: {:?}", uuid)]
295    InvalidKDFUUID { uuid: Vec<u8> },
296
297    #[error(transparent)]
298    VariantDictionary(#[from] VariantDictionaryError),
299}
300
301/// Errors while performing cryptographic operations
302#[derive(Debug, thiserror::Error)]
303pub enum CryptographyError {
304    #[error(transparent)]
305    InvalidLength(#[from] cipher::InvalidLength),
306
307    #[error(transparent)]
308    Unpadding(#[from] cipher::block_padding::Error),
309
310    #[error(transparent)]
311    Padding(#[from] cipher::inout::PadError),
312
313    #[error(transparent)]
314    Argon2(#[from] argon2::Error),
315}
316
317/// Errors reading from the HMAC block stream
318#[derive(Debug, thiserror::Error)]
319pub enum BlockStreamError {
320    #[error(transparent)]
321    Cryptography(#[from] CryptographyError),
322
323    #[error("Block hash mismatch for block {}", block_index)]
324    BlockHashMismatch { block_index: u64 },
325
326    #[error("unexpected end of file")]
327    Eof,
328}
329
330/// Errors while parsing a `VariantDictionary`
331#[derive(Debug, thiserror::Error)]
332pub enum VariantDictionaryError {
333    #[error("Invalid variant dictionary version: {}", version)]
334    InvalidVersion { version: u16 },
335
336    #[error("Invalid value type: {}", value_type)]
337    InvalidValueType { value_type: u8 },
338
339    #[error("Missing key: {}", key)]
340    MissingKey { key: String },
341
342    #[error("Mistyped value: {}", key)]
343    Mistyped { key: String },
344
345    #[error("VariantDictionary did not end with null byte, when it should")]
346    NotTerminated,
347}
348
349/// Errors while parsing the XML document inside of a `KeePass` database
350#[derive(Debug, thiserror::Error)]
351pub enum XmlParseError {
352    #[error(transparent)]
353    Xml(#[from] xml::reader::Error),
354
355    #[error(transparent)]
356    Base64(#[from] base64::DecodeError),
357
358    #[error(transparent)]
359    TimestampFormat(#[from] chrono::ParseError),
360
361    #[error(transparent)]
362    IntFormat(#[from] std::num::ParseIntError),
363
364    #[error(transparent)]
365    BoolFormat(#[from] std::str::ParseBoolError),
366
367    #[error(transparent)]
368    Uuid(#[from] uuid::Error),
369
370    #[error(transparent)]
371    Color(#[from] ParseColorError),
372
373    #[error(transparent)]
374    Cryptography(#[from] CryptographyError),
375
376    #[error("Decompression error: {}", _0)]
377    Compression(#[source] std::io::Error),
378
379    /// An unexpected XML event occurred, such as opening an unexpected tag, or an error in the
380    /// underlying XML reader
381    #[error("Bad XML event: expected {}, got {:?}", expected, event)]
382    BadEvent {
383        expected: &'static str,
384        event: crate::format::xml_db::parse::SimpleXmlEvent,
385    },
386
387    /// The stream of XML events ended when more events were expected
388    #[error("Unexpected end of XML document")]
389    Eof,
390}
391
392/// Error parsing a color code
393#[derive(Debug, thiserror::Error)]
394#[error("Cannot parse color: '{}'", _0)]
395pub struct ParseColorError(pub String);
396
397// move error type conversions to a module and exclude them from coverage counting.
398mod conversions {
399    use super::{
400        BlockStreamError, CompressionConfigError, CryptographyError, DatabaseIntegrityError, DatabaseOpenError, InnerCipherConfigError,
401        KdfConfigError, OuterCipherConfigError, VariantDictionaryError, XmlParseError,
402    };
403
404    impl From<CryptographyError> for DatabaseOpenError {
405        fn from(e: CryptographyError) -> Self {
406            DatabaseIntegrityError::from(e).into()
407        }
408    }
409
410    impl From<BlockStreamError> for DatabaseOpenError {
411        fn from(e: BlockStreamError) -> Self {
412            DatabaseIntegrityError::from(e).into()
413        }
414    }
415
416    impl From<XmlParseError> for DatabaseOpenError {
417        fn from(e: XmlParseError) -> Self {
418            DatabaseIntegrityError::from(e).into()
419        }
420    }
421
422    impl From<InnerCipherConfigError> for DatabaseOpenError {
423        fn from(e: InnerCipherConfigError) -> Self {
424            DatabaseIntegrityError::from(e).into()
425        }
426    }
427
428    impl From<OuterCipherConfigError> for DatabaseOpenError {
429        fn from(e: OuterCipherConfigError) -> Self {
430            DatabaseIntegrityError::from(e).into()
431        }
432    }
433
434    impl From<KdfConfigError> for DatabaseOpenError {
435        fn from(e: KdfConfigError) -> Self {
436            DatabaseIntegrityError::from(e).into()
437        }
438    }
439
440    impl From<VariantDictionaryError> for DatabaseOpenError {
441        fn from(e: VariantDictionaryError) -> Self {
442            DatabaseIntegrityError::from(e).into()
443        }
444    }
445
446    impl From<CompressionConfigError> for DatabaseOpenError {
447        fn from(e: CompressionConfigError) -> Self {
448            DatabaseIntegrityError::from(e).into()
449        }
450    }
451}