diff --git a/Cargo.lock b/Cargo.lock index a5e4bade..5be73738 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -222,6 +222,17 @@ dependencies = [ "alloc-no-stdlib", ] +[[package]] +name = "argon2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a27e27b63e4a34caee411ade944981136fdfa535522dc9944d6700196cbd899f" +dependencies = [ + "base64ct", + "blake2", + "password-hash", +] + [[package]] name = "atoi" version = "0.4.0" @@ -254,12 +265,27 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +[[package]] +name = "base64ct" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dea908e7347a8c64e378c17e30ef880ad73e3b4498346b055c2c00ea342f3179" + [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "blake2" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9cf849ee05b2ee5fba5e36f97ff8ec2533916700fc0758d40d92136a42f3388" +dependencies = [ + "digest", +] + [[package]] name = "block-buffer" version = "0.10.2" @@ -537,6 +563,7 @@ checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" dependencies = [ "block-buffer", "crypto-common", + "subtle", ] [[package]] @@ -600,6 +627,7 @@ name = "ffplayout-engine" version = "0.9.8" dependencies = [ "actix-web", + "argon2", "chrono 0.4.19 (git+https://github.com/sbrocket/chrono?branch=parse-error-kind-public)", "clap", "crossbeam-channel", @@ -612,12 +640,12 @@ dependencies = [ "notify", "openssl", "rand", + "rand_core", "regex", "reqwest", "serde", "serde_json", "serde_yaml", - "sha-crypt", "shlex", "simplelog", "sqlx", @@ -1593,6 +1621,17 @@ dependencies = [ "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]] name = "paste" version = "1.0.7" @@ -1933,17 +1972,6 @@ dependencies = [ "digest", ] -[[package]] -name = "sha-crypt" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0193e80e8a92aa7173dad160cfd5f5eda57ba146aac9826ea5c9dc3d5492072" -dependencies = [ - "rand", - "sha2", - "subtle", -] - [[package]] name = "sha2" version = "0.10.2" diff --git a/Cargo.toml b/Cargo.toml index 3396a03e..2184aeb3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ default-run = "ffplayout" [dependencies] actix-web = "4" +argon2 = "0.4" chrono = { git = "https://github.com/sbrocket/chrono", branch = "parse-error-kind-public" } clap = { version = "3.1", features = ["derive"] } crossbeam-channel = "0.5" @@ -21,9 +22,9 @@ lettre = "0.10.0-rc.6" log = "0.4" notify = "4.0" rand = "0.8" +rand_core = { version = "0.6", features = ["std"] } regex = "1" reqwest = { version = "0.11", features = ["blocking"] } -sha-crypt = { version = "0.4", features = ["simple"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" serde_yaml = "0.8" diff --git a/src/api/handles.rs b/src/api/handles.rs index 6b855c8e..3ee3fecc 100644 --- a/src/api/handles.rs +++ b/src/api/handles.rs @@ -43,6 +43,7 @@ async fn cretea_schema() -> Result { email TEXT NOT NULL, username TEXT NOT NULL, password TEXT NOT NULL, + salt TEXT NOT NULL, group_id INTEGER NOT NULL DEFAULT 2, FOREIGN KEY (group_id) REFERENCES groups (id) ON UPDATE SET NULL ON DELETE SET NULL, UNIQUE(email, username) @@ -89,13 +90,16 @@ pub async fn add_user( mail: &str, user: &str, pass: &str, + salt: &str, group: &i64, ) -> Result { - let query = "INSERT INTO user (email, username, password, group_id) VALUES($1, $2, $3, $4)"; + let query = + "INSERT INTO user (email, username, password, salt, group_id) VALUES($1, $2, $3, $4, $5)"; let result = sqlx::query(query) .bind(mail) .bind(user) .bind(pass) + .bind(salt) .bind(group) .execute(instances) .await?; @@ -120,7 +124,7 @@ pub async fn get_users( pub async fn get_login(user: &str) -> Result, sqlx::Error> { let pool = db_connection().await?; - let query = "SELECT id, username, password FROM user WHERE username = $1"; + let query = "SELECT id, username, password, salt FROM user WHERE username = $1"; let result: Vec = sqlx::query_as(query).bind(user).fetch_all(&pool).await?; pool.close().await; diff --git a/src/api/models.rs b/src/api/models.rs index c0e28ada..026baa2e 100644 --- a/src/api/models.rs +++ b/src/api/models.rs @@ -9,6 +9,8 @@ pub struct User { #[sqlx(default)] pub password: String, #[sqlx(default)] + pub salt: Option, + #[sqlx(default)] pub group_id: Option, } diff --git a/src/api/routes.rs b/src/api/routes.rs index 63e78bde..993d85c4 100644 --- a/src/api/routes.rs +++ b/src/api/routes.rs @@ -1,44 +1,46 @@ use crate::api::{ - handles::{add_user, db_connection, get_login, get_users}, + handles::{db_connection, get_login, get_users}, models::User, }; use actix_web::{get, post, web, Responder}; -use sha_crypt::{sha512_check, sha512_simple, Sha512Params}; +use argon2::{password_hash::PasswordHash, Argon2, PasswordVerifier}; +use simplelog::*; #[get("/hello/{name}")] async fn greet(name: web::Path) -> impl Responder { format!("Hello {name}!") } -/// curl -X POST -H "Content-Type: application/json" -d '{"username": "USER", "password": "abc123", "email":"user@example.org" }' http://127.0.0.1:8080/api/user/ -#[post("/api/user/")] -pub async fn user(user: web::Json) -> impl Responder { - let params = Sha512Params::new(10_000).expect("RandomError!"); - let hashed_password = sha512_simple(&user.password, ¶ms).expect("Should not fail"); +// /// curl -X POST -H "Content-Type: application/json" -d '{"username": "USER", "password": "abc123", "email":"user@example.org" }' http://127.0.0.1:8080/api/user/ +// #[post("/api/user/")] +// pub async fn user(user: web::Json) -> impl Responder { +// let params = Sha512Params::new(10_000).expect("RandomError!"); +// let hashed_password = sha512_simple(&user.password, ¶ms).expect("Should not fail"); - // // Verifying a stored password - // assert!(sha512_check("Not so secure password", &hashed_password).is_ok()); +// // // Verifying a stored password +// // assert!(sha512_check("Not so secure password", &hashed_password).is_ok()); - if let Ok(pool) = db_connection().await { - if let Err(e) = add_user( - &pool, - &user.email.clone().unwrap(), - &user.username, - &hashed_password, - &user.group_id.unwrap(), - ) - .await - { - pool.close().await; - return e.to_string(); - }; +// if let Ok(pool) = db_connection().await { +// if let Err(e) = add_user( +// &pool, +// &user.email.clone().unwrap(), +// &user.username, +// &hashed_password, +// &user.group_id.unwrap(), +// ) +// .await +// { +// pool.close().await; +// return e.to_string(); +// }; - pool.close().await; - } +// pool.close().await; +// } - format!("User {} added", user.username) -} +// format!("User {} added", user.username) +// } +/// curl -X GET http://127.0.0.1:8080/api/user/1 #[get("/api/user/{id}")] pub async fn get_user(id: web::Path) -> impl Responder { if let Ok(pool) = db_connection().await { @@ -54,21 +56,26 @@ pub async fn get_user(id: web::Path) -> impl Responder { web::Json(vec![]) } - +/// curl -X POST -H "Content-Type: application/json" -d '{"username": "USER", "password": "abc123" }' http://127.0.0.1:8080/auth/login/ #[post("/auth/login/")] pub async fn login(credentials: web::Json) -> impl Responder { - let params = Sha512Params::new(10_000).expect("RandomError!"); - let hashed_password = sha512_simple(&credentials.password, ¶ms).expect("Should not fail"); - - println!("{hashed_password}"); - if let Ok(u) = get_login(&credentials.username).await { - println!("{}", &u[0].password); - println!("{:?}", sha512_check(&u[0].password, &hashed_password)); - if !u.is_empty() && sha512_check(&u[0].password, &hashed_password).is_ok() { - return "login correct!"; + if u.is_empty() { + return "User not found"; } + let pass = u[0].password.clone(); + + if let Ok(hash) = PasswordHash::new(&pass) { + if Argon2::default() + .verify_password(credentials.password.as_bytes(), &hash) + .is_ok() + { + info!("user {} login", credentials.username); + return "login correct!"; + } + }; }; + error!("Login {} failed!", credentials.username); "Login failed!" } diff --git a/src/api/utils.rs b/src/api/utils.rs index 4f7eafe6..1ed94a47 100644 --- a/src/api/utils.rs +++ b/src/api/utils.rs @@ -1,4 +1,7 @@ -use sha_crypt::{sha512_simple, Sha512Params}; +use argon2::{ + password_hash::{rand_core::OsRng, PasswordHasher, SaltString}, + Argon2, +}; use simplelog::*; use crate::api::{ @@ -27,15 +30,29 @@ pub async fn run_args(args: Args) -> Result<(), i32> { return Err(1); } - let params = Sha512Params::new(10_000).expect("RandomError!"); + let salt = SaltString::generate(&mut OsRng); + let argon2 = Argon2::default(); + let password = args.password.unwrap(); - let hashed_password = - sha512_simple(&args.password.unwrap(), ¶ms).expect("Should not fail"); + let password_hash = match argon2.hash_password(password.as_bytes(), &salt) { + Ok(hash) => hash.to_string(), + Err(e) => { + error!("{e}"); + return Err(1); + } + }; match db_connection().await { Ok(pool) => { - if let Err(e) = - add_user(&pool, &args.email.unwrap(), &username, &hashed_password, &1).await + if let Err(e) = add_user( + &pool, + &args.email.unwrap(), + &username, + &password_hash.to_string(), + &salt.to_string(), + &1, + ) + .await { pool.close().await; error!("{e}"); diff --git a/src/bin/ffpapi.rs b/src/bin/ffpapi.rs index 7ad3663b..cc12d75d 100644 --- a/src/bin/ffpapi.rs +++ b/src/bin/ffpapi.rs @@ -7,7 +7,7 @@ use simplelog::*; use ffplayout_engine::{ api::{ args_parse::Args, - routes::{get_user, login, user}, + routes::{get_user, login}, utils::run_args, }, utils::{init_logging, GlobalConfig}, @@ -35,7 +35,7 @@ async fn main() -> std::io::Result<()> { let port = ip_port[1].parse::().unwrap(); info!("running ffplayout API, listen on {conn}"); - HttpServer::new(|| App::new().service(get_user).service(login).service(user)) + HttpServer::new(|| App::new().service(get_user).service(login)) .bind((addr, port))? .run() .await