pass_manager/src/entity/master_pass.rs

75 lines
2.5 KiB
Rust

//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.2
use rand::{rngs::OsRng, RngCore};
use scrypt::{scrypt, Params};
use sea_orm::{entity::prelude::*, ActiveValue::Set, QuerySelect};
use tokio::task::spawn_blocking;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[sea_orm(table_name = "master_pass")]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub user_id: u64,
#[sea_orm(column_type = "Binary(BlobSize::Blob(Some(64)))")]
pub salt: Vec<u8>,
#[sea_orm(column_type = "Binary(BlobSize::Blob(Some(64)))")]
pub password_hash: Vec<u8>,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}
impl ActiveModelBehavior for ActiveModel {}
/// Hashes the password with Scrypt with the given salt
fn hash_password(password: &[u8], salt: &[u8]) -> crate::Result<Vec<u8>> {
let params = Params::new(14, Params::RECOMMENDED_R, Params::RECOMMENDED_P, 64)?;
let mut password_hash = vec![0; 64];
scrypt(password, salt, &params, &mut password_hash)?;
Ok(password_hash)
}
impl ActiveModel {
/// Hashes the password and creates an ActiveModel with all fields set to Set variant
pub fn from_unencrypted(user_id: u64, password: &str) -> crate::Result<Self> {
let mut salt = vec![0; 64];
OsRng.fill_bytes(&mut salt);
let password_hash = Set(hash_password(password.as_ref(), &salt)?);
Ok(Self {
user_id: Set(user_id),
salt: Set(salt),
password_hash,
})
}
}
impl Entity {
/// Verifies the provided master password against the one from DB
pub async fn verify_master_pass(
user_id: u64,
master_pass: String,
db: &DatabaseConnection,
) -> crate::Result<Option<bool>> {
let model = match Self::find_by_id(user_id).one(db).await? {
Some(model) => model,
None => return Ok(None),
};
let salt = model.salt;
let password_hash =
spawn_blocking(move || hash_password(master_pass.as_bytes(), &salt)).await??;
Ok(Some(password_hash == model.password_hash))
}
/// Checks if the master password for the user exists
pub async fn exists(user_id: u64, db: &DatabaseConnection) -> crate::Result<bool> {
let id = Self::find()
.select_only()
.column(Column::UserId)
.filter(Column::UserId.eq(user_id))
.into_tuple::<u64>()
.one(db)
.await?;
Ok(id.is_some())
}
}