uni_vault/
uni_vault.rs

1//! Universal Vault implementation for secure data storage
2//! 
3//! This module provides the core functionality for storing and managing sensitive data
4//! in both encrypted and unencrypted formats. It will support multiple encryption methods
5//! including password-based encryption and hardware wallet (Trezor) based encryption.
6
7use crate::{
8    encryptor::{
9        Encryptor,
10        SecretType,
11    },
12    re_export::std_anyhow::*,
13    common::traits::JsonSerializable
14};
15
16/// # Example
17///
18/// ```
19/// use uni_vault::{
20///     uni_vault::{
21///         UniVault,
22///         UniVaultBuilder,
23///     },
24///     encryptor::{
25///         asymmetric::rage_trezor::{
26///             RageTrezorEncryptorBuilder,
27///             RageTrezorEncryptor,
28///         },
29///         Encryptor,
30///         HasUserIdentifier,
31///         SecretType,
32///     },
33///     re_export::std_anyhow::*,
34///     common::traits::JsonSerializable,
35///     common::test_helpers::{
36///         TREZOR_ENCRYPT_COMMENT,
37///         print_json_with_line,
38///         debug_with_line
39///     },
40/// };
41///
42/// let password_for_test = 12345678.to_string();
43/// let password_as_bytes = password_for_test.as_bytes();
44/// let mut recipients_with_my_backup_key = HashSet::new();
45/// recipients_with_my_backup_key.insert("ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILD0y5DXSqOmx/Tf0pKP23+JrCVSUrezlPDf/vcFFt3s".to_string());
46///
47/// let mut rage_trezor_encryptor: RageTrezorEncryptor = RageTrezorEncryptorBuilder::default()
48///     .platform_id("uni.vault.onion")
49///     .username("test_account")
50///     .should_create_new_recipient(true)
51///     .recipients(recipients_with_my_backup_key)
52///     .secret_type(SecretType::Password)
53///     .bip32_path("m/44h/60h/11h/0/12")
54///     .comment(TREZOR_ENCRYPT_COMMENT)
55///     .build()
56///     .unwrap();
57///
58/// let mut uni_vault: UniVault<RageTrezorEncryptor> = UniVaultBuilder::default()
59///     .encryptor(rage_trezor_encryptor)
60///     .data_type(SecretType::Password)
61///     .data(password_as_bytes)
62///     .build().unwrap();
63/// print_json_with_line!(&uni_vault);
64///
65/// uni_vault.encrypt().unwrap();
66/// print_json_with_line!(&uni_vault);
67///
68/// uni_vault.decrypt().unwrap();
69/// print_json_with_line!(&uni_vault);
70///
71/// assert_eq!(password_as_bytes, uni_vault.data);
72/// ```
73
74#[derive(Debug, Clone, Serialize, Deserialize, Builder, Zeroize, ZeroizeOnDrop)]
75pub struct UniVault<T: Encryptor + Serialize> {
76    /// The encryption mechanism used by this vault.
77    #[zeroize(skip)]
78    pub encryptor: T,
79
80    /// Type of data stored in the vault, which influences how the data is handled.
81    #[zeroize(skip)]
82    #[builder(setter(into))]
83    #[builder(default = "SecretType::Text")]
84    pub data_type: SecretType,
85
86    /// Flag indicating whether the data in the vault is currently encrypted.
87    #[zeroize(skip)]
88    #[builder(setter(into))]
89    #[builder(default = "false")]
90    pub encrypted: bool,
91
92    /// The actual data content of the vault, which could be plaintext or encrypted bytes.
93    #[builder(setter(into))]
94    #[builder(default = "Vec::new()")]
95    pub data: Vec<u8>,
96}
97
98impl<T: Encryptor + Serialize + for<'de> Deserialize<'de>> JsonSerializable for UniVault<T> {}
99
100impl<T: Encryptor + Serialize + for<'de> Deserialize<'de>> UniVault<T> {
101    /// Encrypts the vault contents.
102    /// 
103    /// # Returns
104    /// 
105    /// Returns `Ok(())` if encryption is successful, or an `Error` if:
106    /// - The data is already encrypted.
107    /// - Encryption fails.
108    pub fn encrypt(&mut self) -> anyhow::Result<()> {
109        if self.encrypted {
110            return Err(Error::msg("Do not encrypt already encrypted Bytes again"));
111        }
112
113        let encrypted_bytes = self.encryptor.encrypt(&self.data)?;
114        self.data.zeroize();
115        self.data = encrypted_bytes;
116        self.encrypted = true;
117
118        Ok(())
119    }
120
121    /// Decrypts the vault contents.
122    /// 
123    /// # Returns
124    /// 
125    /// Returns `Ok(())` if decryption is successful, or an `Error` if:
126    /// - The data is not encrypted.
127    /// - Decryption fails.
128    pub fn decrypt(&mut self) -> anyhow::Result<()> {
129        if !self.encrypted {
130            return Err(Error::msg("Operation only supported on encrypted bytes"));
131        }
132        self.data = self.encryptor.decrypt(&self.data)?;
133        self.encrypted = false;
134        Ok(())
135    }
136
137    /// Decrypts and decodes a JSON string representation of a vault.
138    ///
139    /// This method parses a JSON string into a vault instance and then decrypts it.
140    ///
141    /// # Arguments
142    ///
143    /// * `json_str` - The JSON string representation of the vault.
144    ///
145    /// # Returns
146    ///
147    /// Returns `Ok(Self)` if decryption and decoding are successful, or an `Error` if:
148    /// - JSON parsing fails.
149    /// - Decryption fails.
150    pub fn decrypt_and_decode_json_string(
151        json_str: &str,
152    ) -> anyhow::Result<Self> {
153        let mut uni_vault = UniVault::from_json(json_str)?;
154
155        uni_vault.decrypt()?;
156
157        Ok(uni_vault)
158    }
159
160    /// Converts the vault's data to a UTF-8 String.
161    /// 
162    /// This method will decrypt the data if it's encrypted before attempting to convert it to a String.
163    ///
164    /// # Returns
165    /// 
166    /// Returns `Ok(String)` if conversion is successful, or an `Error` if:
167    /// - The data isn't valid UTF-8.
168    /// - Decryption fails (if the data is encrypted).
169    pub fn data_to_string(&mut self) -> anyhow::Result<String> {
170        if self.encrypted {
171            self.decrypt()?;
172        }
173
174        let data_str = String::from_utf8(self.data.clone())
175            .map_err(Error::msg)?;
176
177        Ok(data_str)
178    }
179}