diff --git a/Cargo.lock b/Cargo.lock index 8c24563..92f97bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1419,6 +1419,20 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "pbkdf2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "pear" version = "0.1.2" @@ -1486,10 +1500,10 @@ dependencies = [ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "metaflac 0.1.8 (git+https://github.com/agersant/rust-metaflac)", "mp3-duration 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "reqwest 0.9.20 (registry+https://github.com/rust-lang/crates.io-index)", - "ring 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)", "rocket 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "rocket_contrib 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "rusqlite 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1580,6 +1594,18 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rand" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rand" version = "0.6.5" @@ -2793,6 +2819,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13" "checksum parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337" "checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" +"checksum pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "006c038a43a45995a9670da19e67600114740e8511d4333bf97a56e66a7542d9" "checksum pear 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c26d2b92e47063ffce70d3e3b1bd097af121a9e0db07ca38a6cc1cf0cc85ff25" "checksum pear_codegen 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "336db4a192cc7f54efeb0c4e11a9245394824cc3bcbd37ba3ff51240c35d7a6e" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" @@ -2808,6 +2835,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" "checksum rand 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)" = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c" "checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" +"checksum rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" "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_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" diff --git a/Cargo.toml b/Cargo.toml index 9bbc28a..64dc175 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,9 +26,9 @@ lewton = "0.9.1" log = "0.4.5" metaflac = { git = "https://github.com/agersant/rust-metaflac" } mp3-duration = "0.1.0" +pbkdf2 = "0.3" rand = "0.7" regex = "1.2" -ring = "0.13.5" reqwest = "0.9.2" rocket = "0.4.2" rust-crypto = "0.2.36" diff --git a/migrations/2019-09-28-231910_pbkdf2_simple/down.sql b/migrations/2019-09-28-231910_pbkdf2_simple/down.sql new file mode 100644 index 0000000..573f83f --- /dev/null +++ b/migrations/2019-09-28-231910_pbkdf2_simple/down.sql @@ -0,0 +1,11 @@ +DROP TABLE users; +CREATE TABLE users ( + id INTEGER PRIMARY KEY NOT NULL, + name TEXT NOT NULL, + password_salt BLOB NOT NULL, + password_hash BLOB NOT NULL, + admin INTEGER NOT NULL, + lastfm_username TEXT, + lastfm_session_key TEXT, + UNIQUE(name) +); diff --git a/migrations/2019-09-28-231910_pbkdf2_simple/up.sql b/migrations/2019-09-28-231910_pbkdf2_simple/up.sql new file mode 100644 index 0000000..99a4fe5 --- /dev/null +++ b/migrations/2019-09-28-231910_pbkdf2_simple/up.sql @@ -0,0 +1,10 @@ +DROP TABLE users; +CREATE TABLE users ( + id INTEGER PRIMARY KEY NOT NULL, + name TEXT NOT NULL, + password_hash TEXT NOT NULL, + admin INTEGER NOT NULL, + lastfm_username TEXT, + lastfm_session_key TEXT, + UNIQUE(name) +); diff --git a/src/config.rs b/src/config.rs index b969160..7cdcaf0 100644 --- a/src/config.rs +++ b/src/config.rs @@ -196,7 +196,7 @@ where }) .collect::<_>(); for config_user in &insert_users { - let new_user = User::new(&config_user.name, &config_user.password); + let new_user = User::new(&config_user.name, &config_user.password)?; diesel::insert_into(users::table) .values(&new_user) .execute(connection.deref())?; @@ -206,11 +206,7 @@ where for user in config_users.iter() { // Update password if provided if !user.password.is_empty() { - let salt: Vec = users::table - .select(users::columns::password_salt) - .filter(users::name.eq(&user.name)) - .get_result(connection.deref())?; - let hash = hash_password(&salt, &user.password); + let hash = hash_password(&user.password)?; diesel::update(users::table.filter(users::name.eq(&user.name))) .set(users::password_hash.eq(hash)) .execute(connection.deref())?; @@ -371,8 +367,8 @@ fn test_amend_preserve_password_hashes() { use self::users::dsl::*; let db = _get_test_db("amend_preserve_password_hashes.sqlite"); - let initial_hash: Vec; - let new_hash: Vec; + let initial_hash: String; + let new_hash: String; let initial_config = Config { album_art_pattern: None, diff --git a/src/db/schema.rs b/src/db/schema.rs index 4ca18b7..9fa28d1 100644 --- a/src/db/schema.rs +++ b/src/db/schema.rs @@ -1,99 +1,98 @@ table! { - ddns_config (id) { - id -> Integer, - host -> Text, - username -> Text, - password -> Text, - } + ddns_config (id) { + id -> Integer, + host -> Text, + username -> Text, + password -> Text, + } } table! { - directories (id) { - id -> Integer, - path -> Text, - parent -> Nullable, - artist -> Nullable, - year -> Nullable, - album -> Nullable, - artwork -> Nullable, - date_added -> Integer, - } + directories (id) { + id -> Integer, + path -> Text, + parent -> Nullable, + artist -> Nullable, + year -> Nullable, + album -> Nullable, + artwork -> Nullable, + date_added -> Integer, + } } table! { - misc_settings (id) { - id -> Integer, - auth_secret -> Binary, - index_sleep_duration_seconds -> Integer, - index_album_art_pattern -> Text, - prefix_url -> Text, - } + misc_settings (id) { + id -> Integer, + auth_secret -> Binary, + index_sleep_duration_seconds -> Integer, + index_album_art_pattern -> Text, + prefix_url -> Text, + } } table! { - mount_points (id) { - id -> Integer, - source -> Text, - name -> Text, - } + mount_points (id) { + id -> Integer, + source -> Text, + name -> Text, + } } table! { - playlist_songs (id) { - id -> Integer, - playlist -> Integer, - path -> Text, - ordering -> Integer, - } + playlist_songs (id) { + id -> Integer, + playlist -> Integer, + path -> Text, + ordering -> Integer, + } } table! { - playlists (id) { - id -> Integer, - owner -> Integer, - name -> Text, - } + playlists (id) { + id -> Integer, + owner -> Integer, + name -> Text, + } } table! { - songs (id) { - id -> Integer, - path -> Text, - parent -> Text, - track_number -> Nullable, - disc_number -> Nullable, - title -> Nullable, - artist -> Nullable, - album_artist -> Nullable, - year -> Nullable, - album -> Nullable, - artwork -> Nullable, - duration -> Nullable, - } + songs (id) { + id -> Integer, + path -> Text, + parent -> Text, + track_number -> Nullable, + disc_number -> Nullable, + title -> Nullable, + artist -> Nullable, + album_artist -> Nullable, + year -> Nullable, + album -> Nullable, + artwork -> Nullable, + duration -> Nullable, + } } table! { - users (id) { - id -> Integer, - name -> Text, - password_salt -> Binary, - password_hash -> Binary, - admin -> Integer, - lastfm_username -> Nullable, - lastfm_session_key -> Nullable, - } + users (id) { + id -> Integer, + name -> Text, + password_hash -> Text, + admin -> Integer, + lastfm_username -> Nullable, + lastfm_session_key -> Nullable, + } } joinable!(playlist_songs -> playlists (playlist)); joinable!(playlists -> users (owner)); allow_tables_to_appear_in_same_query!( - ddns_config, - directories, - misc_settings, - mount_points, - playlist_songs, - playlists, - songs, - users, + ddns_config, + directories, + misc_settings, + mount_points, + playlist_songs, + playlists, + songs, + users, ); diff --git a/src/user.rs b/src/user.rs index a45025a..306abd5 100644 --- a/src/user.rs +++ b/src/user.rs @@ -2,8 +2,6 @@ use core::ops::Deref; use diesel; use diesel::prelude::*; use error_chain::bail; -use rand; -use ring::{digest, pbkdf2}; use crate::db::users; use crate::db::ConnectionSource; @@ -13,54 +11,32 @@ use crate::errors::*; #[table_name = "users"] pub struct User { pub name: String, - pub password_salt: Vec, - pub password_hash: Vec, + pub password_hash: String, pub admin: i32, } -static DIGEST_ALG: &'static digest::Algorithm = &digest::SHA256; -const CREDENTIAL_LEN: usize = digest::SHA256_OUTPUT_LEN; const HASH_ITERATIONS: u32 = 10000; -type PasswordHash = [u8; CREDENTIAL_LEN]; impl User { - pub fn new(name: &str, password: &str) -> User { - let salt = rand::random::<[u8; 16]>().to_vec(); - let hash = hash_password(&salt, password); - User { + pub fn new(name: &str, password: &str) -> Result { + let hash = hash_password(password)?; + Ok(User { name: name.to_owned(), - password_salt: salt, password_hash: hash, admin: 0, - } + }) } } -pub fn hash_password(salt: &[u8], password: &str) -> Vec { - let mut hash: PasswordHash = [0; CREDENTIAL_LEN]; - pbkdf2::derive( - DIGEST_ALG, - HASH_ITERATIONS, - salt, - password.as_bytes(), - &mut hash, - ); - hash.to_vec() +pub fn hash_password(password: &str) -> Result { + match pbkdf2::pbkdf2_simple(password, HASH_ITERATIONS) { + Ok(hash) => Ok(hash), + Err(e) => Err(e.into()), + } } -fn verify_password( - password_hash: &Vec, - password_salt: &Vec, - attempted_password: &str, -) -> bool { - pbkdf2::verify( - DIGEST_ALG, - HASH_ITERATIONS, - password_salt, - attempted_password.as_bytes(), - password_hash, - ) - .is_ok() +fn verify_password(password_hash: &str, attempted_password: &str) -> bool { + pbkdf2::pbkdf2_check(attempted_password, password_hash).is_ok() } pub fn auth(db: &T, username: &str, password: &str) -> Result @@ -70,12 +46,15 @@ where use crate::db::users::dsl::*; let connection = db.get_connection(); match users - .select((password_hash, password_salt)) + .select(password_hash) .filter(name.eq(username)) .get_result(connection.deref()) { Err(diesel::result::Error::NotFound) => Ok(false), - Ok((hash, salt)) => Ok(verify_password(&hash, &salt, password)), + Ok(hash) => { + let hash: String = hash; + Ok(verify_password(&hash, password)) + } Err(e) => Err(e.into()), } }