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;
}