1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
//! This crate is for generating random passphrases from characters. //! The heavy lifting is done by `PasswordGenerator` structs, which needs to be //! mutable because of the encapsulated //! [underlying RNG](https://docs.rs/rand/0.8.3/rand/rngs/struct.ThreadRng.html). //! //! # Examples //! ``` //! let mut pwg = yapg::PasswordGenerator::from("ab").length(10); //! let pass = pwg.generate(); //! assert_eq!(pass.len(), 10); //! assert_eq!(pass.to_ascii_lowercase(), pass); //! assert_eq!(pwg.entropy(), 10); //! //! let pass_vec = pwg.length(2).generate_n(2); //! assert_eq!(pass_vec.len(), 2); //! let permutations = vec![ //! "aa".to_string(), //! "ab".to_string(), //! "ba".to_string(), //! "bb".to_string(), //! ]; //! assert!(permutations.contains(&pass_vec[0])); //! assert!(permutations.contains(&pass_vec[1])); //! ``` //! //! # Future ideas //! - creating passphrases from syllables or words use rand::Rng; mod charsets; pub use charsets::*; /// Encapsulates RNG and set of characters. See crate documentation for more. #[derive(Debug)] pub struct PasswordGenerator { charset: Vec<char>, length: usize, rng: rand::ThreadRng, } impl PasswordGenerator { /// Creates the `PasswordGenerator` to yield passwords using either /// `PasswordGenerator::generate` or `PasswordGenerator::generate_n` /// `charset` will not be deduplicated, so that you could (but should not!) /// increase the the probability density of the chars in the generated /// passwords. pub fn new(charset: Vec<char>, length: usize) -> Self { PasswordGenerator { charset, length, rng: rand::thread_rng() } } /// Changes the length of the generated passwords, consumes and returns /// itself. #[inline] pub fn length(mut self, length: usize) -> Self { self.length = length; self } /// Generates one password, with characters randomly chosen from the /// charset. #[inline] pub fn generate(&mut self) -> String { let mut s = String::with_capacity(self.length); for _ in (0..self.length).into_iter() { s.push(*self.rng.choose(&self.charset).unwrap()); } s } /// Generates a vector of passwords with length n, calling /// `PasswordGenerator::generate` internally. /// Cannot return an iterator, because that iterator would need to hold /// a mutable reference to the generator. #[inline] pub fn generate_n(&mut self, n: usize) -> Vec<String> { (0..n).into_iter().map(|_| self.generate()).collect() } /// Number of all possible combinations arising from charset and length. #[inline] pub fn combinations(&self) -> f64 { (self.charset.len() as f64).powf(self.length as f64) } /// Entropy of the generated passwords in bits. #[inline] pub fn entropy(&self) -> usize { self.combinations().log2().floor() as usize } } impl std::convert::From<Vec<char>> for PasswordGenerator { fn from(charset: Vec<char>) -> PasswordGenerator { PasswordGenerator::new(charset, 20) } } impl std::convert::From<&str> for PasswordGenerator { fn from(charset: &str) -> PasswordGenerator { PasswordGenerator::new((charset).chars().collect(), 20) } } // notes for id's: // target collision probability: 1/1e21 // humans: 1e10 (10 billion) // items/human: 1e5 (100 thousand) // => total items: 1e15 // => total number of required ids: 1e30 // => digits in base64: 16.6 // TODO: how to calculate the mean draws until collision?