kdbx_rs/xml/
parse.rs

1use super::decoders::{decode_datetime, decode_uuid};
2use crate::database::{
3    Database, Entry, Field, Group, History, MemoryProtection, Meta, Times, Value,
4};
5use base64::prelude::{Engine, BASE64_STANDARD};
6use chrono::NaiveDateTime;
7use cipher::StreamCipher;
8use std::io::Read;
9use thiserror::Error;
10use uuid::Uuid;
11use xml::reader::{EventReader, XmlEvent};
12
13#[derive(Debug, Error)]
14/// Error encountered parsing XML
15pub enum Error {
16    /// Error from the underlying XML parser
17    #[error("Error parsing database XML: {0}")]
18    Xml(String),
19    /// A field has an empty name
20    #[error("A field has an empty name")]
21    KeyEmptyName,
22    /// A UUID field is not valid
23    #[error("UUID is not valid")]
24    InvalidUuid,
25    /// A UUID field is not valid
26    #[error("Datetime is not valid")]
27    InvalidDatetime,
28    /// A numeric field is not valid
29    #[error("Invalid numeric value")]
30    InvalidNumber,
31    /// A string field did not decrypt correctly
32    #[error("Could not decrypt value for Key {0:?}")]
33    DecryptFailed(String),
34}
35
36pub type Result<T> = std::result::Result<T, Error>;
37
38impl From<xml::reader::Error> for Error {
39    fn from(e: xml::reader::Error) -> Error {
40        Error::Xml(e.msg().to_string())
41    }
42}
43
44fn parse_string<R: Read>(xml_event_reader: &mut EventReader<R>) -> Result<Option<String>> {
45    let mut content = None;
46    loop {
47        match xml_event_reader.next()? {
48            XmlEvent::Characters(chardata) => {
49                content = content.map_or_else(
50                    || Some(chardata.clone()),
51                    |mut existing: String| {
52                        existing.push_str(&chardata);
53                        Some(existing)
54                    },
55                )
56            }
57            XmlEvent::EndElement { .. } => break,
58            _ => {}
59        }
60    }
61    Ok(content)
62}
63
64macro_rules! parse_numeric_type {
65    ($name:ident, $ty:ty) => {
66        fn $name<R: Read>(xml_event_reader: &mut EventReader<R>) -> Result<Option<$ty>> {
67            parse_string(xml_event_reader)?
68                .map(|num| num.parse())
69                .transpose()
70                .map_err(|_| Error::InvalidNumber)
71        }
72    };
73}
74
75parse_numeric_type!(parse_u32, u32);
76
77fn parse_uuid<R: Read>(xml_event_reader: &mut EventReader<R>) -> Result<Uuid> {
78    parse_string(xml_event_reader)?
79        .and_then(|uuid| decode_uuid(&uuid))
80        .ok_or(Error::InvalidUuid)
81}
82
83fn parse_datetime<R: Read>(xml_event_reader: &mut EventReader<R>) -> Result<NaiveDateTime> {
84    parse_string(xml_event_reader)?
85        .and_then(|dt| decode_datetime(&dt))
86        .ok_or(Error::InvalidDatetime)
87}
88
89fn parse_bool<R: Read>(xml_event_reader: &mut EventReader<R>) -> Result<bool> {
90    Ok(parse_string(xml_event_reader)?
91        .map(|b| b.to_lowercase() == "true")
92        .unwrap_or_default())
93}
94
95fn parse_field<R: Read, S: StreamCipher + ?Sized>(
96    xml_event_reader: &mut EventReader<R>,
97    tag_name: &str,
98    stream_cipher: &mut S,
99) -> Result<Field> {
100    let mut field = Field::default();
101    loop {
102        match xml_event_reader.next()? {
103            XmlEvent::StartElement { name, .. } if &name.local_name == "Key" => {
104                let parse_result = parse_string(xml_event_reader)?;
105                let val = parse_result.ok_or(Error::KeyEmptyName)?;
106                field.key = val;
107            }
108            XmlEvent::StartElement {
109                name, attributes, ..
110            } if &name.local_name == "Value" => {
111                let protected = attributes.iter().any(|attr| {
112                    attr.name.local_name == "Protected" && attr.value.to_lowercase() == "true"
113                });
114                field.value = if let Some(contents) = parse_string(xml_event_reader)? {
115                    if protected {
116                        // Would be nice to avoid the clone but it gets moved into the map_err closure
117                        let key_clone = field.key.clone();
118                        match BASE64_STANDARD.decode(&contents) {
119                            Ok(mut decoded) => {
120                                stream_cipher
121                                    .try_apply_keystream(decoded.as_mut())
122                                    .map_err(|e| {
123                                        Error::DecryptFailed(format!(
124                                            "Failed to apply stream cipher: {}",
125                                            e
126                                        ))
127                                    })?;
128                                let to_str = String::from_utf8(decoded)
129                                    .map_err(|_| Error::DecryptFailed(key_clone))?;
130                                Value::Protected(to_str)
131                            }
132                            Err(_) => return Err(Error::DecryptFailed(key_clone)),
133                        }
134                    } else {
135                        Value::Standard(contents)
136                    }
137                } else {
138                    Value::Empty
139                }
140            }
141            XmlEvent::EndElement { name, .. } if name.local_name == tag_name => break,
142            _ => {}
143        }
144    }
145    Ok(field)
146}
147
148fn parse_history<R: Read, S: StreamCipher + ?Sized>(
149    xml_event_reader: &mut EventReader<R>,
150    stream_cipher: &mut S,
151) -> Result<History> {
152    let mut history = History::default();
153    loop {
154        match xml_event_reader.next()? {
155            XmlEvent::StartElement { name, .. } if &name.local_name == "Entry" => {
156                history.push(parse_entry(xml_event_reader, stream_cipher)?);
157            }
158            XmlEvent::EndElement { name, .. } if &name.local_name == "History" => break,
159            _ => {}
160        }
161    }
162    Ok(history)
163}
164
165fn parse_times<R: Read>(xml_event_reader: &mut EventReader<R>) -> Result<Times> {
166    let mut times = Times::default();
167    loop {
168        match xml_event_reader.next()? {
169            XmlEvent::StartElement { name, .. } => {
170                if &name.local_name == "LastModificationTime" {
171                    times.last_modification_time = parse_datetime(xml_event_reader)?;
172                } else if &name.local_name == "LastAccessTime" {
173                    times.last_access_time = parse_datetime(xml_event_reader)?;
174                } else if &name.local_name == "CreationTime" {
175                    times.creation_time = parse_datetime(xml_event_reader)?;
176                } else if &name.local_name == "ExpiryTime" {
177                    times.expiry_time = parse_datetime(xml_event_reader)?;
178                } else if &name.local_name == "LocationChanged" {
179                    times.location_changed = parse_datetime(xml_event_reader)?;
180                } else if &name.local_name == "Expires" {
181                    times.expires = parse_bool(xml_event_reader)?;
182                } else if &name.local_name == "UsageCount" {
183                    times.usage_count = parse_u32(xml_event_reader)?.unwrap_or_default();
184                }
185            }
186            XmlEvent::EndElement { name, .. } if &name.local_name == "Times" => break,
187            _ => {}
188        }
189    }
190    Ok(times)
191}
192
193fn parse_entry<R: Read, S: StreamCipher + ?Sized>(
194    xml_event_reader: &mut EventReader<R>,
195    stream_cipher: &mut S,
196) -> Result<Entry> {
197    let mut entry = Entry::default();
198    loop {
199        match xml_event_reader.next()? {
200            XmlEvent::StartElement { name, .. } => {
201                if &name.local_name == "History" {
202                    entry.history = parse_history(xml_event_reader, stream_cipher)?;
203                } else if &name.local_name == "String" {
204                    entry.add_field(parse_field(xml_event_reader, "String", stream_cipher)?);
205                } else if &name.local_name == "UUID" {
206                    entry.set_uuid(parse_uuid(xml_event_reader)?);
207                } else if &name.local_name == "Times" {
208                    entry.times = parse_times(xml_event_reader)?;
209                }
210            }
211            XmlEvent::EndElement { name, .. } if &name.local_name == "Entry" => break,
212            _ => {}
213        }
214    }
215    Ok(entry)
216}
217
218fn parse_group<R: Read, S: StreamCipher + ?Sized>(
219    xml_event_reader: &mut EventReader<R>,
220    stream_cipher: &mut S,
221) -> Result<Group> {
222    let mut group = Group::default();
223    loop {
224        match xml_event_reader.next()? {
225            XmlEvent::StartElement { name, .. } => {
226                if &name.local_name == "Group" {
227                    group.add_group(parse_group(xml_event_reader, stream_cipher)?);
228                } else if &name.local_name == "Entry" {
229                    group.add_entry(parse_entry(xml_event_reader, stream_cipher)?);
230                } else if &name.local_name == "UUID" {
231                    group.set_uuid(parse_uuid(xml_event_reader)?);
232                } else if &name.local_name == "Name" {
233                    group.set_name(parse_string(xml_event_reader)?.unwrap_or_default());
234                } else if &name.local_name == "Times" {
235                    group.times = parse_times(xml_event_reader)?;
236                }
237            }
238            XmlEvent::EndElement { name, .. } if &name.local_name == "Group" => break,
239            _ => {}
240        }
241    }
242    Ok(group)
243}
244
245fn parse_root<R: Read, S: StreamCipher + ?Sized>(
246    xml_event_reader: &mut EventReader<R>,
247    stream_cipher: &mut S,
248) -> Result<Vec<Group>> {
249    let mut groups = Vec::new();
250    loop {
251        match xml_event_reader.next()? {
252            XmlEvent::StartElement { name, .. } if &name.local_name == "Group" => {
253                groups.push(parse_group(xml_event_reader, stream_cipher)?);
254            }
255            XmlEvent::EndElement { name, .. } if &name.local_name == "Root" => break,
256            _ => {}
257        }
258    }
259    Ok(groups)
260}
261
262fn parse_custom_data<R: Read, S: StreamCipher + ?Sized>(
263    xml_event_reader: &mut EventReader<R>,
264    stream_cipher: &mut S,
265) -> Result<Vec<Field>> {
266    let mut fields = Vec::new();
267    loop {
268        match xml_event_reader.next()? {
269            XmlEvent::StartElement { name, .. } if &name.local_name == "Item" => {
270                fields.push(parse_field(xml_event_reader, "Item", stream_cipher)?);
271            }
272            XmlEvent::EndElement { name, .. } if &name.local_name == "CustomData" => break,
273            _ => {}
274        }
275    }
276    Ok(fields)
277}
278
279fn parse_memory_protection<R: Read>(
280    xml_event_reader: &mut EventReader<R>,
281) -> Result<MemoryProtection> {
282    let mut protection = MemoryProtection::default();
283    loop {
284        match xml_event_reader.next()? {
285            XmlEvent::StartElement { name, .. } => match name.local_name.as_ref() {
286                "ProtectTitle" => {
287                    protection.protect_title = parse_bool(xml_event_reader)?;
288                }
289                "ProtectUserName" => {
290                    protection.protect_user_name = parse_bool(xml_event_reader)?;
291                }
292                "ProtectPassword" => {
293                    protection.protect_password = parse_bool(xml_event_reader)?;
294                }
295                "ProtectURL" => {
296                    protection.protect_url = parse_bool(xml_event_reader)?;
297                }
298                "ProtectNotes" => {
299                    protection.protect_notes = parse_bool(xml_event_reader)?;
300                }
301                _ => {}
302            },
303            XmlEvent::EndElement { name, .. } if &name.local_name == "MemoryProtection" => break,
304            _ => {}
305        }
306    }
307    Ok(protection)
308}
309
310fn parse_meta<R: Read, S: StreamCipher + ?Sized>(
311    xml_event_reader: &mut EventReader<R>,
312    stream_cipher: &mut S,
313) -> Result<Meta> {
314    let mut meta = Meta::default();
315    loop {
316        match xml_event_reader.next()? {
317            XmlEvent::StartElement { name, .. } => match name.local_name.as_ref() {
318                "Generator" => {
319                    meta.generator = parse_string(xml_event_reader)?.unwrap_or_default();
320                }
321                "DatabaseName" => {
322                    meta.database_name = parse_string(xml_event_reader)?.unwrap_or_default();
323                }
324                "DatabaseDescription" => {
325                    meta.database_description = parse_string(xml_event_reader)?.unwrap_or_default();
326                }
327                "CustomData" => {
328                    meta.custom_data = parse_custom_data(xml_event_reader, stream_cipher)?;
329                }
330                "MemoryProtection" => {
331                    meta.memory_protection = parse_memory_protection(xml_event_reader)?;
332                }
333                _ => {}
334            },
335            XmlEvent::EndElement { name, .. } if &name.local_name == "Meta" => break,
336            _ => {}
337        }
338    }
339    Ok(meta)
340}
341
342fn parse_file<R: Read, S: StreamCipher + ?Sized>(
343    xml_event_reader: &mut EventReader<R>,
344    stream_cipher: &mut S,
345) -> Result<Database> {
346    let mut db = Database::default();
347    loop {
348        match xml_event_reader.next()? {
349            XmlEvent::StartElement { name, .. } if &name.local_name == "Root" => {
350                db.groups = parse_root(xml_event_reader, stream_cipher)?;
351            }
352            XmlEvent::StartElement { name, .. } if &name.local_name == "Meta" => {
353                db.meta = parse_meta(xml_event_reader, stream_cipher)?;
354            }
355            XmlEvent::EndElement { name, .. } if &name.local_name == "KeePassFile" => break,
356            _ => {}
357        }
358    }
359    Ok(db)
360}
361
362/// Parse decrypted XML into a database
363///
364/// If you need to obtain a stream cipher, consider using
365/// [`InnerStreamCipherAlgorithm::stream_cipher`][crate::binary::InnerStreamCipherAlgorithm#stream_cipher]
366/// if the XML contains encrypted data, or [`utils::NullStreamCipher`][crate::utils::NullStreamCipher]
367/// if it does not (such as an export from the official client).
368pub fn parse_xml<R: Read, S: StreamCipher + ?Sized>(
369    xml_data: R,
370    stream_cipher: &mut S,
371) -> Result<Database> {
372    let xml_config = xml::ParserConfig::new()
373        .trim_whitespace(true)
374        .cdata_to_characters(true);
375    let mut xml_event_reader = EventReader::new_with_config(xml_data, xml_config);
376    parse_file(&mut xml_event_reader, stream_cipher)
377}