//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.2 use chacha20poly1305::{aead::Aead, AeadCore, ChaCha20Poly1305, KeyInit}; use futures::{Stream, TryStreamExt}; use pbkdf2::pbkdf2_hmac_array; use rand::{rngs::OsRng, RngCore}; use sea_orm::{prelude::*, ActiveValue::Set, QuerySelect}; use sha2::Sha256; #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] #[sea_orm(table_name = "account")] pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub user_id: u64, #[sea_orm(primary_key, auto_increment = false)] pub name: String, #[sea_orm(column_type = "Binary(BlobSize::Blob(Some(64)))")] pub salt: Vec, #[sea_orm(column_type = "VarBinary(256)")] pub enc_login: Vec, #[sea_orm(column_type = "VarBinary(256)")] pub enc_password: Vec, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation {} impl ActiveModelBehavior for ActiveModel {} struct Cipher { chacha: ChaCha20Poly1305, } impl Cipher { fn new(password: &[u8], salt: &[u8]) -> Self { let key = pbkdf2_hmac_array::(password, salt, 480000); Self { chacha: ChaCha20Poly1305::new(&key.into()), } } pub fn encrypt(&self, value: &[u8]) -> crate::Result> { let nonce = ChaCha20Poly1305::generate_nonce(&mut OsRng); let mut result = self.chacha.encrypt(&nonce, value).unwrap(); result.extend(nonce); Ok(result) } fn decrypt(&self, value: &[u8]) -> crate::Result> { let (data, nonce) = value.split_at(value.len() - 12); self.chacha .decrypt(nonce.into(), data) .map_err(|err| err.into()) } } impl ActiveModel { pub fn from_unencrypted( user_id: u64, name: String, login: &str, password: &str, master_pass: &str, ) -> crate::Result { let mut salt = vec![0; 64]; OsRng.fill_bytes(&mut salt); let cipher = Cipher::new(master_pass.as_ref(), &salt); let enc_login = Set(cipher.encrypt(login.as_ref())?); let enc_password = Set(cipher.encrypt(password.as_ref())?); Ok(Self { name: Set(name), user_id: Set(user_id), salt: Set(salt), enc_login, enc_password, }) } } impl Model { pub fn decrypt(&self, master_pass: &str) -> crate::Result<(String, String)> { let cipher = Cipher::new(master_pass.as_ref(), self.salt.as_ref()); let login = String::from_utf8(cipher.decrypt(self.enc_login.as_ref())?)?; let password = String::from_utf8(cipher.decrypt(self.enc_password.as_ref())?)?; Ok((login, password)) } } impl Entity { /// Gets a list of account names of a user pub async fn get_names( user_id: u64, db: &DatabaseConnection, ) -> crate::Result> + '_> { let result = Self::find() .select_only() .column(Column::Name) .filter(Column::UserId.eq(user_id)) .into_tuple() .stream(db) .await?; Ok(result.map_err(Into::into)) } }