Ditched ring dependency, simplified password hashing
This commit is contained in:
parent
fbb5d7d526
commit
8dbaad98f5
7 changed files with 141 additions and 118 deletions
30
Cargo.lock
generated
30
Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
11
migrations/2019-09-28-231910_pbkdf2_simple/down.sql
Normal file
11
migrations/2019-09-28-231910_pbkdf2_simple/down.sql
Normal 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)
|
||||
);
|
10
migrations/2019-09-28-231910_pbkdf2_simple/up.sql
Normal file
10
migrations/2019-09-28-231910_pbkdf2_simple/up.sql
Normal 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)
|
||||
);
|
|
@ -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<u8> = 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<u8>;
|
||||
let new_hash: Vec<u8>;
|
||||
let initial_hash: String;
|
||||
let new_hash: String;
|
||||
|
||||
let initial_config = Config {
|
||||
album_art_pattern: None,
|
||||
|
|
139
src/db/schema.rs
139
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<Text>,
|
||||
artist -> Nullable<Text>,
|
||||
year -> Nullable<Integer>,
|
||||
album -> Nullable<Text>,
|
||||
artwork -> Nullable<Text>,
|
||||
date_added -> Integer,
|
||||
}
|
||||
directories (id) {
|
||||
id -> Integer,
|
||||
path -> Text,
|
||||
parent -> Nullable<Text>,
|
||||
artist -> Nullable<Text>,
|
||||
year -> Nullable<Integer>,
|
||||
album -> Nullable<Text>,
|
||||
artwork -> Nullable<Text>,
|
||||
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<Integer>,
|
||||
disc_number -> Nullable<Integer>,
|
||||
title -> Nullable<Text>,
|
||||
artist -> Nullable<Text>,
|
||||
album_artist -> Nullable<Text>,
|
||||
year -> Nullable<Integer>,
|
||||
album -> Nullable<Text>,
|
||||
artwork -> Nullable<Text>,
|
||||
duration -> Nullable<Integer>,
|
||||
}
|
||||
songs (id) {
|
||||
id -> Integer,
|
||||
path -> Text,
|
||||
parent -> Text,
|
||||
track_number -> Nullable<Integer>,
|
||||
disc_number -> Nullable<Integer>,
|
||||
title -> Nullable<Text>,
|
||||
artist -> Nullable<Text>,
|
||||
album_artist -> Nullable<Text>,
|
||||
year -> Nullable<Integer>,
|
||||
album -> Nullable<Text>,
|
||||
artwork -> Nullable<Text>,
|
||||
duration -> Nullable<Integer>,
|
||||
}
|
||||
}
|
||||
|
||||
table! {
|
||||
users (id) {
|
||||
id -> Integer,
|
||||
name -> Text,
|
||||
password_salt -> Binary,
|
||||
password_hash -> Binary,
|
||||
admin -> Integer,
|
||||
lastfm_username -> Nullable<Text>,
|
||||
lastfm_session_key -> Nullable<Text>,
|
||||
}
|
||||
users (id) {
|
||||
id -> Integer,
|
||||
name -> Text,
|
||||
password_hash -> Text,
|
||||
admin -> Integer,
|
||||
lastfm_username -> Nullable<Text>,
|
||||
lastfm_session_key -> Nullable<Text>,
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
);
|
||||
|
|
55
src/user.rs
55
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<u8>,
|
||||
pub password_hash: Vec<u8>,
|
||||
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<User> {
|
||||
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<u8> {
|
||||
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<String> {
|
||||
match pbkdf2::pbkdf2_simple(password, HASH_ITERATIONS) {
|
||||
Ok(hash) => Ok(hash),
|
||||
Err(e) => Err(e.into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_password(
|
||||
password_hash: &Vec<u8>,
|
||||
password_salt: &Vec<u8>,
|
||||
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<T>(db: &T, username: &str, password: &str) -> Result<bool>
|
||||
|
@ -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()),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue