aegis_vault_utils/
vault.rs

1use color_eyre::eyre::{eyre, Result};
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}