rs_pwsafe/
lib.rs

1//!# rs-pwsafe
2//!
3//! A libary to read pw-safe files and decrypt them
4//! currently only version 3 is supported
5extern crate core;
6
7mod pwsfile;
8pub mod pwserrors;
9pub mod pwsdb;
10mod util;
11
12use std::collections::HashSet;
13use std::fs::File;
14use std::io::Read;
15use std::path::{Path, PathBuf};
16use std::slice::Iter;
17use crate::pwsdb::PwDb;
18use crate::pwsdb::record::DbRecord;
19use crate::pwserrors::PwSafeError;
20use crate::pwsfile::{PwSafeEncrypted, PwSafeTransition};
21use crate::PwSafeError::{FailedToOpenFile, FileNotFound, FileReadError};
22/// Size of a twofish block
23const BLOCK_SIZE: usize = 16;
24
25
26/// High level abstraction of the PwSafe Database
27///
28///# Example
29///```
30/// use rs_pwsafe::PwFile;
31/// let mut file = match PwFile::open("DevTest.psafe3") {
32///     Ok(f) => f,
33///     Err(e) => panic!("failed to open safe: {:?}", e)
34/// };
35///
36/// match file.unlock("PswSafe123") {
37///     Ok(_) => (),
38///     Err(e) => panic!("failed to unlock db with {:?}", e)
39/// }
40/// for record in file.iter() {
41///     println!("{:?}", record)
42/// }
43/// ```
44#[derive(Debug)]
45pub struct PwFile {
46    pub path: PathBuf,
47    pub db: PwDb,
48    s: PwSafeEncrypted,
49    pub is_open: bool,
50    pub is_valid: bool
51}
52
53impl PwFile {
54    /// Return iterator over all records
55    pub fn iter(&self) -> Iter<DbRecord> {
56        self.db.records.iter()
57    }
58    /// Returns a list of all Groups in the database
59    pub fn groups(&self) -> HashSet<String> {
60        let mut groups = HashSet::new();
61        for record in &self.db.records {
62            if let Some(g) = record.group() {
63                groups.insert(g);
64            }
65        }
66        groups
67    }
68    /// Returns all items in a group
69    pub fn by_broup(&self, group: String) -> Vec<&DbRecord> {
70        self.iter().filter(| &r | r.group().is_some())
71            .filter(| &r | r.group().unwrap() == group).collect::<Vec<&DbRecord>>()
72    }
73    /// Decrypt file data and load header and field
74    pub fn unlock(&mut self, phrase: &str) -> Result<(), PwSafeError> {
75        let trans = self.s.prepare_db(phrase.to_string())?;
76        self.db = trans.try_into()?;
77        Ok(())
78    }
79
80    /// Read file and parse binary data in an acording struct
81    pub fn open(file_name: &str) -> Result<PwFile, PwSafeError> {
82        let path = Path::new(file_name);
83        if !path.exists() {
84            return Err(FileNotFound)
85        }
86        let mut fs = match File::open(file_name) {
87            Ok(fs) => fs,
88            Err(_) => return Err(FailedToOpenFile)
89        };
90        let mut buff = Vec::new();
91        let _ = match fs.read_to_end(&mut buff) {
92            Ok(s) => s,
93            Err(_) => return Err(FileReadError)
94        };
95
96        let mut safe = PwSafeEncrypted::new();
97        safe.check_format(&buff)?;
98        safe.load(&buff)?;
99        Ok(PwFile {
100                is_open: true,
101                is_valid: true,
102                s: safe,
103                db: PwDb::new(),
104                path: path.to_path_buf()
105            })
106    }
107}
108
109impl TryFrom<PwSafeTransition> for PwDb {
110    type Error = PwSafeError;
111
112    fn try_from(value: PwSafeTransition) -> Result<Self, Self::Error> {
113        let mut db = PwDb{
114            header: vec![],
115            records: vec![],
116            hmac: Some(value.hmac),
117            sig: value.sig
118        };
119        db.load(value.plt)?;
120        Ok(db)
121    }
122}