WIP: added crypto tools

This commit is contained in:
Kyattsukuro 2025-09-11 22:06:48 +02:00
parent 1914993d7b
commit bba1912507
7 changed files with 81 additions and 1 deletions

17
Cargo.lock generated
View File

@ -222,8 +222,11 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
name = "auto-decrypt"
version = "0.1.0"
dependencies = [
"argon2",
"async-ssh2-tokio",
"async-trait",
"base64",
"chacha20poly1305",
"clap",
"custom_error",
"diesel",
@ -411,6 +414,19 @@ dependencies = [
"cpufeatures",
]
[[package]]
name = "chacha20poly1305"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35"
dependencies = [
"aead",
"chacha20",
"cipher",
"poly1305",
"zeroize",
]
[[package]]
name = "chrono"
version = "0.4.41"
@ -433,6 +449,7 @@ checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
dependencies = [
"crypto-common",
"inout",
"zeroize",
]
[[package]]

View File

@ -17,4 +17,7 @@ url = "2"
custom_error = "1.9.2"
enum_dispatch = "0.3.13"
rocket_dyn_templates = { version = "0.2.0", features = ["minijinja"]}
diesel-derive-enum = { version = "3.0.0-beta.1", features = ["sqlite"] }
diesel-derive-enum = { version = "3.0.0-beta.1", features = ["sqlite"] }
argon2 = "0.5.3"
base64 = "0.22.1"
chacha20poly1305 = "0.10.1"

View File

@ -7,6 +7,7 @@ pub (super) struct TomlAppConfig {
pub (super) port: Option<u16>,
pub (super) addresses: Option<Vec<String>>,
pub (super) platform: Option<Platform>,
pub (super) crypto_key: Option<String>,
pub (super) ssh_known_host_file: Option<String>,
pub (super) user_confirmation_expiration: Option<i64>,
pub (super) db_file: Option<String>,

View File

@ -24,6 +24,7 @@ pub(crate) struct AppConfig {
pub(crate) ssh_known_host_file: String,
pub(crate) user_confirmation_expiration: i64,
pub(crate) db_file: String,
pub(crate) crypto_key: String,
pub(crate) beggars: HashMap<String, Beggars>, // We request to unlock owned services
pub(crate) providers: HashMap<String, Providers>, // We offer to unlock these services
@ -35,6 +36,7 @@ impl Default for AppConfig {
port: 8080,
addresses: vec!["*".to_string()],
beggars: HashMap::new(),
crypto_key: "default_key_change_me".to_string(),
providers: HashMap::new(),
user_confirmation_expiration: 60 * 60 * 24, // 24 hours
platform: Platform::OMV,
@ -51,6 +53,7 @@ impl AppConfig {
return AppConfig {
port: toml_config.port.unwrap_or(default.port),
addresses: toml_config.addresses.unwrap_or(default.addresses),
crypto_key: toml_config.crypto_key.unwrap_or(default.crypto_key),
platform: toml_config.platform.unwrap_or(default.platform),
user_confirmation_expiration: toml_config.user_confirmation_expiration.unwrap_or(default.user_confirmation_expiration),
ssh_known_host_file: toml_config.ssh_known_host_file.unwrap_or(default.ssh_known_host_file),

54
src/crypto/mod.rs Normal file
View File

@ -0,0 +1,54 @@
use argon2::{
password_hash::{
rand_core::OsRng,
PasswordHash, PasswordHasher, PasswordVerifier, SaltString
},
Argon2
};
use crate::errors::AutoDecryptError;
use base64::prelude::*;
use chacha20poly1305::{
aead::{Aead, AeadCore, KeyInit, OsRng as AeadOsRng},
ChaCha20Poly1305, Nonce
};
use crate::app_config::CONFIG;
fn hash_hey(input: &str) -> Result<(String), AutoDecryptError> {
let argon2 = Argon2::default();
let salt = SaltString::generate(&mut OsRng);
Ok(argon2.hash_password(input.as_bytes(), &salt).map_err(
|e| AutoDecryptError::APIError { comment: (e.to_string()) })?
.to_string())
}
fn derive_key(password: &str, salt: &str) -> Result<[u8; 32], AutoDecryptError> {
let mut output = [0u8; 32];
Argon2::default().hash_password_into(password.as_bytes(), salt.as_bytes(), &mut output).map_err(
|e| AutoDecryptError::CryptoError { comment: (e.to_string()) })?;
Ok(output)
}
fn encrypt_data(data: &str, key: &[u8; 32], salt: Option<[u8; 96]>) -> Result<(String, [u8; 96]), AutoDecryptError> {
let cipher = ChaCha20Poly1305::new(key.into());
let salt = match salt {
Some(s) => Nonce::clone_from_slice(&s),
None => ChaCha20Poly1305::generate_nonce(&mut AeadOsRng),
};
let salt_arr: [u8; 96] = salt.as_slice().try_into().expect("Nonce should be 96 bytes");
let ciphertext = cipher.encrypt(&salt, data.as_bytes()).map_err(
|e| AutoDecryptError::APIError { comment: (e.to_string()) })?;
Ok((BASE64_STANDARD.encode(ciphertext), salt_arr))
}
fn decrypt_data(encrypted_data: &str, key: &[u8; 32], salt: &[u8; 96]) -> Result<String, AutoDecryptError> {
let cipher = ChaCha20Poly1305::new(key.into());
let nonce = Nonce::from_slice(salt);
let ciphertext = BASE64_STANDARD.decode(encrypted_data).map_err(
|e| AutoDecryptError::APIError { comment: (e.to_string()) })?;
let plaintext = cipher.decrypt(nonce, ciphertext.as_ref()).map_err(
|e| AutoDecryptError::CryptoError { comment: (e.to_string()) })?;
Ok(String::from_utf8(plaintext).map_err(
|e| AutoDecryptError::CryptoError { comment: (e.to_string()) })?)
}

View File

@ -4,4 +4,5 @@ custom_error!{pub AutoDecryptError
ConfigurationError{comment:String} = "{comment}",
APIError{comment:String} = "{comment}",
ORMError{comment:String} = "{comment}",
CryptoError{comment:String} = "{comment}",
}

View File

@ -1,6 +1,7 @@
mod services;
mod app_config;
mod api;
mod crypto;
use clap::Parser;
use crate::app_config::{init_config, CONFIG};