use std::{
fs::File,
io::Read,
path::{Path, PathBuf},
};
use ansi_term::Color;
use argon2;
use chacha20poly1305::{
aead::{Aead, NewAead},
Key, XChaCha20Poly1305, XNonce,
};
use directories::ProjectDirs;
use spinners::{Spinner, Spinners};
use crate::{authentication, errors::HimitsuError, utils::clipboard};
pub enum DecryptionMode {
EditSecret,
UseSecret,
}
pub fn decrypt_secret(
decryption_mode: DecryptionMode,
hash_id: &str,
password: &str,
) -> Result<Option<String>, HimitsuError> {
let mut decryption_spinner =
Spinner::new(Spinners::Aesthetic, "Decrypting the secret...".into());
let hash_path = get_secret_hash_path(hash_id)?;
let salt = get_secret_salt(&hash_path)?;
let raw_nonce = get_secret_nonce(&hash_path)?;
let nonce = XNonce::from_slice(&raw_nonce);
let encrypted_secret = &get_secret(&hash_path)?[..];
let argon2_config = authentication::get_argon2_config();
let key = argon2::hash_raw(password.as_bytes(), &salt, &argon2_config)?;
let cipher = XChaCha20Poly1305::new(Key::from_slice(&key));
match cipher.decrypt(nonce, encrypted_secret) {
Ok(decrypted_secret) => {
decryption_spinner.stop_and_persist(
"🔓",
Color::Green
.bold()
.paint("Successfully decrypted your secret.")
.to_string(),
);
let secret = String::from_utf8_lossy(&decrypted_secret).to_string();
match decryption_mode {
DecryptionMode::EditSecret => Ok(Some(secret)),
DecryptionMode::UseSecret => match clipboard::set_clipboard(secret) {
Ok(()) => {
println!(
"{}",
Color::Green
.bold()
.paint("\n📋 The secret is copied to your clipboard.")
);
Ok(None)
}
Err(error) => Err(error),
},
}
}
Err(error) => {
decryption_spinner.stop_and_persist(
"❗️",
Color::Red
.bold()
.paint("SECRET DECRYPTION FAILED!".to_string())
.to_string(),
);
Err(HimitsuError::AEADDencryptionError(format!(
"Secret decryption error: {}",
error
)))
}
}
}
fn get_secret_hash_path(hash_id: &str) -> Result<PathBuf, HimitsuError> {
match ProjectDirs::from("", "", "himitsu") {
Some(project_directory) => Ok(project_directory.data_dir().join("closet").join(hash_id)),
None => Err(HimitsuError::PathError(
"Could not get the path to the himitsu application directory!".to_string(),
)),
}
}
fn get_secret_nonce(hash_path: &Path) -> Result<[u8; 24], HimitsuError> {
let mut nonce_file = File::open(hash_path.join("nonce"))?;
let mut raw_nonce = [0u8; 24];
nonce_file.read_exact(&mut raw_nonce)?;
Ok(raw_nonce)
}
fn get_secret_salt(hash_path: &Path) -> Result<[u8; 32], HimitsuError> {
let mut nonce_file = File::open(hash_path.join("salt"))?;
let mut salt = [0u8; 32];
nonce_file.read_exact(&mut salt)?;
Ok(salt)
}
fn get_secret(hash_path: &Path) -> Result<Vec<u8>, HimitsuError> {
let mut secret_file = File::open(hash_path.join("skeleton"))?;
let mut encrypted_secret = Vec::new();
secret_file.read_to_end(&mut encrypted_secret)?;
Ok(encrypted_secret)
}