remove all todos
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
- on the server side, remove all unwraps and actually check and return to client an error message, by sing if let statements. - on the client side, remove unneeded uwnraps by making req_server_salt() and get_server_salt() return SaltString, remove the load from hash function.
This commit is contained in:
parent
27e68e3eeb
commit
30a20873ea
|
@ -5,8 +5,8 @@ use argon2::password_hash::PasswordHash;
|
|||
use crate::common::command::*;
|
||||
use crate::common::message::*;
|
||||
|
||||
use crate::client::network::cmd::req_server_salt::*;
|
||||
use crate::client::account::hash::*;
|
||||
use crate::client::network::cmd::req_server_salt::*;
|
||||
|
||||
use tokio::net::TcpStream;
|
||||
use tokio_rustls::client::TlsStream;
|
||||
|
@ -87,13 +87,13 @@ 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()
|
||||
),
|
||||
))
|
||||
io::ErrorKind::ConnectionRefused,
|
||||
format!(
|
||||
"Failed authorizing account, {}, server returned error: {}.",
|
||||
username,
|
||||
ret_msg.get_err()
|
||||
),
|
||||
))
|
||||
} else {
|
||||
/* authorized */
|
||||
Ok(ret_msg.get_data().map_err(|_| {
|
||||
|
@ -102,8 +102,8 @@ pub async fn acc_auth(
|
|||
format!(
|
||||
"Failed authorizing account, {}, server returned invalid data.",
|
||||
username
|
||||
),
|
||||
)
|
||||
),
|
||||
)
|
||||
})?)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,24 +37,24 @@ pub async fn acc_create(
|
|||
socket: &mut TlsStream<TcpStream>,
|
||||
username: &str,
|
||||
email: &str,
|
||||
password: &str,
|
||||
passw: &str,
|
||||
) -> std::io::Result<()> {
|
||||
/*
|
||||
* get two server salts for email, and password
|
||||
* */
|
||||
let email_server_salt = get_server_salt(socket).await?;
|
||||
let password_server_salt = get_server_salt(socket).await?;
|
||||
let passw_server_salt = get_server_salt(socket).await?;
|
||||
|
||||
/*
|
||||
* generate hashes for email, password
|
||||
* */
|
||||
let email_hash_phc = hash(email, &email_server_salt, true);
|
||||
let password_hash_phc = hash(password, &password_server_salt, true);
|
||||
let passw_hash_phc = hash(passw, &passw_server_salt, true);
|
||||
|
||||
/* generate message to be sent to the server */
|
||||
let data = object! {
|
||||
email_client_hash_phc: email_hash_phc,
|
||||
password_client_hash_phc: password_hash_phc,
|
||||
passw_client_hash_phc: passw_hash_phc,
|
||||
username: username
|
||||
};
|
||||
/* build message request */
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use argon2::{
|
||||
password_hash::{PasswordHasher, SaltString},
|
||||
Argon2
|
||||
Argon2,
|
||||
};
|
||||
use rand_core::OsRng;
|
||||
|
||||
|
@ -22,15 +22,18 @@ use rand_core::OsRng;
|
|||
/// ```rust
|
||||
/// let enc = hash_email("totallyrealemail@anemail.c0m", server_salt).unwrap();
|
||||
/// ```
|
||||
pub fn hash(email: &str, server_salt: &str, resalt: bool) -> String {
|
||||
let mut raw_salt: String = server_salt.to_string();
|
||||
pub fn hash(email: &str, server_salt: &SaltString, resalt: bool) -> String {
|
||||
let mut raw_salt: String = server_salt.as_str().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 salt = SaltString::new(&raw_salt).unwrap();
|
||||
|
||||
let argon2id = Argon2::default();
|
||||
let phc = argon2id.hash_password_simple(&email.as_bytes(), &salt).unwrap();
|
||||
let phc = argon2id
|
||||
.hash_password_simple(&email.as_bytes(), &salt)
|
||||
.unwrap();
|
||||
|
||||
phc.to_string()
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
use std::io;
|
||||
|
||||
use argon2::password_hash::SaltString;
|
||||
|
||||
use crate::common::command::*;
|
||||
use crate::common::message::*;
|
||||
|
||||
|
@ -21,7 +23,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<SaltString> {
|
||||
/*
|
||||
* request to generate a salt from the server.
|
||||
* */
|
||||
|
@ -39,11 +41,18 @@ pub async fn get_server_salt(socket: &mut TlsStream<TcpStream>) -> std::io::Resu
|
|||
"Failed getting generated server Salt, received an invalid message.",
|
||||
))
|
||||
} else {
|
||||
ret_msg.get_data().map_err(|_| {
|
||||
let salt_raw: String = ret_msg.get_data().map_err(|_| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"Failed getting generated server Salt, received an invalid message.",
|
||||
)
|
||||
)
|
||||
})?;
|
||||
|
||||
/* verify that the salt is actually valid */
|
||||
SaltString::new(salt_raw.as_str()).map_err(|_| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"Failed getting generated server Salt, received an invalid message.")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
use std::io;
|
||||
|
||||
use argon2::password_hash::SaltString;
|
||||
|
||||
use crate::common::command::*;
|
||||
use crate::common::message::*;
|
||||
|
||||
|
@ -28,11 +30,10 @@ pub async fn req_server_salt(
|
|||
socket: &mut TlsStream<TcpStream>,
|
||||
username: &str,
|
||||
salt_type: Command,
|
||||
) -> std::io::Result<String> {
|
||||
) -> std::io::Result<SaltString> {
|
||||
/* enforce salt_type to be either email or password */
|
||||
assert_eq!(
|
||||
(salt_type == Command::GetEmailSalt) || (salt_type == Command::GetPasswordSalt),
|
||||
true
|
||||
(salt_type == Command::GetEmailSalt) || (salt_type == Command::GetPasswordSalt), true
|
||||
);
|
||||
|
||||
/* generate message to send */
|
||||
|
@ -52,10 +53,18 @@ pub async fn req_server_salt(
|
|||
));
|
||||
}
|
||||
|
||||
Ok(ret_msg.get_data().map_err(|_| {
|
||||
let salt_raw: String = ret_msg.get_data().map_err(|_| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
format!("Could not get server salt, received invalid data."),
|
||||
)
|
||||
})?)
|
||||
"Could not get server salt, received invalid data."
|
||||
)
|
||||
})?;
|
||||
|
||||
/* verify that the salt is actually valid */
|
||||
SaltString::new(salt_raw.as_str()).map_err(|_| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
format!("Could not get server salt, received invalid salt length."),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use log::warn;
|
|||
|
||||
use argon2::{
|
||||
password_hash::{PasswordHash, PasswordVerifier},
|
||||
Argon2
|
||||
Argon2,
|
||||
};
|
||||
|
||||
use crate::common::command::*;
|
||||
|
@ -32,11 +32,12 @@ pub async fn acc_auth(
|
|||
* Parse account data.
|
||||
* */
|
||||
/* get json data */
|
||||
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 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();
|
||||
|
@ -79,8 +80,20 @@ pub async fn acc_auth(
|
|||
* */
|
||||
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() {
|
||||
let parsed_email_hash = match PasswordHash::new(&email_server_hash) {
|
||||
Ok(val) => val,
|
||||
Err(_) => {
|
||||
return Message::new()
|
||||
.command(Command::Failure)
|
||||
.data("Email Hash Invalid")
|
||||
.send(socket)
|
||||
.await;
|
||||
}
|
||||
};
|
||||
if argon2id
|
||||
.verify_password(&email_client_hash.as_bytes(), &parsed_email_hash)
|
||||
.is_err()
|
||||
{
|
||||
return Message::new()
|
||||
.command(Command::Failure)
|
||||
.data("Email Incorrect")
|
||||
|
@ -88,8 +101,19 @@ pub async fn acc_auth(
|
|||
.await;
|
||||
}
|
||||
|
||||
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() {
|
||||
let parsed_passw_hash = match PasswordHash::new(&passw_server_hash) {
|
||||
Ok(val) => val,
|
||||
Err(_) => {
|
||||
return Message::new()
|
||||
.command(Command::Failure)
|
||||
.data("Password Hash Invalid")
|
||||
.send(socket)
|
||||
.await;
|
||||
}
|
||||
}; if argon2id
|
||||
.verify_password(&passw_client_hash.as_bytes(), &parsed_passw_hash)
|
||||
.is_err()
|
||||
{
|
||||
return Message::new()
|
||||
.command(Command::Failure)
|
||||
.data("Password Incorrect")
|
||||
|
@ -97,7 +121,6 @@ pub async fn acc_auth(
|
|||
.await;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Generate JWT token
|
||||
* */
|
||||
|
@ -123,10 +146,11 @@ pub async fn acc_auth(
|
|||
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) })?;
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs(),
|
||||
)
|
||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
|
||||
|
||||
Message::new()
|
||||
.command(Command::Success)
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
use log::warn;
|
||||
use argon2::password_hash::PasswordHash;
|
||||
use log::warn;
|
||||
|
||||
use crate::common::message::*;
|
||||
use crate::common::account::portfolio::Portfolio;
|
||||
use crate::common::message::*;
|
||||
|
||||
use crate::server::account::hash::*;
|
||||
|
||||
use crate::server::ds::account::Account;
|
||||
use crate::server::db::cmd::user_exists::*;
|
||||
use crate::server::ds::account::Account;
|
||||
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use tokio::net::TcpStream;
|
||||
|
@ -36,8 +36,7 @@ pub async fn acc_create(
|
|||
let username = data["username"].as_str().unwrap();
|
||||
|
||||
/* search for an account with same name */
|
||||
if !user_exists(sql_conn, username).await
|
||||
{
|
||||
if !user_exists(sql_conn, username).await {
|
||||
/*
|
||||
* Inform cient that user already exists
|
||||
* Note: figure out if this is a security? issue
|
||||
|
@ -50,16 +49,46 @@ pub async fn acc_create(
|
|||
}
|
||||
|
||||
/* 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_hash_phc, passw_client_hash_phc) =
|
||||
match (data["email_client_hash_phc"].as_str(), data["passw_client_hash_phc"].as_str()) {
|
||||
(Some(a), Some(b)) => (a, b),
|
||||
_ => {
|
||||
/* received empty PHC strings */
|
||||
return Message::new()
|
||||
.command(Command::Failure)
|
||||
.data("Received empty PHC strings")
|
||||
.send(socket)
|
||||
.await;
|
||||
}
|
||||
};
|
||||
|
||||
let email_client = PasswordHash::new(email_client_hash_phc).unwrap();
|
||||
let password_client = PasswordHash::new(password_client_hash_phc).unwrap();
|
||||
let (email_client, passw_client) =
|
||||
match (PasswordHash::new(email_client_hash_phc), PasswordHash::new(passw_client_hash_phc)) {
|
||||
(Ok(a), Ok(b)) => (a, b),
|
||||
_ => {
|
||||
return Message::new()
|
||||
.command(Command::Failure)
|
||||
.data("Received invalid PHC strings")
|
||||
.send(socket)
|
||||
.await;
|
||||
}
|
||||
};
|
||||
|
||||
/* 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
|
||||
let (email_client_salt, passw_client_salt) =
|
||||
match (email_client.salt, passw_client.salt) {
|
||||
(Some(a), Some(b)) => (a, b),
|
||||
_ => {
|
||||
return Message::new()
|
||||
.command(Command::Failure)
|
||||
.data("Received invalid salts")
|
||||
.send(socket)
|
||||
.await;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* generate account struct */
|
||||
let mut account: Account = Account {
|
||||
|
@ -68,33 +97,45 @@ pub async fn acc_create(
|
|||
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(),
|
||||
passw_hash_phc: "".to_string(),
|
||||
client_passw_salt: passw_client_salt.to_string(),
|
||||
|
||||
portfolio: Portfolio::default(),
|
||||
transactions: Vec::new(),
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Hash the email and password.
|
||||
* Hash the email and password and store them.
|
||||
* */
|
||||
/* hash the email */
|
||||
account.email_hash_phc = hash(email_client.hash.unwrap().to_string());
|
||||
/* hash the password */
|
||||
// TODO: actually merge those functions into one.
|
||||
account.pass_hash_phc = hash(password_client.hash.unwrap().to_string());
|
||||
if let (Some(email_client_hash), Some(passw_client_hash)) = (email_client.hash, passw_client.hash) {
|
||||
account.email_hash_phc = hash(email_client_hash.to_string());
|
||||
account.passw_hash_phc = hash(passw_client_hash.to_string());
|
||||
} else {
|
||||
return Message::new()
|
||||
.command(Command::Failure)
|
||||
.data("Received empty hashes")
|
||||
.send(socket)
|
||||
.await;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write the account to the database.
|
||||
* */
|
||||
let creation_result = sql_conn.execute("INSERT INTO accounts_schema.accounts \
|
||||
let creation_result = sql_conn
|
||||
.execute(
|
||||
"INSERT INTO accounts_schema.accounts \
|
||||
(username, email_hash_phc, client_email_salt, pass_hash_phc, client_pass_salt)
|
||||
VALUES \
|
||||
($1, $2, $3, $4, $5)",
|
||||
&[&account.username,
|
||||
&account.email_hash_phc, &account.client_email_salt,
|
||||
&account.pass_hash_phc, &account.client_pass_salt]).await;
|
||||
&[
|
||||
&account.username,
|
||||
&account.email_hash_phc,
|
||||
&account.client_email_salt,
|
||||
&account.passw_hash_phc,
|
||||
&account.client_passw_salt,
|
||||
],
|
||||
)
|
||||
.await;
|
||||
|
||||
/*
|
||||
* Send to client SQL result
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use argon2::{
|
||||
password_hash::{PasswordHasher, SaltString},
|
||||
Argon2
|
||||
Argon2,
|
||||
};
|
||||
use rand_core::OsRng;
|
||||
|
||||
|
@ -25,7 +25,9 @@ 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();
|
||||
let phc = argon2id
|
||||
.hash_password_simple(&hashed_email.as_bytes(), &salt)
|
||||
.unwrap();
|
||||
|
||||
phc.to_string()
|
||||
}
|
||||
|
|
|
@ -22,9 +22,12 @@ pub async fn get_client_salt(
|
|||
|
||||
for row in &sql_conn
|
||||
.query(
|
||||
format!("SELECT username, {} FROM accounts_schema.accounts WHERE username LIKE $1",
|
||||
&query_variable).as_str(),
|
||||
&[&username],
|
||||
format!(
|
||||
"SELECT username, {} FROM accounts_schema.accounts WHERE username LIKE $1",
|
||||
&query_variable
|
||||
)
|
||||
.as_str(),
|
||||
&[&username],
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
|
|
|
@ -24,16 +24,25 @@ pub async fn get_server_salt(
|
|||
|
||||
for row in &sql_conn
|
||||
.query(
|
||||
format!("SELECT username, {} FROM accounts_schema.accounts WHERE username LIKE $1",
|
||||
query_variable).as_str(),
|
||||
&[&username],
|
||||
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());
|
||||
let parsed_phc = PasswordHash::new(row.get(1)).map_err(|_| {
|
||||
format!("Account, {}, corrupted, invalid parsed PHC", username)
|
||||
})?;
|
||||
if let Some(salt) = parsed_phc.salt {
|
||||
return Ok(salt.to_string());
|
||||
} else {
|
||||
return Err(format!("Account, {}, corrupted, empty salt!", username));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,8 +7,8 @@ pub mod get_stock;
|
|||
pub mod create_position;
|
||||
pub mod create_transaction;
|
||||
|
||||
pub mod get_user_hash;
|
||||
pub mod get_user_id;
|
||||
pub mod get_client_salt;
|
||||
pub mod get_server_salt;
|
||||
pub mod get_user_hash;
|
||||
pub mod get_user_id;
|
||||
pub mod user_exists;
|
||||
|
|
|
@ -8,8 +8,8 @@ pub struct Account {
|
|||
pub email_hash_phc: String,
|
||||
pub client_email_salt: String,
|
||||
|
||||
pub pass_hash_phc: String,
|
||||
pub client_pass_salt: String,
|
||||
pub passw_hash_phc: String,
|
||||
pub client_passw_salt: String,
|
||||
|
||||
pub portfolio: Portfolio,
|
||||
pub transactions: Vec<Transaction>,
|
||||
|
@ -23,8 +23,8 @@ impl std::fmt::Display for Account {
|
|||
self.username,
|
||||
self.email_hash_phc,
|
||||
self.client_email_salt,
|
||||
self.pass_hash_phc,
|
||||
self.client_pass_salt,
|
||||
self.passw_hash_phc,
|
||||
self.client_passw_salt,
|
||||
self.portfolio,
|
||||
self.transactions
|
||||
)
|
||||
|
|
|
@ -32,8 +32,7 @@ pub async fn handle_data(
|
|||
.await
|
||||
}
|
||||
Command::GetEmailSalt | Command::GetPasswordSalt => {
|
||||
let salt =
|
||||
get_client_salt(sql_conn, client_msg.get_data()?, client_msg.command).await;
|
||||
let salt = get_client_salt(sql_conn, client_msg.get_data()?, client_msg.command).await;
|
||||
let mut response = Message::new();
|
||||
if salt.is_ok() {
|
||||
response
|
||||
|
|
Loading…
Reference in New Issue