convert to argon2 church
continuous-integration/drone/push Build is passing
Details
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:
parent
4f420925cf
commit
27e68e3eeb
|
@ -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"] }
|
||||
|
|
|
@ -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()
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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()
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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.",
|
||||
))
|
||||
}
|
||||
}
|
|
@ -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.",
|
||||
));
|
||||
}
|
||||
}
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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.",
|
||||
)})
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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."),
|
||||
)
|
||||
})?)
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
|
@ -1,6 +1,4 @@
|
|||
pub mod hash;
|
||||
pub mod order;
|
||||
pub mod portfolio;
|
||||
pub mod position;
|
||||
pub mod salt;
|
||||
pub mod transaction;
|
||||
|
|
|
@ -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()
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
pub mod authorization;
|
||||
pub mod creation;
|
||||
pub mod hash_email;
|
||||
pub mod hash_pwd;
|
||||
pub mod hash;
|
||||
|
|
|
@ -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()
|
|
@ -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())
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue