aegis_vault_utils/vault.rs
1use color_eyre::eyre::{Result, eyre};
2use serde::Deserialize;
3
4use crate::otp;
5
6/// Cryptographic functions and data structures used to decrypt database with OTP entries
7///
8/// The official Aegis documentation for vault decryption and contents can be found
9/// [here](https://github.com/beemdevelopment/Aegis/blob/master/docs/vault.md#aegis-vault).
10mod crypto;
11
12/// Database containing the user data
13#[derive(Debug, Deserialize)]
14pub struct Database {
15 /// Database version
16 pub version: u32,
17 /// List of OTP entries
18 pub entries: Vec<otp::Entry>,
19}
20
21/// Vault database as found in the JSON vault backup file. It can be either plain text or encrypted.
22#[derive(Debug, Deserialize)]
23#[serde(untagged)]
24pub enum VaultDatabase {
25 /// Database in plain text
26 Plain(Database),
27 /// Base64 decoded AES265 encrypted JSON
28 Encrypted(String),
29}
30
31/// Trait for getting the password from the user or from the environment.
32///
33pub trait PasswordGetter {
34 /// Get the password from the user or from the environment used to decrypt the vault
35 /// with [`parse_vault`].
36 fn get_password(&self) -> Result<String>;
37}
38
39/// Aegis vault backup file contents
40#[derive(Debug, Deserialize)]
41pub struct Vault {
42 /// Backup version
43 pub version: u32,
44 /// Information to decrypt master key
45 pub header: crypto::Header,
46 /// Entry database in plain text or encrypted
47 pub db: VaultDatabase,
48}
49
50/// Parse vault from JSON as found in the vault backup file
51///
52/// # Arguments
53/// - `vault_backup_contents` - JSON string containing vault backup, encrypted or not
54/// - `password_getter` - Password getter implementation. Used to get the password to decrypt the vault.
55///
56/// Required even if the vault is not encrypted.
57///
58/// # Returns
59/// - `Result` containing the parsed [database][`Database`]
60pub fn parse_vault(
61 vault_backup_contents: &str,
62 password_getter: &impl PasswordGetter,
63) -> Result<Database> {
64 let vault: Vault = serde_json::from_str(vault_backup_contents)?;
65 if vault.version != 1 {
66 return Err(eyre!(format!(
67 "Unsupported vault version: {}",
68 vault.version
69 )));
70 }
71 let db = match vault.db {
72 VaultDatabase::Plain(db) => db,
73 VaultDatabase::Encrypted(_) => {
74 let password = password_getter.get_password()?;
75 let json = crypto::decrypt_database(&password, vault)?;
76 let db: Database = serde_json::from_str(&json)?;
77 db
78 }
79 };
80
81 // The models are delevoped for the version 2 of the database
82 // but the changes have after version 2 are not significant
83 // so we can still use the same models.
84 if db.version > 3 {
85 return Err(eyre!(format!(
86 "Unsupported database version: {}",
87 db.version
88 )));
89 }
90
91 Ok(db)
92}