First prototype

This commit is contained in:
StNicolay 2023-04-24 20:48:34 +03:00
parent c755de93b8
commit b92ce0b0fa
Signed by: StNicolay
GPG Key ID: 9693D04DCD962B0D
6 changed files with 213 additions and 7 deletions

79
Cargo.lock generated
View File

@ -40,6 +40,15 @@ dependencies = [
"version_check",
]
[[package]]
name = "aho-corasick"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04"
dependencies = [
"memchr",
]
[[package]]
name = "aliasable"
version = "0.1.3"
@ -55,6 +64,12 @@ dependencies = [
"libc",
]
[[package]]
name = "anyhow"
version = "1.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4"
[[package]]
name = "aquamarine"
version = "0.1.12"
@ -235,6 +250,17 @@ version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3"
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi 0.1.19",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.1.0"
@ -744,6 +770,19 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "env_logger"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
dependencies = [
"atty",
"humantime",
"log",
"regex",
"termcolor",
]
[[package]]
name = "erasable"
version = "1.2.1"
@ -1032,6 +1071,15 @@ dependencies = [
"unicode-segmentation",
]
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "hermit-abi"
version = "0.2.6"
@ -1096,6 +1144,15 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
[[package]]
name = "humantime"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
dependencies = [
"quick-error",
]
[[package]]
name = "hyper"
version = "0.14.26"
@ -1596,12 +1653,16 @@ dependencies = [
name = "pass_manager"
version = "0.1.0"
dependencies = [
"anyhow",
"chacha20poly1305",
"dotenv",
"log",
"migration",
"pbkdf2",
"pretty_env_logger",
"scrypt",
"sea-orm",
"sha2",
"teloxide",
"thiserror",
"tokio",
@ -1742,6 +1803,16 @@ version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "pretty_env_logger"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d"
dependencies = [
"env_logger",
"log",
]
[[package]]
name = "proc-macro-crate"
version = "0.1.5"
@ -1804,6 +1875,12 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "quick-error"
version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quote"
version = "1.0.26"
@ -1876,6 +1953,8 @@ version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax 0.7.1",
]

View File

@ -12,12 +12,16 @@ strip = true
members = [".", "migration"]
[dependencies]
chacha20poly1305 = "0.10.1"
anyhow = "1.0.70"
chacha20poly1305 = { version = "0.10.1", features = ["std"] }
dotenv = "0.15.0"
log = "0.4.17"
migration = { version = "0.1.0", path = "migration" }
pbkdf2 = "0.12.1"
pretty_env_logger = "0.4.0"
scrypt = "0.11.0"
sea-orm = { version = "0.11.2", features = ["sqlx-mysql", "runtime-tokio-rustls"] }
sha2 = "0.10.6"
teloxide = { version = "0.12.2", features = ["macros"] }
thiserror = "1.0.40"
tokio = { version = "1.27.0", features = ["macros"] }

View File

@ -36,7 +36,7 @@ impl MigrationTrait for Migration {
.if_not_exists()
.col(
ColumnDef::new(MasterPass::UserId)
.integer()
.big_unsigned()
.primary_key()
.not_null(),
)
@ -54,7 +54,7 @@ impl MigrationTrait for Migration {
Table::create()
.table(Account::Table)
.if_not_exists()
.col(ColumnDef::new(Account::UserId).integer().not_null())
.col(ColumnDef::new(Account::UserId).big_unsigned().not_null())
.col(ColumnDef::new(Account::Name).string_len(256).not_null())
.col(ColumnDef::new(Account::Salt).binary_len(64).not_null())
.col(ColumnDef::new(Account::EncLogin).var_binary(256).not_null())

View File

@ -6,7 +6,7 @@ use sea_orm::entity::prelude::*;
#[sea_orm(table_name = "account")]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub user_id: i32,
pub user_id: u64,
#[sea_orm(primary_key, auto_increment = false)]
pub name: String,
#[sea_orm(column_type = "Binary(BlobSize::Blob(Some(64)))")]

View File

@ -6,7 +6,7 @@ use sea_orm::entity::prelude::*;
#[sea_orm(table_name = "master_pass")]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub user_id: i32,
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(128)))")]

View File

@ -1,3 +1,126 @@
fn main() {
println!("Hello, world!")
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;
}