fermah_common/crypto/kdf/
scrypt.rs1use 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 pub n: u32,
11 pub r: u32,
13 pub p: u32,
15 pub dklen: usize,
17 #[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}