genpass3/lib.rs
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
// MIT License - Copyright (c) 2022 Nicolás Castellán
// SPDX License identifier: MIT
// THE SOFTWARE IS PROVIDED "AS IS"
// Read the included LICENSE file for more information
//! # genpass3
//!
//! `genpass3` is a library and binary crate that allows Linux users to generate very long & secure
//! passwords at incredible speeds using `/dev/urandom`.
use std::{
error::Error,
fs::File,
io::{BufReader, Read},
};
/// The config for the generation of passwords. Taken as an argument by the [`run`](run) function.
///
/// You can create an instance of this struct using [`Config::new`](Config::new) or
/// [`Config::build`](Config::build).
pub struct Config {
length: usize,
}
impl Config {
/// Creates a [`Config`](Config) type using the command line arguments of the binary, passed in
/// as arguments. **Assumes** the first iteration of `args` is the program name, so it's
/// ignored.
///
/// Parameter:
/// - `args` - An iterator, meant to iterate over the binary's arguments and flags.
///
/// # Errors
/// This function can result in an error if the length parameter could not be parsed into a
/// [`usize`](usize).
pub fn build(mut args: impl Iterator<Item = String>) -> Result<Config, Box<dyn Error>> {
// Ignore the first argument
args.next();
let length = match args.next() {
Some(length) => length.parse()?,
None => 16,
};
Ok(Config { length })
}
/// Creates a [`Config`](Config) type. Recommended over [`Config::build`](Config::build) for
/// most use cases.
///
/// Parameter:
/// - `length` - A [`usize`](usize) to act as the length of the password.
pub fn new(length: usize) -> Config {
Config { length }
}
/// Prints the configuration options to stderr.
///
/// # Example
///
/// ```
/// # use genpass3::*;
/// Config::print_config();
/// ```
pub fn print_config() {
eprint!(
"\
Usage:
\x1B[01m{} <LENGTH>\x1B[00m\n
The LENGTH is an optional parameter specifying the desired length of the password.\n
Version: {}, {} License
",
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_VERSION"),
env!("CARGO_PKG_LICENSE")
);
}
}
/// Runs the configured password generation
///
/// Parameter:
/// - `config` - A [`Config`](Config) that contains the configuration for the function.
///
/// # Errors
///
/// This function can return errors if `/dev/urandom` does not exist.
///
/// # Example
///
/// ```
/// # use genpass3::*;
/// # let args = vec![
/// # String::from("program_name"),
/// # String::from("20"),
/// # ];
/// # let args = args.into_iter();
/// let config = match Config::build(args) {
/// Ok(config) => config,
/// Err(error) => panic!("Failed to build `Config`: {}", error),
/// };
///
/// genpass3::run(&config);
/// ```
pub fn run(config: &Config) -> Result<String, Box<dyn Error>> {
let mut password = vec![0u8; config.length];
{
// This is a special file available in Linux as a way to get random data.
// It's a device, it generates characters as we request it.
let dev_chars = File::open("/dev/urandom")?;
let mut reader = BufReader::with_capacity(config.length, dev_chars);
// Read all the data at once.
reader.read_exact(&mut password)?;
}
{
// We're bounding our characters within 32 and 127 in ASCII.
let min_ascii = 32;
let diff_ascii = 95;
// We need to iterate over a range of numbers because if we were to iterate over the
// elements of the Vec we would have mutable reference problems.
#[allow(clippy::needless_range_loop)]
for i in 0..config.length {
password[i] = password[i] % diff_ascii + min_ascii;
}
}
// Effectively transforms `Vec<u8>` into `Vec<char>` into `String`.
let password: String = password.into_iter().map(|c| c as char).collect();
Ok(password)
}