diff --git a/Cargo.lock b/Cargo.lock index bccf35b..1dfa7db 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -149,7 +149,7 @@ dependencies = [ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", @@ -199,7 +199,7 @@ dependencies = [ "mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "pin-project 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "ring 0.16.11 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", @@ -277,7 +277,7 @@ dependencies = [ "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "redis-async 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", @@ -642,7 +642,7 @@ dependencies = [ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", "serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -665,7 +665,7 @@ dependencies = [ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", "serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -829,7 +829,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1810,7 +1810,7 @@ dependencies = [ [[package]] name = "rand" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2038,6 +2038,7 @@ dependencies = [ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "r2d2 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)", "r2d2-diesel 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "redis-async 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2469,7 +2470,7 @@ dependencies = [ "idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)", "tokio 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3008,7 +3009,7 @@ dependencies = [ "checksum r2d2 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1497e40855348e4a8a40767d8e55174bce1e445a3ac9254ad44ad468ee0485af" "checksum r2d2-diesel 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9c29bad92da76d02bc2c020452ebc3a3fe6fa74cfab91e711c43116e4fb1a3" "checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" -"checksum rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412" +"checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" "checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" "checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" diff --git a/Cargo.toml b/Cargo.toml index 733b46c..342d35b 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,7 @@ serde_json = "1.0" uuid = { version = "0.7", features = ["serde", "v4"] } validator = "0.8.0" validator_derive = "0.8.0" +rand = "0.7.3" [dev-dependencies] actix-http-test = "0.2.0" diff --git a/migrations/2020-04-27-193614_add_user_salt/down.sql b/migrations/2020-04-27-193614_add_user_salt/down.sql new file mode 100644 index 0000000..b1b024a --- /dev/null +++ b/migrations/2020-04-27-193614_add_user_salt/down.sql @@ -0,0 +1,3 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE users + DROP COLUMN salt; \ No newline at end of file diff --git a/migrations/2020-04-27-193614_add_user_salt/up.sql b/migrations/2020-04-27-193614_add_user_salt/up.sql new file mode 100644 index 0000000..57dfe19 --- /dev/null +++ b/migrations/2020-04-27-193614_add_user_salt/up.sql @@ -0,0 +1,3 @@ +-- Your SQL goes here +ALTER TABLE users + ADD COLUMN salt VARCHAR(36) NOT NULL DEFAULT '' AFTER password; diff --git a/src/auth.rs b/src/auth.rs index 97798d0..70d0c6f 100755 --- a/src/auth.rs +++ b/src/auth.rs @@ -46,8 +46,9 @@ pub fn decode_jwt(token: &str) -> Result { /// /// Uses the argon2i algorithm. /// auth_salt is environment-configured. -pub fn hash(password: &str) -> String { - argon2i_simple(&password, &CONFIG.auth_salt) +pub fn hash(password: &str, salt: &String) -> String { + let masked = mask_str(&salt, &CONFIG.auth_salt); + argon2i_simple(&password, &masked) .iter() .map(|b| format!("{:02x}", b)) .collect() @@ -63,23 +64,47 @@ pub fn get_identity_service() -> IdentityService { ) } + +/// Mask one string with another. Used for combining salts. +fn mask_str(str: &String, mask : &String) -> String{ + let mut strb = str.clone().into_bytes(); + let maskb = mask.clone().into_bytes(); + let str_len = strb.len(); + let mask_len = maskb.len(); + let mut i = 0; + let mut m = 0; + while i < str_len{ + if m >= mask_len { + m = 0; + } + strb[i] = (strb[i].wrapping_add(maskb[m])) % 128; + i += 1; + m+= 1; + } + return String::from_utf8(strb).unwrap(); +} + #[cfg(test)] pub mod tests { use super::*; + use rand::{thread_rng, Rng}; + use rand::distributions::Alphanumeric; static EMAIL: &str = "test@test.com"; #[test] fn it_hashes_a_password() { let password = "password"; - let hashed = hash(password); + let salt = thread_rng().sample_iter(&Alphanumeric).take(32).collect::(); + let hashed = hash(password, &salt); assert_ne!(password, hashed); } #[test] fn it_matches_2_hashed_passwords() { let password = "password"; - let hashed = hash(password); - let hashed_again = hash(password); + let salt = thread_rng().sample_iter(&Alphanumeric).take(32).collect::(); + let hashed = hash(password, &salt); + let hashed_again = hash(password, &salt); assert_eq!(hashed, hashed_again); } @@ -97,4 +122,13 @@ pub mod tests { let decoded = decode_jwt(&jwt).unwrap(); assert_eq!(private_claim, decoded); } + + + #[test] + fn it_masks_a_string() { + let salt = "salt1salt2salt3".to_string(); + let mask = "mask52632".to_string(); + let masked = mask_str(&salt, &mask); + assert_ne!(masked, "".to_string()); + } } diff --git a/src/handlers/auth.rs b/src/handlers/auth.rs index 0b58d73..38ff6ee 100755 --- a/src/handlers/auth.rs +++ b/src/handlers/auth.rs @@ -1,4 +1,4 @@ -use crate::auth::{create_jwt, hash, PrivateClaim}; +use crate::auth::{create_jwt, PrivateClaim}; use crate::database::PoolType; use crate::errors::ApiError; use crate::handlers::user::UserResponse; @@ -31,9 +31,8 @@ pub async fn login( ) -> Result, ApiError> { validate(¶ms)?; - // Validate that the email + hashed password matches - let hashed = hash(¶ms.password); - let user = block(move || find_by_auth(&pool, ¶ms.email, &hashed)).await?; + // Validate that the email + password matches + let user = block(move || find_by_auth(&pool, ¶ms.email, ¶ms.password)).await?; // Create a JWT let private_claim = PrivateClaim::new(user.id, user.email.clone()); diff --git a/src/models/user.rs b/src/models/user.rs index 80f869f..0a67703 100755 --- a/src/models/user.rs +++ b/src/models/user.rs @@ -3,6 +3,8 @@ use crate::database::PoolType; use crate::errors::ApiError; use crate::handlers::user::{UserResponse, UsersResponse}; use crate::schema::users; +use rand::{thread_rng, Rng}; +use rand::distributions::Alphanumeric; use chrono::{NaiveDateTime, Utc}; use diesel::prelude::*; use uuid::Uuid; @@ -14,6 +16,7 @@ pub struct User { pub last_name: String, pub email: String, pub password: String, + pub salt: String, pub created_by: String, pub created_at: NaiveDateTime, pub updated_by: String, @@ -78,15 +81,20 @@ pub fn find_by_auth( user_email: &str, user_password: &str, ) -> Result { - use crate::schema::users::dsl::{email, password, users}; + use crate::schema::users::dsl::{email, users}; let conn = pool.get()?; let user = users .filter(email.eq(user_email.to_string())) - .filter(password.eq(user_password.to_string())) .first::(&conn) .map_err(|_| ApiError::Unauthorized("Invalid login".into()))?; - Ok(user.into()) + let hashed = hash(user_password,&user.salt); + if hashed.eq(&user.password) { + Ok(user.into()) + } else { + Err(ApiError::Unauthorized("Invalid login".into())) + } + } /// Create a new user @@ -123,12 +131,14 @@ pub fn delete(pool: &PoolType, user_id: Uuid) -> Result<(), ApiError> { impl From for User { fn from(user: NewUser) -> Self { + let salt = thread_rng().sample_iter(&Alphanumeric).take(32).collect::(); User { id: user.id, first_name: user.first_name, last_name: user.last_name, email: user.email, - password: hash(&user.password), + password: hash(&user.password,&salt), + salt: salt, created_by: user.created_by, created_at: Utc::now().naive_utc(), updated_by: user.updated_by, diff --git a/src/schema.rs b/src/schema.rs index c792e18..d65ebbc 100755 --- a/src/schema.rs +++ b/src/schema.rs @@ -5,6 +5,7 @@ table! { last_name -> Varchar, email -> Varchar, password -> Varchar, + salt -> Varchar, created_by -> Varchar, created_at -> Timestamp, updated_by -> Varchar,