use std::path::Path;
use clap::Parser;
use serde_json::json;
use basecracker::{self, modules::Base};
fn plaintexts_to_json(cipher: &str, plaintexts: &Vec<(String, Vec<String>)>) -> String {
let mut plaintexts_json = vec![];
for (plaintext, bases) in plaintexts {
plaintexts_json.push(json!({
"plaintext": plaintext,
"bases": json!(bases)
}));
}
json!({
"cipher": cipher,
"plaintexts": json!(plaintexts_json)
})
.to_string()
}
#[derive(Parser, Debug)]
#[clap(version, about)]
struct Args {
#[clap(
short,
long,
value_name = "PLAINTEXT",
conflicts_with_all = &["decode", "crack", "list"]
)]
encode: Option<String>,
#[clap(
short,
long,
value_name = "CIPHER",
conflicts_with_all = &["encode", "crack", "list"]
)]
decode: Option<String>,
#[clap(
short,
long,
value_name = "CIPHER",
conflicts_with_all = &["encode", "decode", "list"]
)]
crack: Option<String>,
#[clap(
short,
long,
min_values = 1,
requires_all = &["encode", "decode", "crack"]
)]
bases: Option<Vec<String>>,
#[clap(
short,
long,
requires_all = &["encode", "decode", "bases"]
)]
reversed: bool,
#[clap(
short,
long,
conflicts_with_all = &["encode", "decode", "crack"],
)]
list: bool,
#[clap(
short,
long,
requires = "crack",
conflicts_with_all = &["verbose", "quiet"]
)]
json: bool,
#[clap(short, long, conflicts_with_all = &["quiet", "json"])]
verbose: bool,
#[clap(short, long, conflicts_with_all = &["verbose", "json"])]
quiet: bool,
}
fn subcommand_list() {
println!("Supported bases are:");
for (short, long) in basecracker::get_bases_names() {
println!(" - {:15}({})", long, short);
}
}
fn parse_bases(bases: &Vec<String>, reversed: bool) -> Result<Vec<Box<dyn Base>>, String> {
let mut bases = bases
.iter()
.flat_map(|base| base.split(',').map(|b| b.to_string()))
.collect::<Vec<String>>()
.iter()
.flat_map(|base| base.split(' ').map(|b| b.to_string()))
.collect::<Vec<String>>()
.iter()
.map(|base| base.trim())
.filter(|base| !base.is_empty())
.map(|base| base.to_string())
.collect::<Vec<String>>();
if reversed {
bases.reverse();
}
basecracker::get_bases_from_names(&bases)
}
fn get_file_content(arg: &str) -> Result<String, String> {
if Path::new(arg).exists() {
let content = std::fs::read_to_string(arg).map_err(|e| e.to_string())?;
Ok(content)
} else {
Ok(arg.to_string())
}
}
fn subcommand_encode(
plaintext: &str,
specified_bases: &Vec<Box<dyn Base>>,
arg_verbose: bool,
) -> Result<(), String> {
let plaintext = get_file_content(&plaintext)?;
if arg_verbose {
let ciphers = basecracker::encode_steps(&plaintext, specified_bases)?;
println!("Plaintext: {}\n", plaintext);
for (i, cipher) in ciphers.iter().enumerate() {
println!(
"Applying {:10} {}",
format!("{}:", specified_bases[i].get_name()),
cipher
);
}
println!("\nCipher: {}", ciphers[ciphers.len() - 1]);
} else {
let cipher = basecracker::encode(&plaintext, specified_bases)?;
println!("{}", cipher);
}
Ok(())
}
fn subcommand_decode(
cipher: &str,
specified_bases: &Vec<Box<dyn Base>>,
arg_verbose: bool,
) -> Result<(), String> {
let cipher = get_file_content(&cipher)?;
if arg_verbose {
let plaintexts = basecracker::decode_steps(&cipher, specified_bases)?;
println!("Cipher: {}\n", cipher);
for (i, plaintext) in plaintexts.iter().enumerate() {
println!(
"Applying {:10} {}",
format!("{}:", specified_bases[i].get_name()),
plaintext
);
}
println!("\nPlaintext: {}", plaintexts[plaintexts.len() - 1]);
} else {
let plaintext = basecracker::decode(&cipher, specified_bases)?;
println!("{}", plaintext);
}
Ok(())
}
fn subcommand_crack(
cipher: &str,
specified_bases: &Option<Vec<Box<dyn Base>>>,
arg_verbose: bool,
arg_json: bool,
arg_quiet: bool,
) -> Result<(), String> {
let cipher = get_file_content(&cipher)?;
let plaintexts = match specified_bases {
Some(bases) => basecracker::basecracker_with_bases(&cipher, &bases),
None => basecracker::basecracker(&cipher),
};
if arg_json {
println!("{}", plaintexts_to_json(&cipher, &plaintexts));
if plaintexts.len() == 0 {
std::process::exit(1);
}
} else {
if plaintexts.len() != 0 {
for (plaintext, bases) in &plaintexts {
if arg_quiet {
println!("{}", plaintext);
} else {
println!("Recipe: {}", bases.join(","));
if arg_verbose {
println!("Cipher: {}", cipher);
let mut tmp_cipher = cipher.clone();
for base_name in bases.iter() {
let base = basecracker::get_base_from_name(base_name).unwrap();
tmp_cipher = base.decode(&tmp_cipher).unwrap();
println!(
"Applying {:10} {}",
format!("{}:", base.get_name()),
tmp_cipher
);
}
println!("Result: {}", plaintext);
} else {
println!("Result: {}", plaintext);
}
}
if plaintexts.len() > 1 {
println!();
}
}
} else {
if !arg_quiet {
eprintln!("No plaintexts found");
}
std::process::exit(1);
}
}
Ok(())
}
fn main() {
let args = Args::parse();
if args.list {
subcommand_list();
} else {
let specified_bases = match args.bases {
Some(bases) => match parse_bases(&bases, args.reversed) {
Ok(bases) => Some(bases),
Err(err) => {
eprintln!("{}", err);
std::process::exit(1);
}
},
None => None,
};
match {
if let Some(cipher) = args.crack {
subcommand_crack(
&cipher,
&specified_bases,
args.verbose,
args.json,
args.quiet,
)
} else {
let specified_bases = match specified_bases {
Some(bases) => bases,
None => {
eprintln!("No bases specified");
eprintln!("Use --bases to specify bases");
std::process::exit(1);
}
};
if let Some(plaintext) = args.encode {
subcommand_encode(&plaintext, &specified_bases, args.verbose)
} else if let Some(cipher) = args.decode {
subcommand_decode(&cipher, &specified_bases, args.verbose)
} else {
Err(String::from(
"No subcommand specified\nFor more information try --help",
))
}
}
} {
Ok(_) => (),
Err(err) => {
eprintln!("{}", err);
std::process::exit(1);
}
}
}
}