utils (crypto/database): update cryptographic functions/add comments and tests in database

This commit is contained in:
MedzikUser 2022-04-18 00:07:37 +02:00
parent 0867e44d58
commit 5b79b4f7e2
No known key found for this signature in database
GPG Key ID: A5FAC1E185C112DB
8 changed files with 166 additions and 55 deletions

26
Cargo.lock generated
View File

@ -275,6 +275,7 @@ dependencies = [
"sha-1", "sha-1",
"sha2", "sha2",
"toml", "toml",
"uuid",
] ]
[[package]] [[package]]
@ -535,6 +536,21 @@ dependencies = [
"digest", "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]] [[package]]
name = "sha2" name = "sha2"
version = "0.10.2" version = "0.10.2"
@ -694,6 +710,16 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 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]] [[package]]
name = "vcpkg" name = "vcpkg"
version = "0.2.15" version = "0.2.15"

View File

@ -1,6 +1,6 @@
[workspace] [workspace]
members = [ members = [
"core",
"utils", "utils",
"core"
] ]
resolver = "2" resolver = "2"

View File

@ -1,6 +1,9 @@
mod init; mod init;
use homedisk_utils::{config::Config, database::Database}; use homedisk_utils::{
config::Config,
database::{Database, User},
};
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
@ -8,5 +11,11 @@ async fn main() {
let _config = Config::parse().expect("parse configuration file"); 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();
} }

View File

@ -1,8 +1,5 @@
CREATE TABLE user ( CREATE TABLE user (
id INTEGER PRIMARY KEY, id TEXT PRIMARY KEY UNIQUE,
username TEXT NOT NULL, username TEXT NOT NULL UNIQUE,
password TEXT NOT NULL password TEXT NOT NULL
); );
/* create root user with password 'root' */
INSERT INTO user (username, password) VALUES ('root', '99adc231b045331e514a516b4b7680f588e3823213abe901738bc3ad67b2f6fcb3c64efb93d18002588d3ccc1a49efbae1ce20cb43df36b38651f11fa75678e8');

View File

@ -7,7 +7,7 @@ edition = "2021"
full = ["crypto", "config", "database"] full = ["crypto", "config", "database"]
crypto = ["sha-1", "sha2", "hex"] crypto = ["sha-1", "sha2", "hex"]
config = ["toml", "anyhow", "dirs"] config = ["toml", "anyhow", "dirs"]
database = ["rusqlite"] database = ["rusqlite", "uuid"]
[dependencies] [dependencies]
log = "0.4.16" log = "0.4.16"
@ -43,3 +43,7 @@ optional = true
version = "0.27.0" version = "0.27.0"
features = ["bundled"] features = ["bundled"]
optional = true optional = true
[dependencies.uuid]
version = "0.8.2"
features = ["v4", "v5"]
optional = true

View File

@ -1,4 +1,4 @@
use hex::encode; pub use hex::encode;
use sha1::Sha1; use sha1::Sha1;
use sha2::{Digest, Sha256, Sha512}; use sha2::{Digest, Sha256, Sha512};
@ -7,73 +7,102 @@ use super::{Error, Result};
/// create a cryptographic hash from a string (sha1, sha256, sha512) /// 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(); /// let mut sha1 = CryptographicHash::new("SHA-1").unwrap();
/// assert_eq!(sha1, "661295c9cbf9d6b2f6428414504a8deed3020641".to_string()) /// 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<String> { #[derive(Debug, Clone)]
let hash = match algo { pub enum CryptographicHash {
"SHA-1" | "SHA1" | "Sha1" | "sha1" => { Sha1(Sha1),
let mut hasher = Sha1::new(); Sha256(Sha256),
hasher.update(input.as_bytes()); Sha512(Sha512),
}
encode(hasher.finalize()) impl CryptographicHash {
pub fn new(algo: &str) -> Result<Self> {
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" => { pub fn update(&mut self, input: &[u8]) {
let mut hasher = Sha256::new(); match self {
hasher.update(input.as_bytes()); Self::Sha1(sha1) => sha1.update(input),
Self::Sha256(sha256) => sha256.update(input),
encode(hasher.finalize()) Self::Sha512(sha512) => sha512.update(input),
} }
}
"SHA-512" | "SHA512" | "Sha512" | "sha512" => { pub fn finalize(&mut self) -> Vec<u8> {
let mut hasher = Sha512::new(); match self {
hasher.update(input.as_bytes()); Self::Sha1(sha1) => sha1.finalize_reset().to_vec(),
Self::Sha256(sha256) => sha256.finalize_reset().to_vec(),
encode(hasher.finalize()) Self::Sha512(sha512) => sha512.finalize_reset().to_vec(),
} }
}
_ => return Err(Error::UnknownAlgorithm("digest", algo.to_string())), pub fn hash(algo: &str, input: &[u8]) -> Result<Vec<u8>> {
}; let mut hasher = Self::new(algo)?;
Ok(hash) hasher.update(input);
Ok(hasher.finalize())
}
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::crypto::{hasher, Error}; use crate::crypto::{encode, CryptographicHash, Error};
#[test] #[test]
fn sha1() { fn sha1() {
let sha1 = hasher("sha1", "test string".to_string()).unwrap(); let mut sha1 = CryptographicHash::new("SHA-1").unwrap();
assert_eq!(sha1, "661295c9cbf9d6b2f6428414504a8deed3020641".to_string()); sha1.update(b"test sha1 hash");
let hash = encode(sha1.finalize());
assert_eq!(hash, "7726bd9560e1ad4a1a4f056cae5c0c9ea8bacfc2".to_string())
} }
#[test] #[test]
fn sha256() { 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!( assert_eq!(
sha1, hash,
"d5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b".to_string() "eaf6e4198f39ccd63bc3e957d43bf4ef67f12c318c8e3cdc2567a37339902dac".to_string()
); )
} }
#[test] #[test]
fn sha512() { 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!( assert_eq!(
sha1, hash,
"10e6d647af44624442f388c2c14a787ff8b17e6165b83d767ec047768d8cbcb71a1a3226e7cc7816bc79c0427d94a9da688c41a3992c7bf5e4d7cc3e0be5dbac".to_string() "b43b4d7178014c92f55be828d66c9f98211fc67b385f7790a5b4b2fcb89fe1831645b5a4c17f3f7f11d8f34d2800a77a2b8faa5a0fb9d6b8f7befbc29a9ce795".to_string()
); )
} }
#[test] #[test]
fn unknown_algorithm() { fn unknown_algorithm() {
let algo = "unknow_algo"; 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()))
} }
} }

View File

@ -1,8 +1,6 @@
use log::debug; use log::debug;
use rusqlite::Connection; use rusqlite::Connection;
use crate::crypto;
use super::{user, Error}; use super::{user, Error};
pub struct Database { pub struct Database {
@ -10,22 +8,40 @@ pub struct Database {
} }
impl Database { impl Database {
pub fn open() -> Result<Self, Error> { /// Open SQLite Database file
/// ```
/// use homedisk_utils::database::Database;
///
/// Database::open("/tmp/homedisk.db").unwrap();
/// ```
pub fn open(path: &str) -> Result<Self, Error> {
debug!("opening SQLite database"); debug!("opening SQLite database");
let conn = Connection::open("homedisk.db")?; let conn = Connection::open(path)?;
Ok(Self { conn }) Ok(Self { conn })
} }
pub fn create_user(&mut self, user: user::User) -> Result<usize, Error> { /// 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<usize, Error> {
debug!("creating user - {}", user.username); debug!("creating user - {}", user.username);
// hash password using sha512
let password = crypto::hasher("sha512", user.password)?;
Ok(self.conn.execute( Ok(self.conn.execute(
"INSERT INTO user (username, password) VALUES (?1, ?2)", "INSERT INTO user (id, username, password) VALUES (?1, ?2, ?3)",
[user.username, password], [user.id, user.username, user.password],
)?) )?)
} }
} }

View File

@ -1,5 +1,35 @@
use hex::encode;
use uuid::Uuid;
use crate::crypto::{self, CryptographicHash};
#[derive(Debug)]
pub struct User { pub struct User {
pub id: String, pub id: String,
pub username: String, pub username: String,
pub password: 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<Self, crypto::Error> {
// 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,
})
}
}