pass_manager/src/main.rs

127 lines
3.8 KiB
Rust
Raw Normal View History

2023-04-24 17:48:34 +00:00
mod entity;
use entity::{account, prelude::Account};
use anyhow::Result;
use chacha20poly1305::{
aead::{rand_core::RngCore, Aead, AeadCore, OsRng},
ChaCha20Poly1305, KeyInit,
};
use dotenv::dotenv;
use pbkdf2::pbkdf2_hmac_array;
use sea_orm::{
ActiveModelTrait, ActiveValue::Set, ColumnTrait, ConnectOptions, Database, EntityTrait,
QueryFilter,
};
use sha2::Sha256;
use std::env;
use teloxide::{prelude::*, utils::command::BotCommands};
#[derive(BotCommands, Clone)]
#[command(rename_rule = "lowercase")]
enum Command {
#[command()]
Help,
#[command(parse_with = "split")]
AddAccount(String, String, String, String),
#[command(parse_with = "split")]
GetAccount(String, String),
}
use Command::*;
struct Cipher {
chacha: ChaCha20Poly1305,
}
impl Cipher {
fn new(password: &[u8], salt: &[u8]) -> Self {
let key = pbkdf2_hmac_array::<Sha256, 32>(password, salt, 480000);
Self {
chacha: ChaCha20Poly1305::new(&key.into()),
}
}
fn encrypt(&self, value: &[u8]) -> Result<Vec<u8>> {
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]) -> Result<Vec<u8>> {
let (data, nonce) = value.split_at(value.len() - 12);
assert!(nonce.len() == 12);
self.chacha
.decrypt(nonce.into(), data)
.map_err(|err| err.into())
}
}
async fn answer_command(db: ConnectOptions, bot: Bot, msg: Message, cmd: Command) -> Result<()> {
let user_id = msg.from().unwrap().id.0;
let db = Database::connect(db).await?;
match cmd {
Help => {
bot.send_message(msg.chat.id, Command::descriptions().to_string())
.await?;
}
AddAccount(name, login, password, master_pass) => {
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())?);
let account = account::ActiveModel {
name: Set(name),
user_id: Set(user_id),
salt: Set(salt),
enc_login,
enc_password,
};
account.insert(&db).await?;
bot.send_message(msg.chat.id, "Success").await?;
}
GetAccount(name, master_pass) => {
let account = Account::find()
.filter(
account::Column::UserId
.eq(user_id)
.add(account::Column::Name.eq(&name)),
)
.one(&db)
.await?
.unwrap();
let cipher = Cipher::new(master_pass.as_ref(), &account.salt);
let login = String::from_utf8(cipher.decrypt(&account.enc_login)?)?;
let password = String::from_utf8(cipher.decrypt(&account.enc_password)?)?;
let message = format!("Account `{name}`\nLogin: `{login}`\nPassword: `{password}`");
bot.send_message(msg.chat.id, message).await?;
}
}
Ok(())
}
#[tokio::main]
async fn main() {
let _ = dotenv();
pretty_env_logger::init();
let token = env::var("TOKEN").unwrap();
let database_url = env::var("DATABASE_URL").unwrap();
let bot = Bot::new(token);
let db = ConnectOptions::new(database_url);
Dispatcher::builder(
bot,
Update::filter_message()
.filter_command::<Command>()
.endpoint(answer_command),
)
.dependencies(dptree::deps![db])
.enable_ctrlc_handler()
.build()
.dispatch()
.await;
2023-04-23 17:54:16 +00:00
}