Ditched ring dependency, simplified password hashing

This commit is contained in:
Antoine Gersant 2019-09-28 16:38:43 -07:00
parent fbb5d7d526
commit 8dbaad98f5
7 changed files with 141 additions and 118 deletions

30
Cargo.lock generated
View file

@ -1419,6 +1419,20 @@ dependencies = [
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "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]] [[package]]
name = "pear" name = "pear"
version = "0.1.2" version = "0.1.2"
@ -1486,10 +1500,10 @@ dependencies = [
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"metaflac 0.1.8 (git+https://github.com/agersant/rust-metaflac)", "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)", "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)", "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)", "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)", "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 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)", "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)", "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)", "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]] [[package]]
name = "rand" name = "rand"
version = "0.6.5" 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 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 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 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 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 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" "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 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.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.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.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.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" "checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"

View file

@ -26,9 +26,9 @@ lewton = "0.9.1"
log = "0.4.5" log = "0.4.5"
metaflac = { git = "https://github.com/agersant/rust-metaflac" } metaflac = { git = "https://github.com/agersant/rust-metaflac" }
mp3-duration = "0.1.0" mp3-duration = "0.1.0"
pbkdf2 = "0.3"
rand = "0.7" rand = "0.7"
regex = "1.2" regex = "1.2"
ring = "0.13.5"
reqwest = "0.9.2" reqwest = "0.9.2"
rocket = "0.4.2" rocket = "0.4.2"
rust-crypto = "0.2.36" rust-crypto = "0.2.36"

View file

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

View file

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

View file

@ -196,7 +196,7 @@ where
}) })
.collect::<_>(); .collect::<_>();
for config_user in &insert_users { 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) diesel::insert_into(users::table)
.values(&new_user) .values(&new_user)
.execute(connection.deref())?; .execute(connection.deref())?;
@ -206,11 +206,7 @@ where
for user in config_users.iter() { for user in config_users.iter() {
// Update password if provided // Update password if provided
if !user.password.is_empty() { if !user.password.is_empty() {
let salt: Vec<u8> = users::table let hash = hash_password(&user.password)?;
.select(users::columns::password_salt)
.filter(users::name.eq(&user.name))
.get_result(connection.deref())?;
let hash = hash_password(&salt, &user.password);
diesel::update(users::table.filter(users::name.eq(&user.name))) diesel::update(users::table.filter(users::name.eq(&user.name)))
.set(users::password_hash.eq(hash)) .set(users::password_hash.eq(hash))
.execute(connection.deref())?; .execute(connection.deref())?;
@ -371,8 +367,8 @@ fn test_amend_preserve_password_hashes() {
use self::users::dsl::*; use self::users::dsl::*;
let db = _get_test_db("amend_preserve_password_hashes.sqlite"); let db = _get_test_db("amend_preserve_password_hashes.sqlite");
let initial_hash: Vec<u8>; let initial_hash: String;
let new_hash: Vec<u8>; let new_hash: String;
let initial_config = Config { let initial_config = Config {
album_art_pattern: None, album_art_pattern: None,

View file

@ -1,99 +1,98 @@
table! { table! {
ddns_config (id) { ddns_config (id) {
id -> Integer, id -> Integer,
host -> Text, host -> Text,
username -> Text, username -> Text,
password -> Text, password -> Text,
} }
} }
table! { table! {
directories (id) { directories (id) {
id -> Integer, id -> Integer,
path -> Text, path -> Text,
parent -> Nullable<Text>, parent -> Nullable<Text>,
artist -> Nullable<Text>, artist -> Nullable<Text>,
year -> Nullable<Integer>, year -> Nullable<Integer>,
album -> Nullable<Text>, album -> Nullable<Text>,
artwork -> Nullable<Text>, artwork -> Nullable<Text>,
date_added -> Integer, date_added -> Integer,
} }
} }
table! { table! {
misc_settings (id) { misc_settings (id) {
id -> Integer, id -> Integer,
auth_secret -> Binary, auth_secret -> Binary,
index_sleep_duration_seconds -> Integer, index_sleep_duration_seconds -> Integer,
index_album_art_pattern -> Text, index_album_art_pattern -> Text,
prefix_url -> Text, prefix_url -> Text,
} }
} }
table! { table! {
mount_points (id) { mount_points (id) {
id -> Integer, id -> Integer,
source -> Text, source -> Text,
name -> Text, name -> Text,
} }
} }
table! { table! {
playlist_songs (id) { playlist_songs (id) {
id -> Integer, id -> Integer,
playlist -> Integer, playlist -> Integer,
path -> Text, path -> Text,
ordering -> Integer, ordering -> Integer,
} }
} }
table! { table! {
playlists (id) { playlists (id) {
id -> Integer, id -> Integer,
owner -> Integer, owner -> Integer,
name -> Text, name -> Text,
} }
} }
table! { table! {
songs (id) { songs (id) {
id -> Integer, id -> Integer,
path -> Text, path -> Text,
parent -> Text, parent -> Text,
track_number -> Nullable<Integer>, track_number -> Nullable<Integer>,
disc_number -> Nullable<Integer>, disc_number -> Nullable<Integer>,
title -> Nullable<Text>, title -> Nullable<Text>,
artist -> Nullable<Text>, artist -> Nullable<Text>,
album_artist -> Nullable<Text>, album_artist -> Nullable<Text>,
year -> Nullable<Integer>, year -> Nullable<Integer>,
album -> Nullable<Text>, album -> Nullable<Text>,
artwork -> Nullable<Text>, artwork -> Nullable<Text>,
duration -> Nullable<Integer>, duration -> Nullable<Integer>,
} }
} }
table! { table! {
users (id) { users (id) {
id -> Integer, id -> Integer,
name -> Text, name -> Text,
password_salt -> Binary, password_hash -> Text,
password_hash -> Binary, admin -> Integer,
admin -> Integer, lastfm_username -> Nullable<Text>,
lastfm_username -> Nullable<Text>, lastfm_session_key -> Nullable<Text>,
lastfm_session_key -> Nullable<Text>, }
}
} }
joinable!(playlist_songs -> playlists (playlist)); joinable!(playlist_songs -> playlists (playlist));
joinable!(playlists -> users (owner)); joinable!(playlists -> users (owner));
allow_tables_to_appear_in_same_query!( allow_tables_to_appear_in_same_query!(
ddns_config, ddns_config,
directories, directories,
misc_settings, misc_settings,
mount_points, mount_points,
playlist_songs, playlist_songs,
playlists, playlists,
songs, songs,
users, users,
); );

View file

@ -2,8 +2,6 @@ use core::ops::Deref;
use diesel; use diesel;
use diesel::prelude::*; use diesel::prelude::*;
use error_chain::bail; use error_chain::bail;
use rand;
use ring::{digest, pbkdf2};
use crate::db::users; use crate::db::users;
use crate::db::ConnectionSource; use crate::db::ConnectionSource;
@ -13,54 +11,32 @@ use crate::errors::*;
#[table_name = "users"] #[table_name = "users"]
pub struct User { pub struct User {
pub name: String, pub name: String,
pub password_salt: Vec<u8>, pub password_hash: String,
pub password_hash: Vec<u8>,
pub admin: i32, pub admin: i32,
} }
static DIGEST_ALG: &'static digest::Algorithm = &digest::SHA256;
const CREDENTIAL_LEN: usize = digest::SHA256_OUTPUT_LEN;
const HASH_ITERATIONS: u32 = 10000; const HASH_ITERATIONS: u32 = 10000;
type PasswordHash = [u8; CREDENTIAL_LEN];
impl User { impl User {
pub fn new(name: &str, password: &str) -> User { pub fn new(name: &str, password: &str) -> Result<User> {
let salt = rand::random::<[u8; 16]>().to_vec(); let hash = hash_password(password)?;
let hash = hash_password(&salt, password); Ok(User {
User {
name: name.to_owned(), name: name.to_owned(),
password_salt: salt,
password_hash: hash, password_hash: hash,
admin: 0, admin: 0,
} })
} }
} }
pub fn hash_password(salt: &[u8], password: &str) -> Vec<u8> { pub fn hash_password(password: &str) -> Result<String> {
let mut hash: PasswordHash = [0; CREDENTIAL_LEN]; match pbkdf2::pbkdf2_simple(password, HASH_ITERATIONS) {
pbkdf2::derive( Ok(hash) => Ok(hash),
DIGEST_ALG, Err(e) => Err(e.into()),
HASH_ITERATIONS, }
salt,
password.as_bytes(),
&mut hash,
);
hash.to_vec()
} }
fn verify_password( fn verify_password(password_hash: &str, attempted_password: &str) -> bool {
password_hash: &Vec<u8>, pbkdf2::pbkdf2_check(attempted_password, password_hash).is_ok()
password_salt: &Vec<u8>,
attempted_password: &str,
) -> bool {
pbkdf2::verify(
DIGEST_ALG,
HASH_ITERATIONS,
password_salt,
attempted_password.as_bytes(),
password_hash,
)
.is_ok()
} }
pub fn auth<T>(db: &T, username: &str, password: &str) -> Result<bool> pub fn auth<T>(db: &T, username: &str, password: &str) -> Result<bool>
@ -70,12 +46,15 @@ where
use crate::db::users::dsl::*; use crate::db::users::dsl::*;
let connection = db.get_connection(); let connection = db.get_connection();
match users match users
.select((password_hash, password_salt)) .select(password_hash)
.filter(name.eq(username)) .filter(name.eq(username))
.get_result(connection.deref()) .get_result(connection.deref())
{ {
Err(diesel::result::Error::NotFound) => Ok(false), 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()), Err(e) => Err(e.into()),
} }
} }