keynesis_core/
seed.rs

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    /// Generate a random see with the given Cryptographically secure
17    /// Random Number Generator (RNG).
18    ///
19    /// This is useful to generate one time only `Seed` that does not
20    /// need to be remembered or saved.
21    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    /// it is possible to derive the Seed from a given key
31    ///
32    /// the key may be given by a secure hardware or simply be
33    /// a mnemonic phrase given by a user and a password.
34    ///
35    /// It is possible, but not recommended, that the password is left
36    /// empty. However, the key needs to be large enough to generate
37    /// enough entropy for the derived seed.
38    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    /// use this to seed a ChaCha RNG
59    ///
60    /// then you can use the RNG to create new private key. This is an
61    /// handy way to derive a private key from a key and a password
62    /// (or an HSM and a password?)
63    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}