Implement the set password method

This commit is contained in:
Emi Tatsuo 2020-11-19 16:25:13 -05:00
parent f0517ef0b7
commit 1908d0a0d7
Signed by: Emi
GPG key ID: 68FAB2E2E6DFC98B
2 changed files with 56 additions and 9 deletions

View file

@ -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"

View file

@ -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<UserData> {
pub data: UserData,
pub certificates: Vec<u32>,
pub pass_hash: Option<String>,
pub pass_hash: Option<(Vec<u8>, [u8; 32])>,
}
impl<UserData> PartialUser<UserData> {
@ -46,11 +62,6 @@ impl<UserData> PartialUser<UserData> {
&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<UserData: Serialize + DeserializeOwned> SignedInUser<UserData> {
&self,
try_password: impl AsRef<[u8]>
) -> Result<bool> {
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.