use std::{convert::TryInto, path::Path, str::FromStr}; use crypto::CryptoError; use ed25519_dalek::{Keypair, Signer, SIGNATURE_LENGTH, PUBLIC_KEY_LENGTH}; use rusqlite::{Connection, NO_PARAMS, params}; use dirs; use utils::to_uuid_bytes; use uuid::Uuid; use zeroize::Zeroize; use crate::{constants, crypto, print_error, utils}; const DB_NAME: &str = "Identities.db"; const IDENTITY_TABLE: &str = "identities"; const CONTACTS_TABLE: &str = "contacts"; const FILES_TABLE: &str = "files"; fn bool_to_byte(b: bool) -> u8 { if b { 75 } else { 30 } //completely arbitrary values } fn byte_to_bool(b: u8) -> Result { if b == 75 { Ok(true) } else if b == 30 { Ok(false) } else { Err(()) } } fn get_database_path() -> String { Path::new(&dirs::config_dir().unwrap()).join(constants::APPLICATION_FOLDER).join(DB_NAME).to_str().unwrap().to_owned() } struct EncryptedIdentity { name: String, encrypted_keypair: Vec, salt: Vec, encrypted_master_key: Vec } fn get_identity_by_uuid(uuid: Uuid) -> Result, rusqlite::Error>{ let db = Connection::open(get_database_path())?; let mut stmt = db.prepare(&("SELECT name, key, salt, masterkey FROM ".to_owned()+IDENTITY_TABLE+" WHERE uuid=?1"))?; let mut rows = stmt.query(vec![&uuid.as_bytes()[..]])?; Ok(match rows.next()? { Some(row) => { let name: Vec = row.get(0)?; Some( EncryptedIdentity { name: std::str::from_utf8(name.as_slice()).unwrap().to_owned(), encrypted_keypair: row.get(1)?, salt: row.get(2)?, encrypted_master_key: row.get(3)? } ) } None => None }) } pub struct Contact { pub uuid: Uuid, pub public_key: [u8; PUBLIC_KEY_LENGTH], pub name: String, pub verified: bool, } pub struct Identity { pub uuid: Uuid, pub name: String, key: Keypair, pub master_key: [u8; crypto::MASTER_KEY_LEN] } impl Identity { pub fn sign(&self, input: &[u8]) -> [u8; SIGNATURE_LENGTH] { self.key.sign(input).to_bytes() } pub fn get_public_key(&self) -> [u8; PUBLIC_KEY_LENGTH] { self.key.public.to_bytes() } fn get_database_path(&self) -> String { Path::new(&dirs::config_dir().unwrap()).join(constants::APPLICATION_FOLDER).join(self.uuid.to_string()+".db").to_str().unwrap().to_owned() } pub fn add_contact(&self, name: String, public_key: [u8; PUBLIC_KEY_LENGTH]) -> Result { let db = Connection::open(self.get_database_path())?; db.execute(&("CREATE TABLE IF NOT EXISTS ".to_owned()+CONTACTS_TABLE+"(uuid BLOB PRIMARY KEY, name BLOB, key BLOB, verified BLOB)"), NO_PARAMS)?; let contact_uuid = Uuid::new_v4(); let encrypted_name = crypto::encrypt_data(name.as_bytes(), &self.master_key).unwrap(); let encrypted_public_key = crypto::encrypt_data(&public_key, &self.master_key).unwrap(); let encrypted_verified = crypto::encrypt_data(&[bool_to_byte(false)], &self.master_key).unwrap(); db.execute(&("INSERT INTO ".to_owned()+CONTACTS_TABLE+" (uuid, name, key, verified) VALUES (?1, ?2, ?3, ?4)"), vec![&contact_uuid.as_bytes()[..], encrypted_name.as_slice(), encrypted_public_key.as_slice(), encrypted_verified.as_slice()])?; Ok(Contact { uuid: contact_uuid, public_key: public_key, name: name, verified: false }) } pub fn remove_contact(&self, uuid: &Uuid) -> Result<(), rusqlite::Error> { let db = Connection::open(self.get_database_path())?; db.execute(&("DELETE FROM ".to_owned()+CONTACTS_TABLE+" WHERE uuid=?"), &[&uuid.as_bytes()[..]])?; db.execute(&format!("DROP TABLE IF EXISTS \"{}\"", uuid), NO_PARAMS)?; db.execute(&format!("DELETE FROM {} WHERE contact_uuid=?", FILES_TABLE), &[&uuid.as_bytes()[..]])?; Ok(()) } pub fn set_verified(&self, uuid: &Uuid) -> Result<(), rusqlite::Error> { let db = Connection::open(self.get_database_path())?; let encrypted_verified = crypto::encrypt_data(&[bool_to_byte(true)], &self.master_key).unwrap(); db.execute(&format!("UPDATE {} SET verified=?1 WHERE uuid=?2", CONTACTS_TABLE), &[encrypted_verified.as_slice(), &uuid.as_bytes()[..]])?; Ok(()) } pub fn load_contacts(&self) -> Option> { match Connection::open(self.get_database_path()) { Ok(db) => { match db.prepare(&("SELECT uuid, name, key, verified FROM ".to_owned()+CONTACTS_TABLE)) { Ok(mut stmt) => { let mut rows = stmt.query(NO_PARAMS).unwrap(); let mut contacts = Vec::new(); while let Some(row) = rows.next().unwrap() { let encrypted_public_key: Vec = row.get(2).unwrap(); match crypto::decrypt_data(encrypted_public_key.as_slice(), &self.master_key){ Ok(public_key) => { if public_key.len() == PUBLIC_KEY_LENGTH { let encrypted_name: Vec = row.get(1).unwrap(); match crypto::decrypt_data(encrypted_name.as_slice(), &self.master_key) { Ok(name) => { let encrypted_verified: Vec = row.get(3).unwrap(); match crypto::decrypt_data(encrypted_verified.as_slice(), &self.master_key){ Ok(verified) => { let uuid: Vec = row.get(0).unwrap(); match to_uuid_bytes(&uuid) { Some(uuid_bytes) => { contacts.push(Contact { uuid: Uuid::from_bytes(uuid_bytes), public_key: public_key.try_into().unwrap(), name: std::str::from_utf8(name.as_slice()).unwrap().to_owned(), verified: byte_to_bool(verified[0]).unwrap() }) } None => {} } } Err(e) => print_error(e) } } Err(e) => print_error(e) } } else { print_error("Invalid public key length: database corrupted"); } } Err(e) => print_error(e) } } Some(contacts) } Err(e) => { print_error(e); None } } } Err(e) => { print_error(e); None } } } pub fn clear_temporary_files(&self) -> Result { let db = Connection::open(self.get_database_path())?; db.execute(&format!("DELETE FROM {} WHERE contact_uuid IS NULL", FILES_TABLE), NO_PARAMS) } pub fn load_file(&self, uuid: Uuid) -> Option> { match Connection::open(self.get_database_path()) { Ok(db) => { match db.prepare(&format!("SELECT uuid, data FROM \"{}\"", FILES_TABLE)) { Ok(mut stmt) => { let mut rows = stmt.query(NO_PARAMS).unwrap(); while let Some(row) = rows.next().unwrap() { let encrypted_uuid: Vec = row.get(0).unwrap(); match crypto::decrypt_data(encrypted_uuid.as_slice(), &self.master_key){ Ok(test_uuid) => { if test_uuid == uuid.as_bytes() { let encrypted_data: Vec = row.get(1).unwrap(); match crypto::decrypt_data(encrypted_data.as_slice(), &self.master_key) { Ok(data) => return Some(data), Err(e) => print_error(e) } } } Err(e) => print_error(e) } } None } Err(e) => { print_error(e); None } } } Err(e) => { print_error(e); None } } } pub fn store_file(&self, contact_uuid: Option, data: &[u8]) -> Result { let db = Connection::open(self.get_database_path())?; db.execute(&format!("CREATE TABLE IF NOT EXISTS \"{}\" (contact_uuid BLOB, uuid BLOB, data BLOB)", FILES_TABLE), NO_PARAMS)?; let file_uuid = Uuid::new_v4(); let encrypted_uuid = crypto::encrypt_data(file_uuid.as_bytes(), &self.master_key).unwrap(); let encrypted_data = crypto::encrypt_data(data, &self.master_key).unwrap(); let query = format!("INSERT INTO \"{}\" (contact_uuid, uuid, data) VALUES (?1, ?2, ?3)", FILES_TABLE); match contact_uuid { Some(uuid) => db.execute(&query, params![&uuid.as_bytes()[..], &encrypted_uuid, &encrypted_data])?, None => db.execute(&query, params![None as Option>, &encrypted_uuid, &encrypted_data])? }; Ok(file_uuid) } pub fn store_msg(&self, contact_uuid: &Uuid, outgoing: bool, data: &[u8]) -> Result<(), rusqlite::Error> { let db = Connection::open(self.get_database_path())?; db.execute(&format!("CREATE TABLE IF NOT EXISTS \"{}\" (outgoing BLOB, data BLOB)", contact_uuid), NO_PARAMS)?; let outgoing_byte: u8 = bool_to_byte(outgoing); let encrypted_outgoing = crypto::encrypt_data(&[outgoing_byte], &self.master_key).unwrap(); let encrypted_data = crypto::encrypt_data(data, &self.master_key).unwrap(); db.execute(&format!("INSERT INTO \"{}\" (outgoing, data) VALUES (?1, ?2)", contact_uuid), vec![encrypted_outgoing, encrypted_data])?; Ok(()) } pub fn load_msgs(&self, contact_uuid: &Uuid) -> Option)>> { match Connection::open(self.get_database_path()) { Ok(db) => { match db.prepare(&format!("SELECT outgoing, data FROM \"{}\"", contact_uuid)) { Ok(mut stmt) => { let mut rows = stmt.query(NO_PARAMS).unwrap(); let mut msgs = Vec::new(); while let Some(row) = rows.next().unwrap() { let encrypted_outgoing: Vec = row.get(0).unwrap(); match crypto::decrypt_data(encrypted_outgoing.as_slice(), &self.master_key){ Ok(outgoing) => { match byte_to_bool(outgoing[0]) { Ok(outgoing) => { let encrypted_data: Vec = row.get(1).unwrap(); match crypto::decrypt_data(encrypted_data.as_slice(), &self.master_key) { Ok(data) => { msgs.push( ( outgoing, data ) ) }, Err(e) => print_error(e) } } Err(_) => {} } } Err(e) => print_error(e) } } Some(msgs) } Err(e) => { print_error(e); None } } } Err(e) => { print_error(e); None } } } pub fn zeroize(&mut self){ self.master_key.zeroize(); self.key.secret.zeroize(); } pub fn get_identity(uuid: &str, password: &str) -> Result { let uuid = Uuid::from_str(&uuid).unwrap(); match get_identity_by_uuid(uuid) { Ok(encrypted_identity) => { match encrypted_identity { Some(encrypted_identity) => { match crypto::decrypt_master_key(encrypted_identity.encrypted_master_key.as_slice(), password, encrypted_identity.salt.as_slice()) { Ok(master_key) => { match crypto::decrypt_data(encrypted_identity.encrypted_keypair.as_slice(), &master_key) { Ok(keypair) => { Ok(Identity{ uuid: uuid, name: encrypted_identity.name, key: Keypair::from_bytes(&keypair[..]).unwrap(), master_key: master_key }) } Err(e) => { print_error(e); Err("Database corrupted".to_owned()) } } } Err(e) => { Err(match e { CryptoError::DecryptionFailed => "Bad password".to_owned(), CryptoError::InvalidLength => "Database corrupted".to_owned() }) } } } None => { Err("No such identity".to_owned()) } } } Err(e) => { Err(e.to_string()) } } } pub fn get_identities() -> Result, rusqlite::Error> { let db = Connection::open(get_database_path())?; let mut stmt = db.prepare(&("SELECT uuid, name FROM ".to_owned()+IDENTITY_TABLE))?; let mut rows = stmt.query(NO_PARAMS)?; let mut identities = Vec::new(); while let Some(row) = rows.next()? { let uuid: Vec = row.get(0)?; let name: Vec = row.get(1)?; match to_uuid_bytes(&uuid) { Some(uuid_bytes) => identities.push( ( Uuid::from_bytes(uuid_bytes), std::str::from_utf8(name.as_slice()).unwrap().to_owned() ) ), None => {} } } Ok(identities) } pub fn create_new_identidy(name: &str, password: &str) -> Result { let db = Connection::open(get_database_path())?; db.execute(&("CREATE TABLE IF NOT EXISTS ".to_owned()+IDENTITY_TABLE+"(uuid BLOB PRIMARY KEY, name TEXT, key BLOB, salt BLOB, masterkey BLOB)"), NO_PARAMS)?; let uuid = Uuid::new_v4(); let keypair = Keypair::generate(&mut rand_7::rngs::OsRng); let master_key = crypto::generate_master_key(); let encrypted_keypair = crypto::encrypt_data(&keypair.to_bytes(), &master_key).unwrap(); let (salt, encrypted_master_key) = crypto::encrypt_master_key(master_key, password); db.execute(&("INSERT INTO ".to_owned()+IDENTITY_TABLE+" (uuid, name, key, salt, masterkey) VALUES (?1, ?2, ?3, ?4, ?5)"), vec![&uuid.as_bytes()[..], name.as_bytes(), encrypted_keypair.as_slice(), &salt, &encrypted_master_key]).unwrap(); Ok(Identity { uuid: uuid, name: name.to_owned(), key: keypair, master_key: master_key }) } }