use rand::{seq::IndexedRandom as _, Rng as _}; pub fn main() -> Result<(), String> { let mut args = std::env::args(); let _ = args.next(); match args.next().as_deref() { None => {} Some("--version") => { println!("{} {}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")); return Ok(()); } Some(_) => return Err("Unknown CLI arg".to_string()), } println!("{}", create_passphrase(" ", 8)); println!("{}", create_pin(9)); println!("{}", create_base32(8)); println!("Press Enter"); let mut input = String::new(); std::io::stdin().read_line(&mut input).ok(); Ok(()) } /// Generates a Base32 string per RFC 4648, Section 6. /// /// Wikipedia says this is the most popular encoding, and it's what /// `base32` in GNU coreutils uses. /// /// /// This is useful for short IDs, e.g. bugs that aren't living in an issue /// tracker. 8 characters of Base32 is 40 bits of entropy, which is /// not enough for a password pub fn create_base32(len: usize) -> String { let encoding: Vec<_> = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567".chars().collect(); assert_eq!(encoding.len(), 32); let chars = (0..len).map(|_| *encoding.choose(&mut rand::rng()).unwrap()); String::from_iter(chars) } /// Generates a diceware passphrase pub fn create_passphrase(separator: &str, len: usize) -> String { let dice = Dice::default(); let words: Vec<_> = (0..len).map(|_| dice.pick_word()).collect(); words.join(separator) } /// Generates a decimal PIN /// /// PINs are returned as a String because you do not do math with PINs, therefore they are not numbers. pub fn create_pin(len: usize) -> String { let digits: Vec<_> = (0..len) .map(|_| format!("{}", rand::rng().random_range(0..=9))) .collect(); digits.join("") } pub struct Dice { words: Vec, } impl Default for Dice { fn default() -> Self { let wordlist = include_str!("eff_short_wordlist_1.txt"); let words: Vec<_> = wordlist.split('\n').map(|s| s.to_string()).collect(); assert_eq!(words.len(), 1253); assert_eq!(words[0], "acid"); assert_eq!(words[600], "large"); assert_eq!(words[1252], "zoom"); Self { words } } } impl Dice { pub fn pick_word(&self) -> &str { self.words .choose(&mut rand::rng()) .expect("`choose` should always return `Some`") } }