Генератор паролей на Rust
Привет, Хабр!
Представляю вам свою первую статью о программировании на Rust. Я только начинаю изучать этот язык, и в качестве первого задания выбрал написание генератора паролей.
Процесс разработки консольного приложения будет состоять из нескольких этапов:
Формирование структуры пароля.
Разработка функции генерации пароля.
Создание функций для проверки пароля и их объединение в одну.
Обеспечение возможности пользовательского ввода.
Формирование функции генерации проверенного пароля.
1. Формирование структуры пароля.
Пароль представляет собой комбинацию символов и их количество. В нём могут использоваться следующие символы:
Строчные и заглавные буквы латинского алфавита.(A, a, B, b, C, c, и т.д.).
Цифры.(0, 1, 2, и т.д.).
Специальные символы.(@, !, _, и т.д.).
Создадим модуль, в котором будет хранится структура и поместим ее туда.
pub struct PasswordStructure {
pub long: usize,
pub register_up: u8,
pub register_low: u8,
pub numbers: u8,
pub special_char: u8,
}
2. Разработка функции генерации пароля.
Чтобы создать пароль, нам необходимо добавить разрешённые символы в строку. Для этого мы воспользуемся модулем IteratorRandom из библиотеки rand. Также нам понадобится строка, в которую мы будем добавлять символы. Создадим изменяемую переменную password, с заранее выделенным пространством для хранения символов. В этом нам поможет метод with_capacity (). Для создания пароля заданной длинны мы будем использовать цикл while, который будет выполняться, пока длинна пароля не будет равна указанной длине. Пока цикл выполняется мы будем брать случайный символ из заранее подготовленной строки symbolss и добавлять его к строке password.
fn creating_password(long: usize) -> String {
let mut rng = rand::thread_rng();
let symbolss = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890!@#$%&*;:?_-";
let mut password = String::with_capacity(long);
while password.len() != long {
password.push(symbolss.chars().choose(&mut rng).unwrap());
}
password
}
3. Создание функций для проверки пароля и их объединение в одну.
Мы создаем приложение, которое генерирует пароль по заданным критериям:
Длине.
Количеству строчных и заглавных букв.
Количеству цифр.
Количеству специальных символов.
Для правильного создания нам нужно будет реализовать проверки. Каждая созданная функция будет возвращать true, если проверка пройдена успешно, и false в случае неудачи.
Длинна пароля.
Функция принимает указанную длину и сгенерированный пароль.
Чтобы проверить длину пароля, мы сравним количество символов в полученном пароле с требуемым количеством символов. По сути эта проверка не нужна, но реализация дополнительной функции не помешает.
fn checking_length(long: usize, password: &str) -> bool {
if password.len() == long {
return true;
}
false
}
Строчные и заглавные буквы.
Функция принимает длину пароля, количество маленьких и больших букв, сгенерированный пароль.
Мы создадим две переменные, в которые запишем количество строчных и заглавных букв в сгенерированом пароле. С помощью цикла for пройдемся по всему паролю. Воспользовавшись методами is_ascii_uppercase (), is_ascii_lowercase () и as_bytes () мы проверим каждый код каждого символа в ASCII. После сравним созданные переменные с переменными хранящие количество маленьких и больших букв.
fn checking_register(long: usize, register_up: u8, register_low: u8, password: &str) -> bool {
let mut capital_letters_up = 0;
let mut capital_letters_low = 0;
for i in 0..long {
if password.as_bytes()[i].is_ascii_uppercase() {
capital_letters_up += 1;
}
if password.as_bytes()[i].is_ascii_lowercase(){
capital_letters_low += 1;
}
}
(capital_letters_up >= register_up) && (capital_letters_low >= register_low)
}
Количество цифр.
Функция принимает длину, количество цифр и сгенерированный пароль.
Проверка цифр будет аналогична проверке букв. Создадим переменную, считающую цифры. Организуем цикл for по длине переданного пароля. Пройдемся по всем символам и будем прибавлять единицу к нашей созданной переменной, каждый раз, когда код символа совпадет с кодом цифры из ASCII. Помогут искать методы is_ascii_digit () и as_bytes () . Дальше сравним получившееся значение с нужным количеством цифр.
fn checking_numbers(long: usize,numbers: u8, password: &str) -> bool {
let mut quantity_numbers = 0;
for i in 0..long{
if password.as_bytes()[i].is_ascii_digit() {
quantity_numbers += 1;
}
}
quantity_numbers >= numbers
}
Спец. символы.
Функция принимает количество специальных символов и сгенерированный пароль.
Создадим переменную, считающую спец. символы. Организуем цикл for таким образом, чтобы в переменную цикла i записывался номер символа из ASCII. С помощью match будем сравнивать коды разрешенных специальных символов с кодом текущего символа, если хотя бы один символ совпадет, то увеличим значение переменной на один. В конце как обычно сравним найденое количество символов с нужным количеством.
fn checking_special_char(special_char: u8, password: &str) -> bool {
let mut quantity_special_char = 0;
for i in password.as_bytes() {
match i {
b'!' | b'@' | b'#' | b'$' | b'%' |
b'&' | b'*' | b';' | b':' |
b'?' | b'_' | b'-' => quantity_special_char += 1,
_ => (),
}
}
quantity_special_char >= special_char
}
После реализации всех проверок соберем их в одну маленькую функцию возвещающую true или false в зависимости от результатов всех проверок.
fn password_verification(long: usize, register_up: u8, register_low: u8, numbers: u8, special_char: u8, password: &str) -> bool {
matches!((checking_length(long, password), checking_register(long, register_up, register_low, password), checking_numbers(long, numbers, password), checking_special_char(special_char, password)), (true, true, true, true))
}
4. Обеспечение возможности пользовательского ввода.
Теперь нам нужна функция, которая будет давать критерии для генерации пароля. Для начала воспользовавшись созданной структурой создадим функции, возвращающие структуры с заданными полями. Они будут храниться в том же модуле, что и материнская структура.
pub fn vk() -> PasswordStructure {
let long: usize = 8;
let register_up: u8 = 1;
let register_low: u8 = 1;
let numbers: u8 = 1;
let special_char: u8 = 1;
PasswordStructure {long, register_up, register_low, numbers, special_char}
}
pub fn ok() -> PasswordStructure {
let long: usize = 12;
let register_up: u8 = 1;
let register_low: u8 = 1;
let numbers: u8 = 1;
let special_char: u8 = 1;
PasswordStructure {long, register_up, register_low, numbers, special_char}
}
pub fn default() -> PasswordStructure {
let long: usize = 15;
let register_up: u8 = 1;
let register_low: u8 = 1;
let numbers: u8 = 1;
let special_char: u8 = 1;
PasswordStructure {long, register_up, register_low, numbers, special_char}
}
Настроем пользовательский ввод. Человек, использующий программу должен выбрать какой пароль он хочет сгенерировать. На данный момент представлены пароли для VK, OK и значения по умолчанию.
fn sites_standards() -> Option<(usize, u8, u8, u8, u8)> {
let mut site_name = String::new();
println!("\nВыбирете сайт, для которого хотите сгенерировать пароль(введите цифру): \n1. VK \n2. OK \n3. Стандартный пароль\n");
let _ = io::stdin().read_line( &mut site_name);
match site_name.as_str().trim() {
"1" => Some((vk().long, vk().register_up, vk().register_low, vk().numbers, vk().special_char)),
"2" => Some((ok().long, vk().register_up, ok().register_low, ok().numbers, ok().special_char)),
"3" => Some((default().long, default().register_up, default().register_low, default().numbers, default().special_char)),
_ => {
println!("\n{}Вы ввели неверное значение! Мы создадим пароль по дефольтным значениям.\n", color::Fg(color::Red));
Some((default().long, default().register_up, default().register_low, default().numbers, default().special_char))
}
}
}
Эта функция вернет значения, которые находятся в выбранной пользователем структуре.
3. Формирование функции генерации проверенного пароля.
После того как мы реализовали все необходимые функции, пришло время объединить их. Мы создадим переменную, принимающую кортеж из функции критериев пароля. Далее с помощью match мы обработаем значения, переданные в переменную. Внутри match мы создадим переменные password и verification. В password запишем созданный пароль, а в verification результат проверки пароля. Потом создадим цикл while, который будет выполняться, пока verification не станет true. До этого момента мы будем перезаписывать переменную password, генерируя новые пароли. Результатом выполнения функции станет правильно сгенерированный пароль.
fn verified_password() -> Option {
let site_values = sites_standards();
match site_values {
Some((long, register_up, register_low, numbers, special_char)) => {
let mut password = creating_password(long);
let mut verification = password_verification(long, register_up, register_low, numbers, special_char, &password.clone());
if !verification {
while !verification {
password = creating_password(long);
verification = password_verification(long, register_up, register_low, numbers, special_char, &password.clone());
}
}
Some(password)
}
None => None,
}
}
Остается только красиво вывеси пароль в функции main ().
fn main() {
let password: Option<_> = verified_password();
match password {
Some(password) => println!("{}Ваш пароль: {}", color::Fg(color::Green), password),
None => println!("{}!!!ОШИБКА!!!", color::Fg(color::Red)),
}
}
Заключение
Мы создали простую консольную программу на Rust, которая быстро и безошибочно генерирует пароль по заданным значениям.
Я оставил много возможностей для дальнейшего развития. Например, можно реализовать возможность ручного ввода критериев для пароля или создать веб-приложение, основанное на логике этого кода. Если кто-нибудь будет делать или уже делал подобное, прошу поделиться в комментариях.
Ссылка на полный код