vitaminc_password/
lib.rs

1use vitaminc_protected::Protected;
2use vitaminc_random::{Generatable, RandomError, SafeRand};
3
4const STANDARD_CHARS: [char; 94] = [
5    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
6    'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'e', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
7    'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4',
8    '5', '6', '7', '8', '9', '~', '`', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '-',
9    '+', '=', '{', '[', '}', ']', '|', '\\', ':', ';', '"', '\'', '<', ',', '>', '.', '?', '/',
10];
11
12// TODO: Additional ideas
13// To include a random string for additional "entropy"
14// we could generate a random seed using SafeRand::from_entropy()
15// then use an HKDF to generate a new seed from the random string along with user provided context
16// and then use that as the seed for the password generation.
17
18pub struct Password<const N: usize>(Protected<[char; N]>); // TODO: Use a Paranoid
19pub struct AlphaNumericPassword<const N: usize>(Password<N>);
20pub struct AlphaPassword<const N: usize>(Password<N>);
21
22impl<const N: usize> Password<N> {
23    // TODO: Use Into<Protected<String>>
24    pub fn new(password: [char; N]) -> Self {
25        Self(Protected::new(password))
26    }
27    /// Converts the password into a standard `String`.
28    /// Once this happens, Zeroization is no longer guaranteed
29    /// so only do this as a final step where the password is needed.
30    pub fn into_unprotected_string(self) -> String {
31        //self.0.iter().collect()
32        unimplemented!()
33    }
34
35    pub fn into_protected_string(self) -> Protected<String> {
36        unimplemented!("Paranoid string conversion")
37    }
38}
39
40impl<const N: usize> AlphaNumericPassword<N> {
41    /// Converts the password into a standard `String`.
42    /// Once this happens, Zeroization is no longer guaranteed
43    /// so only do this as a final step where the password is needed.
44    pub fn into_unprotected_string(self) -> String {
45        self.0.into_unprotected_string()
46    }
47
48    pub fn into_protected_string(self) -> Protected<String> {
49        self.0.into_protected_string()
50    }
51}
52
53impl<const N: usize> AlphaPassword<N> {
54    /// Converts the password into a standard `String`.
55    /// Once this happens, Zeroization is no longer guaranteed
56    /// so only do this as a final step where the password is needed.
57    pub fn into_unprotected_string(self) -> String {
58        self.0.into_unprotected_string()
59    }
60
61    pub fn into_protected_string(self) -> Protected<String> {
62        self.0.into_protected_string()
63    }
64}
65
66impl<const N: usize> Generatable for Password<N> {
67    fn random(rng: &mut SafeRand) -> Result<Self, RandomError> {
68        let mut password: [char; N] = [0x00 as char; N];
69        (0..N).for_each(|i| {
70            let char = rng.next_bounded_u32(STANDARD_CHARS.len() as u32);
71            password[i] = STANDARD_CHARS[char as usize];
72        });
73        Ok(Password::new(password))
74    }
75}
76
77impl<const N: usize> Generatable for AlphaNumericPassword<N> {
78    fn random(rng: &mut SafeRand) -> Result<Self, RandomError> {
79        let mut password: [char; N] = [0x00 as char; N];
80        (0..N).for_each(|i| {
81            let char = rng.next_bounded_u32(62);
82            password[i] = STANDARD_CHARS[char as usize];
83        });
84        Ok(Self(Password::new(password)))
85    }
86}
87
88impl<const N: usize> Generatable for AlphaPassword<N> {
89    fn random(rng: &mut SafeRand) -> Result<Self, RandomError> {
90        let mut password: [char; N] = [0x00 as char; N];
91        (0..N).for_each(|i| {
92            let char = rng.next_bounded_u32(52);
93            password[i] = STANDARD_CHARS[char as usize];
94        });
95        Ok(Self(Password::new(password)))
96    }
97}
98
99#[cfg(test)]
100mod tests {
101    use crate::{AlphaNumericPassword, AlphaPassword};
102
103    use super::Password;
104    use vitaminc_random::{Generatable, SafeRand, SeedableRng};
105
106    #[ignore = "wip"]
107    #[test]
108    fn test_generate_password() -> Result<(), crate::RandomError> {
109        let mut rng = SafeRand::from_entropy();
110        let value: Password<16> = Generatable::random(&mut rng)?;
111        dbg!(value.into_unprotected_string());
112
113        Ok(())
114    }
115
116    #[ignore = "wip"]
117    #[test]
118    fn test_generate_alphanumeric_password() -> Result<(), crate::RandomError> {
119        let mut rng = SafeRand::from_entropy();
120        let value: AlphaNumericPassword<16> = Generatable::random(&mut rng)?;
121        dbg!(value.into_unprotected_string());
122
123        Ok(())
124    }
125
126    #[ignore = "wip"]
127    #[test]
128    fn test_generate_alpha_password() -> Result<(), crate::RandomError> {
129        let mut rng = SafeRand::from_entropy();
130        let value: AlphaPassword<16> = Generatable::random(&mut rng)?;
131        dbg!(value.into_unprotected_string());
132
133        Ok(())
134    }
135}