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 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
//! Generation scheme module to define how to generate passphrases //! //! This module defines the [`Scheme`](Scheme) type, with a corresponding [build](SchemeBuilder) if //! that pattern is desired. //! //! As both provided and custom structures may produce a [`Scheme`](Scheme) for passphrase //! generation, the [`ToScheme`](ToScheme) trait is used for a generic way of doing this. use entropy::Entropy; use prelude::*; /// A passphrase generation scheme. /// /// The scheme defines how passphrases should be generated, and can be directly used to so. /// This scheme holds various components used during generation to modify and combine passphrase /// parts or words. The scheme may be used as iterator, which will produce an infinite number of /// passphrases. /// /// It is recommended to use a configuration struct to confige and build a specific `Scheme` /// instead of setting one up manually. /// The `chbs` crate provides [`BasicConfig`](::config::BasicConfig). /// /// A scheme cannot be modified after creation, to ensure passphrase generation and calculating /// entropy is consistent. /// /// # Components /// /// The following components are part of this scheme and the passphrase generation process: /// /// - The word generator is used once for each passphrase to generate, and provides a set of words /// to use for that specific phrase. The generator internally samples a known wordlist or /// generates randomized strings depending on how it is configured. /// - A set of word stylers is used to modify each passphrase word from the generated set, to /// randomize capitalization, to add special characters and more depending on their /// configuration. Each styler is applied once to each phrase word in the specified order. /// If no word styler is available, the words are kept intact. /// - The phrase builder combines the set of now modified passphrase words into a full passphrase, /// the builder separates the words with a space or anything else depending on it's /// configuration. /// - A set of phrase stylers is used to modify the full passphrase that is now combined. They /// may be used for further modifications with full control over the phrase. If no phrase /// styler is available, the phrase is kept intact. /// /// # Examples /// /// The scheme implements `Iterator`. You may easily generate many passphrases this way: /// /// ```rust /// extern crate chbs; /// use chbs::{config::BasicConfig, prelude::*, scheme::Scheme}; /// /// let scheme = BasicConfig::default().to_scheme(); /// /// scheme.take(8) /// .for_each(|passphrase| println!("{}", passphrase)); /// ``` #[derive(Builder, Debug)] #[builder(pattern = "owned")] pub struct Scheme { /// A word set provider, which sources a set of random words to use in the passphrase. word_set_provider: Box<dyn WordSetProvider>, /// A set of word stylers to apply to each passphrase word. word_stylers: Vec<Box<dyn WordStyler>>, /// A phrase builder that builds a passphrase out of a styled set of passphrase words. phrase_builder: Box<dyn PhraseBuilder>, /// A set of phrase stylers to apply to each passphrase. phrase_stylers: Vec<Box<dyn PhraseStyler>>, } impl Scheme { /// Construct a scheme with the given components /// /// When all components for a scheme are collected, a scheme can be constructed using this /// method. /// /// If you prefer the builder pattern to build a scheme, use [`SchemeBuilder`](SchemeBuilder) /// instead. pub fn new( word_set_provider: Box<dyn WordSetProvider>, word_stylers: Vec<Box<dyn WordStyler>>, phrase_builder: Box<dyn PhraseBuilder>, phrase_stylers: Vec<Box<dyn PhraseStyler>>, ) -> Self { Self { word_set_provider, word_stylers, phrase_builder, phrase_stylers, } } /// Build a configuration based on the given object. pub fn from<S: ToScheme>(config: &S) -> Self { config.to_scheme() } /// Generate a single passphrase based on this scheme. pub fn generate(&mut self) -> String { // Generate the passphrase words let mut words = self.word_set_provider.words(); // Run the passphrase words through the word stylers for p in &self.word_stylers { words = words.into_iter().map(|w| p.style_word(w)).collect(); } // Build the passphrase let mut phrase = self.phrase_builder.build_phrase(words); // Run the phrase through the passphrase stylers for p in &self.phrase_stylers { phrase = p.style_phrase(phrase); } phrase } /// Calculate the entropy that passphrases based on this scheme have. /// /// See the documentation on [Entropy](Entropy) for details on what entropy is and how it /// should be calculated. pub fn entropy(&self) -> Entropy { self.word_set_provider.entropy() + self .word_stylers .iter() .map(|p| p.entropy()) .sum::<Entropy>() + self.phrase_builder.entropy() + self .phrase_stylers .iter() .map(|p| p.entropy()) .sum::<Entropy>() } } impl Iterator for Scheme { type Item = String; /// Generate a new passphrase based on this scheme. /// /// This method always returns `Some` holding a passphrase. fn next(&mut self) -> Option<String> { Some(self.generate()) } } impl SchemeBuilder { /// Add a single word styler to the scheme. pub fn add_word_styler(mut self, styler: Box<WordStyler>) -> Self { match self.word_stylers { Some(ref mut stylers) => stylers.push(styler), None => self.word_stylers = Some(vec![styler]), } self } /// Add a single phrase styler to the scheme. pub fn add_phrase_styler(mut self, styler: Box<PhraseStyler>) -> Self { match self.phrase_stylers { Some(ref mut stylers) => stylers.push(styler), None => self.phrase_stylers = Some(vec![styler]), } self } } /// A trait providing an interface to build a password scheme based on some sort of configuration. pub trait ToScheme { /// Build a password scheme based on configuration in this object. fn to_scheme(&self) -> Scheme; }