move to argon2 crate for password hashing

main
Hannaeko 2022-04-24 01:06:43 +02:00
parent e99c18704e
commit 54b18a8585
4 changed files with 65 additions and 89 deletions

105
Cargo.lock generated
View File

@ -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"

View File

@ -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"

View File

@ -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 {

View File

@ -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);
} }