use crypto::hmac::Hmac; use crypto::mac::{Mac, MacResult}; use crypto::sha2::Sha256; use dialoguer::{theme::ColorfulTheme, PasswordInput}; use docopt::Docopt; use log::debug; use rand::rngs::OsRng; use rand::seq::IteratorRandom; use rand::seq::SliceRandom; use serde::Deserialize; use std::error::Error; pub mod crypt; pub mod db; pub mod generate; static USAGE: &'static str = " foil Usage: foil set [--db=] foil get [--db=] foil list [--db=] foil generate foil (-h | --help) Options: --db= The path to the sqlite database [default: db.sqlite3]. -h --help Show this screen. --version Show version. "; #[derive(Debug, Deserialize)] struct Args { cmd_set: bool, cmd_get: bool, cmd_list: bool, cmd_generate: bool, flag_db: Option, arg_spec: Option, } fn get_master_key(db_conn: &mut db::DbHandle) -> [u8; 32] { let known_string = db_conn .get_db_property("known_string") .expect("There was a problem reading from the db") .unwrap_or_else(|| { println!("No master password set yet, create new one:"); let mut random = OsRng::new().unwrap(); let new_known: String = { let mut new_chars: Vec = "abcdefghijklmnopqrstuvwxyz" .chars() .choose_multiple(&mut random, 64); new_chars.shuffle(&mut random); new_chars.into_iter().collect() }; db_conn.set_db_property("known_string", &new_known); new_known }); let master_key: [u8; 32] = { let master_password = PasswordInput::with_theme(&ColorfulTheme::default()) .with_prompt("Master password") .with_confirmation( "Repeat master password", "Error: the passwords don't match.", ) .interact() .unwrap(); crypt::get_master_key(&db_conn, &master_password).unwrap() }; let mut hmac = Hmac::new(Sha256::new(), &master_key); hmac.input(known_string.as_bytes()); let existing_hmac = db_conn .get_db_property_bytes("known_hmac") .expect("There was a problem reading from the db") .unwrap_or_else(|| { let mut raw_result: Vec = std::iter::repeat(0).take(hmac.output_bytes()).collect(); hmac.raw_result(&mut raw_result); db_conn.set_db_property_bytes("known_hmac", &raw_result); raw_result }); if hmac.result() != MacResult::new(&existing_hmac[..]) { panic!("Incorrect master password"); } master_key } fn main() -> Result<(), Box> { pretty_env_logger::init(); let args: Args = Docopt::new(USAGE) .and_then(|dopt| dopt.deserialize()) .unwrap_or_else(|e| e.exit()); debug!("{:?}", args); if args.cmd_generate { generate::generate(&args.arg_spec.unwrap()); return Ok(()); } let mut db_conn: db::DbHandle = db::DbHandle::new(&args.flag_db); let master_key: [u8; 32] = get_master_key(&mut db_conn); Ok(()) }