1use crate::memsec::Scrubbed as _;
2use cryptoxide::{hmac::Hmac, pbkdf2::pbkdf2, sha2::Sha512};
3use rand_chacha::ChaChaRng;
4use rand_core::{CryptoRng, RngCore, SeedableRng};
5use std::{
6 fmt::{self, Debug, Display, Formatter},
7 str::FromStr,
8};
9
10#[derive(Clone)]
11pub struct Seed([u8; Self::SIZE]);
12
13impl Seed {
14 pub const SIZE: usize = 32;
15
16 pub fn generate<RNG>(rng: &mut RNG) -> Self
22 where
23 RNG: RngCore + CryptoRng,
24 {
25 let mut bytes = [0; Self::SIZE];
26 rng.fill_bytes(&mut bytes);
27 Self(bytes)
28 }
29
30 pub fn derive_from_key<K, P>(key: K, password: P) -> Self
39 where
40 K: AsRef<[u8]>,
41 P: AsRef<[u8]>,
42 {
43 debug_assert!(
44 key.as_ref().len() >= 32,
45 "It is highly unsafe to use key with less than 32bytes"
46 );
47
48 let iteration = 1_000_000;
49 let mut bytes = [0; Self::SIZE];
50
51 let mut mac = Hmac::new(Sha512::new(), password.as_ref());
52
53 pbkdf2(&mut mac, key.as_ref(), iteration, &mut bytes);
54
55 Self(bytes)
56 }
57
58 pub fn into_rand_chacha(self) -> ChaChaRng {
64 ChaChaRng::from_seed(self.0)
65 }
66}
67
68impl Drop for Seed {
69 fn drop(&mut self) {
70 self.0.scrub()
71 }
72}
73
74impl Display for Seed {
75 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
76 Display::fmt(&hex::encode(&self.0), f)
77 }
78}
79
80impl Debug for Seed {
81 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
82 f.debug_tuple("Seed").field(&hex::encode(&self.0)).finish()
83 }
84}
85
86impl AsRef<[u8]> for Seed {
87 fn as_ref(&self) -> &[u8] {
88 &self.0
89 }
90}
91
92impl FromStr for Seed {
93 type Err = hex::FromHexError;
94 fn from_str(s: &str) -> Result<Self, Self::Err> {
95 let mut bytes = [0; Self::SIZE];
96 hex::decode_to_slice(s, &mut bytes)?;
97 Ok(Self(bytes))
98 }
99}
100
101impl From<[u8; Self::SIZE]> for Seed {
102 fn from(seed: [u8; Self::SIZE]) -> Self {
103 Self(seed)
104 }
105}
106
107#[cfg(test)]
108mod tests {
109 use super::*;
110 use quickcheck::{Arbitrary, Gen};
111
112 impl Arbitrary for Seed {
113 fn arbitrary(g: &mut Gen) -> Self {
114 let mut bytes = [0; Self::SIZE];
115 bytes.iter_mut().for_each(|byte| {
116 *byte = u8::arbitrary(g);
117 });
118 Self(bytes)
119 }
120 }
121}