convert to argon2 church
continuous-integration/drone/push Build is passing Details

- convert to argon2
- make hahsing actually works
- fix minute stuff with bincode conversion
- sloppy code
- works
This commit is contained in:
ayham 2021-08-19 10:51:26 +03:00
parent 4f420925cf
commit 27e68e3eeb
Signed by: ayham
GPG Key ID: EAB7F5A9DF503678
26 changed files with 208 additions and 436 deletions

View File

@ -46,3 +46,5 @@ rust-crypto="0.2.36"
jsonwebtoken="7.2.0"
json="0.12.4"
rand="0.8.4"
rand_core = { version = "0.6", features = ["std"] }
argon2= { version = "0.2", features = ["password-hash"] }

View File

@ -1,10 +1,12 @@
use std::io;
use crate::common::account::hash::hash;
use argon2::password_hash::PasswordHash;
use crate::common::command::*;
use crate::common::message::*;
use crate::client::network::cmd::req_server_salt::req_server_salt;
use crate::client::network::cmd::req_server_salt::*;
use crate::client::account::hash::*;
use tokio::net::TcpStream;
use tokio_rustls::client::TlsStream;
@ -36,7 +38,7 @@ pub async fn acc_auth(
socket: &mut TlsStream<TcpStream>,
username: &str,
email: &str,
password: &str,
passw: &str,
) -> std::io::Result<String> {
/*
* get email salt
@ -46,22 +48,30 @@ pub async fn acc_auth(
/*
* get password salt
* */
let password_salt = req_server_salt(socket, username, Command::GetPasswordSalt).await?;
let passw_salt = req_server_salt(socket, username, Command::GetPasswordSalt).await?;
/*
* hash the email
*/
let hashed_email = hash(email, &email_salt, 175_000);
let hashed_email = hash(email, &email_salt, false);
/*
* hash the password
*/
let hashed_password = hash(password, &password_salt, 250_000);
let hashed_passw = hash(passw, &passw_salt, false);
/*
* only send hash without other PHC information
* */
let parsed_email = PasswordHash::new(&hashed_email).unwrap();
let parsed_passw = PasswordHash::new(&hashed_passw).unwrap();
let parsed_email_hash = parsed_email.hash.unwrap().to_string();
let parsed_passw_hash = parsed_passw.hash.unwrap().to_string();
/* generate message to be sent to the server */
let data = object! {
hashed_email: hashed_email,
hashed_password: hashed_password,
hashed_email: parsed_email_hash,
hashed_passw: parsed_passw_hash,
username: username
};
/* build message request */
@ -76,22 +86,24 @@ pub async fn acc_auth(
/* decode response */
if !ret_msg.assert_command(Command::Success) || !ret_msg.assert_data() {
Err(io::Error::new(
io::ErrorKind::ConnectionRefused,
format!(
"Failed authorizing account, {}, server returned error: {}.",
username,
ret_msg.get_err()
),
))
} else {
/* authorized */
Ok(ret_msg.get_data().map_err(|_| {
io::Error::new(
io::ErrorKind::InvalidData,
format!(
"Failed authorizing account, {}, server returned invalid data.",
username))
username
),
)
})?)
} else {
Err(io::Error::new(
io::ErrorKind::ConnectionRefused,
format!(
"Failed authorizing account, {}, server returned error: {}.",
username,
ret_msg.get_err()
),
))
}
}

View File

@ -3,8 +3,7 @@ use std::io;
use crate::common::command::*;
use crate::common::message::*;
use crate::client::account::hash_email::hash_email;
use crate::client::account::hash_pwd::hash_pwd;
use crate::client::account::hash::*;
use crate::client::network::cmd::get_server_salt::get_server_salt;
@ -49,15 +48,13 @@ pub async fn acc_create(
/*
* generate hashes for email, password
* */
let email_hash = hash_email(email, &email_server_salt);
let password_hash = hash_pwd(password, &password_server_salt);
let email_hash_phc = hash(email, &email_server_salt, true);
let password_hash_phc = hash(password, &password_server_salt, true);
/* generate message to be sent to the server */
let data = object! {
email_hash: email_hash.0,
email_client_salt: email_hash.1,
password_hash: password_hash.0,
password_client_salt: password_hash.1,
email_client_hash_phc: email_hash_phc,
password_client_hash_phc: password_hash_phc,
username: username
};
/* build message request */

View File

@ -1,5 +1,8 @@
use crate::common::account::hash::*;
use crate::common::account::salt::*;
use argon2::{
password_hash::{PasswordHasher, SaltString},
Argon2
};
use rand_core::OsRng;
/// Generates a client email hash from a raw email.
///
@ -18,14 +21,16 @@ use crate::common::account::salt::*;
/// Example:
/// ```rust
/// let enc = hash_email("totallyrealemail@anemail.c0m", server_salt).unwrap();
/// println!("Client Email Hash: {}", HEXUPPER.encode(&enc.0));
/// println!("Client Email Salt: {}", HEXUPPER.encode(&enc.1));
/// ```
pub fn hash_email(
email: &str,
server_salt: &str) -> (String, String) {
let mut salt = gen_salt();
salt.push_str(server_salt);
let hash = hash(&email, &salt, 175_000);
(hash, salt.to_string())
pub fn hash(email: &str, server_salt: &str, resalt: bool) -> String {
let mut raw_salt: String = server_salt.to_string();
if resalt {
raw_salt.push_str(SaltString::generate(&mut OsRng).as_str());
}
let salt = SaltString::new(&raw_salt).unwrap(); // TODO: check if server gave longer than possible salt
let argon2id = Argon2::default();
let phc = argon2id.hash_password_simple(&email.as_bytes(), &salt).unwrap();
phc.to_string()
}

View File

@ -1,28 +0,0 @@
use crate::common::account::hash::*;
use crate::common::account::salt::*;
/// Generates a client password hash from a raw password.
///
/// Takes in a raw password, outputs a hashed version of the client password to be sent to the
/// server with the returned client random bits that make up the whole client salt. This function
/// is to be used on client side account creation. The result from this function is not be stored
/// directly on the database, result must be run through the server side hashing again.
///
/// Arguments:
/// pass - The raw user password to be hashed.
/// server_salt - The server's part sent of the salt.
///
/// Returns: a tuple containing the client hash and full salt, nothing on failure.
///
/// Example:
/// ```rust
/// let enc = hash_pwd("this is my real password!", server_salt).unwrap();
/// println!("Client Pass Hash: {}", HEXUPPER.encode(&enc.0));
/// println!("Client Pass Salt: {}", HEXUPPER.encode(&enc.1));
/// ```
pub fn hash_pwd(pass: &str, server_salt: &str) -> (String, String) {
let mut salt = gen_salt();
salt.push_str(server_salt);
let hash = hash(&pass, &salt, 250_000);
(hash, salt)
}

View File

@ -1,6 +1,3 @@
pub mod authorization;
pub mod creation;
pub mod hash_email;
pub mod hash_pwd;
pub mod retrieval_portfolio;
pub mod retrieval_transaction;
pub mod hash;

View File

@ -1,65 +0,0 @@
use std::io;
use crate::common::account::portfolio::Portfolio;
use crate::common::command::*;
use crate::common::message::*;
use tokio::net::TcpStream;
use tokio_rustls::client::TlsStream;
/// Retrieves from the connected TLS server an authorized portfolio.
///
/// Sends a request for portfolio with the JWT token of the client connection. Handles any response
/// and returns.
/// Should be used in contexts that return ```io::Result```.
/// Should be used in Async context.
///
/// Arguments:
/// socket - TLS socket to use.
/// auth_jwt - JWT token to authenticate with.
///
/// Returns: ```io::Result``` wraps ```Portfolio```.
///
/// Example:
/// ```rust
/// let mut portfolio = acc_retrieve_portfolio(&mut tls_client, &mut poll)?;
/// ```
pub async fn acc_retrieve_portfolio(
socket: &mut TlsStream<TcpStream>,
auth_jwt: String,
) -> std::io::Result<Portfolio> {
if auth_jwt.is_empty() == true {
return Err(io::Error::new(
io::ErrorKind::PermissionDenied,
"ACC_RETRIEVE_PORTFOLIO: JWT TOKEN EMPTY",
));
}
/* build message request */
Message::new()
.command(Command::GetUserPortfolio)
.data(auth_jwt)
.send(socket)
.await?;
/* decode response */
let ret_msg: Message = Message::receive(socket).await?;
/* assert received message */
if !ret_msg.assert_command(Command::Success) || !ret_msg.assert_data() {
/* returned data */
let portfolio: Portfolio = bincode::deserialize(&ret_msg.data).map_err(|_| {
io::Error::new(
io::ErrorKind::InvalidInput,
"Failed retrieving portfolio, received invalid data.",
)
})?;
Ok(portfolio)
} else {
/* could not get data */
Err(io::Error::new(
io::ErrorKind::InvalidData,
"Failed retrieving portfolio, received invalid message.",
))
}
}

View File

@ -1,66 +0,0 @@
use std::io;
use crate::common::command::*;
use crate::common::message::*;
use crate::common::account::transaction::Transaction;
use tokio::net::TcpStream;
use tokio_rustls::client::TlsStream;
/// Retrieves from the connected TLS server an authorized transaction history.
///
/// Sends a request for a transaction history with the JWT token of the client connection. Handles
/// any response and returns.
/// Should be used in contexts that return ```io::Result```.
/// Should be used in Async contexts.
///
/// Arguments:
/// socket - TLS socket to use.
/// auth_jwt - JWT Token to authenticate with
///
/// Returns: ```io::Result``` wrapped ```Vec<Transaction>```
///
/// Example:
/// ```rust
/// let mut transaction = acc_retrieve_transaction(&mut tls_client, &mut poll)?
/// ```
pub async fn acc_retrieve_transaction(
socket: &mut TlsStream<TcpStream>,
auth_jwt: String,
) -> std::io::Result<Vec<Transaction>> {
if auth_jwt.is_empty() == true {
return Err(io::Error::new(
io::ErrorKind::PermissionDenied,
"ACC_RETRIEVE_TRANSACTION: JWT TOKEN EMPTY",
));
}
/* build message request */
Message::new()
.command(Command::GetUserTransactionHist)
.data(auth_jwt)
.send(socket)
.await?;
/* decode response */
let ret_msg = Message::receive(socket).await?;
/* assert received message */
if !ret_msg.assert_command(Command::Success) || !ret_msg.assert_data() {
/* returned data*/
let transactions: Vec<Transaction> = bincode::deserialize(&ret_msg.data).map_err(|_| {
io::Error::new(
io::ErrorKind::InvalidInput,
"Failed retrieving transaction, received invalid data.",
)
})?;
return Ok(transactions);
} else {
/* could not get data */
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Failed retrieving transaction, received invalid message.",
));
}
}

View File

@ -158,17 +158,5 @@ pub async fn libtrader_init_client() -> std::io::Result<()> {
Err(err) => panic!("panik! {}", err),
}
use crate::client::account::retrieval_portfolio::acc_retrieve_portfolio;
match acc_retrieve_portfolio(&mut socket, String::from(jwt.as_str())).await {
Ok(portfolio) => println!("we got portfolio {:#?}", portfolio),
Err(err) => panic!("panik! {}", err),
}
use crate::client::account::retrieval_transaction::acc_retrieve_transaction;
match acc_retrieve_transaction(&mut socket, jwt).await {
Ok(transaction) => println!("we got the transactions {:#?}", transaction),
Err(err) => panic!("panik! {}", err),
}
Ok(())
}

View File

@ -21,9 +21,7 @@ use tokio_rustls::client::TlsStream;
/// ```rust
/// let server_salt: [u8; digest::SHA512_OUTPUT_LEN/2] = get_server_salt(tls_client)?;
/// ```
pub async fn get_server_salt(
socket: &mut TlsStream<TcpStream>,
) -> std::io::Result<String> {
pub async fn get_server_salt(socket: &mut TlsStream<TcpStream>) -> std::io::Result<String> {
/*
* request to generate a salt from the server.
* */
@ -45,6 +43,7 @@ pub async fn get_server_salt(
io::Error::new(
io::ErrorKind::InvalidData,
"Failed getting generated server Salt, received an invalid message.",
)})
)
})
}
}

View File

@ -52,8 +52,10 @@ pub async fn req_server_salt(
));
}
Ok(ret_msg.get_data().map_err(|_| {
io::Error::new(io::ErrorKind::InvalidData,
format!("Could not get server salt, received invalid data."))
Ok(ret_msg.get_data().map_err(|_| {
io::Error::new(
io::ErrorKind::InvalidData,
format!("Could not get server salt, received invalid data."),
)
})?)
}

View File

@ -1,31 +0,0 @@
use ring::{digest, pbkdf2};
use std::num::NonZeroU32;
/// A generic hashing abstraction function.
///
/// Useful for quickly swapping the current hashing system.
///
/// Arguments:
/// val - The value to be hashed.
/// salt - The whole salt to be used.
/// iter - The number of iteration to use.
///
/// Returns: u8 array of size 64 bytes.
///
/// Example:
/// ```rust
/// let email_hash = hash("test@test.com", [0u8; 64], 124000);
/// ```
pub fn hash(val: &str, salt: &str, iter: u32) -> String {
let iterations: NonZeroU32 = NonZeroU32::new(iter).unwrap();
let mut hash = [0u8; digest::SHA512_OUTPUT_LEN];
pbkdf2::derive(
pbkdf2::PBKDF2_HMAC_SHA512,
iterations,
salt.as_bytes(),
val.as_bytes(),
&mut hash,
);
String::from_utf8(hash.into()).unwrap()
}

View File

@ -1,6 +1,4 @@
pub mod hash;
pub mod order;
pub mod portfolio;
pub mod position;
pub mod salt;
pub mod transaction;

View File

@ -1,14 +0,0 @@
use rand::distributions::Alphanumeric;
use rand::{thread_rng, Rng};
use ring::digest;
/// generates a random salt
/// always assumes salt is sha512 output length
/// for "security" ofcourse
pub fn gen_salt() -> String {
thread_rng()
.sample_iter(&Alphanumeric)
.take(digest::SHA512_OUTPUT_LEN)
.map(char::from)
.collect()
}

View File

@ -1,14 +1,15 @@
use log::warn;
use std::num::NonZeroU32;
use ring::pbkdf2;
use argon2::{
password_hash::{PasswordHash, PasswordVerifier},
Argon2
};
use crate::common::command::*;
use crate::common::message::*;
use crate::server::db::cmd::get_user_hash::get_user_hash;
use crate::server::db::cmd::get_user_id::get_user_id;
use crate::server::db::cmd::get_user_salt::get_user_salt;
use crate::server::network::jwt_wrapper::create_jwt_token;
@ -31,45 +32,17 @@ pub async fn acc_auth(
* Parse account data.
* */
/* get json data */
let stringified_data = std::str::from_utf8(&message.data).unwrap();
let stringified_data: String =
message.get_data().map_err(|_|
std::io::Error::new(
std::io::ErrorKind::InvalidData,
"Failed authorizing account, could not parse message data!"))?;
let data = json::parse(&stringified_data).unwrap();
/* get email, password, and username hashes */
let email_client_hash = data["hashed_email"].as_str().unwrap();
let password_client_hash = data["hashed_password"].as_str().unwrap();
let passw_client_hash = data["hashed_passw"].as_str().unwrap();
let username = data["username"].as_str().unwrap();
/*
* Get server salts
* TODO: FIND A SMALLER CODE FOR THIS NONSENSE GARBAGE CODE
* */
let email_salt = match get_user_salt(sql_conn, username, Command::GetEmailSalt, true).await {
Ok(val) => val,
Err(_) => {
return Message::new()
.command(Command::Failure)
.data(format!(
"Failed authorizing user, {}, does not exist!",
username
))
.send(socket)
.await;
}
};
let password_salt =
match get_user_salt(sql_conn, username, Command::GetPasswordSalt, true).await {
Ok(val) => val,
Err(_) => {
return Message::new()
.command(Command::Failure)
.data(format!(
"Failed authorizing user, {}, does not exist!",
username
))
.send(socket)
.await;
}
};
/*
* Get server hashes
* */
@ -87,7 +60,7 @@ pub async fn acc_auth(
}
};
let password_server_hash = match get_user_hash(sql_conn, username, false).await {
let passw_server_hash = match get_user_hash(sql_conn, username, false).await {
Ok(val) => val,
Err(_) => {
return Message::new()
@ -104,14 +77,10 @@ pub async fn acc_auth(
/*
* Verify creds
* */
let email_ret = pbkdf2::verify(
pbkdf2::PBKDF2_HMAC_SHA512,
NonZeroU32::new(350_000).unwrap(),
email_salt.as_bytes(),
email_client_hash.as_bytes(),
email_server_hash.as_bytes(),
);
if email_ret.is_err() {
let argon2id = Argon2::default();
let parsed_email_hash = PasswordHash::new(&email_server_hash).unwrap(); // TODO: check
if argon2id.verify_password(&email_client_hash.as_bytes(), &parsed_email_hash).is_err() {
return Message::new()
.command(Command::Failure)
.data("Email Incorrect")
@ -119,14 +88,8 @@ pub async fn acc_auth(
.await;
}
let pass_ret = pbkdf2::verify(
pbkdf2::PBKDF2_HMAC_SHA512,
NonZeroU32::new(500_000).unwrap(),
password_salt.as_bytes(),
password_client_hash.as_bytes(),
password_server_hash.as_bytes(),
);
if pass_ret.is_err() {
let parsed_passw_hash = PasswordHash::new(&passw_server_hash).unwrap(); // TODO: check
if argon2id.verify_password(&passw_client_hash.as_bytes(), &parsed_passw_hash).is_err() {
return Message::new()
.command(Command::Failure)
.data("Password Incorrect")
@ -134,6 +97,7 @@ pub async fn acc_auth(
.await;
}
/*
* Generate JWT token
* */
@ -156,15 +120,17 @@ pub async fn acc_auth(
use std::time::{Duration, SystemTime, UNIX_EPOCH};
let beginning_of_time = SystemTime::now() + Duration::from_secs(4 * 60 * 60);
let jwt_result = create_jwt_token(
user_id,
beginning_of_time
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs(),
).map_err(|e| { std::io::Error::new(std::io::ErrorKind::Other, e) })?;
Message::new()
.command(Command::Success)
.data(create_jwt_token(
user_id,
beginning_of_time
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs(),
))
.data(jwt_result)
.send(socket)
.await
}

View File

@ -1,13 +1,13 @@
use log::warn;
use argon2::password_hash::PasswordHash;
use crate::common::command::*;
use crate::common::message::*;
use crate::common::account::portfolio::Portfolio;
use crate::server::account::hash_email::hash_email;
use crate::server::account::hash_pwd::hash_pwd;
use crate::server::account::hash::*;
use crate::server::ds::account::Account;
use crate::server::db::cmd::user_exists::*;
use tokio::io::AsyncWriteExt;
use tokio::net::TcpStream;
@ -23,52 +23,20 @@ pub async fn acc_create(
warn!("REGISTER_INVALID_MESSAGE");
return socket.shutdown().await; // just get off of my lawn
}
/*
* Parse account data
* */
/* get json data */
let stringified_data: String = bincode::deserialize(&message.data).unwrap();
let data = json::parse(&stringified_data).unwrap();
/* get email, password salts and client hashes */
let email_hash = data["email_hash"].as_str().unwrap(); // TODO: fix this
let email_client_salt = data["email_client_salt"].as_str().unwrap();
let password_hash = data["password_hash"].as_str().unwrap();
let password_client_salt = data["password_client_salt"].as_str().unwrap();
/* get username */
let username = data["username"].as_str().unwrap();
/* expect all received values to be non-None */
/* generate account struct */
let mut account: Account = Account {
username: username.to_string(),
email_hash: "".to_string(),
server_email_salt: "".to_string(),
client_email_salt: email_client_salt.to_string(),
pass_hash: "".to_string(),
server_pass_salt: "".to_string(),
client_pass_salt: password_client_salt.to_string(),
is_pass: true,
portfolio: Portfolio::default(),
transactions: Vec::new(),
};
/*
* check if username is available in the database
* */
let username = data["username"].as_str().unwrap();
/* search for an account with same name */
for _ in &sql_conn
.query(
"SELECT username FROM accounts_schema.accounts WHERE username LIKE $1",
&[&account.username],
)
.await
.unwrap()
if !user_exists(sql_conn, username).await
{
/*
* Inform cient that user already exists
@ -81,28 +49,52 @@ pub async fn acc_create(
.await;
}
/* get email, password client PHC strings */
let email_client_hash_phc = data["email_client_hash_phc"].as_str().unwrap(); // TODO: fix this
let password_client_hash_phc = data["password_client_hash_phc"].as_str().unwrap();
let email_client = PasswordHash::new(email_client_hash_phc).unwrap();
let password_client = PasswordHash::new(password_client_hash_phc).unwrap();
/* store salt _ONLY_ from the PHC string received,
* Note: discard the main hash sent from client after hashing by server */
let email_client_salt = email_client.salt.unwrap(); // TODO check for error
let password_client_salt = password_client.salt.unwrap(); // TODO check for error
/* generate account struct */
let mut account: Account = Account {
username: username.to_string(),
email_hash_phc: "".to_string(),
client_email_salt: email_client_salt.to_string(),
pass_hash_phc: "".to_string(),
client_pass_salt: password_client_salt.to_string(),
portfolio: Portfolio::default(),
transactions: Vec::new(),
};
/*
* Hash the email and password.
* */
/* hash the email */
let email_server_hash = hash_email(email_hash);
account.email_hash = email_server_hash.0;
account.server_email_salt = email_server_hash.1;
account.email_hash_phc = hash(email_client.hash.unwrap().to_string());
/* hash the password */
let password_server_hash = hash_pwd(password_hash);
account.pass_hash = password_server_hash.0;
account.server_pass_salt = password_server_hash.1;
// TODO: actually merge those functions into one.
account.pass_hash_phc = hash(password_client.hash.unwrap().to_string());
/*
* Write the account to the database.
* */
let creation_result = sql_conn.execute("INSERT INTO accounts_schema.accounts \
(username, email_hash, server_email_salt, client_email_salt, pass_hash, server_pass_salt, client_pass_salt)
(username, email_hash_phc, client_email_salt, pass_hash_phc, client_pass_salt)
VALUES \
($1, $2, $3, $4, $5, $6, $7)",
($1, $2, $3, $4, $5)",
&[&account.username,
&account.email_hash, &account.server_email_salt, &account.client_email_salt,
&account.pass_hash, &account.server_pass_salt, &account.client_pass_salt]).await;
&account.email_hash_phc, &account.client_email_salt,
&account.pass_hash_phc, &account.client_pass_salt]).await;
/*
* Send to client SQL result

View File

@ -1,5 +1,8 @@
use crate::common::account::hash::*;
use crate::common::account::salt::*;
use argon2::{
password_hash::{PasswordHasher, SaltString},
Argon2
};
use rand_core::OsRng;
/// Generates a storable server email hash from a client hashed email.
///
@ -18,8 +21,11 @@ use crate::common::account::salt::*;
/// println!("Server Email Hash: {}", HEXUPPER.encode(&enc.0));
/// println!("Server Email Salt: {}", HEXUPPER.encode(&enc.1));
/// ```
pub fn hash_email(hashed_email: &str) -> (String, String) {
let salt = gen_salt();
let hash = hash(&hashed_email, &salt, 350_000);
(hash, salt)
pub fn hash(hashed_email: String) -> String {
let salt = SaltString::generate(&mut OsRng);
let argon2id = Argon2::default();
let phc = argon2id.hash_password_simple(&hashed_email.as_bytes(), &salt).unwrap();
phc.to_string()
}

View File

@ -1,25 +0,0 @@
use crate::common::account::hash::*;
use crate::common::account::salt::*;
/// Generates a storable server password hash from a client hashed password.
///
/// Takes in a client hashed password, outputs a storable new hash. The returned result is 'safe'
/// to be stored on the server side. The salt returned is for the hashed version of the hashed
/// client password.
///
/// Arguments:
/// hashed_pass - The client hashed password sent to the server.
///
/// Returns: a tuple containing the final hash and the hash's salt, nothing on failure.
///
/// Example:
/// ```rust
/// let enc = hash_pwd("THISISTOTALLYAHASHEDTHING...").unwrap();
/// println!("Server Hash: {}", HEXUPPER.encode(&enc.0));
/// println!("Server Salt: {}", HEXUPPER.encode(&enc.1));
/// ```
pub fn hash_pwd(hashed_pass: &str) -> (String, String) {
let salt = gen_salt();
let hash = hash(&hashed_pass, &salt, 500_000);
(hash, salt)
}

View File

@ -1,4 +1,3 @@
pub mod authorization;
pub mod creation;
pub mod hash_email;
pub mod hash_pwd;
pub mod hash;

View File

@ -1,19 +1,16 @@
use crate::common::command::*;
use crate::server::db::cmd::user_exists::user_exists;
pub async fn get_user_salt(
pub async fn get_client_salt(
sql_conn: &tokio_postgres::Client,
username: &str,
command: Command,
is_server_owned: bool,
) -> Result<String, String> {
/* check that user exists */
if user_exists(sql_conn, username).await {
let query_variable: String = match command {
Command::GetEmailSalt if is_server_owned => "server_email_salt",
Command::GetPasswordSalt if is_server_owned => "server_pass_salt",
Command::GetEmailSalt if !is_server_owned => "client_email_salt",
Command::GetPasswordSalt if !is_server_owned => "client_pass_salt",
Command::GetEmailSalt => "client_email_salt",
Command::GetPasswordSalt => "client_pass_salt",
_ => {
return Err(format!(
"Could not get salt for user, {}, requested salt type, {}, is invalid.",
@ -25,8 +22,9 @@ pub async fn get_user_salt(
for row in &sql_conn
.query(
"SELECT username, $1 FROM accounts_schema.accounts WHERE username LIKE $2",
&[&query_variable, &username],
format!("SELECT username, {} FROM accounts_schema.accounts WHERE username LIKE $1",
&query_variable).as_str(),
&[&username],
)
.await
.unwrap()

View File

@ -0,0 +1,41 @@
use argon2::password_hash::PasswordHash;
use crate::common::command::*;
use crate::server::db::cmd::user_exists::user_exists;
pub async fn get_server_salt(
sql_conn: &tokio_postgres::Client,
username: &str,
command: Command,
) -> Result<String, String> {
/* check that user exists */
if user_exists(sql_conn, username).await {
let query_variable: String = match command {
Command::GetEmailSalt => "email_hash_phc",
Command::GetPasswordSalt => "pass_hash_phc",
_ => {
return Err(format!(
"Could not get salt for user, {}, requested salt type, {}, is invalid.",
username, command
))
}
}
.into();
for row in &sql_conn
.query(
format!("SELECT username, {} FROM accounts_schema.accounts WHERE username LIKE $1",
query_variable).as_str(),
&[&username],
)
.await
.unwrap()
{
/* parse PHC string */
let parsed_phc = PasswordHash::new(row.get(1)).unwrap(); // TODO: check if corrupted account
return Ok(parsed_phc.salt.unwrap().to_string());
}
}
Err("Failed to retrieve salt of non-existing user.".to_string())
}

View File

@ -9,13 +9,13 @@ pub async fn get_user_hash(
if user_exists(sql_conn, username).await {
if is_email {
for row in
&sql_conn.query("SELECT username, email_hash FROM accounts_schema.accounts WHERE username LIKE $1",
&sql_conn.query("SELECT username, email_hash_phc FROM accounts_schema.accounts WHERE username LIKE $1",
&[&username]).await.unwrap() {
return Ok(row.get(1));
}
} else {
for row in
&sql_conn.query("SELECT username, pass_hash FROM accounts_schema.accounts WHERE username LIKE $1",
&sql_conn.query("SELECT username, pass_hash_phc FROM accounts_schema.accounts WHERE username LIKE $1",
&[&username]).await.unwrap() {
return Ok(row.get(1));
}

View File

@ -9,5 +9,6 @@ pub mod create_transaction;
pub mod get_user_hash;
pub mod get_user_id;
pub mod get_user_salt;
pub mod get_client_salt;
pub mod get_server_salt;
pub mod user_exists;

View File

@ -2,11 +2,9 @@ CREATE TABLE accounts_schema.accounts (
id BIGSERIAL PRIMARY KEY,
username TEXT UNIQUE NOT NULL,
email_hash TEXT UNIQUE NOT NULL,
server_email_salt TEXT UNIQUE NOT NULL,
email_hash_phc TEXT UNIQUE NOT NULL,
client_email_salt TEXT UNIQUE NOT NULL,
pass_hash TEXT UNIQUE NOT NULL,
server_pass_salt TEXT UNIQUE NOT NULL,
pass_hash_phc TEXT UNIQUE NOT NULL,
client_pass_salt TEXT UNIQUE NOT NULL
)

View File

@ -5,15 +5,12 @@ use crate::common::account::transaction::Transaction;
pub struct Account {
pub username: String,
pub email_hash: String,
pub server_email_salt: String,
pub email_hash_phc: String,
pub client_email_salt: String,
pub pass_hash: String,
pub server_pass_salt: String,
pub pass_hash_phc: String,
pub client_pass_salt: String,
pub is_pass: bool,
pub portfolio: Portfolio,
pub transactions: Vec<Transaction>,
}
@ -22,11 +19,12 @@ impl std::fmt::Display for Account {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"({}, {}, {}, {}, {}, {:#?})",
"({}, {}, {}, {}, {}, {}, {:#?})",
self.username,
self.email_hash,
self.is_pass,
self.pass_hash,
self.email_hash_phc,
self.client_email_salt,
self.pass_hash_phc,
self.client_pass_salt,
self.portfolio,
self.transactions
)

View File

@ -1,10 +1,12 @@
use crate::common::account::salt::*;
use argon2::password_hash::SaltString;
use rand_core::OsRng;
use crate::common::command::*;
use crate::common::message::*;
use crate::server::account::authorization::acc_auth;
use crate::server::account::creation::acc_create;
use crate::server::db::cmd::get_user_salt::get_user_salt;
use crate::server::db::cmd::get_client_salt::*;
use tokio::net::TcpStream;
use tokio_rustls::server::TlsStream;
@ -25,13 +27,13 @@ pub async fn handle_data(
Command::GenHashSalt => {
Message::new()
.command(Command::Success)
.data(gen_salt())
.data(SaltString::generate(&mut OsRng).as_str().to_string())
.send(socket)
.await
}
Command::GetEmailSalt | Command::GetPasswordSalt => {
let salt =
get_user_salt(sql_conn, client_msg.get_data()?, client_msg.command, false).await;
get_client_salt(sql_conn, client_msg.get_data()?, client_msg.command).await;
let mut response = Message::new();
if salt.is_ok() {
response