added simple password generator to duralumin binary

This commit is contained in:
Janis 2022-04-04 18:58:16 +02:00
parent edc25af9de
commit 9e7d8c3494
5 changed files with 171 additions and 35 deletions

View file

@ -8,13 +8,14 @@ name = "libduralumin"
path = "src/lib.rs" path = "src/lib.rs"
[features] [features]
default = ["passphrase-gen", "ed25519", "clap", "rpassword", "base64"] default = ["passphrase-gen", "password-gen", "ed25519", "clap", "rpassword", "base64"]
ed25519 = ["osshkeys", "sha2"] ed25519 = ["osshkeys", "sha2"]
passphrase-gen = [] passphrase-gen = []
password-gen = []
[[bin]] [[bin]]
name = "duralumin" name = "duralumin"
required-features = ["passphrase-gen", "clap"] required-features = ["passphrase-gen", "password-gen", "clap", "clap/derive"]
[[bin]] [[bin]]
name = "duralumin-keygen" name = "duralumin-keygen"

View file

@ -1,7 +1,10 @@
use std::path::PathBuf; use std::path::PathBuf;
use clap::Parser; use clap::{Parser, Subcommand};
use libduralumin::passphrase_gen::{PassPhraseGenerator, Words}; use libduralumin::{
passphrase_gen::{PassPhraseGenerator, Words},
password_gen::PasswordGenerator,
};
/// program that generates random passphrases. /// program that generates random passphrases.
#[derive(Parser)] #[derive(Parser)]
@ -11,6 +14,18 @@ use libduralumin::passphrase_gen::{PassPhraseGenerator, Words};
author = "No One <noonebtw@nirgendwo.xyz>" author = "No One <noonebtw@nirgendwo.xyz>"
)] )]
struct Opts { struct Opts {
#[clap(subcommand)]
command: Subcommands,
}
#[derive(Subcommand)]
enum Subcommands {
Passphrase(PassphraseGenArgs),
Password(PasswordGenArgs),
}
#[derive(Parser)]
struct PassphraseGenArgs {
#[clap(short = 'l', long)] #[clap(short = 'l', long)]
num_words: Option<i16>, num_words: Option<i16>,
#[clap(long)] #[clap(long)]
@ -27,7 +42,35 @@ struct Opts {
backend: String, backend: String,
} }
impl Opts { #[derive(Parser)]
struct PasswordGenArgs {
/// number of passwords to generate
#[clap(short = 'c', long = "count", default_value = "1")]
count: u16,
/// if given, generate a password with an exact length, otherwise choses a length between min and max length.
#[clap(long = "length")]
exact_length: Option<u16>,
/// minimum length of the generated password
#[clap(long = "min", default_value = "10")]
min_length: u16,
/// maximum length of the generated password
#[clap(long = "max", default_value = "20")]
max_length: u16,
/// whether to use lowercase letters in the password
#[clap(short = 'l', long)]
use_letters: bool,
/// whether to use uppercase letters in the password
#[clap(short = 'L', long)]
use_capital_letters: bool,
/// whether to use numbers in the password
#[clap(short = 'N', long)]
use_numbers: bool,
/// whether to use special symbols `(!"§$%$%&\/\\()=?_:;,.-*+#'\\)` in the password
#[clap(short = 'S', long)]
use_symbols: bool,
}
impl PassphraseGenArgs {
/// gets the number of words per passphrase /// gets the number of words per passphrase
pub fn words_per_passphrase(&self) -> i16 { pub fn words_per_passphrase(&self) -> i16 {
self.num_words.unwrap_or(6) self.num_words.unwrap_or(6)
@ -76,28 +119,53 @@ impl Backend {
} }
fn main() { fn main() {
let opts = Opts::parse(); let opts: Opts = Opts::parse();
let gen = PassPhraseGenerator::new()
.with_capitalized(opts.capitalize())
.with_min_word_length(opts.min_word_length)
.with_length(opts.words_per_passphrase());
let words = match opts.backend() { match opts.command {
Some(Backend::UseAPassPhrase) => Words::from_str(include_str!("../../useapassphrase.txt")), Subcommands::Passphrase(opts) => {
Some(Backend::EnglishWords) => Words::from_str(include_str!("../../words_alpha.txt")), let gen = PassPhraseGenerator::new()
Some(path) => { .with_capitalized(opts.capitalize())
Words::from_text_file(std::fs::File::open(path).expect("failed to read word list.")) .with_min_word_length(opts.min_word_length)
.expect("word list from path.") .with_length(opts.words_per_passphrase());
}
None => {
panic!("invalid backend.")
}
};
for passphrase in gen let words = match opts.backend() {
.generate_n(&words, opts.passphrase_count() as usize) Some(Backend::UseAPassPhrase) => {
.expect("failed to generate passphrases. this is bad.") Words::from_str(include_str!("../../useapassphrase.txt"))
{ }
println!("{}", passphrase); Some(Backend::EnglishWords) => {
Words::from_str(include_str!("../../words_alpha.txt"))
}
Some(Backend::File(path)) => Words::from_text_file(
std::fs::File::open(path).expect("failed to read word list."),
)
.expect("word list from path."),
None => {
panic!("invalid backend.")
}
};
for passphrase in gen
.generate_n(&words, opts.passphrase_count() as usize)
.expect("failed to generate passphrases. this is bad.")
{
println!("{}", passphrase);
}
}
Subcommands::Password(args) => {
let min_length = args.exact_length.unwrap_or(args.min_length);
let max_length = args.exact_length.unwrap_or(args.max_length);
let gen = PasswordGenerator::new(
min_length,
max_length,
args.use_letters,
args.use_capital_letters,
args.use_numbers,
args.use_symbols,
);
for password in gen.generate_n(args.count as usize) {
println!("{}", password);
}
}
} }
} }

View file

@ -1,5 +1,8 @@
#[cfg(feature = "passphrase-gen")] #[cfg(feature = "passphrase-gen")]
pub mod passphrase_gen; pub mod passphrase_gen;
#[cfg(feature = "password-gen")]
pub mod password_gen;
#[cfg(feature = "ed25519")] #[cfg(feature = "ed25519")]
pub mod ed25519; pub mod ed25519;

View file

@ -80,11 +80,7 @@ impl PassPhraseGenerator {
self self
} }
pub fn generate_n( pub fn generate_n(&self, words: &Words, count: usize) -> Option<Vec<String>> {
&self,
words: &Words,
count: usize,
) -> Option<Vec<String>> {
(0..count).map(|_| self.generate(words)).collect() (0..count).map(|_| self.generate(words)).collect()
} }
@ -104,10 +100,7 @@ impl PassPhraseGenerator {
match chars.next() { match chars.next() {
None => String::new(), None => String::new(),
Some(c) => { Some(c) => c.to_uppercase().collect::<String>() + chars.as_str(),
c.to_uppercase().collect::<String>()
+ chars.as_str()
}
} }
} else { } else {
word.to_string() word.to_string()

71
src/password_gen.rs Normal file
View file

@ -0,0 +1,71 @@
use rand::{prelude::SliceRandom, rngs::OsRng, Rng};
const LETTERS: &[u8] = b"abcdefghijklmnopqrstuvwxyz";
const CAPITAL_LETTERS: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const NUMBERS: &[u8] = b"0123456789";
const SYMBOLS: &[u8] = br#" !#$%"&'()*+,-./:;<=>?@[\]^_`{|}~"#;
pub struct PasswordGenerator {
min_length: u16,
max_length: u16,
letters: bool,
capital_letters: bool,
numbers: bool,
symbols: bool,
}
impl PasswordGenerator {
pub fn new_exact_length(
length: u16,
letters: bool,
capital_letters: bool,
numbers: bool,
symbols: bool,
) -> Self {
Self::new(length, length, letters, capital_letters, numbers, symbols)
}
pub fn new(
min_length: u16,
max_length: u16,
letters: bool,
capital_letters: bool,
numbers: bool,
symbols: bool,
) -> Self {
Self {
min_length,
max_length,
letters,
capital_letters,
numbers,
symbols,
}
}
pub fn generate(&self) -> String {
let length = OsRng.gen_range(self.min_length..=self.max_length) as usize;
let set = [
self.letters.then(|| LETTERS.iter()),
self.capital_letters.then(|| CAPITAL_LETTERS.iter()),
self.numbers.then(|| NUMBERS.iter()),
self.symbols.then(|| SYMBOLS.iter()),
]
.into_iter()
.flatten()
.flatten()
.collect::<Vec<_>>();
let mut bytes = std::iter::repeat_with(|| **set.choose(&mut OsRng).unwrap())
.take(length)
.collect::<Vec<_>>();
bytes.shuffle(&mut OsRng);
String::from_utf8(bytes).expect("password bytes to utf8 string.")
}
pub fn generate_n(&self, n: usize) -> Vec<String> {
std::iter::repeat_with(|| self.generate()).take(n).collect()
}
}