From 1908d0a0d7ff3e7988966f471f4d9be42eed8820 Mon Sep 17 00:00:00 2001 From: Emi Tatsuo Date: Thu, 19 Nov 2020 16:25:13 -0500 Subject: [PATCH] Implement the set password method --- Cargo.toml | 3 +- src/user_management/user.rs | 62 ++++++++++++++++++++++++++++++++----- 2 files changed, 56 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9027145..af9a29d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ repository = "https://github.com/panicbit/northstar" documentation = "https://docs.rs/northstar" [features] -user_management = ["sled", "bincode", "serde/derive", "bcrypt", "crc32fast"] +user_management = ["sled", "bincode", "serde/derive", "rust-argon2", "crc32fast", "ring"] default = ["serve_dir"] serve_dir = ["mime_guess", "tokio/fs"] @@ -31,6 +31,7 @@ bincode = { version = "1.3.1", optional = true } serde = { version = "1.0", optional = true } rust-argon2 = { version = "0.8.2", optional = true } crc32fast = { version = "1.2.1", optional = true } +ring = { version = "0.16.15", optional = true } [[example]] name = "user_management" diff --git a/src/user_management/user.rs b/src/user_management/user.rs index 3abba2b..a1ad27c 100644 --- a/src/user_management/user.rs +++ b/src/user_management/user.rs @@ -26,6 +26,22 @@ use crate::user_management::UserManager; use crate::user_management::Result; use crate::user_management::manager::CertificateData; +const ARGON2_CONFIG: argon2::Config = argon2::Config { + ad: &[], + hash_length: 32, + lanes: 1, + mem_cost: 4096, + secret: &[], + thread_mode: argon2::ThreadMode::Sequential, + time_cost: 3, + variant: argon2::Variant::Argon2id, + version: argon2::Version::Version13, +}; + +lazy_static::lazy_static! { + static ref RANDOM: ring::rand::SystemRandom = ring::rand::SystemRandom::new(); +} + /// An struct corresponding to the data stored in the user tree /// /// In order to generate a full user obj, you need to perform a lookup with a specific @@ -34,7 +50,7 @@ use crate::user_management::manager::CertificateData; pub struct PartialUser { pub data: UserData, pub certificates: Vec, - pub pass_hash: Option, + pub pass_hash: Option<(Vec, [u8; 32])>, } impl PartialUser { @@ -46,11 +62,6 @@ impl PartialUser { &self.certificates } - /// The bcrypt hash of the user's password - pub fn pass_hash(&self) -> Option<&str> { - self.pass_hash.as_deref() - } - /// Write user data to the database /// /// This MUST be called if the user data is modified using the AsMut trait, or else @@ -258,13 +269,48 @@ impl SignedInUser { &self, try_password: impl AsRef<[u8]> ) -> Result { - if let Some(hash) = &self.inner.pass_hash { - Ok(argon2::verify_encoded(hash.as_str(), try_password.as_ref())?) + if let Some((hash, salt)) = &self.inner.pass_hash { + Ok(argon2::verify_raw( + try_password.as_ref(), + salt, + hash.as_ref(), + &ARGON2_CONFIG, + )?) } else { Err(super::UserManagerError::PasswordNotSet) } } + /// Set's the password for this user + /// + /// By default, users have no password, meaning the cannot add any certificates beyond + /// the one they created their account with. However, by setting their password, + /// users are able to add more devices to their account, and recover their account if + /// it's lost. Note that this will completely overwrite the users old password. + /// + /// Use [`SignedInUser::check_password()`] and [`NotSignedInUser::attach()`] to check + /// the password against another one, or to link a new certificate. + /// + /// Because this method uses a key derivation algorithm, this should be considered a + /// very expensive operation. + pub fn set_password( + &mut self, + password: impl AsRef<[u8]>, + ) -> Result<()> { + let salt: [u8; 32] = ring::rand::generate(&*RANDOM) + .expect("Error generating random salt") + .expose(); + self.inner.pass_hash = Some(( + argon2::hash_raw( + password.as_ref(), + salt.as_ref(), + &ARGON2_CONFIG, + )?, + salt, + )); + Ok(()) + } + /// Write any updates to the user to the database. /// /// Updates caused by calling methods directly on the user do not need to be saved.