Skip to main content

fermah_common/crypto/kdf/
scrypt.rs

1use rand_core::{CryptoRngCore, OsRng};
2use scrypt::{password_hash::SaltString, scrypt};
3use serde::{Deserialize, Serialize};
4
5use crate::{crypto::kdf::Kdf, serialization::encoding::hex_encoded_no_prefix};
6
7#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
8pub struct ScryptKdfParams {
9    /// log2(n) of the number of iterations
10    pub n: u32,
11    /// Block size
12    pub r: u32,
13    /// Parallelism
14    pub p: u32,
15    /// Derived key length
16    pub dklen: usize,
17    /// Salt used when deriving the key
18    #[serde(with = "hex_encoded_no_prefix")]
19    pub salt: Vec<u8>,
20}
21
22impl ScryptKdfParams {
23    pub const BLOCK_SIZE: u32 = 8;
24
25    pub const FAST_LOG_N: u32 = 4096;
26    pub const FAST_PARALLELISM: u32 = 6;
27
28    pub const SECURE_LOG_N: u32 = 262144;
29    pub const SECURE_PARALLELISM: u32 = 1;
30}
31
32impl From<ScryptKdfParams> for scrypt::Params {
33    fn from(params: ScryptKdfParams) -> Self {
34        scrypt::Params::new(params.n.ilog(2) as u8, params.r, params.p, params.dklen).unwrap()
35    }
36}
37
38#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
39pub struct ScryptKdf {
40    #[serde(flatten)]
41    params: ScryptKdfParams,
42}
43
44impl Default for ScryptKdf {
45    fn default() -> Self {
46        Self::secure(&mut OsRng)
47    }
48}
49
50impl Kdf for ScryptKdf {
51    const NAME: &'static str = "scrypt";
52
53    fn fast(mut rng: impl CryptoRngCore) -> Self {
54        Self::new(Self::Params {
55            n: ScryptKdfParams::FAST_LOG_N,
56            r: ScryptKdfParams::BLOCK_SIZE,
57            p: ScryptKdfParams::FAST_PARALLELISM,
58            dklen: 32,
59            salt: SaltString::generate(&mut rng).as_ref().as_bytes().to_vec(),
60        })
61    }
62
63    fn secure(mut rng: impl CryptoRngCore) -> Self {
64        Self::new(Self::Params {
65            n: ScryptKdfParams::SECURE_LOG_N,
66            r: ScryptKdfParams::BLOCK_SIZE,
67            p: ScryptKdfParams::SECURE_PARALLELISM,
68            dklen: 32,
69            salt: SaltString::generate(&mut rng).as_ref().as_bytes().to_vec(),
70        })
71    }
72
73    type Error = scrypt::errors::InvalidOutputLen;
74    type Params = ScryptKdfParams;
75
76    fn new(params: Self::Params) -> Self {
77        Self { params }
78    }
79
80    fn derive_key(&self, password: &[u8], out: &mut [u8]) -> Result<(), Self::Error> {
81        let salt = self.params.salt.clone();
82        scrypt(password, salt.as_slice(), &self.params.clone().into(), out)?;
83        Ok(())
84    }
85}
86
87#[cfg(test)]
88mod tests {
89    use const_hex::ToHexExt;
90    use rand::prelude::StdRng;
91    use rand_core::SeedableRng;
92
93    use super::*;
94
95    #[test]
96    fn test_scrypt_kdf() {
97        let kdf = ScryptKdf::fast(&mut StdRng::seed_from_u64(0));
98        let mut key = [0u8; 32];
99        kdf.derive_key(b"password", &mut key).unwrap();
100
101        assert_eq!(
102            key.encode_hex_with_prefix(),
103            "0x5ca8e8322ab4b64069e816acbdbc1d9387684f9972994d0c8187f049aad1be4d"
104        );
105    }
106}