libspmg 0.2.1

Secure password manager library
Documentation
mod eipc {
    pub mod auth;
}


mod pin {
    pub mod entry;
}

use std::io::{self, Write, Read};
use std::sync::Arc;

use std::path::Path;

use eipc::auth::EIPC_Auth;

use flypto::hkdf_sha256::HkdfSha256;
use flypto::x25519::X25519;
use flypto::aes::AES;
use flypto::argon2::Argon2Hash;
use pin::entry::PinEntry;

use std::os::unix::net::UnixStream;



use base64::{encode, decode};

use crate::config::Config;

pub struct SPMG {
    socket: EIPC_Auth,
    config: Arc<Config>,
}

impl SPMG {
    pub fn new(config: Config) -> Self {
        let cfg_rc = Arc::new(config);

        let mut x25519 = X25519::new();
        let socket_path = format!("{}/socket", cfg_rc.spmg_path);
        let mut socket = EIPC_Auth::new(&socket_path, cfg_rc.clone());
        socket.eipc_client.connect();

        SPMG {
            socket,
            config: cfg_rc
        }
    }

    pub fn init(&mut self, mut lock_pwd: Option<String>) {
        println!("Creating a new lock...");

        let lock_path_str = format!("{}/pwds.lock", self.config.spmg_path);
        let lock_path = Path::new(&lock_path_str);
        if lock_path.exists() {
            println!("Password lock file {}/pwds.lock already exists!", self.config.spmg_path);
            return;
        }

        if lock_pwd.is_none() {
            lock_pwd = Some(
                PinEntry::prompt_repeat("Please provide a password to lock spmg").unwrap()
            );
        }

        lock_pwd = Some(
            urlencoding::decode(&lock_pwd.clone().unwrap()).unwrap().to_string()
        );
        self.socket.eipc_client.send_event("init", &lock_pwd.unwrap());
        println!("Password lock created!");
    }

    pub fn add(&mut self, pwd_id: String) -> Result<(), io::Error> {
        match PinEntry::prompt_repeat("Please enter your new password to store") {
            Ok(npwd) => {
                match self.socket.auth_command(|key: &str| -> String {
                    format!(
                        "get {} {}",
                        "spmg-lock",
                        key
                    )
                }) {
                    Ok(mut lock_token) => {
                        lock_token = urlencoding::decode(&lock_token).unwrap().to_string();
                        if lock_token == self.config.lock_token {
                            self.socket.auth_command(|key: &str| -> String {
                                format!(
                                    "add {} {} {}",
                                    pwd_id,
                                    npwd,
                                    key
                                )
                            });
                            Ok(())
                        } else {
                            return Err(io::Error::new(io::ErrorKind::PermissionDenied, "Unauthorized!"))
                        }
                    }
                    Err(error) => {
                        return Err(error)
                    }
                }

            }
            Err(error) => {
                return Err(error)
            }
        }

    }

    pub fn set(&mut self, pwd_id: String, mut password: String) {
        self.socket.auth_command(|key: &str| -> String {
            format!(
                "add {} {} {}",
                pwd_id,
                password,
                key
            )
        });
        println!("Password added!");
    }
/* 
    pub fn new(&mut self, pwd_id: String) {
               spmgd.auth_command(|key: &str| -> String {
                    let npwd = PinEntry::prompt_repeat("Please enter your new password to store").unwrap();
                    format!(
                        "add {} {} {}",
                        args[2],
                        npwd,
                        key
                    )
                });
                let cmd_res = spmgd.send_command("add 123");
                println!("cmd_res: {}", cmd_res.unwrap());
    }
*/

    pub fn get(&mut self, pwd_id: String, lock_pwd: Option<String>) -> Option<String> {
        if lock_pwd.is_none() {
            match self.socket.auth_command(|key: &str| -> String {
                format!(
                    "get {} {}",
                    pwd_id,
                    key
                )
            }) {
                Ok(mut pwd) => {
                    pwd = urlencoding::decode(&pwd).unwrap().to_string();
                    if pwd == "ERR NOTFOUND" {
                        eprintln!("No entry with identifier '{}'!", pwd_id);
                        None
                    } else {
                        Some(pwd)
                    }
                }
                Err(error) => {
                    eprintln!("{}", error);
                    return None;
                }
            }
        } else {
            let dkey = Argon2Hash::derive_key(&lock_pwd.unwrap(), &self.config.argon_salt);
            let enc_key = encode(dkey);
            let command = format!(
                "get {} {}",
                pwd_id,
                enc_key
            );
            let mut pwd = self.socket.eipc_client.send_event(&command, "").unwrap();
            pwd = urlencoding::decode(&pwd).unwrap().to_string();
            if pwd == "ERR NOTFOUND" {
                eprintln!("No entry with identifier '{}'!", pwd_id);
                None
            } else {
                Some(pwd)
            }
        }
    }

    pub fn clear_cache(&mut self) -> Option<String> {
        match self.socket.eipc_client.send_event("clear-cache", "") {
            Ok(mut response) => {
                if (response == "OK") {
                    Some(response)
                } else {
                    return None;
                }
            }
            Err(error) => {
                eprintln!("{}", error);
                return None;
            }
        }
    }

    pub fn cache_expiration(&mut self) -> Option<String> {
        let command = "cache-expiration";
        let mut cache_expiration = self.socket.eipc_client.send_event(&command, "").unwrap();
        if cache_expiration == "NONE" {
            None
        } else {
            Some(cache_expiration)
        }
    }

    pub fn kill(&mut self) {
        let cmd_res = self.socket.eipc_client.send_event("kill", "");
        println!("cmd_res: {}", cmd_res.unwrap());
    }

    pub fn disconnect(&mut self) {
        self.socket.eipc_client.disconnect();
    }
}