Skip to main content

keepass_ng/db/
open.rs

1use crate::db::types::Database;
2use crate::{
3    DatabaseKey,
4    error::{
5        BlockStreamError, CompressionConfigError, CryptographyError, DatabaseKeyError, InnerCipherConfigError, KdfConfigError,
6        OuterCipherConfigError, VariantDictionaryError, XmlParseError,
7    },
8    format::{
9        DatabaseVersion, DatabaseVersionParseError,
10        kdb::KdbOpenError,
11        kdb::parse_kdb,
12        kdbx3::{Kdbx3OpenError, decrypt_kdbx3, parse_kdbx3},
13        kdbx4::{Kdbx4OpenError, decrypt_kdbx4, parse_kdbx4},
14    },
15};
16
17/// Errors upon reading a database.
18#[derive(Debug, thiserror::Error)]
19pub enum DatabaseOpenError {
20    #[error(transparent)]
21    Io(#[from] std::io::Error),
22
23    #[error(transparent)]
24    Key(#[from] DatabaseKeyError),
25
26    #[error(transparent)]
27    DatabaseIntegrity(#[from] DatabaseIntegrityError),
28
29    #[error(transparent)]
30    Version(#[from] DatabaseVersionParseError),
31
32    #[error("Opening this database version is not supported")]
33    UnsupportedVersion,
34}
35
36/// Errors stemming from corrupted databases.
37#[derive(Debug, thiserror::Error)]
38pub enum DatabaseIntegrityError {
39    #[error(transparent)]
40    Kdb(#[from] KdbOpenError),
41
42    #[error(transparent)]
43    Kdbx3(#[from] Kdbx3OpenError),
44
45    #[error(transparent)]
46    Kdbx4(#[from] Kdbx4OpenError),
47
48    #[error(transparent)]
49    Version(#[from] DatabaseVersionParseError),
50
51    #[error("Invalid KDBX identifier")]
52    InvalidKDBXIdentifier,
53
54    #[error("Invalid KDBX version: {}.{}.{}", version, file_major_version, file_minor_version)]
55    InvalidKDBXVersion {
56        version: u32,
57        file_major_version: u32,
58        file_minor_version: u32,
59    },
60
61    #[error("Invalid header size: {}", size)]
62    InvalidFixedHeader { size: usize },
63
64    #[error("Invalid field length for type {}: {} (expected {})", field_type, field_size, expected_field_size)]
65    InvalidKDBFieldLength {
66        field_type: u16,
67        field_size: u32,
68        expected_field_size: u32,
69    },
70
71    #[error("Missing group level")]
72    MissingKDBGroupLevel,
73    #[error("Invalid KDBX header field ID: {}", field_id)]
74    InvalidKDBXHeaderFieldID { field_id: u8 },
75    #[error("Invalid group level {} (current level {})", group_level, current_level)]
76    InvalidKDBGroupLevel { group_level: u16, current_level: u16 },
77    #[error("Missing group ID")]
78    MissingKDBGroupId,
79    #[error("Invalid group ID {}", group_id)]
80    InvalidKDBGroupId { group_id: u32 },
81    #[error("Invalid group field type: {}", field_type)]
82    InvalidKDBGroupFieldType { field_type: u16 },
83    #[error("Invalid entry field type: {}", field_type)]
84    InvalidKDBEntryFieldType { field_type: u16 },
85    #[error("Incomplete group")]
86    IncompleteKDBGroup,
87    #[error("Incomplete entry")]
88    IncompleteKDBEntry,
89    #[error("Invalid fixed cipher ID: {}", cid)]
90    InvalidFixedCipherID { cid: u32 },
91    #[error("Header hash masmatch")]
92    HeaderHashMismatch,
93    #[error("Invalid outer header entry: {}", entry_type)]
94    InvalidOuterHeaderEntry { entry_type: u8 },
95    #[error("Incomplete outer header: Missing {}", missing_field)]
96    IncompleteOuterHeader { missing_field: String },
97    #[error("Invalid inner header entry: {}", entry_type)]
98    InvalidInnerHeaderEntry { entry_type: u8 },
99    #[error("Incomplete outer header: Missing {}", missing_field)]
100    IncompleteInnerHeader { missing_field: String },
101
102    #[error(transparent)]
103    Cryptography(#[from] CryptographyError),
104    #[error(transparent)]
105    Xml(#[from] XmlParseError),
106    #[error(transparent)]
107    OuterCipher(#[from] OuterCipherConfigError),
108    #[error(transparent)]
109    InnerCipher(#[from] InnerCipherConfigError),
110    #[error(transparent)]
111    Compression(#[from] CompressionConfigError),
112    #[error(transparent)]
113    BlockStream(#[from] BlockStreamError),
114    #[error(transparent)]
115    VariantDictionary(#[from] VariantDictionaryError),
116    #[error(transparent)]
117    KdfSettings(#[from] KdfConfigError),
118    #[error(transparent)]
119    Io(#[from] std::io::Error),
120}
121
122impl Database {
123    /// Parse a database from a `std::io::Read`
124    pub fn open(source: &mut dyn std::io::Read, key: DatabaseKey) -> Result<Database, DatabaseOpenError> {
125        let mut data = Vec::new();
126        source.read_to_end(&mut data)?;
127
128        Database::parse(data.as_ref(), key)
129    }
130
131    pub fn parse(data: &[u8], key: DatabaseKey) -> Result<Database, DatabaseOpenError> {
132        let database_version = DatabaseVersion::parse(data)?;
133
134        match database_version {
135            DatabaseVersion::KDB(_) => parse_kdb(data, &key),
136            DatabaseVersion::KDB2(_) => Err(DatabaseOpenError::UnsupportedVersion),
137            DatabaseVersion::KDB3(_) => parse_kdbx3(data, &key),
138            DatabaseVersion::KDB4(_) => parse_kdbx4(data, &key),
139        }
140    }
141
142    /// Helper function to load a database into its internal XML chunks
143    pub fn get_xml(source: &mut dyn std::io::Read, key: DatabaseKey) -> Result<Vec<u8>, DatabaseOpenError> {
144        let mut data = Vec::new();
145        source.read_to_end(&mut data)?;
146
147        let database_version = DatabaseVersion::parse(data.as_ref())?;
148
149        let data = match database_version {
150            DatabaseVersion::KDB(_) => return Err(DatabaseOpenError::UnsupportedVersion),
151            DatabaseVersion::KDB2(_) => return Err(DatabaseOpenError::UnsupportedVersion),
152            DatabaseVersion::KDB3(_) => decrypt_kdbx3(data.as_ref(), &key)?.2,
153            DatabaseVersion::KDB4(_) => decrypt_kdbx4(data.as_ref(), &key)?.3,
154        };
155
156        Ok(data)
157    }
158
159    /// Get the version of a database without decrypting it
160    pub fn get_version(source: &mut dyn std::io::Read) -> Result<DatabaseVersion, DatabaseIntegrityError> {
161        let mut data = vec![0; DatabaseVersion::get_version_header_size()];
162        _ = source.read(&mut data)?;
163        Ok(DatabaseVersion::parse(data.as_ref())?)
164    }
165}