From 5b79b4f7e20b6e400099975e904cba0f425eba9e Mon Sep 17 00:00:00 2001 From: MedzikUser Date: Mon, 18 Apr 2022 00:07:37 +0200 Subject: [PATCH] utils (crypto/database): update cryptographic functions/add comments and tests in database --- Cargo.lock | 26 +++++++++ Cargo.toml | 2 +- core/src/main.rs | 13 ++++- tables.sql | 7 +-- utils/Cargo.toml | 6 ++- utils/src/crypto/hash.rs | 101 ++++++++++++++++++++++------------- utils/src/database/sqlite.rs | 36 +++++++++---- utils/src/database/user.rs | 30 +++++++++++ 8 files changed, 166 insertions(+), 55 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fa2e15f..ea2c880 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -275,6 +275,7 @@ dependencies = [ "sha-1", "sha2", "toml", + "uuid", ] [[package]] @@ -535,6 +536,21 @@ dependencies = [ "digest", ] +[[package]] +name = "sha1" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" +dependencies = [ + "sha1_smol", +] + +[[package]] +name = "sha1_smol" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" + [[package]] name = "sha2" version = "0.10.2" @@ -694,6 +710,16 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +[[package]] +name = "uuid" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +dependencies = [ + "getrandom", + "sha1", +] + [[package]] name = "vcpkg" version = "0.2.15" diff --git a/Cargo.toml b/Cargo.toml index e1ceb7d..f45b1d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] members = [ + "core", "utils", - "core" ] resolver = "2" diff --git a/core/src/main.rs b/core/src/main.rs index 4648b8d..053f634 100644 --- a/core/src/main.rs +++ b/core/src/main.rs @@ -1,6 +1,9 @@ mod init; -use homedisk_utils::{config::Config, database::Database}; +use homedisk_utils::{ + config::Config, + database::{Database, User}, +}; #[tokio::main] async fn main() { @@ -8,5 +11,11 @@ async fn main() { let _config = Config::parse().expect("parse configuration file"); - let _db = Database::open().expect("open SQLite database"); + let _db = Database::open("homedisk.db").expect("open SQLite database"); + + let user = User::new("medzik", "password").unwrap(); + + println!("{:?}", user); + + _db.create_user(user).unwrap(); } diff --git a/tables.sql b/tables.sql index 330c156..83fc716 100644 --- a/tables.sql +++ b/tables.sql @@ -1,8 +1,5 @@ CREATE TABLE user ( - id INTEGER PRIMARY KEY, - username TEXT NOT NULL, + id TEXT PRIMARY KEY UNIQUE, + username TEXT NOT NULL UNIQUE, password TEXT NOT NULL ); - -/* create root user with password 'root' */ -INSERT INTO user (username, password) VALUES ('root', '99adc231b045331e514a516b4b7680f588e3823213abe901738bc3ad67b2f6fcb3c64efb93d18002588d3ccc1a49efbae1ce20cb43df36b38651f11fa75678e8'); diff --git a/utils/Cargo.toml b/utils/Cargo.toml index e5a8b40..ad182b0 100644 --- a/utils/Cargo.toml +++ b/utils/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" full = ["crypto", "config", "database"] crypto = ["sha-1", "sha2", "hex"] config = ["toml", "anyhow", "dirs"] -database = ["rusqlite"] +database = ["rusqlite", "uuid"] [dependencies] log = "0.4.16" @@ -43,3 +43,7 @@ optional = true version = "0.27.0" features = ["bundled"] optional = true +[dependencies.uuid] +version = "0.8.2" +features = ["v4", "v5"] +optional = true diff --git a/utils/src/crypto/hash.rs b/utils/src/crypto/hash.rs index eeb0ca3..efc044f 100644 --- a/utils/src/crypto/hash.rs +++ b/utils/src/crypto/hash.rs @@ -1,4 +1,4 @@ -use hex::encode; +pub use hex::encode; use sha1::Sha1; use sha2::{Digest, Sha256, Sha512}; @@ -7,73 +7,102 @@ use super::{Error, Result}; /// create a cryptographic hash from a string (sha1, sha256, sha512) /// /// ``` -/// use homedisk_utils::crypto::hasher; +/// use homedisk_utils::crypto::{CryptographicHash, encode}; /// -/// let sha1 = hasher("SHA-1", "test string".to_string()).unwrap(); -/// assert_eq!(sha1, "661295c9cbf9d6b2f6428414504a8deed3020641".to_string()) +/// let mut sha1 = CryptographicHash::new("SHA-1").unwrap(); +/// sha1.update(b"test sha1 hash"); +/// +/// let hash = encode(sha1.finalize()); +/// +/// assert_eq!(hash, "7726bd9560e1ad4a1a4f056cae5c0c9ea8bacfc2".to_string()) /// ``` -pub fn hasher(algo: &str, input: String) -> Result { - let hash = match algo { - "SHA-1" | "SHA1" | "Sha1" | "sha1" => { - let mut hasher = Sha1::new(); - hasher.update(input.as_bytes()); +#[derive(Debug, Clone)] +pub enum CryptographicHash { + Sha1(Sha1), + Sha256(Sha256), + Sha512(Sha512), +} - encode(hasher.finalize()) +impl CryptographicHash { + pub fn new(algo: &str) -> Result { + match algo { + "SHA-1" | "SHA1" | "Sha1" | "sha1" => Ok(Self::Sha1(Sha1::new())), + "SHA-256" | "SHA256" | "Sha256" | "sha256" => Ok(Self::Sha256(Sha256::new())), + "SHA-512" | "SHA512" | "Sha512" | "sha512" => Ok(Self::Sha512(Sha512::new())), + _ => Err(Error::UnknownAlgorithm("digest", algo.to_string())), } + } - "SHA-256" | "SHA256" | "Sha256" | "sha256" => { - let mut hasher = Sha256::new(); - hasher.update(input.as_bytes()); - - encode(hasher.finalize()) + pub fn update(&mut self, input: &[u8]) { + match self { + Self::Sha1(sha1) => sha1.update(input), + Self::Sha256(sha256) => sha256.update(input), + Self::Sha512(sha512) => sha512.update(input), } + } - "SHA-512" | "SHA512" | "Sha512" | "sha512" => { - let mut hasher = Sha512::new(); - hasher.update(input.as_bytes()); - - encode(hasher.finalize()) + pub fn finalize(&mut self) -> Vec { + match self { + Self::Sha1(sha1) => sha1.finalize_reset().to_vec(), + Self::Sha256(sha256) => sha256.finalize_reset().to_vec(), + Self::Sha512(sha512) => sha512.finalize_reset().to_vec(), } + } - _ => return Err(Error::UnknownAlgorithm("digest", algo.to_string())), - }; + pub fn hash(algo: &str, input: &[u8]) -> Result> { + let mut hasher = Self::new(algo)?; - Ok(hash) + hasher.update(input); + + Ok(hasher.finalize()) + } } #[cfg(test)] mod tests { - use crate::crypto::{hasher, Error}; + use crate::crypto::{encode, CryptographicHash, Error}; #[test] fn sha1() { - let sha1 = hasher("sha1", "test string".to_string()).unwrap(); - assert_eq!(sha1, "661295c9cbf9d6b2f6428414504a8deed3020641".to_string()); + let mut sha1 = CryptographicHash::new("SHA-1").unwrap(); + sha1.update(b"test sha1 hash"); + + let hash = encode(sha1.finalize()); + + assert_eq!(hash, "7726bd9560e1ad4a1a4f056cae5c0c9ea8bacfc2".to_string()) } #[test] fn sha256() { - let sha1 = hasher("sha256", "test string".to_string()).unwrap(); + let mut sha256 = CryptographicHash::new("SHA-256").unwrap(); + sha256.update(b"test sha256 hash"); + + let hash = encode(sha256.finalize()); + assert_eq!( - sha1, - "d5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b".to_string() - ); + hash, + "eaf6e4198f39ccd63bc3e957d43bf4ef67f12c318c8e3cdc2567a37339902dac".to_string() + ) } #[test] fn sha512() { - let sha1 = hasher("sha512", "test string".to_string()).unwrap(); + let mut sha512 = CryptographicHash::new("SHA-512").unwrap(); + sha512.update(b"test sha512 hash"); + + let hash = encode(sha512.finalize()); + assert_eq!( - sha1, - "10e6d647af44624442f388c2c14a787ff8b17e6165b83d767ec047768d8cbcb71a1a3226e7cc7816bc79c0427d94a9da688c41a3992c7bf5e4d7cc3e0be5dbac".to_string() - ); + hash, + "b43b4d7178014c92f55be828d66c9f98211fc67b385f7790a5b4b2fcb89fe1831645b5a4c17f3f7f11d8f34d2800a77a2b8faa5a0fb9d6b8f7befbc29a9ce795".to_string() + ) } #[test] fn unknown_algorithm() { let algo = "unknow_algo"; - let err = hasher(algo, "test string".to_string()).unwrap_err(); + let err = CryptographicHash::new(algo).unwrap_err(); - assert_eq!(err, Error::UnknownAlgorithm("digest", algo.to_string())); + assert_eq!(err, Error::UnknownAlgorithm("digest", algo.to_string())) } } diff --git a/utils/src/database/sqlite.rs b/utils/src/database/sqlite.rs index 5622bc0..025eefb 100644 --- a/utils/src/database/sqlite.rs +++ b/utils/src/database/sqlite.rs @@ -1,8 +1,6 @@ use log::debug; use rusqlite::Connection; -use crate::crypto; - use super::{user, Error}; pub struct Database { @@ -10,22 +8,40 @@ pub struct Database { } impl Database { - pub fn open() -> Result { + /// Open SQLite Database file + /// ``` + /// use homedisk_utils::database::Database; + /// + /// Database::open("/tmp/homedisk.db").unwrap(); + /// ``` + pub fn open(path: &str) -> Result { debug!("opening SQLite database"); - let conn = Connection::open("homedisk.db")?; + let conn = Connection::open(path)?; + Ok(Self { conn }) } - pub fn create_user(&mut self, user: user::User) -> Result { + /// Create new User + /// ``` + /// use std::fs; + /// + /// use rusqlite::Connection; + /// use homedisk_utils::database::{Database, User}; + /// + /// let db = Database { conn: Connection::open_in_memory().unwrap() }; + /// let user = User::new("medzik", "SuperSecretPassword123").unwrap(); + /// + /// db.conn.execute(&fs::read_to_string("../template.sql").unwrap(), []).unwrap(); + /// + /// db.create_user(user).unwrap(); + /// ``` + pub fn create_user(&self, user: user::User) -> Result { debug!("creating user - {}", user.username); - // hash password using sha512 - let password = crypto::hasher("sha512", user.password)?; - Ok(self.conn.execute( - "INSERT INTO user (username, password) VALUES (?1, ?2)", - [user.username, password], + "INSERT INTO user (id, username, password) VALUES (?1, ?2, ?3)", + [user.id, user.username, user.password], )?) } } diff --git a/utils/src/database/user.rs b/utils/src/database/user.rs index dbd46ab..871ba2e 100644 --- a/utils/src/database/user.rs +++ b/utils/src/database/user.rs @@ -1,5 +1,35 @@ +use hex::encode; +use uuid::Uuid; + +use crate::crypto::{self, CryptographicHash}; + +#[derive(Debug)] pub struct User { pub id: String, pub username: String, pub password: String, } + +impl User { + /// Create a new User type (note **this doesn't create a new user in the database!**) + /// + /// This function creates a unique UUID for the user and creates a password hash using SHA-512 + /// ``` + /// use homedisk_utils::database::User; + /// + /// let user = User::new("medzik", "SuperSecretPassword123!").unwrap(); + /// ``` + pub fn new(username: &str, password: &str) -> Result { + // create user UUID + let sha1_name = CryptographicHash::hash("SHA-1", username.as_bytes())?; + let id = Uuid::new_v5(&Uuid::NAMESPACE_X500, &sha1_name).to_string(); + + let password = encode(CryptographicHash::hash("SHA-512", password.as_bytes())?); + + Ok(Self { + id, + username: username.to_string(), + password, + }) + } +}