1pub mod crypto;
2pub mod secret;
3
4use std::fs;
5
6use crate::error::{AuthyError, Result};
7use crate::policy::Policy;
8use crate::session::SessionRecord;
9use crate::types::*;
10use crate::vault::secret::SecretEntry;
11
12#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct Vault {
15 pub version: u32,
16 pub created_at: DateTime<Utc>,
17 pub modified_at: DateTime<Utc>,
18 pub secrets: BTreeMap<String, SecretEntry>,
19 pub policies: BTreeMap<String, Policy>,
20 pub sessions: Vec<SessionRecord>,
21}
22
23impl Default for Vault {
24 fn default() -> Self {
25 Self::new()
26 }
27}
28
29impl Vault {
30 pub fn new() -> Self {
32 let now = Utc::now();
33 Self {
34 version: 1,
35 created_at: now,
36 modified_at: now,
37 secrets: BTreeMap::new(),
38 policies: BTreeMap::new(),
39 sessions: Vec::new(),
40 }
41 }
42
43 pub fn touch(&mut self) {
45 self.modified_at = Utc::now();
46 }
47}
48
49#[derive(Debug, Clone)]
51pub enum VaultKey {
52 Passphrase(String),
53 Keyfile { identity: String, pubkey: String },
54}
55
56pub fn authy_dir() -> PathBuf {
58 dirs::home_dir()
59 .expect("Could not determine home directory")
60 .join(".authy")
61}
62
63pub fn vault_path() -> PathBuf {
65 authy_dir().join("vault.age")
66}
67
68pub fn config_path() -> PathBuf {
70 authy_dir().join("authy.toml")
71}
72
73pub fn audit_path() -> PathBuf {
75 authy_dir().join("audit.log")
76}
77
78pub fn is_initialized() -> bool {
80 vault_path().exists()
81}
82
83pub fn load_vault(key: &VaultKey) -> Result<Vault> {
85 let path = vault_path();
86 if !path.exists() {
87 return Err(AuthyError::VaultNotInitialized);
88 }
89
90 let ciphertext = fs::read(&path)?;
91 let plaintext = match key {
92 VaultKey::Passphrase(pass) => crypto::decrypt_with_passphrase(&ciphertext, pass)?,
93 VaultKey::Keyfile { identity, .. } => {
94 crypto::decrypt_with_keyfile(&ciphertext, identity)?
95 }
96 };
97
98 let vault: Vault =
99 rmp_serde::from_slice(&plaintext).map_err(|e| AuthyError::Serialization(e.to_string()))?;
100
101 Ok(vault)
102}
103
104pub fn save_vault(vault: &Vault, key: &VaultKey) -> Result<()> {
106 let path = vault_path();
107 let dir = path.parent().unwrap();
108 fs::create_dir_all(dir)?;
109
110 let plaintext =
111 rmp_serde::to_vec(vault).map_err(|e| AuthyError::Serialization(e.to_string()))?;
112
113 let ciphertext = match key {
114 VaultKey::Passphrase(pass) => crypto::encrypt_with_passphrase(&plaintext, pass)?,
115 VaultKey::Keyfile { pubkey, .. } => crypto::encrypt_with_keyfile(&plaintext, pubkey)?,
116 };
117
118 let tmp_path = path.with_extension("age.tmp");
120 fs::write(&tmp_path, &ciphertext)?;
121 fs::rename(&tmp_path, &path)?;
122
123 Ok(())
124}