genpass3/lib.rs
1// MIT License - Copyright (c) 2022 Nicolás Castellán
2// SPDX License identifier: MIT
3// THE SOFTWARE IS PROVIDED "AS IS"
4// Read the included LICENSE file for more information
5
6//! # genpass3
7//!
8//! `genpass3` is a library and binary crate that allows Linux users to generate very long & secure
9//! passwords at incredible speeds using `/dev/urandom`.
10
11use std::{
12 error::Error,
13 fs::File,
14 io::{BufReader, Read},
15};
16
17/// The config for the generation of passwords. Taken as an argument by the [`run`](run) function.
18///
19/// You can create an instance of this struct using [`Config::new`](Config::new) or
20/// [`Config::build`](Config::build).
21pub struct Config {
22 length: usize,
23}
24
25impl Config {
26 /// Creates a [`Config`](Config) type using the command line arguments of the binary, passed in
27 /// as arguments. **Assumes** the first iteration of `args` is the program name, so it's
28 /// ignored.
29 ///
30 /// Parameter:
31 /// - `args` - An iterator, meant to iterate over the binary's arguments and flags.
32 ///
33 /// # Errors
34 /// This function can result in an error if the length parameter could not be parsed into a
35 /// [`usize`](usize).
36 pub fn build(mut args: impl Iterator<Item = String>) -> Result<Config, Box<dyn Error>> {
37 // Ignore the first argument
38 args.next();
39
40 let length = match args.next() {
41 Some(length) => length.parse()?,
42 None => 16,
43 };
44
45 Ok(Config { length })
46 }
47
48 /// Creates a [`Config`](Config) type. Recommended over [`Config::build`](Config::build) for
49 /// most use cases.
50 ///
51 /// Parameter:
52 /// - `length` - A [`usize`](usize) to act as the length of the password.
53 pub fn new(length: usize) -> Config {
54 Config { length }
55 }
56
57 /// Prints the configuration options to stderr.
58 ///
59 /// # Example
60 ///
61 /// ```
62 /// # use genpass3::*;
63 /// Config::print_config();
64 /// ```
65 pub fn print_config() {
66 eprint!(
67 "\
68Usage:
69 \x1B[01m{} <LENGTH>\x1B[00m\n
70The LENGTH is an optional parameter specifying the desired length of the password.\n
71Version: {}, {} License
72",
73 env!("CARGO_PKG_NAME"),
74 env!("CARGO_PKG_VERSION"),
75 env!("CARGO_PKG_LICENSE")
76 );
77 }
78}
79
80/// Runs the configured password generation
81///
82/// Parameter:
83/// - `config` - A [`Config`](Config) that contains the configuration for the function.
84///
85/// # Errors
86///
87/// This function can return errors if `/dev/urandom` does not exist.
88///
89/// # Example
90///
91/// ```
92/// # use genpass3::*;
93/// # let args = vec![
94/// # String::from("program_name"),
95/// # String::from("20"),
96/// # ];
97/// # let args = args.into_iter();
98/// let config = match Config::build(args) {
99/// Ok(config) => config,
100/// Err(error) => panic!("Failed to build `Config`: {}", error),
101/// };
102///
103/// genpass3::run(&config);
104/// ```
105pub fn run(config: &Config) -> Result<String, Box<dyn Error>> {
106 let mut password = vec![0u8; config.length];
107
108 {
109 // This is a special file available in Linux as a way to get random data.
110 // It's a device, it generates characters as we request it.
111 let dev_chars = File::open("/dev/urandom")?;
112 let mut reader = BufReader::with_capacity(config.length, dev_chars);
113
114 // Read all the data at once.
115 reader.read_exact(&mut password)?;
116 }
117
118 {
119 // We're bounding our characters within 32 and 127 in ASCII.
120 let min_ascii = 32;
121 let diff_ascii = 95;
122
123 // We need to iterate over a range of numbers because if we were to iterate over the
124 // elements of the Vec we would have mutable reference problems.
125 #[allow(clippy::needless_range_loop)]
126 for i in 0..config.length {
127 password[i] = password[i] % diff_ascii + min_ascii;
128 }
129 }
130
131 // Effectively transforms `Vec<u8>` into `Vec<char>` into `String`.
132 let password: String = password.into_iter().map(|c| c as char).collect();
133
134 Ok(password)
135}