reading and writing passwords
This commit is contained in:
parent
b8f407c2b9
commit
7276f84233
17
src/crypt.rs
17
src/crypt.rs
@ -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());
|
cipher.process(&value, output.as_mut_slice());
|
||||||
output
|
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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
59
src/db.rs
59
src/db.rs
@ -1,4 +1,5 @@
|
|||||||
use super::crypt;
|
use super::crypt;
|
||||||
|
use crate::crypt::EncryptedValue;
|
||||||
use rusqlite::{Connection, NO_PARAMS};
|
use rusqlite::{Connection, NO_PARAMS};
|
||||||
use rustc_serialize::base64;
|
use rustc_serialize::base64;
|
||||||
use rustc_serialize::base64::{FromBase64, ToBase64};
|
use rustc_serialize::base64::{FromBase64, ToBase64};
|
||||||
@ -119,6 +120,64 @@ impl DbHandle {
|
|||||||
};
|
};
|
||||||
result
|
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> {
|
fn decrypt_base64(iv: String, ciphertext: String, mac: String, master_key: [u8; 32]) -> Vec<u8> {
|
||||||
|
48
src/main.rs
48
src/main.rs
@ -1,7 +1,7 @@
|
|||||||
use crypto::hmac::Hmac;
|
use crypto::hmac::Hmac;
|
||||||
use crypto::mac::{Mac, MacResult};
|
use crypto::mac::{Mac, MacResult};
|
||||||
use crypto::sha2::Sha256;
|
use crypto::sha2::Sha256;
|
||||||
use dialoguer::{theme::ColorfulTheme, PasswordInput};
|
use dialoguer::{theme::ColorfulTheme, Input, PasswordInput};
|
||||||
use docopt::Docopt;
|
use docopt::Docopt;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use rand::rngs::OsRng;
|
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>> {
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
pretty_env_logger::init();
|
pretty_env_logger::init();
|
||||||
let args: Args = Docopt::new(USAGE)
|
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);
|
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);
|
list(db_conn, master_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user