Skip to main content

keepass/db/
open.rs

1use thiserror::Error;
2
3use crate::{
4    config::DatabaseVersion,
5    db::Database,
6    format::{
7        kdb::parse_kdb,
8        kdbx3::{decrypt_kdbx3, parse_kdbx3},
9        kdbx4::{decrypt_kdbx4, parse_kdbx4},
10        DatabaseVersionParseError,
11    },
12    DatabaseKey,
13};
14
15impl Database {
16    /// Parse a database from a std::io::Read
17    pub fn open(source: &mut dyn std::io::Read, key: DatabaseKey) -> Result<Database, DatabaseOpenError> {
18        let mut data = Vec::new();
19        source.read_to_end(&mut data)?;
20
21        Database::parse(data.as_ref(), key)
22    }
23
24    /// Parse a database from a byte slice
25    pub fn parse(data: &[u8], key: DatabaseKey) -> Result<Database, DatabaseOpenError> {
26        let database_version = DatabaseVersion::parse(data)?;
27
28        match database_version {
29            DatabaseVersion::KDB(_) => parse_kdb(data, &key),
30            DatabaseVersion::KDB2(_) => Err(DatabaseOpenError::UnsupportedVersion),
31            DatabaseVersion::KDB3(_) => parse_kdbx3(data, &key),
32            DatabaseVersion::KDB4(_) => parse_kdbx4(data, &key),
33        }
34    }
35
36    /// Helper function to load a database into its internal XML chunks
37    pub fn get_xml(source: &mut dyn std::io::Read, key: DatabaseKey) -> Result<Vec<u8>, DatabaseOpenError> {
38        let mut data = Vec::new();
39        source.read_to_end(&mut data)?;
40
41        let database_version = DatabaseVersion::parse(data.as_ref())?;
42
43        let data = match database_version {
44            DatabaseVersion::KDB(_) => return Err(DatabaseOpenError::UnsupportedVersion),
45            DatabaseVersion::KDB2(_) => return Err(DatabaseOpenError::UnsupportedVersion),
46            DatabaseVersion::KDB3(_) => decrypt_kdbx3(data.as_ref(), &key)?.2,
47            DatabaseVersion::KDB4(_) => decrypt_kdbx4(data.as_ref(), &key)?.3,
48        };
49
50        Ok(data)
51    }
52
53    /// Get the version of a database without decrypting it
54    pub fn get_version(source: &mut dyn std::io::Read) -> Result<DatabaseVersion, DatabaseOpenError> {
55        let mut data = vec![0; DatabaseVersion::get_version_header_size()];
56        source.read_exact(&mut data)?;
57        let version = DatabaseVersion::parse(data.as_ref())?;
58        Ok(version)
59    }
60}
61
62/// Errors that can occur when opening a database
63#[derive(Debug, Error)]
64#[non_exhaustive]
65pub enum DatabaseOpenError {
66    /// I/O errors that can occur while reading the database from the source
67    #[error(transparent)]
68    Io(#[from] std::io::Error),
69
70    /// An unexpected end of file was encountered while reading the database
71    #[error("Unexpected end of file")]
72    UnexpectedEof,
73
74    /// Errors related to parsing the database version from the file header
75    #[error(transparent)]
76    VersionParse(#[from] DatabaseVersionParseError),
77
78    /// Attempted to open a database with an unsupported version
79    #[error("Unsupported database version")]
80    UnsupportedVersion,
81
82    /// Errors related to the database key, such as incorrect keys
83    #[error(transparent)]
84    Key(#[from] crate::key::DatabaseKeyError),
85
86    /// Errors related to decryption
87    #[error(transparent)]
88    Cryptography(#[from] crate::crypt::CryptographyError),
89
90    /// Errors related to parsing the database format
91    #[error(transparent)]
92    Format(#[from] DatabaseFormatError),
93}
94
95/// Format-specific database parsing errors
96#[derive(Debug, Error)]
97#[non_exhaustive]
98pub enum DatabaseFormatError {
99    /// Errors related to parsing KDB files
100    #[error(transparent)]
101    Kdb(#[from] crate::format::kdb::KdbOpenError),
102
103    /// Errors related to parsing KDBX3 files
104    #[error(transparent)]
105    Kdbx3(#[from] crate::format::kdbx3::Kdbx3OpenError),
106
107    /// Errors related to parsing KDBX4 files
108    #[error(transparent)]
109    Kdbx4(#[from] crate::format::kdbx4::Kdbx4OpenError),
110}