1use super::errors::HeaderError as Error;
2use super::header_fields;
3use super::variant_dict;
4use crate::crypto;
5use crate::utils;
6use rand::{rngs::OsRng, RngCore};
7use sha2::{Digest, Sha256};
8use std::convert::TryInto;
9use std::io::{Read, Write};
10use std::marker::PhantomData;
11use uuid::Uuid;
12
13type Result<T> = std::result::Result<T, Error>;
14
15pub trait HeaderId: From<u8> + Into<u8> {
16 fn is_final(&self) -> bool;
17}
18
19#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
20pub enum OuterHeaderId {
22 EndOfHeader,
24 Comment,
26 CipherId,
28 CompressionFlags,
30 MasterSeed,
32 LegacyTransformSeed,
34 LegacyTransformRounds,
36 EncryptionIv,
38 ProtectedStreamKey,
40 StreamStartBytes,
42 InnerRandomStreamId,
44 KdfParameters,
46 PublicCustomData,
48 Unknown(u8),
50}
51
52impl From<u8> for OuterHeaderId {
53 fn from(id: u8) -> OuterHeaderId {
54 match id {
55 0 => OuterHeaderId::EndOfHeader,
56 0x1 => OuterHeaderId::Comment,
57 0x2 => OuterHeaderId::CipherId,
58 0x3 => OuterHeaderId::CompressionFlags,
59 0x4 => OuterHeaderId::MasterSeed,
60 0x5 => OuterHeaderId::LegacyTransformSeed,
61 0x6 => OuterHeaderId::LegacyTransformRounds,
62 0x7 => OuterHeaderId::EncryptionIv,
63 0x8 => OuterHeaderId::ProtectedStreamKey,
64 0x9 => OuterHeaderId::StreamStartBytes,
65 0xA => OuterHeaderId::InnerRandomStreamId,
66 0xB => OuterHeaderId::KdfParameters,
67 0xC => OuterHeaderId::PublicCustomData,
68 x => OuterHeaderId::Unknown(x),
69 }
70 }
71}
72
73impl From<OuterHeaderId> for u8 {
74 fn from(id: OuterHeaderId) -> u8 {
75 match id {
76 OuterHeaderId::EndOfHeader => 0,
77 OuterHeaderId::Comment => 0x1,
78 OuterHeaderId::CipherId => 0x2,
79 OuterHeaderId::CompressionFlags => 0x3,
80 OuterHeaderId::MasterSeed => 0x4,
81 OuterHeaderId::LegacyTransformSeed => 0x5,
82 OuterHeaderId::LegacyTransformRounds => 0x6,
83 OuterHeaderId::EncryptionIv => 0x7,
84 OuterHeaderId::ProtectedStreamKey => 0x8,
85 OuterHeaderId::StreamStartBytes => 0x9,
86 OuterHeaderId::InnerRandomStreamId => 0xA,
87 OuterHeaderId::KdfParameters => 0xB,
88 OuterHeaderId::PublicCustomData => 0xC,
89 OuterHeaderId::Unknown(x) => x,
90 }
91 }
92}
93
94impl HeaderId for OuterHeaderId {
95 fn is_final(&self) -> bool {
96 *self == OuterHeaderId::EndOfHeader
97 }
98}
99
100#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
101pub enum InnerHeaderId {
103 EndOfHeader,
105 InnerRandomStreamCipherId,
107 InnerRandomStreamKey,
109 Binary,
111 Unknown(u8),
113}
114
115impl From<u8> for InnerHeaderId {
116 fn from(id: u8) -> InnerHeaderId {
117 match id {
118 0 => InnerHeaderId::EndOfHeader,
119 1 => InnerHeaderId::InnerRandomStreamCipherId,
120 2 => InnerHeaderId::InnerRandomStreamKey,
121 3 => InnerHeaderId::Binary,
122 x => InnerHeaderId::Unknown(x),
123 }
124 }
125}
126
127impl From<InnerHeaderId> for u8 {
128 fn from(id: InnerHeaderId) -> u8 {
129 match id {
130 InnerHeaderId::EndOfHeader => 0,
131 InnerHeaderId::InnerRandomStreamCipherId => 1,
132 InnerHeaderId::InnerRandomStreamKey => 2,
133 InnerHeaderId::Binary => 3,
134 InnerHeaderId::Unknown(x) => x,
135 }
136 }
137}
138
139impl HeaderId for InnerHeaderId {
140 fn is_final(&self) -> bool {
141 *self == InnerHeaderId::EndOfHeader
142 }
143}
144
145#[derive(Clone, Debug, PartialEq, Eq)]
146pub struct HeaderField<T> {
147 ty: T,
148 data: Vec<u8>,
149}
150
151impl<T> HeaderField<T> {
152 pub(crate) fn new(ty: T, data: Vec<u8>) -> HeaderField<T> {
153 HeaderField { ty, data }
154 }
155}
156pub struct HeaderParser<'a, R: Read + 'a, T: HeaderId> {
157 _id: PhantomData<T>,
158 reader: &'a mut R,
159}
160
161impl<'a, R, T> HeaderParser<'a, R, T>
162where
163 R: Read + 'a,
164 T: HeaderId,
165{
166 pub(crate) fn new(reader: &'a mut R) -> HeaderParser<'a, R, T> {
167 HeaderParser {
168 _id: PhantomData,
169 reader,
170 }
171 }
172
173 pub(crate) fn read_one_header(&mut self, major_version: u16) -> Result<HeaderField<T>> {
174 let mut ty_buffer = [0u8];
175 self.reader.read_exact(&mut ty_buffer)?;
176 let ty = T::from(ty_buffer[0]);
177
178 let len = if major_version >= 4 {
179 let mut len_buffer = [0u8; 4];
180 self.reader.read_exact(&mut len_buffer)?;
181 u32::from_le_bytes(len_buffer)
182 } else {
183 let mut len_buffer = [0u8; 2];
184 self.reader.read_exact(&mut len_buffer)?;
185 u16::from_le_bytes(len_buffer) as u32
186 };
187 let mut header_buffer = utils::buffer(len as usize);
188 self.reader.read_exact(&mut header_buffer)?;
189
190 Ok(HeaderField {
191 ty,
192 data: header_buffer,
193 })
194 }
195
196 pub(crate) fn read_all_headers(&mut self, major_version: u16) -> Result<Vec<HeaderField<T>>> {
197 let mut headers = Vec::new();
198 let mut header = self.read_one_header(major_version)?;
199 while !header.ty.is_final() {
200 headers.push(header);
201 header = self.read_one_header(major_version)?;
202 }
203
204 Ok(headers)
205 }
206}
207
208#[derive(Default)]
209pub struct KdbxHeaderBuilder {
210 pub cipher: Option<header_fields::Cipher>,
211 pub kdf_params: Option<header_fields::KdfParams>,
212 pub compression_type: Option<header_fields::CompressionType>,
213 pub stream_start_bytes: Option<Vec<u8>>,
214 pub other_headers: Vec<HeaderField<OuterHeaderId>>,
215 pub master_seed: Option<Vec<u8>>,
216 pub encryption_iv: Option<Vec<u8>>,
217}
218
219impl KdbxHeaderBuilder {
220 fn add_header(&mut self, header: HeaderField<OuterHeaderId>) -> Result<()> {
221 match header.ty {
222 OuterHeaderId::CipherId => {
223 let cipher = Uuid::from_slice(&header.data)
224 .map(From::from)
225 .map_err(|_e| {
226 Error::MalformedField(header.ty, "Cipher UUID not valid".into())
227 })?;
228
229 self.cipher = Some(cipher);
230 }
231 OuterHeaderId::StreamStartBytes => {
232 self.stream_start_bytes = Some(header.data);
233 }
234 OuterHeaderId::KdfParameters => {
235 self.kdf_params = match variant_dict::parse_variant_dict(&*header.data) {
236 Ok(vdict) => Some(vdict.try_into()?),
237 Err(e) => {
238 println!("Malformed field: {}", e);
239 return Err(Error::MalformedField(
240 OuterHeaderId::KdfParameters,
241 "Corrupt variant dictionary".into(),
242 ));
243 }
244 };
245 }
246 OuterHeaderId::CompressionFlags => {
247 if header.data.len() != 4 {
248 return Err(Error::MalformedField(
249 OuterHeaderId::CompressionFlags,
250 "Wrong size for compression ID".into(),
251 ));
252 }
253 self.compression_type =
254 Some(header_fields::CompressionType::from(u32::from_le_bytes([
255 header.data[0],
256 header.data[1],
257 header.data[2],
258 header.data[3],
259 ])))
260 }
261 OuterHeaderId::EncryptionIv => self.encryption_iv = Some(header.data),
262 OuterHeaderId::MasterSeed => self.master_seed = Some(header.data),
263 _ => self.other_headers.push(header),
264 }
265
266 Ok(())
267 }
268
269 fn get_kdf_params(&mut self) -> Option<header_fields::KdfParams> {
270 if self.kdf_params.is_some() {
271 self.kdf_params.take()
272 } else {
273 let rounds = self
274 .other_headers
275 .iter()
276 .find(|h| h.ty == OuterHeaderId::LegacyTransformRounds)
277 .map(|h| {
278 let mut buf = [0u8; 8];
279 buf.clone_from_slice(&h.data[0..8]);
280 u64::from_le_bytes(buf)
281 });
282 let seed = self
283 .other_headers
284 .iter()
285 .find(|h| h.ty == OuterHeaderId::LegacyTransformSeed)
286 .map(|h| h.data.clone());
287
288 match (rounds, seed) {
289 (Some(r), Some(s)) => Some(header_fields::KdfParams::Aes { rounds: r, salt: s }),
290 _ => None,
291 }
292 }
293 }
294
295 fn build(mut self) -> Result<KdbxHeader> {
296 let kdf_params = self.get_kdf_params();
297 Ok(KdbxHeader {
298 cipher: self
299 .cipher
300 .ok_or(Error::MissingRequiredField(OuterHeaderId::CipherId))?,
301 compression_type: self
302 .compression_type
303 .ok_or(Error::MissingRequiredField(OuterHeaderId::CompressionFlags))?,
304 master_seed: self
305 .master_seed
306 .ok_or(Error::MissingRequiredField(OuterHeaderId::MasterSeed))?,
307 encryption_iv: self
308 .encryption_iv
309 .ok_or(Error::MissingRequiredField(OuterHeaderId::EncryptionIv))?,
310 kdf_params: kdf_params
311 .ok_or(Error::MissingRequiredField(OuterHeaderId::KdfParameters))?,
312 stream_start_bytes: self.stream_start_bytes,
313 other_headers: self.other_headers,
314 })
315 }
316}
317
318#[derive(Debug, PartialEq, Eq)]
319pub struct KdbxHeader {
325 pub cipher: header_fields::Cipher,
327 pub kdf_params: header_fields::KdfParams,
329 pub compression_type: header_fields::CompressionType,
331 pub stream_start_bytes: Option<Vec<u8>>,
333 pub other_headers: Vec<HeaderField<OuterHeaderId>>,
335 pub master_seed: Vec<u8>,
337 pub encryption_iv: Vec<u8>,
339}
340
341impl KdbxHeader {
342 pub fn from_os_random() -> KdbxHeader {
355 let mut master_seed = vec![0u8; 32];
356 let mut encryption_iv = vec![0u8; 16];
357 let mut cipher_salt = vec![0u8; 32];
358 OsRng.fill_bytes(&mut master_seed);
359 OsRng.fill_bytes(&mut encryption_iv);
360 OsRng.fill_bytes(&mut cipher_salt);
361 KdbxHeader {
362 cipher: header_fields::Cipher::Aes256,
363 kdf_params: header_fields::KdfParams::Argon2 {
364 variant: argon2::Variant::Argon2d,
365 iterations: 10,
366 memory_bytes: 0xFFFF * 1024,
367 salt: cipher_salt,
368 version: 19,
369 lanes: 2,
370 },
371 other_headers: Vec::new(),
372 compression_type: super::CompressionType::None,
373 stream_start_bytes: None,
374 master_seed,
375 encryption_iv,
376 }
377 }
378
379 pub(crate) fn read<R: Read>(
380 mut caching_reader: utils::CachingReader<R>,
381 major_version: u16,
382 ) -> Result<(KdbxHeader, Vec<u8>)> {
383 let mut header_builder = KdbxHeaderBuilder::default();
384 let headers = HeaderParser::new(&mut caching_reader).read_all_headers(major_version)?;
385 for header in headers {
386 header_builder.add_header(header)?;
387 }
388
389 let (header_bin, input) = caching_reader.into_inner();
390
391 if major_version < 4 {
392 return Ok((header_builder.build()?, header_bin));
393 }
394
395 let mut sha = utils::buffer(Sha256::output_size());
396 input.read_exact(&mut sha)?;
397
398 if crypto::verify_sha256(&header_bin, &sha) {
399 Ok((header_builder.build()?, header_bin))
400 } else {
401 Err(Error::ChecksumFailed)
402 }
403 }
404
405 pub(crate) fn write<W: Write>(&self, mut writer: W) -> std::io::Result<()> {
406 use std::iter::once;
407 let headers = self
408 .other_headers
409 .iter()
410 .cloned()
411 .chain(once(self.cipher.into()))
412 .chain(once(self.compression_type.into()))
413 .chain(once(HeaderField::new(
414 OuterHeaderId::MasterSeed,
415 self.master_seed.clone(),
416 )))
417 .chain(once(HeaderField::new(
418 OuterHeaderId::EncryptionIv,
419 self.encryption_iv.clone(),
420 )))
421 .chain(once(self.kdf_params.clone().into()))
422 .chain(once(HeaderField::new(
423 OuterHeaderId::EndOfHeader,
424 Vec::new(),
425 )));
426
427 for header in headers {
428 writer.write_all(&[header.ty.into()])?;
429 writer.write_all(&(header.data.len() as u32).to_le_bytes())?;
430 writer.write_all(&header.data)?;
431 }
432 Ok(())
433 }
434}
435
436#[derive(Default)]
437pub struct KdbxInnerHeaderBuilder {
438 pub inner_stream_cipher: Option<header_fields::InnerStreamCipherAlgorithm>,
439 pub inner_stream_key: Option<Vec<u8>>,
440 pub other_headers: Vec<HeaderField<InnerHeaderId>>,
442}
443
444impl KdbxInnerHeaderBuilder {
445 fn add_header(&mut self, header: HeaderField<InnerHeaderId>) -> Result<()> {
446 match header.ty {
447 InnerHeaderId::InnerRandomStreamCipherId => {
448 let d = header.data;
449 self.inner_stream_cipher =
450 Some(u32::from_le_bytes([d[0], d[1], d[2], d[3]]).into());
451 }
452 InnerHeaderId::InnerRandomStreamKey => self.inner_stream_key = Some(header.data),
453 _ => self.other_headers.push(header),
454 }
455
456 Ok(())
457 }
458
459 fn build(self) -> Result<KdbxInnerHeader> {
460 Ok(KdbxInnerHeader {
461 inner_stream_cipher: self.inner_stream_cipher.ok_or(
462 Error::MissingRequiredInnerField(InnerHeaderId::InnerRandomStreamCipherId),
463 )?,
464 inner_stream_key: self
465 .inner_stream_key
466 .ok_or(Error::MissingRequiredInnerField(
467 InnerHeaderId::InnerRandomStreamKey,
468 ))?,
469 other_headers: self.other_headers,
470 })
471 }
472}
473
474#[derive(Debug, PartialEq, Eq)]
476pub struct KdbxInnerHeader {
477 pub inner_stream_cipher: header_fields::InnerStreamCipherAlgorithm,
479 pub inner_stream_key: Vec<u8>,
481 pub other_headers: Vec<HeaderField<InnerHeaderId>>,
483}
484
485impl KdbxInnerHeader {
486 pub(crate) fn from_legacy_fields(header: &KdbxHeader) -> Result<KdbxInnerHeader> {
487 let cipher = &header
488 .other_headers
489 .iter()
490 .find(|h| h.ty == OuterHeaderId::InnerRandomStreamId)
491 .ok_or(Error::MissingRequiredField(
492 OuterHeaderId::InnerRandomStreamId,
493 ))?
494 .data;
495 let key = header
496 .other_headers
497 .iter()
498 .find(|h| h.ty == OuterHeaderId::ProtectedStreamKey)
499 .ok_or(Error::MissingRequiredField(
500 OuterHeaderId::ProtectedStreamKey,
501 ))?
502 .data
503 .clone();
504 let mut cipher_id_buf = [0u8; 4];
505 cipher_id_buf.clone_from_slice(&cipher[0..4]);
506 let cipher_id = u32::from_le_bytes(cipher_id_buf);
507
508 Ok(KdbxInnerHeader {
509 inner_stream_cipher: cipher_id.into(),
510 inner_stream_key: key,
511 other_headers: Vec::new(),
512 })
513 }
514
515 pub fn from_os_random() -> KdbxInnerHeader {
526 let inner_stream_cipher = header_fields::InnerStreamCipherAlgorithm::ChaCha20;
527 let mut inner_stream_key = vec![0u8; 44]; OsRng.fill_bytes(&mut inner_stream_key);
529
530 KdbxInnerHeader {
531 inner_stream_cipher,
532 inner_stream_key,
533 other_headers: Vec::new(),
534 }
535 }
536
537 pub(crate) fn read<R: Read>(reader: &mut R, major_version: u16) -> Result<KdbxInnerHeader> {
538 let mut header_builder = KdbxInnerHeaderBuilder::default();
539 let headers = HeaderParser::new(reader).read_all_headers(major_version)?;
540 for header in headers {
541 header_builder.add_header(header)?;
542 }
543
544 header_builder.build()
545 }
546
547 pub(crate) fn write<W: Write>(&self, mut writer: W) -> std::io::Result<()> {
548 use std::iter::once;
549 let headers = self
550 .other_headers
551 .iter()
552 .cloned()
553 .chain(once(self.inner_stream_cipher.into()))
554 .chain(once(HeaderField::new(
555 InnerHeaderId::InnerRandomStreamKey,
556 self.inner_stream_key.clone(),
557 )))
558 .chain(once(HeaderField::new(
559 InnerHeaderId::EndOfHeader,
560 Vec::new(),
561 )));
562
563 for header in headers {
564 writer.write_all(&[header.ty.into()])?;
565 writer.write_all(&(header.data.len() as i32).to_le_bytes())?;
566 writer.write_all(&header.data)?;
567 }
568 Ok(())
569 }
570}