passgen-rs 0.1.0

Password generator with a regular-expression-like syntax
Documentation
use argon2::Argon2;
use rand::RngCore;
use rand::SeedableRng;
use rand_chacha::ChaCha20Rng;
use rand_xoshiro::Xoshiro256PlusPlus;
use std::str::FromStr;

#[derive(Debug, Clone, PartialEq)]
pub enum Random {
    System(System),
    Null(Null),
    Xoshiro(Xoshiro),
    ChaCha20(ChaCha20),
}

impl Default for Random {
    fn default() -> Self {
        Self::System(System)
    }
}

impl Random {
    pub fn from_master_pass(pass: &str, salt: &str) -> Self {
        let mut seed = [0; 32];
        Argon2::new_with_secret(
            b"passgen",
            Default::default(),
            Default::default(),
            Default::default(),
        )
        .unwrap()
        .hash_password_into(pass.as_bytes(), salt.as_bytes(), &mut seed)
        .unwrap();
        Self::ChaCha20(ChaCha20(seed))
    }
}

#[derive(thiserror::Error, Debug)]
pub enum ParseError {}

impl FromStr for Random {
    type Err = ParseError;

    fn from_str(input: &str) -> Result<Self, Self::Err> {
        match input {
            "system" => return Ok(Self::System(System)),
            "null" => return Ok(Self::Null(Null)),
            _ => {}
        }

        if let Some((name, params)) = input.split_once(':') {
            if name == "xoshiro" {
                return Ok(Self::Xoshiro(Xoshiro(params.parse().unwrap())));
            }
        }

        todo!()
    }
}

pub trait RandomSource {
    type Rng: RngCore;

    fn get_rng(&self, index: usize) -> Self::Rng;
}

pub trait BoxedRandomSource {
    fn get_rng_boxed(&self, index: usize) -> Box<dyn RngCore>;
}

impl<S: RandomSource> BoxedRandomSource for S
where
    S::Rng: 'static,
{
    fn get_rng_boxed(&self, index: usize) -> Box<dyn RngCore> {
        Box::new(RandomSource::get_rng(self, index))
    }
}

impl BoxedRandomSource for Random {
    fn get_rng_boxed(&self, index: usize) -> Box<dyn RngCore> {
        match self {
            Self::System(random) => random.get_rng_boxed(index),
            Self::Null(random) => random.get_rng_boxed(index),
            Self::Xoshiro(random) => random.get_rng_boxed(index),
            Self::ChaCha20(random) => random.get_rng_boxed(index),
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct System;

impl RandomSource for System {
    type Rng = rand::rngs::ThreadRng;

    fn get_rng(&self, _index: usize) -> Self::Rng {
        rand::thread_rng()
    }
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Null;

impl RandomSource for Null {
    type Rng = rand::rngs::mock::StepRng;

    fn get_rng(&self, _index: usize) -> Self::Rng {
        rand::rngs::mock::StepRng::new(0, 0)
    }
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Xoshiro(u64);

impl RandomSource for Xoshiro {
    type Rng = Xoshiro256PlusPlus;

    fn get_rng(&self, index: usize) -> Self::Rng {
        let mut rng = Xoshiro256PlusPlus::seed_from_u64(self.0);
        for _ in 0..index {
            rng.jump();
        }
        rng
    }
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub struct ChaCha20([u8; 32]);

impl RandomSource for ChaCha20 {
    type Rng = ChaCha20Rng;

    fn get_rng(&self, index: usize) -> Self::Rng {
        let mut rng = ChaCha20Rng::from_seed(self.0);
        rng.set_stream(index as u64);
        rng
    }
}