hapi_iron_oxide/
password.rs

1use std::collections::HashMap;
2
3use crate::errors::HapiIronOxideError;
4
5/// An enum that is holding the value of the password inside by its type.
6///
7/// The original implementation of [brc-dd/iron-webcrypto](https://github.com/brc-dd/iron-webcrypto) uses any incoming password in vector form as
8/// the key directly without any salt. But when the password is in [`String`] format, we will
9/// generate salt and create the key using `pbkdf2` algorithm.
10#[derive(Debug, Clone, PartialEq)]
11pub enum Password {
12    U8(Vec<u8>),
13    String(String),
14}
15
16/// A struct for storing a password with an ID. Uses same password for both encryption and
17/// integrity.
18#[derive(Clone)]
19pub struct SecretPassword {
20    /// The ID of the password.
21    pub id: String,
22    /// The password.
23    pub secret: Password,
24}
25
26/// A password with an ID, encryption, and integrity password.
27#[derive(Clone)]
28pub struct SpecificPassword {
29    /// The ID of the password
30    pub id: String,
31    /// The password used for encryption.
32    pub encryption: Password,
33    /// The password used for integrity.
34    pub integrity: Password,
35}
36
37/// A trait for converting a set of types to [`SpecificPassword`].
38///
39/// ```
40/// use hapi_iron_oxide::password::{SpecificPassword, SpecificPasswordInit};
41///
42/// let password: [u8; 5] = [0x52, 0x6f, 0x5c, 0x42, 0xee];
43/// let normalized_password: SpecificPassword = password.normalize().unwrap();
44///
45/// assert_eq!(normalized_password.id, "".to_string());
46/// ```
47pub trait SpecificPasswordInit {
48    /// Normalizes the password to [`SpecificPassword`]
49    ///
50    /// ```
51    /// use hapi_iron_oxide::password::{SpecificPassword, SpecificPasswordInit};
52    ///
53    /// let password: [u8; 5] = [0x52, 0x6f, 0x5c, 0x42, 0xee];
54    /// let normalized_password: SpecificPassword = password.normalize().unwrap();
55    ///
56    /// assert_eq!(normalized_password.id, "".to_string());
57    /// ```
58    fn normalize(&self) -> Result<SpecificPassword, HapiIronOxideError> {
59        Err(HapiIronOxideError::NormalizeUnimplemented)
60    }
61
62    /// Normalizes the password to [`SpecificPassword`], except this one takes in the optional
63    /// index of the password in case the user provides the [`HashMap`] of password with an ID and
64    /// the password.
65    ///
66    /// If the function is given a [`HashMap`], it will try to find the password using the given
67    /// index, otherwise it will find the password using the `default` as key.
68    ///
69    /// ```
70    /// use std::collections::HashMap;
71    ///
72    /// use hapi_iron_oxide::password::{Password, SpecificPassword, SpecificPasswordInit};
73    ///
74    /// let password_hashmap: HashMap<String, SpecificPassword> =
75    ///     HashMap::from([(
76    ///         "default".to_string(),
77    ///         SpecificPassword {
78    ///             id: "".to_string(),
79    ///             encryption: Password::String("raw_encryption_password_here".to_string()),
80    ///             integrity: Password::String("raw_integrity_password_here".to_string()),
81    ///         }
82    ///     )]);
83    /// let specific: SpecificPassword = password_hashmap.normalize_unseal(None).unwrap();
84    ///
85    /// assert_eq!(specific.id, "".to_string());
86    /// assert_eq!(specific.encryption, Password::String("raw_encryption_password_here".to_string()));
87    /// assert_eq!(specific.integrity, Password::String("raw_integrity_password_here".to_string()));
88    /// ```
89    fn normalize_unseal(&self, _idx: Option<&str>) -> Result<SpecificPassword, HapiIronOxideError> {
90        Err(HapiIronOxideError::NormalizeUnsealUnimplemented)
91    }
92}
93
94impl SpecificPasswordInit for &[u8] {
95    fn normalize(&self) -> Result<SpecificPassword, HapiIronOxideError> {
96        if self.is_empty() {
97            return Err(HapiIronOxideError::PasswordRequired);
98        }
99
100        Ok(SpecificPassword {
101            id: "".to_string(),
102            encryption: Password::U8(self.to_vec()),
103            integrity: Password::U8(self.to_vec()),
104        })
105    }
106
107    fn normalize_unseal(&self, _idx: Option<&str>) -> Result<SpecificPassword, HapiIronOxideError> {
108        if self.is_empty() {
109            return Err(HapiIronOxideError::PasswordRequired);
110        }
111
112        Ok(SpecificPassword {
113            id: "".to_string(),
114            encryption: Password::U8(self.to_vec()),
115            integrity: Password::U8(self.to_vec()),
116        })
117    }
118}
119
120impl SpecificPasswordInit for Vec<u8> {
121    fn normalize(&self) -> Result<SpecificPassword, HapiIronOxideError> {
122        if self.is_empty() {
123            return Err(HapiIronOxideError::PasswordRequired);
124        }
125
126        Ok(SpecificPassword {
127            id: "".to_string(),
128            encryption: Password::U8(self.to_vec()),
129            integrity: Password::U8(self.to_vec()),
130        })
131    }
132
133    fn normalize_unseal(&self, _idx: Option<&str>) -> Result<SpecificPassword, HapiIronOxideError> {
134        if self.is_empty() {
135            return Err(HapiIronOxideError::PasswordRequired);
136        }
137
138        Ok(SpecificPassword {
139            id: "".to_string(),
140            encryption: Password::U8(self.to_vec()),
141            integrity: Password::U8(self.to_vec()),
142        })
143    }
144}
145
146impl SpecificPasswordInit for &str {
147    fn normalize(&self) -> Result<SpecificPassword, HapiIronOxideError> {
148        if self.is_empty() {
149            return Err(HapiIronOxideError::PasswordRequired);
150        }
151
152        Ok(SpecificPassword {
153            id: "".to_string(),
154            encryption: Password::String(self.to_string()),
155            integrity: Password::String(self.to_string()),
156        })
157    }
158
159    fn normalize_unseal(&self, _idx: Option<&str>) -> Result<SpecificPassword, HapiIronOxideError> {
160        if self.is_empty() {
161            return Err(HapiIronOxideError::PasswordRequired);
162        }
163
164        Ok(SpecificPassword {
165            id: "".to_string(),
166            encryption: Password::String(self.to_string()),
167            integrity: Password::String(self.to_string()),
168        })
169    }
170}
171
172impl SpecificPasswordInit for String {
173    fn normalize(&self) -> Result<SpecificPassword, HapiIronOxideError> {
174        if self.is_empty() {
175            return Err(HapiIronOxideError::PasswordRequired);
176        }
177
178        Ok(SpecificPassword {
179            id: "".to_string(),
180            encryption: Password::String(self.to_string()),
181            integrity: Password::String(self.to_string()),
182        })
183    }
184
185    fn normalize_unseal(&self, _idx: Option<&str>) -> Result<SpecificPassword, HapiIronOxideError> {
186        if self.is_empty() {
187            return Err(HapiIronOxideError::PasswordRequired);
188        }
189
190        Ok(SpecificPassword {
191            id: "".to_string(),
192            encryption: Password::String(self.to_string()),
193            integrity: Password::String(self.to_string()),
194        })
195    }
196}
197
198impl SpecificPasswordInit for Password {
199    fn normalize(&self) -> Result<SpecificPassword, HapiIronOxideError> {
200        let specific: SpecificPassword = match self {
201            Password::U8(password_vector) => {
202                if password_vector.is_empty() {
203                    return Err(HapiIronOxideError::PasswordRequired);
204                }
205
206                SpecificPassword {
207                    id: "".to_string(),
208                    encryption: Clone::clone(self),
209                    integrity: Clone::clone(self),
210                }
211            }
212            Password::String(password_string) => {
213                if password_string.is_empty() {
214                    return Err(HapiIronOxideError::PasswordRequired);
215                }
216
217                SpecificPassword {
218                    id: "".to_string(),
219                    encryption: Clone::clone(self),
220                    integrity: Clone::clone(self),
221                }
222            }
223        };
224
225        Ok(specific)
226    }
227
228    fn normalize_unseal(&self, _idx: Option<&str>) -> Result<SpecificPassword, HapiIronOxideError> {
229        Ok(SpecificPassword {
230            id: "".to_string(),
231            encryption: Clone::clone(self),
232            integrity: Clone::clone(self),
233        })
234    }
235}
236
237impl SpecificPasswordInit for SecretPassword {
238    fn normalize(&self) -> Result<SpecificPassword, HapiIronOxideError> {
239        Ok(SpecificPassword {
240            id: self.id.clone(),
241            encryption: self.secret.clone(),
242            integrity: self.secret.clone(),
243        })
244    }
245
246    fn normalize_unseal(&self, _idx: Option<&str>) -> Result<SpecificPassword, HapiIronOxideError> {
247        Ok(SpecificPassword {
248            id: self.id.clone(),
249            encryption: self.secret.clone(),
250            integrity: self.secret.clone(),
251        })
252    }
253}
254
255impl SpecificPasswordInit for SpecificPassword {
256    fn normalize(&self) -> Result<SpecificPassword, HapiIronOxideError> {
257        Ok(Clone::clone(self))
258    }
259
260    fn normalize_unseal(&self, _idx: Option<&str>) -> Result<SpecificPassword, HapiIronOxideError> {
261        Ok(Clone::clone(self))
262    }
263}
264
265impl SpecificPasswordInit for HashMap<String, &[u8]> {
266    fn normalize_unseal(&self, idx: Option<&str>) -> Result<SpecificPassword, HapiIronOxideError> {
267        if self.is_empty() {
268            return Err(HapiIronOxideError::PasswordRequired);
269        }
270
271        if let Some(password_idx) = idx {
272            match self.get(password_idx) {
273                Some(p) => p.normalize(),
274                None => Err(HapiIronOxideError::PasswordRequired),
275            }
276        } else {
277            match self.get("default") {
278                Some(p) => p.normalize(),
279                None => Err(HapiIronOxideError::PasswordRequired),
280            }
281        }
282    }
283}
284
285impl SpecificPasswordInit for HashMap<String, String> {
286    fn normalize_unseal(&self, idx: Option<&str>) -> Result<SpecificPassword, HapiIronOxideError> {
287        if self.is_empty() {
288            return Err(HapiIronOxideError::PasswordRequired);
289        }
290
291        if let Some(password_idx) = idx {
292            match self.get(password_idx) {
293                Some(p) => p.normalize(),
294                None => Err(HapiIronOxideError::PasswordRequired),
295            }
296        } else {
297            match self.get("default") {
298                Some(p) => p.normalize(),
299                None => Err(HapiIronOxideError::PasswordRequired),
300            }
301        }
302    }
303}
304
305impl SpecificPasswordInit for HashMap<String, Password> {
306    fn normalize_unseal(&self, idx: Option<&str>) -> Result<SpecificPassword, HapiIronOxideError> {
307        if self.is_empty() {
308            return Err(HapiIronOxideError::PasswordRequired);
309        }
310
311        if let Some(password_idx) = idx {
312            match self.get(password_idx) {
313                Some(p) => p.normalize(),
314                None => Err(HapiIronOxideError::PasswordRequired),
315            }
316        } else {
317            match self.get("default") {
318                Some(p) => p.normalize(),
319                None => Err(HapiIronOxideError::PasswordRequired),
320            }
321        }
322    }
323}
324
325impl SpecificPasswordInit for HashMap<String, SecretPassword> {
326    fn normalize_unseal(&self, idx: Option<&str>) -> Result<SpecificPassword, HapiIronOxideError> {
327        if self.is_empty() {
328            return Err(HapiIronOxideError::PasswordRequired);
329        }
330
331        if let Some(password_idx) = idx {
332            match self.get(password_idx) {
333                Some(p) => p.normalize(),
334                None => Err(HapiIronOxideError::PasswordRequired),
335            }
336        } else {
337            match self.get("default") {
338                Some(p) => p.normalize(),
339                None => Err(HapiIronOxideError::PasswordRequired),
340            }
341        }
342    }
343}
344
345impl SpecificPasswordInit for HashMap<String, SpecificPassword> {
346    fn normalize_unseal(&self, idx: Option<&str>) -> Result<SpecificPassword, HapiIronOxideError> {
347        if self.is_empty() {
348            return Err(HapiIronOxideError::PasswordRequired);
349        }
350
351        if let Some(password_idx) = idx {
352            match self.get(password_idx) {
353                Some(p) => Ok(Clone::clone(p)),
354                None => Err(HapiIronOxideError::PasswordRequired),
355            }
356        } else {
357            match self.get("default") {
358                Some(p) => Ok(Clone::clone(p)),
359                None => Err(HapiIronOxideError::PasswordRequired),
360            }
361        }
362    }
363}
364
365impl<const N: usize> SpecificPasswordInit for [u8; N] {
366    fn normalize(&self) -> Result<SpecificPassword, HapiIronOxideError> {
367        Ok(SpecificPassword {
368            id: "".to_string(),
369            encryption: Password::U8(self.to_vec()),
370            integrity: Password::U8(self.to_vec()),
371        })
372    }
373
374    fn normalize_unseal(&self, _idx: Option<&str>) -> Result<SpecificPassword, HapiIronOxideError> {
375        Ok(SpecificPassword {
376            id: "".to_string(),
377            encryption: Password::U8(self.to_vec()),
378            integrity: Password::U8(self.to_vec()),
379        })
380    }
381}