75 lines
2.5 KiB
Rust
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, ¶ms, &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())
|
|
}
|
|
}
|