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 112 113 114 115 116 117 118 119
//! A set of memorable words, with convenience passphrase functions //! //! The list of words is just [`WORDS`](WORDS). //! //! You may likely prefer to use one of the provided passphrase //! generators. These accept a number of bits of entropy desired. //! This corresponds to the `log2` of the number of passphrases that //! should be possible to generate. This interface allows the number //! of memorable words to be adjusted without making your code any //! less secure. You get to decide how secore the passphrase needs to //! be. If your concern is a network attacker, then you might think //! that the 44 bits of entropy [advertized by //! xkcd](https://m.xkcd.com/936) is sufficient. If you are concerned //! about an offline attack on a hashed password file, then you'd //! better ask for more entropy or ensure that you use a seriously //! slow hashing algorithm and a good salt. //! //! Note that while the algorithm guarantees the number of bits of //! entropy you request, it doesn't go far above that. So the fewer //! bits you request, the easier to remember the result should be. mod words; /// The list of memorable words /// /// This list is ordered from "most memorable" to "least" so if you /// want to use a subset you may use the first `N` words. pub const WORDS: &[&str] = words::LIST; /// A javascript script that generates passphrases. /// /// If it is prefered to generate passphrases on the client rather /// than the server, this string provides javascript code that /// generates passphrases in the same way that the rust code does. /// The functions have `passphrase_` prepended to them, /// e.g. `passphrase_camel_case`, and accept a number of bits just /// like the rust functions do. pub const JAVASCRIPT: &str = include_str!("../memorable-wordlist.js"); const NUM_BITS: usize = 14; fn words_for_bits(bits: usize) -> impl Iterator<Item=&'static str> { use rand::seq::SliceRandom; let num_words = if bits % NUM_BITS == 0 { bits/NUM_BITS } else { bits/NUM_BITS + 1 }; let number_per_word = (bits as f64/num_words as f64).exp2() as usize; let mut words = Vec::with_capacity(num_words); let mut rng = rand::thread_rng(); for _ in 0..num_words { words.push(WORDS[0..number_per_word].choose(&mut rng).unwrap()); } words.into_iter().map(|&x| x) } /// Generate a space-delimited passphrase /// /// The passphrase will be generated with `bits` amount of entropy. pub fn space_delimited(bits: usize) -> String { let mut code = String::new(); for w in words_for_bits(bits) { code.push_str(w); code.push(' '); } code.pop(); code } /// Generate a snake_case passphrase /// /// The passphrase will be generated with `bits` amount of entropy. pub fn snake_case(bits: usize) -> String { let mut code = String::new(); for w in words_for_bits(bits) { code.push_str(w); code.push('_'); } code.pop(); code } /// Generate a kebab-case passphrase /// /// The passphrase will be generated with `bits` amount of entropy. pub fn kebab_case(bits: usize) -> String { let mut code = String::new(); for w in words_for_bits(bits) { code.push_str(w); code.push('-'); } code.pop(); code } /// Generate a CamelCase passphrase /// /// The passphrase will be generated with `bits` amount of entropy. pub fn camel_case(bits: usize) -> String { let mut code = String::new(); for w in words_for_bits(bits) { let mut c = w.chars(); code.push(c.next().unwrap().to_uppercase().next().unwrap()); code.push_str(c.as_str()); } code } #[test] fn has_length() { assert!(space_delimited(41).len() > 4); assert!(snake_case(40).len() > 4); assert!(camel_case(40).len() > 4); assert!(kebab_case(40).len() > 4); } #[test] fn num_bits_correct() { assert!(1 << NUM_BITS <= WORDS.len()); }