|
|
|
@ -1,7 +1,9 @@
|
|
|
|
|
#![warn(clippy::all)]
|
|
|
|
|
|
|
|
|
|
use crypto::hmac::Hmac;
|
|
|
|
|
use crypto::mac::{Mac, MacResult};
|
|
|
|
|
use crypto::sha2::Sha256;
|
|
|
|
|
use dialoguer::{theme::ColorfulTheme, Input, PasswordInput};
|
|
|
|
|
use dialoguer::{theme::ColorfulTheme, Input, PasswordInput, Select};
|
|
|
|
|
use docopt::Docopt;
|
|
|
|
|
use log::debug;
|
|
|
|
|
use rand::rngs::OsRng;
|
|
|
|
@ -14,20 +16,21 @@ pub mod crypt;
|
|
|
|
|
pub mod db;
|
|
|
|
|
pub mod generate;
|
|
|
|
|
|
|
|
|
|
static USAGE: &'static str = "
|
|
|
|
|
static USAGE: &str = "
|
|
|
|
|
foil
|
|
|
|
|
|
|
|
|
|
Usage:
|
|
|
|
|
foil set [--namespace=<ns>] [--db=<db>]
|
|
|
|
|
foil get [--namespace=<ns>] [--db=<db>]
|
|
|
|
|
foil list [--db=<db>]
|
|
|
|
|
foil transfer [--db=<db>]
|
|
|
|
|
foil list [--namespace=<ns>] [--db=<db>]
|
|
|
|
|
foil shell [--namespace=<ns>] [--db=<db>]
|
|
|
|
|
foil dump [--db=<db>]
|
|
|
|
|
foil generate <spec>
|
|
|
|
|
foil (-h | --help)
|
|
|
|
|
|
|
|
|
|
Options:
|
|
|
|
|
--db=<db> The path to the sqlite database [default: db.sqlite3].
|
|
|
|
|
--src=<db> The path to the old sqlite database [default: db.sqlite3].
|
|
|
|
|
-n DB, --namespace=<db> An identifier for a group of secrets [default: main]
|
|
|
|
|
-h, --help Show this screen.
|
|
|
|
|
--version Show version.
|
|
|
|
@ -39,9 +42,10 @@ struct Args {
|
|
|
|
|
cmd_get: bool,
|
|
|
|
|
cmd_list: bool,
|
|
|
|
|
cmd_generate: bool,
|
|
|
|
|
cmd_transfer: bool,
|
|
|
|
|
cmd_dump: bool,
|
|
|
|
|
cmd_shell: bool,
|
|
|
|
|
flag_db: Option<String>,
|
|
|
|
|
flag_src: Option<String>,
|
|
|
|
|
flag_namespace: String,
|
|
|
|
|
arg_spec: Option<String>,
|
|
|
|
|
}
|
|
|
|
@ -92,17 +96,15 @@ fn get_master_key(db_conn: &mut db::DbHandle) -> [u8; 32] {
|
|
|
|
|
master_key
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn list(mut db_conn: db::DbHandle, master_key: [u8; 32]) {
|
|
|
|
|
for host in db_conn
|
|
|
|
|
.list_accounts(master_key)
|
|
|
|
|
.into_iter()
|
|
|
|
|
.map(|account: db::Account| account.host)
|
|
|
|
|
{
|
|
|
|
|
println!("{}", host);
|
|
|
|
|
fn list(db_conn: &mut db::DbHandle, master_key: [u8; 32], namespace: &str) {
|
|
|
|
|
for note in db_conn.read_notes(master_key).unwrap() {
|
|
|
|
|
if note.namespace == namespace && note.category == "account" {
|
|
|
|
|
println!("{}", note.title);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn get(mut db_conn: db::DbHandle, master_key: [u8; 32], namespace: &str) {
|
|
|
|
|
fn get(db_conn: &mut db::DbHandle, master_key: [u8; 32], namespace: &str) {
|
|
|
|
|
println!("Reading a site from the database");
|
|
|
|
|
let host: String = Input::with_theme(&ColorfulTheme::default())
|
|
|
|
|
.with_prompt("hostname")
|
|
|
|
@ -120,7 +122,7 @@ fn get(mut db_conn: db::DbHandle, master_key: [u8; 32], namespace: &str) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn set(mut db_conn: db::DbHandle, master_key: [u8; 32], namespace: &str) {
|
|
|
|
|
fn set(db_conn: &mut db::DbHandle, master_key: [u8; 32], namespace: &str) {
|
|
|
|
|
println!("Adding a site to the database");
|
|
|
|
|
let host: String = Input::with_theme(&ColorfulTheme::default())
|
|
|
|
|
.with_prompt("hostname")
|
|
|
|
@ -148,7 +150,7 @@ fn set(mut db_conn: db::DbHandle, master_key: [u8; 32], namespace: &str) {
|
|
|
|
|
println!("Successfully added password");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn dump(mut db_conn: db::DbHandle, master_key: [u8; 32]) {
|
|
|
|
|
fn dump(db_conn: &mut db::DbHandle, master_key: [u8; 32]) {
|
|
|
|
|
for note in db_conn.read_notes(master_key).unwrap() {
|
|
|
|
|
println!("===== note =====");
|
|
|
|
|
println!("namespace: {}", note.namespace);
|
|
|
|
@ -158,19 +160,71 @@ fn dump(mut db_conn: db::DbHandle, master_key: [u8; 32]) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn transfer(mut db_conn: db::DbHandle, master_key: [u8; 32]) {
|
|
|
|
|
for account in db_conn.list_accounts(master_key).into_iter() {
|
|
|
|
|
let new_note = db::Note {
|
|
|
|
|
id: 0,
|
|
|
|
|
namespace: "main".to_owned(),
|
|
|
|
|
category: "account".to_owned(),
|
|
|
|
|
title: account.host,
|
|
|
|
|
value: format!(
|
|
|
|
|
"username: {}\npassword: {}\n",
|
|
|
|
|
account.user, account.password
|
|
|
|
|
),
|
|
|
|
|
fn shell_generate() {
|
|
|
|
|
let spec: String = Input::with_theme(&ColorfulTheme::default())
|
|
|
|
|
.with_prompt("Password Spec")
|
|
|
|
|
.default("30,AAAa111..".to_owned())
|
|
|
|
|
.interact()
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
generate::generate(&spec);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn change_namespace(db_conn: &mut db::DbHandle, master_key: [u8; 32]) -> String {
|
|
|
|
|
let namespaces = db_conn.list_namespaces(master_key).unwrap();
|
|
|
|
|
let selection = Select::with_theme(&ColorfulTheme::default())
|
|
|
|
|
.with_prompt("Change Namespace")
|
|
|
|
|
.default(0)
|
|
|
|
|
.items(&namespaces[..])
|
|
|
|
|
.interact()
|
|
|
|
|
.unwrap();
|
|
|
|
|
namespaces[selection].name.to_owned()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn create_namespace(db_conn: &mut db::DbHandle, master_key: [u8; 32]) -> String {
|
|
|
|
|
let namespace: String = Input::with_theme(&ColorfulTheme::default())
|
|
|
|
|
.with_prompt("Namespace")
|
|
|
|
|
.default("main".to_owned())
|
|
|
|
|
.interact()
|
|
|
|
|
.unwrap();
|
|
|
|
|
namespace
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn shell(db_conn: &mut db::DbHandle, master_key: [u8; 32], _namespace: &str) {
|
|
|
|
|
let mut namespace: String = _namespace.to_owned();
|
|
|
|
|
|
|
|
|
|
loop {
|
|
|
|
|
let selections = &[
|
|
|
|
|
"get",
|
|
|
|
|
"list",
|
|
|
|
|
"set",
|
|
|
|
|
"generate",
|
|
|
|
|
"change namespace",
|
|
|
|
|
"create namespace",
|
|
|
|
|
"exit",
|
|
|
|
|
];
|
|
|
|
|
let main_menu_prompt = format!("Main Menu ({})", namespace);
|
|
|
|
|
|
|
|
|
|
let selection = Select::with_theme(&ColorfulTheme::default())
|
|
|
|
|
.with_prompt(&main_menu_prompt)
|
|
|
|
|
.default(0)
|
|
|
|
|
.items(&selections[..])
|
|
|
|
|
.interact()
|
|
|
|
|
.unwrap();
|
|
|
|
|
match selections[selection] {
|
|
|
|
|
"get" => get(db_conn, master_key, &namespace),
|
|
|
|
|
"list" => list(db_conn, master_key, &namespace),
|
|
|
|
|
"set" => set(db_conn, master_key, &namespace),
|
|
|
|
|
"generate" => shell_generate(),
|
|
|
|
|
"change namespace" => {
|
|
|
|
|
namespace = change_namespace(db_conn, master_key);
|
|
|
|
|
}
|
|
|
|
|
"create namespace" => {
|
|
|
|
|
namespace = create_namespace(db_conn, master_key);
|
|
|
|
|
}
|
|
|
|
|
"exit" => break,
|
|
|
|
|
_ => panic!("Unrecognized command"),
|
|
|
|
|
};
|
|
|
|
|
db_conn.write_note(master_key, new_note);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -191,15 +245,15 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|
|
|
|
let master_key: [u8; 32] = get_master_key(&mut db_conn);
|
|
|
|
|
|
|
|
|
|
if args.cmd_set {
|
|
|
|
|
set(db_conn, master_key, &args.flag_namespace);
|
|
|
|
|
set(&mut db_conn, master_key, &args.flag_namespace);
|
|
|
|
|
} else if args.cmd_get {
|
|
|
|
|
get(db_conn, master_key, &args.flag_namespace);
|
|
|
|
|
get(&mut db_conn, master_key, &args.flag_namespace);
|
|
|
|
|
} else if args.cmd_list {
|
|
|
|
|
list(db_conn, master_key);
|
|
|
|
|
} else if args.cmd_transfer {
|
|
|
|
|
transfer(db_conn, master_key);
|
|
|
|
|
list(&mut db_conn, master_key, &args.flag_namespace);
|
|
|
|
|
} else if args.cmd_dump {
|
|
|
|
|
dump(db_conn, master_key);
|
|
|
|
|
dump(&mut db_conn, master_key);
|
|
|
|
|
} else if args.cmd_shell {
|
|
|
|
|
shell(&mut db_conn, master_key, &args.flag_namespace);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|