rust_keylock/api/
safe.rs

1// Copyright 2017 astonbitecode
2// This file is part of rust-keylock password manager.
3//
4// rust-keylock is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// rust-keylock is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with rust-keylock.  If not, see <http://www.gnu.org/licenses/>.
16
17use std::collections::HashMap;
18
19use zeroize::Zeroize;
20
21use crate::{datacrypt, errors};
22
23use super::Entry;
24
25/// Holds the data that should be safe and secret.
26///
27/// This includes the password entries and a Cryptor that is used to encrypt the passwords of the entries when they are stored in memory
28/// and decrypt them when needed (to be presented to the User)
29#[derive(Clone, Debug)]
30pub(crate) struct Safe {
31    pub(crate) entries: Vec<Entry>,
32    filtered_entries: Vec<Entry>,
33    /// Maps the filtered Entries to the Vec that contains all the entries.
34    map_filtered_to_unfiltered: HashMap<usize, usize>,
35    password_cryptor: datacrypt::EntryPasswordCryptor,
36    filter: String,
37}
38
39impl Drop for Safe {
40    fn drop(&mut self) {
41        self.zeroize();
42    }
43}
44
45impl Zeroize for Safe {
46    fn zeroize(&mut self) {
47        self.entries.zeroize();
48        self.filtered_entries.zeroize();
49        self.password_cryptor.zeroize();
50        self.filter.zeroize();
51    }
52}
53
54impl Default for Safe {
55    fn default() -> Self {
56        Safe {
57            entries: Vec::new(),
58            filtered_entries: Vec::new(),
59            map_filtered_to_unfiltered: HashMap::new(),
60            password_cryptor: datacrypt::EntryPasswordCryptor::new(),
61            filter: "".to_string(),
62        }
63    }
64}
65
66impl Safe {
67    pub(crate) fn new() -> Safe {
68        Safe {
69            entries: Vec::new(),
70            filtered_entries: Vec::new(),
71            map_filtered_to_unfiltered: HashMap::new(),
72            password_cryptor: datacrypt::EntryPasswordCryptor::new(),
73            filter: "".to_string(),
74        }
75    }
76
77    /// Adds an Entry to the Safe, with the Entry password encrypted
78    pub(crate) fn add_entry(&mut self, new_entry: Entry) {
79        self.entries.push(new_entry.encrypted(&self.password_cryptor));
80        self.apply_filter();
81    }
82
83    /// Replaces an Entry in the Safe, with a new Entry that has the password encrypted
84    pub(crate) fn replace_entry(&mut self, index: usize, entry: Entry) -> errors::Result<()> {
85        // Push the Entry
86        self.entries.push(entry.encrypted(&self.password_cryptor));
87        // Find the correct index of the edited entry in the main Entries Vec
88        let res = match self.map_filtered_to_unfiltered.get(&index) {
89            Some(index_in_main_vec) => {
90                // Replace
91                self.entries.swap_remove(*index_in_main_vec);
92                Ok(())
93            }
94            None => {
95                Err(errors::RustKeylockError::GeneralError(dbg!("The entry being replaced was not found in the Entries... \
96                                                                            This may be an indication of race conditions..\
97                                                                            Please consider opening a bug to \
98                                                                            the developers.")
99                    .to_string()))
100            }
101        };
102        // Apply the filter once again
103        self.apply_filter();
104        res
105    }
106
107    /// Removes an Entry from the Safe
108    pub(crate) fn remove_entry(&mut self, index: usize) -> errors::Result<()> {
109        let res = match self.map_filtered_to_unfiltered.get(&index) {
110            Some(index_in_main_vec) => {
111                // Remove
112                self.entries.remove(*index_in_main_vec);
113                Ok(())
114            }
115            None => {
116                Err(errors::RustKeylockError::GeneralError("The entry being replaced was not found in the Entries... If the entries \
117                                                            changed meanwhile, this is normal. If not, please consider opening a bug to \
118                                                            the developers."
119                    .to_string()))
120            }
121        };
122        // Apply the filter once again
123        self.apply_filter();
124        res
125    }
126
127    /// Merges the Entries, by appending the incoming elements that are not the same with some existing one in Safe
128    pub(crate) fn merge(&mut self, incoming: Vec<Entry>) {
129        let mut to_add = {
130            incoming.into_iter()
131                .filter(|entry| {
132                    let mut main_iter = self.entries.iter();
133                    let opt = main_iter.find(|main_entry| {
134                        let enrypted_entry = entry.encrypted(&self.password_cryptor);
135                        main_entry.name == enrypted_entry.name && main_entry.url == enrypted_entry.url &&
136                            main_entry.user == enrypted_entry.user && main_entry.pass == enrypted_entry.pass && main_entry.desc == enrypted_entry.desc
137                    });
138                    opt.is_none()
139                })
140                .map(|entry| entry.encrypted(&self.password_cryptor))
141                .collect()
142        };
143
144        self.entries.append(&mut to_add);
145        self.apply_filter();
146    }
147
148    /// Adds the Entries in the Safe
149    pub(crate) fn add_all(&mut self, incoming: Vec<Entry>) {
150        let mut to_add = {
151            incoming.into_iter()
152                .map(|entry| entry.encrypted(&self.password_cryptor))
153                .collect()
154        };
155
156        self.entries.append(&mut to_add);
157        self.apply_filter();
158    }
159
160    /// Retrieves an Entry at a given index, after applying the filter to the Vector
161    pub(crate) fn get_entry(&self, index: usize) -> &Entry {
162        &self.get_entries()[index]
163    }
164
165    /// Retrieves an Entry at a given index with the password decrypted
166    pub(crate) fn get_entry_decrypted(&self, index: usize) -> Entry {
167        self.get_entry(index).decrypted(&self.password_cryptor)
168    }
169
170    /// Retrieves the existing entries, after applying the filter to the Vector
171    pub(crate) fn get_entries(&self) -> &[Entry] {
172        &self.filtered_entries
173    }
174
175    /// Retrieves __all__ the Entries with the passwords decrypted
176    pub(crate) fn get_entries_decrypted(&self) -> Vec<Entry> {
177        self.get_entries()
178            .iter()
179            .map(|entry| entry.decrypted(&self.password_cryptor))
180            .collect()
181    }
182
183    /// Sets a filter to be applied when retrieving the entries
184    pub(crate) fn set_filter(&mut self, filter: String) {
185        self.filter = filter;
186        self.apply_filter();
187    }
188
189    /// Gets the filter of the Safe
190    pub(crate) fn get_filter(&self) -> String {
191        self.filter.clone()
192    }
193
194    fn apply_filter(&mut self) {
195        let m: Vec<Entry> = if !self.filter.is_empty() {
196            let lower_filter = self.filter.to_lowercase();
197            let mut indexes_vec = Vec::new();
198            let mut vec = Vec::new();
199
200            {
201                let iter = self.entries
202                    .iter()
203                    .enumerate()
204                    .filter(|&(_, entry)| {
205                        entry.name.to_lowercase().contains(&lower_filter) || entry.url.to_lowercase().contains(&lower_filter) ||
206                            entry.user.to_lowercase().contains(&lower_filter) || entry.desc.to_lowercase().contains(&lower_filter)
207                    });
208                for tup in iter {
209                    // Push the entry in the vec
210                    vec.push(tup.1.clone());
211                    // Push the index in the indexes vec
212                    indexes_vec.push(tup.0);
213                }
214            }
215
216            // Put the indexes mapping in the map_filtered_to_unfiltered
217            self.map_filtered_to_unfiltered.clear();
218            indexes_vec.iter().enumerate().for_each(|(index_in_filtered_vec, index_in_main_vec)| {
219                self.map_filtered_to_unfiltered.insert(index_in_filtered_vec, *index_in_main_vec);
220            });
221
222            vec
223        } else {
224            // Put the indexes mapping in the map_filtered_to_unfiltered.
225            // The mapping here is one-to-one
226            self.map_filtered_to_unfiltered.clear();
227            for index in 0..self.entries.len() {
228                self.map_filtered_to_unfiltered.insert(index, index);
229            }
230            self.entries.clone()
231        };
232
233        self.filtered_entries = m;
234    }
235
236    pub(crate) fn clear(&mut self) {
237        self.filtered_entries = Vec::new();
238        self.entries = Vec::new();
239        self.map_filtered_to_unfiltered.clear();
240        self.filter = "".to_string();
241    }
242}
243
244#[cfg(test)]
245mod safe_unit_tests {
246    use crate::api::{Entry, EntryMeta};
247
248    #[test]
249    fn merge_entries() {
250        let mut safe = super::Safe::new();
251        assert!(safe.entries.len() == 0);
252
253        // Add some initial Entries
254        let all = vec![
255            Entry::new("1".to_string(), "1".to_string(), "1".to_string(), "1".to_string(), "1".to_string(), EntryMeta::default()),
256            Entry::new("2".to_string(), "2".to_string(), "2".to_string(), "2".to_string(), "2".to_string(), EntryMeta::default())];
257        safe.add_all(all);
258
259        // This one should be added
260        let first = vec![Entry::new("3".to_string(), "3".to_string(), "3".to_string(), "3".to_string(), "3".to_string(), EntryMeta::default())];
261        safe.merge(first);
262        assert!(safe.entries.len() == 3);
263
264        // This one should not be added
265        let second = vec![Entry::new("1".to_string(), "1".to_string(), "1".to_string(), "1".to_string(), "1".to_string(), EntryMeta::default())];
266        safe.merge(second);
267        assert!(safe.entries.len() == 3);
268
269        // This one should not be added either (the description is not the same with any of the existing ones
270        let third = vec![Entry::new("1".to_string(), "1".to_string(), "1".to_string(), "1".to_string(), "3".to_string(), EntryMeta::default())];
271        safe.merge(third);
272        assert!(safe.entries.len() == 4);
273    }
274
275    #[test]
276    fn add_entry() {
277        let mut safe = super::Safe::new();
278        let entry = Entry::new("3".to_string(), "3".to_string(), "3".to_string(), "3".to_string(), "3".to_string(), EntryMeta::default());
279        safe.add_entry(entry.clone());
280        assert!(safe.entries.len() == 1);
281        assert!(safe.entries[0].name == entry.name);
282        assert!(safe.entries[0].url == entry.url);
283        assert!(safe.entries[0].user == entry.user);
284        assert!(safe.entries[0].pass != entry.pass);
285        assert!(safe.entries[0].desc == entry.desc);
286    }
287
288    #[test]
289    fn replace_entry() {
290        let mut safe = super::Safe::new();
291        let entry = Entry::new("3".to_string(), "3".to_string(), "3".to_string(), "3".to_string(), "3".to_string(), EntryMeta::default());
292        safe.add_entry(entry.clone());
293        let new_entry = Entry::new("33".to_string(), "33".to_string(), "33".to_string(), "33".to_string(), "33".to_string(), EntryMeta::default());
294        let _ = safe.replace_entry(0, new_entry.clone());
295
296        assert!(safe.entries.len() == 1);
297        let replaced_entry = safe.get_entry_decrypted(0);
298        assert!(replaced_entry.name == new_entry.name);
299        assert!(replaced_entry.url == new_entry.url);
300        assert!(replaced_entry.user == new_entry.user);
301        assert!(replaced_entry.pass == new_entry.pass);
302        assert!(replaced_entry.desc == new_entry.desc);
303    }
304
305    #[test]
306    fn replace_entry_after_filter() {
307        let mut safe = super::Safe::new();
308        let entry1 = Entry::new("1".to_string(), "1".to_string(), "1".to_string(), "1".to_string(), "1".to_string(), EntryMeta::default());
309        let entry2 = Entry::new("2".to_string(), "2".to_string(), "2".to_string(), "2".to_string(), "2".to_string(), EntryMeta::default());
310        let entry3 = Entry::new("3".to_string(), "3".to_string(), "3".to_string(), "3".to_string(), "3".to_string(), EntryMeta::default());
311        safe.add_entry(entry1.clone());
312        safe.add_entry(entry2.clone());
313        safe.add_entry(entry3.clone());
314        let new_entry = Entry::new("33".to_string(), "33".to_string(), "33".to_string(), "33".to_string(), "33".to_string(), EntryMeta::default());
315
316        safe.set_filter("3".to_string());
317        let _ = safe.replace_entry(0, new_entry.clone());
318        safe.set_filter("".to_string());
319
320        assert!(safe.entries.len() == 3);
321        let replaced_entry = safe.get_entry_decrypted(2);
322        assert!(replaced_entry.name == new_entry.name);
323        assert!(replaced_entry.url == new_entry.url);
324        assert!(replaced_entry.user == new_entry.user);
325        assert!(replaced_entry.pass == new_entry.pass);
326        assert!(replaced_entry.desc == new_entry.desc);
327    }
328
329    #[test]
330    fn remove_entry() {
331        let mut safe = super::Safe::new();
332        let entry1 = Entry::new("3".to_string(), "3".to_string(), "3".to_string(), "3".to_string(), "3".to_string(), EntryMeta::default());
333        let entry2 = Entry::new("33".to_string(), "33".to_string(), "33".to_string(), "33".to_string(), "33".to_string(), EntryMeta::default());
334        safe.add_entry(entry1.clone());
335        safe.add_entry(entry2.clone());
336
337        let _ = safe.remove_entry(1);
338
339        assert!(safe.entries.len() == 1);
340        assert!(safe.entries[0].name == entry1.name);
341        assert!(safe.entries[0].url == entry1.url);
342        assert!(safe.entries[0].user == entry1.user);
343        assert!(safe.entries[0].pass != entry1.pass);
344        assert!(safe.entries[0].desc == entry1.desc);
345    }
346
347    #[test]
348    fn remove_entry_after_filter() {
349        let mut safe = super::Safe::new();
350        let entry1 = Entry::new("3".to_string(), "3".to_string(), "3".to_string(), "3".to_string(), "3".to_string(), EntryMeta::default());
351        let entry2 = Entry::new("33".to_string(), "33".to_string(), "33".to_string(), "33".to_string(), "33".to_string(), EntryMeta::default());
352        safe.add_entry(entry1.clone());
353        safe.add_entry(entry2.clone());
354
355        safe.set_filter("33".to_string());
356        let _ = safe.remove_entry(0);
357        safe.set_filter("".to_string());
358
359        assert!(safe.entries.len() == 1);
360        let decrypted_entry = safe.get_entry_decrypted(0);
361        assert!(decrypted_entry.name == entry1.name);
362        assert!(decrypted_entry.url == entry1.url);
363        assert!(decrypted_entry.user == entry1.user);
364        assert!(decrypted_entry.pass == entry1.pass);
365        assert!(decrypted_entry.desc == entry1.desc);
366    }
367
368    #[test]
369    fn add_all() {
370        let mut safe = super::Safe::new();
371        let entry1 = Entry::new("3".to_string(), "3".to_string(), "3".to_string(), "3".to_string(), "3".to_string(), EntryMeta::default());
372        let entry2 = Entry::new("33".to_string(), "33".to_string(), "33".to_string(), "33".to_string(), "33".to_string(), EntryMeta::default());
373        let entries = vec![entry1.clone(), entry2.clone()];
374
375        safe.add_all(entries);
376
377        assert!(safe.entries.len() == 2);
378        assert!(safe.entries[0].pass != entry1.pass && safe.entries[0].pass != entry2.pass);
379        assert!(safe.entries[1].pass != entry1.pass && safe.entries[0].pass != entry2.pass);
380    }
381
382    #[test]
383    fn get_entry() {
384        let mut safe = super::Safe::new();
385        let entry1 = Entry::new("3".to_string(), "3".to_string(), "3".to_string(), "3".to_string(), "3".to_string(), EntryMeta::default());
386        let entry2 = Entry::new("33".to_string(), "33".to_string(), "33".to_string(), "33".to_string(), "33".to_string(), EntryMeta::default());
387        let entries = vec![entry1.clone(), entry2.clone()];
388        safe.add_all(entries);
389
390        let got_entry = safe.get_entry(1);
391        assert!(got_entry.name == entry2.name);
392        assert!(got_entry.url == entry2.url);
393        assert!(got_entry.user == entry2.user);
394        assert!(got_entry.pass != entry2.pass);
395        assert!(got_entry.desc == entry2.desc);
396    }
397
398    #[test]
399    fn get_entry_decrypted() {
400        let mut safe = super::Safe::new();
401        let entry1 = Entry::new("3".to_string(), "3".to_string(), "3".to_string(), "3".to_string(), "3".to_string(), EntryMeta::default());
402        let entry2 = Entry::new("33".to_string(), "33".to_string(), "33".to_string(), "33".to_string(), "33".to_string(), EntryMeta::default());
403        let entries = vec![entry1.clone(), entry2.clone()];
404        safe.add_all(entries);
405
406        let got_entry = safe.get_entry_decrypted(1);
407        assert!(got_entry.name == entry2.name);
408        assert!(got_entry.url == entry2.url);
409        assert!(got_entry.user == entry2.user);
410        assert!(got_entry.pass == entry2.pass);
411        assert!(got_entry.desc == entry2.desc);
412    }
413
414    #[test]
415    fn get_entries() {
416        let mut safe = super::Safe::new();
417        let entry1 = Entry::new("3".to_string(), "3".to_string(), "3".to_string(), "3".to_string(), "3".to_string(), EntryMeta::default());
418        let entry2 = Entry::new("33".to_string(), "33".to_string(), "33".to_string(), "33".to_string(), "33".to_string(), EntryMeta::default());
419        let entries = vec![entry1.clone(), entry2.clone()];
420        safe.add_all(entries);
421
422        let got_entries = safe.get_entries();
423        assert!(got_entries.len() == 2);
424    }
425
426    #[test]
427    fn get_entries_decrypted() {
428        let mut safe = super::Safe::new();
429        let entry1 = Entry::new("3".to_string(), "3".to_string(), "3".to_string(), "3".to_string(), "3".to_string(), EntryMeta::default());
430        let entry2 = Entry::new("33".to_string(), "33".to_string(), "33".to_string(), "33".to_string(), "33".to_string(), EntryMeta::default());
431        let entries = vec![entry1.clone(), entry2.clone()];
432        safe.add_all(entries);
433
434        let got_entries = safe.get_entries_decrypted();
435        assert!(got_entries.len() == 2);
436        assert!(got_entries[0].pass == entry1.pass);
437        assert!(got_entries[1].pass == entry2.pass);
438    }
439
440    #[test]
441    fn set_filter() {
442        let mut safe = super::Safe::new();
443        let entry1 = Entry::new("1".to_string(), "2".to_string(), "3".to_string(), "5".to_string(), "4".to_string(), EntryMeta::default());
444        let entry2 = Entry::new("11".to_string(), "12".to_string(), "13".to_string(), "15".to_string(), "14".to_string(), EntryMeta::default());
445        let entries = vec![entry1, entry2];
446        safe.add_all(entries);
447
448        // Assert that the filter can be applied on name, url, user and desc fields of Entries
449        safe.set_filter("1".to_string());
450        assert!(safe.get_entries().len() == 2);
451        safe.set_filter("11".to_string());
452        assert!(safe.get_entries().len() == 1);
453
454        safe.set_filter("2".to_string());
455        assert!(safe.get_entries().len() == 2);
456        safe.set_filter("12".to_string());
457        assert!(safe.get_entries().len() == 1);
458
459        safe.set_filter("3".to_string());
460        assert!(safe.get_entries().len() == 2);
461        safe.set_filter("13".to_string());
462        assert!(safe.get_entries().len() == 1);
463
464        safe.set_filter("4".to_string());
465        assert!(safe.get_entries().len() == 2);
466        safe.set_filter("14".to_string());
467        assert!(safe.get_entries().len() == 1);
468
469        // The filter cannot be applied on password
470        safe.set_filter("5".to_string());
471        assert!(safe.get_entries().len() == 0);
472
473        // The filter should by applied ignoring the case
474        let entry3 = Entry::new("NAME".to_string(), "Url".to_string(), "User".to_string(), "pass".to_string(), "Desc".to_string(), EntryMeta::default());
475        safe.add_entry(entry3);
476        safe.set_filter("name".to_string());
477        assert!(safe.get_entries().len() == 1);
478    }
479
480    #[test]
481    fn get_filter() {
482        let mut safe = super::Safe::new();
483        safe.set_filter("33".to_string());
484        assert!(safe.get_filter() == "33".to_string());
485    }
486
487    #[test]
488    fn clear() {
489        let mut safe = super::Safe::new();
490        let entry = Entry::new("3".to_string(), "3".to_string(), "3".to_string(), "3".to_string(), "3".to_string(), EntryMeta::default());
491        safe.add_entry(entry.clone());
492        safe.set_filter("a_filter".to_string());
493
494        safe.clear();
495
496        assert!(safe.entries.len() == 0);
497        assert!(safe.filtered_entries.len() == 0);
498        assert!(safe.filter.len() == 0);
499    }
500}