pass_manager/cryptography/src/password_generation.rs

53 lines
1.6 KiB
Rust

use arrayvec::ArrayString;
use rand::{rngs::OsRng, seq::SliceRandom};
use std::array;
const CHARS: &[u8] = br##"!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_abcdefghijklmnopqrstuvwxyz{|}~"##;
bitflags::bitflags! {
struct PasswordFlags: u8 {
const LOWERCASE = 0b0001;
const UPPERCASE = 0b0010;
const NUMBER = 0b0100;
const SPECIAL_CHARACTER = 0b1000;
}
}
/// Returns true if the generated master password is valid.
/// It checks that it has at least one lowercase, one uppercase, one number and one punctuation char
#[inline]
fn check_generated_password(password: &[u8; 32]) -> bool {
let mut flags = PasswordFlags::empty();
for &byte in password {
match byte {
b'a'..=b'z' => flags |= PasswordFlags::LOWERCASE,
b'A'..=b'Z' => flags |= PasswordFlags::UPPERCASE,
b'0'..=b'9' => flags |= PasswordFlags::NUMBER,
b'!'..=b'/' | b':'..=b'@' | b'['..=b'`' | b'{'..=b'~' => {
flags |= PasswordFlags::SPECIAL_CHARACTER
}
_ => (),
}
if flags.is_all() {
return true;
}
}
false
}
#[inline]
fn generate_password() -> ArrayString<32> {
loop {
let password: [u8; 32] = array::from_fn(|_| *CHARS.choose(&mut OsRng).unwrap());
if check_generated_password(&password) {
return ArrayString::from_byte_string(&password).unwrap();
}
}
}
/// Continuously generates the password until it passes the checks
#[inline]
pub fn generate_passwords() -> [ArrayString<32>; 10] {
array::from_fn(|_| generate_password())
}