use argh::FromArgs;
use entroll_core::Faces;
use entroll::ErasedMkpasswd;
use entroll_dictionaries::*;
#[derive(FromArgs, Debug)]
struct Args {
#[argh(switch, short = 'a')]
all: bool,
#[argh(option, default = "16.", from_str_fn(parse_bytes))]
entropy_bytes: f64,
}
fn parse_bytes(s: &str) -> Result<f64, String> {
let s = s.trim();
match s.chars().next_back() {
Some('B') | Some('b') => {
let (value, unit) = s.split_at(s.len() - 1);
let value = value.parse::<f64>().map_err(|e| e.to_string())?;
match unit {
"B" => Ok(value),
"b" => Ok(value / 8.),
_ => Err(format!("invalid unit: {}", unit)),
}
}
_ => s.parse::<f64>().map_err(|e| e.to_string()),
}
}
type Row = (String, f64, String);
trait Dict: ErasedMkpasswd + Dictionary + Faces {}
impl<const N: usize> Dict for WordList<N> {}
impl<T: AsciiDictionary> Dict for AsciiDictionaryWrapper<T> {}
fn main() {
let args: Args = argh::from_env();
let mut rng = Box::new(rand::rngs::OsRng);
let mut dicts: Vec<Box<dyn Dict>> = vec![];
if args.all {
dicts.push(Box::new(Hexadecimal::default()));
dicts.push(Box::new(Alphanumeric::default()));
dicts.push(Box::new(Expect::default()));
dicts.push(Box::new(EFFDiceware5::default()));
} else {
dicts.push(Box::new(Hexadecimal::default()));
}
let entropy_bits = args.entropy_bytes * 8.;
let rows = dicts
.into_iter()
.map(|dict| {
let entropy = dict.entropy_bits_per_face() * dict.needs_faces(entropy_bits) as f64;
let password = dict.mkpasswd_entropy_bits(entropy_bits, &mut rng);
(dict.canonical().to_string(), entropy, password)
})
.collect::<Vec<Row>>();
let verbose = args.all;
let name_size = rows
.iter()
.map(|(name, _, _)| name.len())
.max()
.unwrap_or(0);
if verbose {
println!("{:name_size$} {:16} password", "name", "entropy(bits)");
}
for (name, entropy, password) in rows {
if verbose {
println!("{:name_size$} {:>16.2} {}", name, entropy, password);
} else {
println!("{}", password);
}
}
}