reading and writing passwords

master
Tom Alexander 5 years ago
parent b8f407c2b9
commit 7276f84233

@ -60,3 +60,20 @@ pub fn decrypt_value(value: Vec<u8>, master_key: [u8; 32], iv: [u8; 32], mac: [u
cipher.process(&value, output.as_mut_slice());
output
}
pub fn encrypt_value(value: &str, master_key: [u8; 32]) -> EncryptedValue {
let mut random = OsRng::new().unwrap();
let iv: [u8; 32] = random.gen::<[u8; 32]>();
let mut cipher = aes::ctr(KeySize::KeySize256, &master_key, &iv);
let mut output: Vec<u8> = vec![0; value.len() as usize];
cipher.process(value.as_bytes(), output.as_mut_slice());
let mut hmac = Hmac::new(Sha256::new(), &master_key);
hmac.input(&output[..]);
EncryptedValue {
ciphertext: output,
iv: iv,
mac: hmac.result(),
}
}

@ -1,4 +1,5 @@
use super::crypt;
use crate::crypt::EncryptedValue;
use rusqlite::{Connection, NO_PARAMS};
use rustc_serialize::base64;
use rustc_serialize::base64::{FromBase64, ToBase64};
@ -119,6 +120,64 @@ impl DbHandle {
};
result
}
pub fn write_encrypted_value(&mut self, val: EncryptedValue) -> i64 {
let b64ciphertext: String = val.ciphertext.to_base64(base64::STANDARD);
let b64iv: String = val.iv.to_base64(base64::STANDARD);
let b64mac: String = val.mac.code().to_base64(base64::STANDARD);
let tx = self.conn.transaction().unwrap();
tx.execute(
"INSERT INTO encrypted_values (iv, ciphertext, mac) VALUES ($1, $2, $3)",
&[&b64iv, &b64ciphertext, &b64mac],
)
.unwrap();
let rowid: i64 = tx.last_insert_rowid();
let _ = tx.commit().unwrap();
rowid
}
pub fn write_account(
&mut self,
host: EncryptedValue,
username: EncryptedValue,
password: EncryptedValue,
) -> i64 {
// TODO: This should be a transaction
let host_id = self.write_encrypted_value(host);
let user_id = self.write_encrypted_value(username);
let password_id = self.write_encrypted_value(password);
self.conn
.execute(
"INSERT INTO accounts (server, user, password) VALUES ($1, $2, $3)",
&[&host_id, &user_id, &password_id],
)
.unwrap();
self.conn.last_insert_rowid()
}
pub fn delete_account_with_host(&mut self, master_key: [u8; 32], host: &str) {
let accounts: Vec<Account> = self.list_accounts(master_key);
let tx = self.conn.transaction().unwrap();
for account in accounts {
if account.host == host {
tx.execute(
"DELETE FROM encrypted_values WHERE exists \
(SELECT 1 FROM accounts WHERE \
(accounts.server = encrypted_values.id OR \
accounts.user = encrypted_values.id OR \
accounts.password = encrypted_values.id) AND \
accounts.id=$1);",
&[&account.id],
)
.unwrap();
tx.execute("DELETE FROM accounts WHERE id=$1;", &[&account.id])
.unwrap();
}
}
let _ = tx.commit().unwrap();
}
}
fn decrypt_base64(iv: String, ciphertext: String, mac: String, master_key: [u8; 32]) -> Vec<u8> {

@ -1,7 +1,7 @@
use crypto::hmac::Hmac;
use crypto::mac::{Mac, MacResult};
use crypto::sha2::Sha256;
use dialoguer::{theme::ColorfulTheme, PasswordInput};
use dialoguer::{theme::ColorfulTheme, Input, PasswordInput};
use docopt::Docopt;
use log::debug;
use rand::rngs::OsRng;
@ -100,6 +100,46 @@ fn list(mut db_conn: db::DbHandle, master_key: [u8; 32]) {
}
}
fn get(mut db_conn: db::DbHandle, master_key: [u8; 32]) {
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);
}
}
}
fn set(mut db_conn: db::DbHandle, master_key: [u8; 32]) {
println!("Adding a site to the database");
let host: String = Input::with_theme(&ColorfulTheme::default())
.with_prompt("hostname")
.interact()
.unwrap();
let username: String = Input::with_theme(&ColorfulTheme::default())
.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);
println!("Successfully added password");
}
fn main() -> Result<(), Box<dyn Error>> {
pretty_env_logger::init();
let args: Args = Docopt::new(USAGE)
@ -116,7 +156,11 @@ fn main() -> Result<(), Box<dyn Error>> {
let master_key: [u8; 32] = get_master_key(&mut db_conn);
if args.cmd_list {
if args.cmd_set {
set(db_conn, master_key);
} else if args.cmd_get {
get(db_conn, master_key);
} else if args.cmd_list {
list(db_conn, master_key);
}

Loading…
Cancel
Save