From 542dedec8deacf238640d9a9855d07caf6744c8b Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 2 Jun 2019 16:52:59 -0400 Subject: [PATCH 1/9] Implementing ToSql and FromSql for the encrypted value type to write to a BLOB type in sqlite --- src/crypt.rs | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 6 +++ 2 files changed, 115 insertions(+) diff --git a/src/crypt.rs b/src/crypt.rs index 5b9465c..0343f5f 100644 --- a/src/crypt.rs +++ b/src/crypt.rs @@ -6,8 +6,17 @@ use crypto::scrypt::{self, ScryptParams}; use crypto::sha2::Sha256; use rand::rngs::OsRng; use rand::Rng; +use rusqlite::types::FromSql; +use rusqlite::types::FromSqlResult; +use rusqlite::types::ToSqlOutput; +use rusqlite::types::ToSqlOutput::Owned; +use rusqlite::types::Value::Blob; +use rusqlite::types::ValueRef; +use rusqlite::ToSql; use rustc_serialize::base64; use rustc_serialize::base64::{FromBase64, ToBase64}; +use std::convert::TryFrom; +use std::convert::TryInto; use std::io; pub struct EncryptedValue { @@ -16,6 +25,66 @@ pub struct EncryptedValue { pub mac: MacResult, } +impl ToSql for EncryptedValue { + fn to_sql(&self) -> rusqlite::Result { + // Format: + // 8 bytes: length of mac + // n bytes: mac + // 8 bytes: length of iv + // n bytes: iv + // 8 bytes: length of cihpertext + // n bytes: ciphertext + let length_of_ciphertext: u64 = self.ciphertext.len().try_into().unwrap(); + let length_of_iv: u64 = self.iv.len().try_into().unwrap(); + let mac_bytes = self.mac.code(); + let length_of_mac: u64 = mac_bytes.len().try_into().unwrap(); + let full_length = (8 * 3) + length_of_mac + length_of_iv + length_of_ciphertext; + + let mut out: Vec = Vec::with_capacity(full_length.try_into().unwrap()); + out.extend(&length_of_mac.to_le_bytes()); + out.extend(mac_bytes); + out.extend(&length_of_iv.to_le_bytes()); + out.extend(&self.iv); + out.extend(&length_of_ciphertext.to_le_bytes()); + out.extend(&self.ciphertext); + + Ok(Owned(Blob(out))) + } +} + +impl FromSql for EncryptedValue { + fn column_result(value: ValueRef) -> FromSqlResult { + let bytes = value.as_blob().unwrap(); + + let length_of_mac: u64 = + u64::from_le_bytes(bytes[0..8].try_into().expect("Invalid number of bytes")); + let length_of_iv: u64 = u64::from_le_bytes( + bytes[usize::try_from(8 + length_of_mac).unwrap() + ..usize::try_from(16 + length_of_mac).unwrap()] + .try_into() + .expect("Invalid number of bytes"), + ); + let length_of_ciphertext: u64 = u64::from_le_bytes( + bytes[usize::try_from(16 + length_of_mac + length_of_iv).unwrap() + ..usize::try_from(24 + length_of_mac + length_of_iv).unwrap()] + .try_into() + .expect("Invalid number of bytes"), + ); + + Ok(EncryptedValue { + ciphertext: bytes[usize::try_from(24 + length_of_mac + length_of_iv).unwrap() + ..usize::try_from(24 + length_of_mac + length_of_iv + length_of_ciphertext) + .unwrap()] + .to_vec(), + iv: bytes[usize::try_from(16 + length_of_mac).unwrap() + ..usize::try_from(16 + length_of_mac + length_of_iv).unwrap()] + .try_into() + .expect("Invalid number of bytes"), + mac: MacResult::new(&bytes[8..usize::try_from(8 + length_of_mac).unwrap()]), + }) + } +} + pub fn get_master_key(db_conn: &db::DbHandle, master_password: &str) -> io::Result<[u8; 32]> { let scrypt_params: ScryptParams = ScryptParams::new(12, 16, 2); let salt: Vec = get_salt(db_conn)?; @@ -77,3 +146,43 @@ pub fn encrypt_value(value: &str, master_key: [u8; 32]) -> EncryptedValue { mac: hmac.result(), } } + +#[cfg(test)] +mod tests { + use crate::crypt::{encrypt_value, EncryptedValue}; + use rusqlite::{Connection, NO_PARAMS}; + + #[test] + fn test_encrypted_value_round_trip() { + let db = Connection::open_in_memory().expect("Failed to open DB"); + db.execute_batch("CREATE TABLE test (content BLOB);") + .expect("Failed to create table"); + let master_key: [u8; 32] = [0u8; 32]; + let encrypted_value = encrypt_value("hunter2", master_key); + db.execute( + "INSERT INTO test (content) VALUES ($1)", + &[&encrypted_value], + ) + .expect("Failed to insert value into DB"); + + let mut stmt = db + .prepare("SELECT * FROM test") + .expect("Failed to prepare statement"); + let rows: Vec> = stmt + .query_map(NO_PARAMS, |row| { + let val: EncryptedValue = row.get(0).expect("Failed to get element from row"); + Ok(val) + }) + .expect("Failed to get rows") + .collect(); + + assert_eq!(rows.len(), 1); + + for returned_result in rows { + let returned_value: EncryptedValue = returned_result.expect("Bad value returned"); + assert_eq!(returned_value.ciphertext, encrypted_value.ciphertext); + assert_eq!(returned_value.iv, encrypted_value.iv); + assert_eq!(returned_value.mac.code(), encrypted_value.mac.code()); + } + } +} diff --git a/src/main.rs b/src/main.rs index 8c2fb31..09a73e0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,6 +21,7 @@ Usage: foil set [--db=] foil get [--db=] foil list [--db=] + foil transfer [--db=] foil generate foil (-h | --help) @@ -36,6 +37,7 @@ struct Args { cmd_get: bool, cmd_list: bool, cmd_generate: bool, + cmd_transfer: bool, flag_db: Option, arg_spec: Option, } @@ -140,6 +142,8 @@ fn set(mut db_conn: db::DbHandle, master_key: [u8; 32]) { println!("Successfully added password"); } +fn transfer(mut db_conn: db::DbHandle, master_key: [u8; 32]) {} + fn main() -> Result<(), Box> { pretty_env_logger::init(); let args: Args = Docopt::new(USAGE) @@ -162,6 +166,8 @@ fn main() -> Result<(), Box> { get(db_conn, master_key); } else if args.cmd_list { list(db_conn, master_key); + } else if args.cmd_transfer { + transfer(db_conn, master_key); } Ok(()) From 395ee67a20da705faec2d206fa4de6b0e1efadc5 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 2 Jun 2019 18:55:01 -0400 Subject: [PATCH 2/9] Schema for new encoding --- src/crypt.rs | 2 ++ src/db.rs | 14 ++++++++++++++ src/init.sql | 16 ++++++++++++++++ src/main.rs | 10 +++++++++- 4 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/crypt.rs b/src/crypt.rs index 0343f5f..fa9fdb7 100644 --- a/src/crypt.rs +++ b/src/crypt.rs @@ -154,6 +154,8 @@ mod tests { #[test] fn test_encrypted_value_round_trip() { + // Test that writing a value to the DB and reading it back + // doesn't result in any corruption let db = Connection::open_in_memory().expect("Failed to open DB"); db.execute_batch("CREATE TABLE test (content BLOB);") .expect("Failed to create table"); diff --git a/src/db.rs b/src/db.rs index 753d1ee..7bd6293 100644 --- a/src/db.rs +++ b/src/db.rs @@ -26,6 +26,20 @@ pub struct Account { pub password: String, } +#[derive(Debug)] +pub struct DbNamespace { + pub id: i64, + pub name: String, +} + +#[derive(Debug)] +pub struct DbNote { + pub id: i64, + pub category: String, + pub title: String, + pub value: String, +} + impl DbHandle { pub fn new(db_path: &Option) -> DbHandle { let path: PathBuf = db_path diff --git a/src/init.sql b/src/init.sql index 2e3c049..cff6f5c 100644 --- a/src/init.sql +++ b/src/init.sql @@ -1,3 +1,5 @@ +PRAGMA foreign_keys = ON; + CREATE TABLE IF NOT EXISTS encrypted_values ( id INTEGER PRIMARY KEY AUTOINCREMENT, iv TEXT, @@ -21,3 +23,17 @@ CREATE TABLE IF NOT EXISTS accounts( user INTEGER NOT NULL, password INTEGER NOT NULL ); + +CREATE TABLE IF NOT EXISTS namespaces( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name BLOB NOT NULL +); + +CREATE TABLE IF NOT EXISTS notes( + id INTEGER PRIMARY KEY AUTOINCREMENT, + namespace INTEGER, + category TEXT NOT NULL CHECK(category IN ('account', 'note')), + title BLOB NOT NULL, + value BLOB NOT NULL, + FOREIGN KEY(namespace) REFERENCES namespaces(id) +); diff --git a/src/main.rs b/src/main.rs index 09a73e0..1d7d411 100644 --- a/src/main.rs +++ b/src/main.rs @@ -142,7 +142,15 @@ fn set(mut db_conn: db::DbHandle, master_key: [u8; 32]) { println!("Successfully added password"); } -fn transfer(mut db_conn: db::DbHandle, master_key: [u8; 32]) {} +fn transfer(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 main() -> Result<(), Box> { pretty_env_logger::init(); From 3c3b7f835ebae70eb48a90f7408de788b41da314 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 2 Jun 2019 20:49:19 -0400 Subject: [PATCH 3/9] getting namespaces --- src/crypt.rs | 24 ++++++++++++++++++++++++ src/db.rs | 44 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/src/crypt.rs b/src/crypt.rs index fa9fdb7..db6f592 100644 --- a/src/crypt.rs +++ b/src/crypt.rs @@ -17,6 +17,7 @@ use rustc_serialize::base64; use rustc_serialize::base64::{FromBase64, ToBase64}; use std::convert::TryFrom; use std::convert::TryInto; +use std::error::Error; use std::io; pub struct EncryptedValue { @@ -85,6 +86,29 @@ impl FromSql for EncryptedValue { } } +impl EncryptedValue { + pub fn decrypt_to_bytes(&self, master_key: [u8; 32]) -> Vec { + let mut hmac = Hmac::new(Sha256::new(), &master_key); + hmac.input(&self.ciphertext); + if hmac.result() != self.mac { + panic!("Mac did not match, corrupted data"); + } + + let mut cipher = aes::ctr(KeySize::KeySize256, &master_key, &self.iv); + let mut output: Vec = vec![0; self.ciphertext.len()]; + cipher.process(&self.ciphertext, output.as_mut_slice()); + output + } + + pub fn decrypt_to_string( + &self, + master_key: [u8; 32], + ) -> Result { + let decrypted_bytes = self.decrypt_to_bytes(master_key); + String::from_utf8(decrypted_bytes) + } +} + pub fn get_master_key(db_conn: &db::DbHandle, master_password: &str) -> io::Result<[u8; 32]> { let scrypt_params: ScryptParams = ScryptParams::new(12, 16, 2); let salt: Vec = get_salt(db_conn)?; diff --git a/src/db.rs b/src/db.rs index 7bd6293..a854a61 100644 --- a/src/db.rs +++ b/src/db.rs @@ -1,6 +1,6 @@ use super::crypt; use crate::crypt::EncryptedValue; -use rusqlite::{Connection, NO_PARAMS}; +use rusqlite::{params, Connection, NO_PARAMS}; use rustc_serialize::base64; use rustc_serialize::base64::{FromBase64, ToBase64}; use std::error::Error; @@ -26,15 +26,15 @@ pub struct Account { pub password: String, } -#[derive(Debug)] pub struct DbNamespace { pub id: i64, - pub name: String, + pub name: EncryptedValue, } #[derive(Debug)] pub struct DbNote { pub id: i64, + pub namespace: String, pub category: String, pub title: String, pub value: String, @@ -53,6 +53,44 @@ impl DbHandle { DbHandle { conn: conn } } + pub fn get_namespace_id( + &mut self, + name: &str, + master_key: [u8; 32], + ) -> Result> { + { + let mut stmt = self + .conn + .prepare("SELECT id, name FROM namespaces") + .unwrap(); + let rows = stmt.query_map(params![], |row| { + Ok(DbNamespace { + id: row.get(0)?, + name: row.get(1)?, + }) + })?; + + for row_result in rows { + let row: DbNamespace = row_result?; + let row_name: String = row.name.decrypt_to_string(master_key)?; + if name == row_name { + return Ok(row.id); + } + } + } + + let new_namespace = crypt::encrypt_value(name, master_key); + let tx = self.conn.transaction().unwrap(); + tx.execute( + "INSERT INTO namespaces (name) VALUES ($1)", + &[&new_namespace], + ) + .unwrap(); + let rowid: i64 = tx.last_insert_rowid(); + let _ = tx.commit().unwrap(); + Ok(rowid) + } + pub fn get_db_property(&self, name: &str) -> Result, Box> { let mut stmt = self .conn From 38c67e3c27c789f0fc2afce3c7d3dfd514e1f585 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 2 Jun 2019 22:21:53 -0400 Subject: [PATCH 4/9] writing and reading notes --- src/db.rs | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/src/db.rs b/src/db.rs index a854a61..b6a5a10 100644 --- a/src/db.rs +++ b/src/db.rs @@ -31,8 +31,16 @@ pub struct DbNamespace { pub name: EncryptedValue, } -#[derive(Debug)] pub struct DbNote { + pub id: i64, + pub namespace: DbNamespace, + pub category: String, + pub title: EncryptedValue, + pub value: EncryptedValue, +} + +#[derive(Debug)] +pub struct Note { pub id: i64, pub namespace: String, pub category: String, @@ -91,6 +99,54 @@ impl DbHandle { Ok(rowid) } + pub fn write_note(&mut self, master_key: [u8; 32], note: Note) { + let namespace_id = self.get_namespace_id(¬e.namespace, master_key).unwrap(); + let encrypted_title = crypt::encrypt_value(¬e.title, master_key); + let encrypted_value = crypt::encrypt_value(¬e.value, master_key); + self.conn + .execute( + "INSERT INTO notes (namespace, category, title, value) VALUES ($1, $2, $3, $4);", + params![ + namespace_id, + note.category, + encrypted_title, + encrypted_value + ], + ) + .unwrap(); + } + + pub fn read_notes(&mut self, master_key: [u8; 32]) -> rusqlite::Result> { + let mut stmt = self.conn.prepare("SELECT notes.id, notes.category, notes.title, notes.value, namespaces.id, namespaces.name FROM notes JOIN namespaces ON notes.namespace=namespaces.id").unwrap(); + let rows = stmt.query_map(params![], |row| { + Ok(DbNote { + id: row.get(0)?, + category: row.get(1)?, + title: row.get(2)?, + value: row.get(3)?, + namespace: DbNamespace { + id: row.get(4)?, + name: row.get(5)?, + }, + }) + })?; + + let notes: Result, _> = rows + .map(|note_result| match note_result { + Ok(note) => Ok(Note { + id: note.id, + namespace: note.namespace.name.decrypt_to_string(master_key).unwrap(), + category: note.category, + title: note.title.decrypt_to_string(master_key).unwrap(), + value: note.value.decrypt_to_string(master_key).unwrap(), + }), + Err(e) => return Err(e), + }) + .collect(); + + notes + } + pub fn get_db_property(&self, name: &str) -> Result, Box> { let mut stmt = self .conn From 121f3bb9cd4183c115f28d99d4d8c2e30694c822 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 2 Jun 2019 23:05:38 -0400 Subject: [PATCH 5/9] transfer function --- src/crypt.rs | 1 - src/db.rs | 36 +++++++++++++++++++++++++----------- src/main.rs | 18 ++++++++++++------ 3 files changed, 37 insertions(+), 18 deletions(-) diff --git a/src/crypt.rs b/src/crypt.rs index db6f592..1d40ae7 100644 --- a/src/crypt.rs +++ b/src/crypt.rs @@ -17,7 +17,6 @@ use rustc_serialize::base64; use rustc_serialize::base64::{FromBase64, ToBase64}; use std::convert::TryFrom; use std::convert::TryInto; -use std::error::Error; use std::io; pub struct EncryptedValue { diff --git a/src/db.rs b/src/db.rs index b6a5a10..d79c916 100644 --- a/src/db.rs +++ b/src/db.rs @@ -100,20 +100,34 @@ impl DbHandle { } pub fn write_note(&mut self, master_key: [u8; 32], note: Note) { + let existing_notes = self.read_notes(master_key).unwrap(); let namespace_id = self.get_namespace_id(¬e.namespace, master_key).unwrap(); + let tx = self.conn.transaction().unwrap(); + + for existing_note in existing_notes { + if existing_note.namespace == note.namespace + && existing_note.category == note.category + && existing_note.title == note.title + { + tx.execute("DELETE FROM notes WHERE id=$1;", params![existing_note.id]) + .unwrap(); + } + } + let encrypted_title = crypt::encrypt_value(¬e.title, master_key); let encrypted_value = crypt::encrypt_value(¬e.value, master_key); - self.conn - .execute( - "INSERT INTO notes (namespace, category, title, value) VALUES ($1, $2, $3, $4);", - params![ - namespace_id, - note.category, - encrypted_title, - encrypted_value - ], - ) - .unwrap(); + tx.execute( + "INSERT INTO notes (namespace, category, title, value) VALUES ($1, $2, $3, $4);", + params![ + namespace_id, + note.category, + encrypted_title, + encrypted_value + ], + ) + .unwrap(); + + let _ = tx.commit().unwrap(); } pub fn read_notes(&mut self, master_key: [u8; 32]) -> rusqlite::Result> { diff --git a/src/main.rs b/src/main.rs index 1d7d411..e38f94d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -143,12 +143,18 @@ fn set(mut db_conn: db::DbHandle, master_key: [u8; 32]) { } fn transfer(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); + 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 + ), + }; + db_conn.write_note(master_key, new_note); } } From a9b3c14b47b444cc04b11c508a0f5c5def5e2634 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 2 Jun 2019 23:21:16 -0400 Subject: [PATCH 6/9] add dump command --- src/main.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index e38f94d..ad54123 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,6 +22,7 @@ Usage: foil get [--db=] foil list [--db=] foil transfer [--db=] + foil dump [--db=] foil generate foil (-h | --help) @@ -38,6 +39,7 @@ struct Args { cmd_list: bool, cmd_generate: bool, cmd_transfer: bool, + cmd_dump: bool, flag_db: Option, arg_spec: Option, } @@ -142,6 +144,16 @@ fn set(mut db_conn: db::DbHandle, master_key: [u8; 32]) { println!("Successfully added password"); } +fn dump(mut db_conn: db::DbHandle, master_key: [u8; 32]) { + for note in db_conn.read_notes(master_key).unwrap() { + println!("===== note ====="); + println!("namespace: {}", note.namespace); + println!("category: {}", note.category); + println!("title: {}", note.title); + println!("{}", note.value); + } +} + 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 { @@ -150,7 +162,7 @@ fn transfer(mut db_conn: db::DbHandle, master_key: [u8; 32]) { category: "account".to_owned(), title: account.host, value: format!( - "username: {}\npassword:{}\n", + "username: {}\npassword: {}\n", account.user, account.password ), }; @@ -182,6 +194,8 @@ fn main() -> Result<(), Box> { list(db_conn, master_key); } else if args.cmd_transfer { transfer(db_conn, master_key); + } else if args.cmd_dump { + dump(db_conn, master_key); } Ok(()) From b362561466e00109cb43c5c304439edb41122417 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 2 Jun 2019 23:42:35 -0400 Subject: [PATCH 7/9] updated get to operate against the new schema --- src/main.rs | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/main.rs b/src/main.rs index ad54123..51f7bd0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,7 +19,7 @@ foil Usage: foil set [--db=] - foil get [--db=] + foil get [--namespace=] [--db=] foil list [--db=] foil transfer [--db=] foil dump [--db=] @@ -27,9 +27,10 @@ Usage: foil (-h | --help) Options: - --db= The path to the sqlite database [default: db.sqlite3]. - -h --help Show this screen. - --version Show version. + --db= The path to the sqlite database [default: db.sqlite3]. + -n DB, --namespace= An identifier for a group of secrets [default: main] + -h, --help Show this screen. + --version Show version. "; #[derive(Debug, Deserialize)] @@ -41,6 +42,7 @@ struct Args { cmd_transfer: bool, cmd_dump: bool, flag_db: Option, + flag_namespace: String, arg_spec: Option, } @@ -65,10 +67,6 @@ fn get_master_key(db_conn: &mut db::DbHandle) -> [u8; 32] { 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() @@ -104,17 +102,20 @@ fn list(mut db_conn: db::DbHandle, master_key: [u8; 32]) { } } -fn get(mut db_conn: db::DbHandle, master_key: [u8; 32]) { +fn get(mut db_conn: 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") .interact() .unwrap(); - for account in db_conn.list_accounts(master_key) { - if account.host == host { - println!("username: {}", account.user); - println!("password: {}", account.password); + for note in db_conn.read_notes(master_key).unwrap() { + if note.namespace == namespace && note.title == host && note.category == "account" { + println!("===== note ====="); + println!("namespace: {}", note.namespace); + println!("category: {}", note.category); + println!("title: {}", note.title); + println!("{}", note.value); } } } @@ -189,7 +190,7 @@ fn main() -> Result<(), Box> { if args.cmd_set { set(db_conn, master_key); } else if args.cmd_get { - get(db_conn, master_key); + get(db_conn, master_key, &args.flag_namespace); } else if args.cmd_list { list(db_conn, master_key); } else if args.cmd_transfer { From cf153397129604912b7017a8fbcc96bd61117a7c Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 2 Jun 2019 23:50:18 -0400 Subject: [PATCH 8/9] set command updated --- src/main.rs | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/main.rs b/src/main.rs index 51f7bd0..97c0af9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,7 +18,7 @@ static USAGE: &'static str = " foil Usage: - foil set [--db=] + foil set [--namespace=] [--db=] foil get [--namespace=] [--db=] foil list [--db=] foil transfer [--db=] @@ -120,7 +120,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]) { +fn set(mut db_conn: 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") @@ -130,18 +130,21 @@ fn set(mut db_conn: db::DbHandle, master_key: [u8; 32]) { .with_prompt("username") .interact() .unwrap(); - let encrypted_host: crypt::EncryptedValue = crypt::encrypt_value(&host, master_key); - let encrypted_username: crypt::EncryptedValue = crypt::encrypt_value(&username, master_key); - let encrypted_password: crypt::EncryptedValue = { - let password: String = PasswordInput::with_theme(&ColorfulTheme::default()) - .with_prompt("Site password") - .with_confirmation("Repeat site password", "Error: the passwords don't match.") - .interact() - .unwrap(); - crypt::encrypt_value(&password, master_key) - }; - db_conn.delete_account_with_host(master_key, &host); - let _account_id = db_conn.write_account(encrypted_host, encrypted_username, encrypted_password); + let password: String = PasswordInput::with_theme(&ColorfulTheme::default()) + .with_prompt("Site password") + .with_confirmation("Repeat site password", "Error: the passwords don't match.") + .interact() + .unwrap(); + db_conn.write_note( + master_key, + db::Note { + id: 0, + namespace: namespace.to_string(), + category: "account".to_string(), + title: host, + value: format!("username: {}\npassword: {}\n", username, password), + }, + ); println!("Successfully added password"); } @@ -188,7 +191,7 @@ fn main() -> Result<(), Box> { let master_key: [u8; 32] = get_master_key(&mut db_conn); if args.cmd_set { - set(db_conn, master_key); + set(db_conn, master_key, &args.flag_namespace); } else if args.cmd_get { get(db_conn, master_key, &args.flag_namespace); } else if args.cmd_list { From 2fabbe8789c9df134501765c6d1087531f1d6723 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 2 Jun 2019 23:56:08 -0400 Subject: [PATCH 9/9] update list to the new code --- src/main.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/main.rs b/src/main.rs index 97c0af9..2a2abf1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,7 +20,7 @@ foil Usage: foil set [--namespace=] [--db=] foil get [--namespace=] [--db=] - foil list [--db=] + foil list [--namespace=] [--db=] foil transfer [--db=] foil dump [--db=] foil generate @@ -92,13 +92,11 @@ 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(mut db_conn: 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); + } } } @@ -195,7 +193,7 @@ fn main() -> Result<(), Box> { } else if args.cmd_get { get(db_conn, master_key, &args.flag_namespace); } else if args.cmd_list { - list(db_conn, master_key); + list(db_conn, master_key, &args.flag_namespace); } else if args.cmd_transfer { transfer(db_conn, master_key); } else if args.cmd_dump {