Skip to main content

passay_rs/
password_generator.rs

1use crate::rule::character::CharacterRule;
2
3use rand::SeedableRng;
4use rand::distr::Distribution;
5use rand::distr::Uniform;
6use rand::rngs::StdRng;
7
8/// Creates passwords that meet password character rule criteria.
9///
10/// ```
11///  use passay_rs::password_generator::PasswordGenerator;
12///  use passay_rs::rule::character::CharacterRule;
13///  use passay_rs::rule::character_data::EnglishCharacterData;
14///
15///  let generate_rules = vec![
16///      CharacterRule::new(Box::new(EnglishCharacterData::Digit), 2).unwrap(),
17///      CharacterRule::new(Box::new(EnglishCharacterData::Special), 2).unwrap(),
18///      CharacterRule::new(Box::new(EnglishCharacterData::UpperCase), 1).unwrap(),
19///      CharacterRule::new(Box::new(EnglishCharacterData::LowerCase), 1).unwrap(),
20///  ];
21///  let mut password_generator = PasswordGenerator::new();
22///  let password = password_generator.generate_password(16, &generate_rules).unwrap();
23///  assert_eq!(password.chars().count(), 16);
24/// ```
25pub struct PasswordGenerator {
26    random: StdRng,
27}
28
29impl PasswordGenerator {
30    pub fn new() -> Self {
31        Self {
32            random: StdRng::from_os_rng(),
33        }
34    }
35
36    pub fn generate_password(
37        &mut self,
38        len: usize,
39        rules: &Vec<CharacterRule>,
40    ) -> Result<String, String> {
41        if len == 0 {
42            return Err("length must be greater than 0".into());
43        }
44        let mut target = String::new();
45        let mut all_chars = String::new();
46        for rule in rules {
47            target = self.fill_random_char(
48                rule.valid_characters(),
49                len.min(rule.num_characters()),
50                target,
51            )?;
52            all_chars.push_str(rule.valid_characters());
53        }
54        target = self.fill_random_char(&all_chars, len - target.chars().count(), target)?;
55        target = self.randomize(target)?;
56        Ok(target)
57    }
58
59    fn fill_random_char(
60        &mut self,
61        source: &str,
62        size: usize,
63        mut target: String,
64    ) -> Result<String, String> {
65        let result = Uniform::try_from(0..source.chars().count());
66        let uni = match result {
67            Ok(x) => x,
68            Err(e) => {
69                return Err(e.to_string());
70            }
71        };
72
73        for _ in 0..size {
74            let src_chars = source.chars().collect::<Vec<char>>();
75            let index = uni.sample(&mut self.random);
76            let char = src_chars[index];
77            target.push(char);
78        }
79        Ok(target)
80    }
81
82    fn randomize(&mut self, str: String) -> Result<String, String> {
83        let mut c: char;
84        let mut n: usize;
85        let mut chars: Vec<char> = str.chars().collect();
86        let result = Uniform::try_from(0..chars.len());
87        let uni = match result {
88            Ok(x) => x,
89            Err(e) => {
90                return Err(e.to_string());
91            }
92        };
93
94        for i in 0..chars.len() {
95            n = uni.sample(&mut self.random);
96            c = chars[n];
97            chars[n] = chars[i];
98            chars[i] = c;
99        }
100        Ok(chars.iter().collect())
101    }
102}
103
104impl Default for PasswordGenerator {
105    fn default() -> Self {
106        Self::new()
107    }
108}
109
110#[cfg(test)]
111mod tests {
112    use crate::password_generator::PasswordGenerator;
113    use crate::rule::character::CharacterRule;
114    use crate::rule::character_characteristics::CharacterCharacteristics;
115    use crate::rule::character_data::EnglishCharacterData;
116    use crate::rule::{PasswordData, Rule};
117    use std::ops::Not;
118
119    #[test]
120    fn test_generator() {
121        let passwords = random_passwords();
122        for pass in passwords {
123            let password_data = PasswordData::with_password(pass);
124            assert!(gen_fail_rule().validate(&password_data).valid().not());
125            assert!(gen_verify_rule().validate(&password_data).valid());
126        }
127    }
128
129    #[test]
130    fn test_buffer_overflow() {
131        let _output = PasswordGenerator::new()
132            .generate_password(
133                5,
134                &vec![CharacterRule::new(Box::new(EnglishCharacterData::Digit), 10).unwrap()],
135            )
136            .unwrap();
137        let _output = PasswordGenerator::new()
138            .generate_password(
139                10,
140                &vec![CharacterRule::new(Box::new(EnglishCharacterData::Digit), 5).unwrap()],
141            )
142            .unwrap();
143        let _output = PasswordGenerator::new()
144            .generate_password(
145                10,
146                &vec![CharacterRule::new(Box::new(EnglishCharacterData::Digit), 10).unwrap()],
147            )
148            .unwrap();
149    }
150    fn random_passwords() -> Vec<String> {
151        let mut passwords = Vec::with_capacity(100);
152        const LEN: usize = 10;
153
154        let mut password_generator = PasswordGenerator::new();
155
156        let rule = gen_rule();
157        for _ in 0..100 {
158            let password = password_generator.generate_password(LEN, &rule).unwrap();
159            assert!(password.chars().count() >= LEN);
160            passwords.push(password);
161        }
162        passwords
163    }
164    fn gen_rule() -> Vec<CharacterRule> {
165        vec![
166            CharacterRule::new(Box::new(EnglishCharacterData::Digit), 2).unwrap(),
167            CharacterRule::new(Box::new(EnglishCharacterData::Special), 2).unwrap(),
168            CharacterRule::new(Box::new(EnglishCharacterData::UpperCase), 1).unwrap(),
169            CharacterRule::new(Box::new(EnglishCharacterData::LowerCase), 1).unwrap(),
170        ]
171    }
172    fn gen_verify_rule() -> CharacterCharacteristics {
173        CharacterCharacteristics::with_rules_and_characteristics(gen_rule(), 3).unwrap()
174    }
175    fn gen_fail_rule() -> CharacterCharacteristics {
176        let char_rules = vec![
177            CharacterRule::new(Box::new(EnglishCharacterData::Digit), 3).unwrap(),
178            CharacterRule::new(Box::new(EnglishCharacterData::Special), 3).unwrap(),
179            CharacterRule::new(Box::new(EnglishCharacterData::UpperCase), 3).unwrap(),
180            CharacterRule::new(Box::new(EnglishCharacterData::LowerCase), 3).unwrap(),
181        ];
182
183        CharacterCharacteristics::with_rules_and_characteristics(char_rules, 4).unwrap()
184    }
185}