use simple_encrypt::{decrypt_bytes, encrypt_bytes};
use base64::{engine::general_purpose::STANDARD, Engine};
use clap::{Parser, Subcommand};
use rpassword::read_password;
use std::fs;
use std::path::PathBuf;
use rand::{rngs::OsRng, TryRngCore};
use std::io::{self, Write};
use std::env;
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand, Debug)]
enum Commands {
GenerateKey {
#[arg(long)]
output: PathBuf,
},
Encrypt {
#[arg(long)]
input: PathBuf,
#[arg(long)]
output: PathBuf,
},
Decrypt {
#[arg(long)]
input: PathBuf,
#[arg(long)]
output: PathBuf,
},
}
fn get_encryption_key() -> Result<Vec<u8>, Box<dyn std::error::Error>> {
if let Ok(encoded_key) = env::var("SECRETS_ENCRYPTION_KEY") {
let key = STANDARD.decode(encoded_key.trim())
.map_err(|_| "Invalid base64-encoded key in SECRETS_ENCRYPTION_KEY environment variable")?;
if key.len() != 32 {
return Err("Invalid key length in SECRETS_ENCRYPTION_KEY - must be 32 bytes when decoded".into());
}
return Ok(key);
}
print!("Enter base64-encoded encryption key: ");
io::stdout().flush()?;
let encoded_key = read_password()?;
let key = STANDARD.decode(encoded_key.trim())
.map_err(|_| "Invalid base64-encoded key")?;
if key.len() != 32 {
return Err("Invalid key length - must be 32 bytes when decoded".into());
}
Ok(key)
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let args = Args::parse();
match args.command {
Commands::GenerateKey { output } => {
let mut key = [0u8; 32];
OsRng.try_fill_bytes(&mut key)?;
let encoded_key = STANDARD.encode(key);
fs::write(&output, encoded_key)?;
println!("Generated new 32-byte AES-GCM-256 key and saved to {:?}", output);
println!("Keep this key safe - you'll need it to decrypt files!");
}
Commands::Encrypt { input, output } => {
let file_content = fs::read(&input)?;
let key = get_encryption_key()?;
let result = encrypt_bytes(&file_content, &key)?;
fs::write(&output, result)?;
println!("Successfully encrypted file from {:?} to {:?}", input, output);
}
Commands::Decrypt { input, output } => {
let file_content = fs::read(&input)?;
let key = get_encryption_key()?;
let result = decrypt_bytes(&file_content, &key)?;
fs::write(&output, result)?;
println!("Successfully decrypted file from {:?} to {:?}", input, output);
}
}
Ok(())
}