1use crate::crypto::{self, AuthKey};
19use crate::kdf;
20use crate::qt::{QReader, QWriter};
21use crate::tdata::{FillRandom, random_bytes};
22use crate::{Error, KeyDataFailure, Result};
23
24pub const DEFAULT_KEY_FILE: &str = "data";
26
27pub const KEY_DATA_MAGIC: u32 = 0x4B44_3200;
29pub const KEY_DATA_FORMAT_VERSION: u32 = 2;
31pub const MAX_WRAP_COUNT: u32 = 2;
33pub const MAX_NEWER_FORMAT_WRAP_COUNT: u32 = 16;
36pub const LOCAL_ENCRYPT_SALT_SIZE: usize = 32;
38
39pub const KDF_PBKDF2: u32 = 0;
41pub const KDF_ARGON2ID: u32 = 1;
43pub const KDF_SCRYPT: u32 = 2;
45
46pub const ARGON2_MEMORY_KIB: u32 = 65_536;
48pub const ARGON2_TIME: u32 = 8;
50pub const ARGON2_LANES: u32 = 1;
52pub const DEFAULT_WRAP_GENERATION: u32 = 1;
54
55#[must_use]
57pub fn file_name(key_file: &str) -> String {
58 format!("key_{key_file}")
59}
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq)]
66pub struct PasscodeKdf {
67 pub kind: u32,
69 pub generation: u32,
71 pub memory: u32,
73 pub time: u32,
75 pub parallelism: u32,
77}
78
79impl PasscodeKdf {
80 #[must_use]
82 pub fn valid(&self) -> bool {
83 if self.time == 0 || self.parallelism == 0 {
84 return false;
85 }
86 match self.kind {
87 KDF_ARGON2ID => u64::from(self.memory) >= 8 * u64::from(self.parallelism),
88 KDF_SCRYPT => self.memory >= 2 && self.memory.is_power_of_two(),
89 _ => false,
90 }
91 }
92
93 #[must_use]
96 pub fn cost_within_limits(&self) -> bool {
97 match self.kind {
98 KDF_ARGON2ID => {
99 self.memory <= kdf::ARGON2_MAX_MEMORY_KIB
100 && self.time <= kdf::ARGON2_MAX_TIME
101 && self.parallelism <= kdf::ARGON2_MAX_LANES
102 }
103 KDF_SCRYPT => {
105 self.time <= kdf::SCRYPT_MAX_R
106 && self.parallelism <= kdf::SCRYPT_MAX_P
107 && self.memory <= kdf::SCRYPT_MAX_N
108 }
109 _ => true,
110 }
111 }
112}
113
114#[must_use]
118pub fn default_passcode_kdf() -> PasscodeKdf {
119 PasscodeKdf {
120 kind: KDF_ARGON2ID,
121 generation: DEFAULT_WRAP_GENERATION,
122 memory: ARGON2_MEMORY_KIB,
123 time: ARGON2_TIME,
124 parallelism: ARGON2_LANES,
125 }
126}
127
128#[derive(Debug, Clone, PartialEq, Eq)]
130pub struct PasscodeWrap {
131 pub kdf: PasscodeKdf,
133 pub salt: Vec<u8>,
136 pub key_encrypted: Vec<u8>,
138}
139
140#[derive(Debug, Clone, PartialEq, Eq)]
142pub struct KeyDataTrailer {
143 pub committed: u32,
145 pub wraps: Vec<PasscodeWrap>,
147}
148
149impl KeyDataTrailer {
150 pub fn parse(blob: &[u8]) -> Result<Self> {
162 let mut reader = QReader::new(blob);
163 let [magic, format, committed, count] = streamed(
164 (|| {
165 Ok([
166 reader.read_u32()?,
167 reader.read_u32()?,
168 reader.read_u32()?,
169 reader.read_u32()?,
170 ])
171 })(),
172 TRAILER_HEADER_STREAM_FAILED,
173 )?;
174 if magic != KEY_DATA_MAGIC {
175 return Err(invalid(KeyDataFailure::BadTrailerMagic));
176 }
177 let limit = if format > KEY_DATA_FORMAT_VERSION {
178 MAX_NEWER_FORMAT_WRAP_COUNT
179 } else {
180 MAX_WRAP_COUNT
181 };
182 if count > limit {
183 return Err(invalid(KeyDataFailure::BadWrapCount { count }));
184 }
185
186 let mut wraps: Vec<PasscodeWrap> = Vec::new();
187 for _ in 0..count {
188 let (kdf, salt, key_encrypted) = streamed(
189 (|| {
190 let kdf = PasscodeKdf {
191 kind: reader.read_u32()?,
192 generation: reader.read_u32()?,
193 memory: reader.read_u32()?,
194 time: reader.read_u32()?,
195 parallelism: reader.read_u32()?,
196 };
197 let salt = reader.read_bytearray()?.unwrap_or_default().to_vec();
198 let key_encrypted = reader.read_bytearray()?.unwrap_or_default().to_vec();
199 Ok((kdf, salt, key_encrypted))
200 })(),
201 WRAP_STREAM_FAILED,
202 )?;
203 if kdf.cost_within_limits() && !kdf.valid() {
206 return Err(invalid(KeyDataFailure::BadKdfFamily { kind: kdf.kind }));
207 }
208 if salt.len() < kdf::PASSCODE_SALT_MIN_SIZE {
209 return Err(invalid(KeyDataFailure::BadWrapSaltSize {
210 size: salt.len(),
211 }));
212 }
213 if wraps.iter().any(|w| w.kdf.generation == kdf.generation) {
214 return Err(invalid(KeyDataFailure::DuplicateWrapGeneration {
215 generation: kdf.generation,
216 }));
217 }
218 wraps.push(PasscodeWrap {
219 kdf,
220 salt,
221 key_encrypted,
222 });
223 }
224 Ok(Self { committed, wraps })
225 }
226
227 pub fn encode(&self) -> Result<Vec<u8>> {
233 let count = u32::try_from(self.wraps.len())
234 .map_err(|_| Error::QDataStreamFailed { context: None })?;
235 let mut writer = QWriter::new();
236 writer.write_u32(KEY_DATA_MAGIC);
237 writer.write_u32(KEY_DATA_FORMAT_VERSION);
238 writer.write_u32(self.committed);
239 writer.write_u32(count);
240 for wrap in &self.wraps {
241 writer.write_u32(wrap.kdf.kind);
242 writer.write_u32(wrap.kdf.generation);
243 writer.write_u32(wrap.kdf.memory);
244 writer.write_u32(wrap.kdf.time);
245 writer.write_u32(wrap.kdf.parallelism);
246 writer.write_bytearray(Some(&wrap.salt))?;
247 writer.write_bytearray(Some(&wrap.key_encrypted))?;
248 }
249 Ok(writer.into_bytes())
250 }
251
252 pub fn live_wraps(&self) -> impl Iterator<Item = &PasscodeWrap> {
254 self.wraps
255 .iter()
256 .filter(move |w| w.kdf.generation == self.committed)
257 }
258}
259
260#[derive(Debug, Clone, PartialEq, Eq)]
262pub struct KeyData {
263 pub salt: Vec<u8>,
265 pub key_encrypted: Option<Vec<u8>>,
268 pub info_encrypted: Vec<u8>,
270 pub trailer: Option<KeyDataTrailer>,
272}
273
274impl KeyData {
275 pub fn parse(payload: &[u8]) -> Result<Self> {
283 let mut reader = QReader::new(payload);
284 let (salt, key_encrypted, info_encrypted) = streamed(
285 (|| {
286 let salt = reader.read_bytearray()?.unwrap_or_default().to_vec();
287 let key_encrypted = reader.read_bytearray()?.map(<[u8]>::to_vec);
288 let info_encrypted = reader.read_bytearray()?.unwrap_or_default().to_vec();
289 Ok((salt, key_encrypted, info_encrypted))
290 })(),
291 KEY_DATA_STREAM_FAILED,
292 )?;
293 if salt.len() != LOCAL_ENCRYPT_SALT_SIZE {
294 return Err(invalid(KeyDataFailure::BadSalt { size: salt.len() }));
295 }
296 let trailer = if reader.is_empty() {
297 None
298 } else {
299 let blob =
300 streamed(reader.read_bytearray(), TRAILER_STREAM_FAILED)?.unwrap_or_default();
301 Some(KeyDataTrailer::parse(blob)?)
302 };
303 Ok(Self {
304 salt,
305 key_encrypted,
306 info_encrypted,
307 trailer,
308 })
309 }
310
311 pub fn encode(&self) -> Result<Vec<u8>> {
317 let mut writer = QWriter::new();
318 writer.write_bytearray(Some(&self.salt))?;
319 writer.write_bytearray(self.key_encrypted.as_deref())?;
320 writer.write_bytearray(Some(&self.info_encrypted))?;
321 if let Some(trailer) = &self.trailer {
322 writer.write_bytearray(Some(&trailer.encode()?))?;
323 }
324 Ok(writer.into_bytes())
325 }
326
327 #[must_use]
329 pub fn live_wrap_count(&self) -> usize {
330 self.trailer
331 .as_ref()
332 .map_or(0, |trailer| trailer.live_wraps().count())
333 }
334}
335
336#[derive(Debug, Clone)]
338pub struct OpenedKey {
339 pub local_key: AuthKey,
341 pub passcode_key: AuthKey,
343}
344
345pub fn create_passcode_key(passcode: &[u8], salt: &[u8], params: &PasscodeKdf) -> Result<AuthKey> {
354 if passcode.is_empty()
355 || salt.len() < kdf::PASSCODE_SALT_MIN_SIZE
356 || !params.cost_within_limits()
357 || !params.valid()
358 {
359 return Err(invalid(KeyDataFailure::UnusableWrapParameters {
360 kind: params.kind,
361 memory: params.memory,
362 time: params.time,
363 parallelism: params.parallelism,
364 }));
365 }
366 let derived = if params.kind == KDF_ARGON2ID {
367 kdf::argon2id(
368 passcode,
369 salt,
370 params.memory,
371 params.time,
372 params.parallelism,
373 )
374 } else {
375 kdf::scrypt(
376 passcode,
377 salt,
378 params.memory,
379 params.time,
380 params.parallelism,
381 )
382 };
383 derived.map(AuthKey::new).map_err(|_| Error::BadDecryptKey)
384}
385
386pub fn wrap_local_key(local_key: &AuthKey, wrap_key: &AuthKey, padding: &[u8]) -> Result<Vec<u8>> {
393 crypto::encrypt_local(local_key.as_bytes(), wrap_key, |_| padding.to_vec())
394}
395
396pub fn unwrap_local_key(key_encrypted: &[u8], wrap_key: &AuthKey) -> Result<AuthKey> {
402 let inner = crypto::decrypt_local(key_encrypted, wrap_key).map_err(|_| Error::BadDecryptKey)?;
403 AuthKey::from_slice(&inner).map_err(|_| Error::BadDecryptKey)
404}
405
406pub fn open_local_key(data: &KeyData, passcode: &[u8]) -> Result<OpenedKey> {
419 let open_wrap = data.key_encrypted.as_deref().unwrap_or_default();
420 let Some(trailer) = &data.trailer else {
421 let wrap_key = kdf::create_local_key(passcode, &data.salt);
422 return opened(open_wrap, wrap_key);
423 };
424 if trailer.wraps.iter().any(|w| !w.kdf.cost_within_limits()) {
425 return Err(Error::BadDecryptKey);
426 }
427 if passcode.is_empty() {
428 if open_wrap.is_empty() {
429 return Err(Error::BadDecryptKey);
430 }
431 return opened(open_wrap, kdf::create_local_key(&[], &data.salt));
432 }
433 let mut live = trailer.live_wraps();
436 let (Some(wrap), None) = (live.next(), live.next()) else {
437 return Err(Error::BadDecryptKey);
438 };
439 let wrap_key = create_passcode_key(passcode, &wrap.salt, &wrap.kdf)?;
440 opened(&wrap.key_encrypted, wrap_key)
441}
442
443pub(crate) fn install_draws(has_passcode: bool) -> Vec<usize> {
446 let padding = crypto::local_padding_len(AuthKey::SIZE);
447 if has_passcode {
448 vec![LOCAL_ENCRYPT_SALT_SIZE, LOCAL_ENCRYPT_SALT_SIZE, padding]
449 } else {
450 vec![LOCAL_ENCRYPT_SALT_SIZE, padding]
451 }
452}
453
454pub(crate) fn install(
463 local_key: &AuthKey,
464 passcode: &[u8],
465 rng: &mut FillRandom<'_>,
466) -> Result<(KeyData, AuthKey)> {
467 let salt = random_bytes(rng, LOCAL_ENCRYPT_SALT_SIZE)?;
468 let padding_len = crypto::local_padding_len(AuthKey::SIZE);
469 if passcode.is_empty() {
470 let wrap_key = kdf::create_local_key(&[], &salt);
471 let padding = random_bytes(rng, padding_len)?;
472 let data = KeyData {
473 key_encrypted: Some(wrap_local_key(local_key, &wrap_key, &padding)?),
474 salt,
475 info_encrypted: Vec::new(),
476 trailer: Some(KeyDataTrailer {
477 committed: 0,
478 wraps: Vec::new(),
479 }),
480 };
481 return Ok((data, wrap_key));
482 }
483 let params = default_passcode_kdf();
484 let wrap_salt = random_bytes(rng, LOCAL_ENCRYPT_SALT_SIZE)?;
485 let wrap_key = create_passcode_key(passcode, &wrap_salt, ¶ms)?;
486 let padding = random_bytes(rng, padding_len)?;
487 let wrap = PasscodeWrap {
488 kdf: params,
489 salt: wrap_salt,
490 key_encrypted: wrap_local_key(local_key, &wrap_key, &padding)?,
491 };
492 let data = KeyData {
493 salt,
494 key_encrypted: None,
495 info_encrypted: Vec::new(),
496 trailer: Some(KeyDataTrailer {
497 committed: params.generation,
498 wraps: vec![wrap],
499 }),
500 };
501 Ok((data, wrap_key))
502}
503
504fn opened(key_encrypted: &[u8], wrap_key: AuthKey) -> Result<OpenedKey> {
505 Ok(OpenedKey {
506 local_key: unwrap_local_key(key_encrypted, &wrap_key)?,
507 passcode_key: wrap_key,
508 })
509}
510
511#[derive(Debug, Clone, PartialEq, Eq)]
513pub struct AccountsInfo {
514 pub indices: Vec<i32>,
516 pub active_index: i32,
518}
519
520impl AccountsInfo {
521 pub fn parse(data: &[u8]) -> Result<Self> {
528 let mut reader = QReader::new(data);
529 let count = reader.read_i32()?;
530 if count <= 0 {
531 return Err(invalid(KeyDataFailure::BadAccountsCount { count }));
532 }
533 let mut indices = Vec::new();
534 for _ in 0..count {
535 indices.push(reader.read_i32()?);
536 }
537 let active_index = if reader.is_empty() {
538 0
539 } else {
540 reader.read_i32()?
541 };
542 Ok(Self {
543 indices,
544 active_index,
545 })
546 }
547
548 pub fn encode(&self) -> Result<Vec<u8>> {
554 let count = i32::try_from(self.indices.len())
555 .map_err(|_| Error::QDataStreamFailed { context: None })?;
556 let mut writer = QWriter::new();
557 writer.write_i32(count);
558 for &index in &self.indices {
559 writer.write_i32(index);
560 }
561 writer.write_i32(self.active_index);
562 Ok(writer.into_bytes())
563 }
564}
565
566pub fn decrypt_accounts_info(
578 data: &KeyData,
579 local_key: &AuthKey,
580 passcode: &[u8],
581) -> Result<AccountsInfo> {
582 let info = crypto::decrypt_local(&data.info_encrypted, local_key).map_err(|_| {
583 if passcode.is_empty() && data.live_wrap_count() == 1 {
584 Error::BadDecryptKey
585 } else {
586 invalid(KeyDataFailure::AccountsInfoDecrypt)
587 }
588 })?;
589 AccountsInfo::parse(&info)
590}
591
592const KEY_DATA_STREAM_FAILED: &str = "Could not stream the key data";
594const TRAILER_STREAM_FAILED: &str = "Could not stream the key data trailer";
596const TRAILER_HEADER_STREAM_FAILED: &str = "Could not stream the key data trailer header";
598const WRAP_STREAM_FAILED: &str = "Could not stream a key data wrap";
600
601fn streamed<T>(result: Result<T>, context: &'static str) -> Result<T> {
604 result.map_err(|error| match error {
605 Error::QDataStreamFailed { context: None } => Error::QDataStreamFailed {
606 context: Some(context),
607 },
608 other => other,
609 })
610}
611
612const fn invalid(context: KeyDataFailure) -> Error {
613 Error::KeyDataInvalid { context }
614}