move to argon2 crate for password hashing
This commit is contained in:
parent
e99c18704e
commit
54b18a8585
4 changed files with 65 additions and 89 deletions
105
Cargo.lock
generated
105
Cargo.lock
generated
|
@ -37,15 +37,6 @@ dependencies = [
|
||||||
"subtle",
|
"subtle",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "aho-corasick"
|
|
||||||
version = "0.7.18"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
|
|
||||||
dependencies = [
|
|
||||||
"memchr",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ansi_term"
|
name = "ansi_term"
|
||||||
version = "0.12.1"
|
version = "0.12.1"
|
||||||
|
@ -56,16 +47,15 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arrayref"
|
name = "argon2"
|
||||||
version = "0.3.6"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
|
checksum = "a27e27b63e4a34caee411ade944981136fdfa535522dc9944d6700196cbd899f"
|
||||||
|
dependencies = [
|
||||||
[[package]]
|
"base64ct",
|
||||||
name = "arrayvec"
|
"blake2",
|
||||||
version = "0.7.2"
|
"password-hash",
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
]
|
||||||
checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-stream"
|
name = "async-stream"
|
||||||
|
@ -137,6 +127,12 @@ version = "0.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64ct"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dea908e7347a8c64e378c17e30ef880ad73e3b4498346b055c2c00ea342f3179"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "binascii"
|
name = "binascii"
|
||||||
version = "0.1.4"
|
version = "0.1.4"
|
||||||
|
@ -150,14 +146,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "blake2b_simd"
|
name = "blake2"
|
||||||
version = "1.0.0"
|
version = "0.10.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "72936ee4afc7f8f736d1c38383b56480b5497b4617b4a77bdbf1d2ababc76127"
|
checksum = "b9cf849ee05b2ee5fba5e36f97ff8ec2533916700fc0758d40d92136a42f3388"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayref",
|
"digest",
|
||||||
"arrayvec",
|
|
||||||
"constant_time_eq 0.1.5",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -261,18 +255,6 @@ dependencies = [
|
||||||
"os_str_bytes",
|
"os_str_bytes",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "constant_time_eq"
|
|
||||||
version = "0.1.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "constant_time_eq"
|
|
||||||
version = "0.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "df04a53a7e91248c27eb6bfc1db165e8f47453e98478e4609f9cce020bb3c65a"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cookie"
|
name = "cookie"
|
||||||
version = "0.16.0"
|
version = "0.16.0"
|
||||||
|
@ -300,16 +282,6 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossbeam-utils"
|
|
||||||
version = "0.8.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"lazy_static",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crypto-common"
|
name = "crypto-common"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
|
@ -424,20 +396,6 @@ dependencies = [
|
||||||
"subtle",
|
"subtle",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "djangohashers"
|
|
||||||
version = "1.5.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8d3972be19526a5cde01c01988f152cd98af8c35bd93228a8ee637715a36a38c"
|
|
||||||
dependencies = [
|
|
||||||
"base64 0.13.0",
|
|
||||||
"constant_time_eq 0.2.1",
|
|
||||||
"lazy_static",
|
|
||||||
"rand",
|
|
||||||
"regex",
|
|
||||||
"rust-argon2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.6.1"
|
version = "1.6.1"
|
||||||
|
@ -979,13 +937,13 @@ dependencies = [
|
||||||
name = "nomilo"
|
name = "nomilo"
|
||||||
version = "0.1.0-dev"
|
version = "0.1.0-dev"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"argon2",
|
||||||
"base64 0.13.0",
|
"base64 0.13.0",
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
"diesel",
|
"diesel",
|
||||||
"diesel-derive-enum",
|
"diesel-derive-enum",
|
||||||
"diesel_migrations",
|
"diesel_migrations",
|
||||||
"djangohashers",
|
|
||||||
"figment",
|
"figment",
|
||||||
"humantime",
|
"humantime",
|
||||||
"jsonwebtoken",
|
"jsonwebtoken",
|
||||||
|
@ -1124,6 +1082,17 @@ dependencies = [
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "password-hash"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e029e94abc8fb0065241c308f1ac6bc8d20f450e8f7c5f0b25cd9b8d526ba294"
|
||||||
|
dependencies = [
|
||||||
|
"base64ct",
|
||||||
|
"rand_core",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pear"
|
name = "pear"
|
||||||
version = "0.2.3"
|
version = "0.2.3"
|
||||||
|
@ -1341,8 +1310,6 @@ version = "1.5.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286"
|
checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
|
||||||
"memchr",
|
|
||||||
"regex-syntax",
|
"regex-syntax",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1485,18 +1452,6 @@ dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rust-argon2"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b50162d19404029c1ceca6f6980fe40d45c8b369f6f44446fa14bb39573b5bb9"
|
|
||||||
dependencies = [
|
|
||||||
"base64 0.13.0",
|
|
||||||
"blake2b_simd",
|
|
||||||
"constant_time_eq 0.1.5",
|
|
||||||
"crossbeam-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustversion"
|
name = "rustversion"
|
||||||
version = "1.0.6"
|
version = "1.0.6"
|
||||||
|
|
|
@ -19,10 +19,10 @@ uuid = { version = "0.8.2", features = ["v4", "serde"] }
|
||||||
diesel = { version = "1.4", features = ["sqlite"] }
|
diesel = { version = "1.4", features = ["sqlite"] }
|
||||||
diesel_migrations = "1.4"
|
diesel_migrations = "1.4"
|
||||||
diesel-derive-enum = { version = "1", features = ["sqlite"] }
|
diesel-derive-enum = { version = "1", features = ["sqlite"] }
|
||||||
djangohashers = { version = "1.4.0", features = ["with_argon2"], default-features = false }
|
|
||||||
jsonwebtoken = "7.2.0"
|
jsonwebtoken = "7.2.0"
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
humantime = "2.1.0"
|
humantime = "2.1.0"
|
||||||
tokio = "1"
|
tokio = "1"
|
||||||
figment = { version = "0.10.6", features = ["toml", "env"] }
|
figment = { version = "0.10.6", features = ["toml", "env"] }
|
||||||
clap = {version = "3", features = ["derive", "cargo"]}
|
clap = {version = "3", features = ["derive", "cargo"]}
|
||||||
|
argon2 = "0.4.0"
|
||||||
|
|
|
@ -5,8 +5,9 @@ use rocket::request::{Request, Outcome};
|
||||||
use rocket::response::{self, Response, Responder};
|
use rocket::response::{self, Response, Responder};
|
||||||
use rocket::serde::json::Json;
|
use rocket::serde::json::Json;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use djangohashers::{HasherError};
|
|
||||||
use diesel::result::Error as DieselError;
|
use diesel::result::Error as DieselError;
|
||||||
|
use argon2::password_hash::errors::Error as PasswordHashError;
|
||||||
|
|
||||||
use crate::dns::ConnectorError;
|
use crate::dns::ConnectorError;
|
||||||
use crate::models;
|
use crate::models;
|
||||||
|
|
||||||
|
@ -21,13 +22,7 @@ pub enum UserError {
|
||||||
MalformedHeader,
|
MalformedHeader,
|
||||||
PermissionDenied,
|
PermissionDenied,
|
||||||
DbError(DieselError),
|
DbError(DieselError),
|
||||||
PasswordError(HasherError),
|
PasswordError(PasswordHashError),
|
||||||
}
|
|
||||||
|
|
||||||
impl From<HasherError> for UserError {
|
|
||||||
fn from(e: HasherError) -> Self {
|
|
||||||
UserError::PasswordError(e)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<DieselError> for UserError {
|
impl From<DieselError> for UserError {
|
||||||
|
@ -36,6 +31,12 @@ impl From<DieselError> for UserError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<PasswordHashError> for UserError {
|
||||||
|
fn from(e: PasswordHashError) -> Self {
|
||||||
|
UserError::PasswordError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Serialize, Debug)]
|
||||||
pub struct ErrorResponse {
|
pub struct ErrorResponse {
|
||||||
|
|
|
@ -5,8 +5,10 @@ use diesel_derive_enum::DbEnum;
|
||||||
use rocket::{State, request::{FromRequest, Request, Outcome}};
|
use rocket::{State, request::{FromRequest, Request, Outcome}};
|
||||||
use rocket::outcome::try_outcome;
|
use rocket::outcome::try_outcome;
|
||||||
use serde::{Deserialize};
|
use serde::{Deserialize};
|
||||||
// TODO: Maybe just use argon2 crate directly
|
use argon2::password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString};
|
||||||
use djangohashers::{make_password_with_algorithm, check_password, Algorithm};
|
use argon2::password_hash::errors::Error as PasswordHashError;
|
||||||
|
use argon2::password_hash::rand_core::OsRng;
|
||||||
|
use argon2::{Algorithm, Argon2, Version, Params};
|
||||||
use jsonwebtoken::{
|
use jsonwebtoken::{
|
||||||
errors::ErrorKind as JwtErrorKind
|
errors::ErrorKind as JwtErrorKind
|
||||||
};
|
};
|
||||||
|
@ -158,6 +160,25 @@ impl<'r> FromRequest<'r> for UserInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LocalUser {
|
impl LocalUser {
|
||||||
|
pub fn hash_password(password: &str) -> String {
|
||||||
|
let salt = SaltString::generate(&mut OsRng);
|
||||||
|
|
||||||
|
// https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
|
||||||
|
let argon2 = Argon2::new(
|
||||||
|
Algorithm::Argon2id,
|
||||||
|
Version::V0x13, // v19
|
||||||
|
Params::new(15000, 2, 1, None).expect("password param error"),
|
||||||
|
);
|
||||||
|
argon2.hash_password(password.as_bytes(), &salt).expect("password hash failed").to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn verify_password(password: &str, password_hash: &str) -> Result<bool, PasswordHashError> {
|
||||||
|
let parsed_hash = PasswordHash::new(&password_hash)?;
|
||||||
|
|
||||||
|
Ok(Argon2::default().verify_password(password.as_bytes(), &parsed_hash).is_ok())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn create_user(conn: &diesel::SqliteConnection, user_request: CreateUserRequest) -> Result<UserInfo, UserError> {
|
pub fn create_user(conn: &diesel::SqliteConnection, user_request: CreateUserRequest) -> Result<UserInfo, UserError> {
|
||||||
use crate::schema::localuser::dsl::*;
|
use crate::schema::localuser::dsl::*;
|
||||||
use crate::schema::user::dsl::*;
|
use crate::schema::user::dsl::*;
|
||||||
|
@ -171,8 +192,7 @@ impl LocalUser {
|
||||||
let new_localuser = LocalUser {
|
let new_localuser = LocalUser {
|
||||||
user_id: new_user_id,
|
user_id: new_user_id,
|
||||||
username: user_request.username.clone(),
|
username: user_request.username.clone(),
|
||||||
password: make_password_with_algorithm(&user_request.password, Algorithm::Argon2),
|
password: LocalUser::hash_password(&user_request.password),
|
||||||
// TODO: Use role from request
|
|
||||||
role: if let Some(user_role) = user_request.role { user_role } else { Role::ZoneAdmin },
|
role: if let Some(user_role) = user_request.role { user_role } else { Role::ZoneAdmin },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -217,7 +237,7 @@ impl LocalUser {
|
||||||
other => UserError::DbError(other)
|
other => UserError::DbError(other)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
if !check_password(&request_password, &client_localuser.password)? {
|
if !LocalUser::verify_password(&request_password, &client_localuser.password)? {
|
||||||
return Err(UserError::BadCreds);
|
return Err(UserError::BadCreds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue