Skip to main content

otplus_core/cipher/
derived_key.rs

1use argon2::{Argon2, Algorithm, Version, Params};
2use chacha20poly1305::Key;
3use serde::{Serialize, Serializer, Deserialize, Deserializer};
4use serde::ser::SerializeStruct;
5use base64::{engine::general_purpose::STANDARD, Engine};
6
7pub struct KdfParams {
8  m_cost: u32,
9  t_cost: u32,
10  p_cost: u32,
11}
12
13impl KdfParams {
14  pub fn new(m_cost: u32, t_cost: u32, p_cost: u32) -> Self {
15    Self { m_cost, t_cost, p_cost }
16  }
17}
18
19#[derive(Debug)]
20pub struct DerivedKey {
21  value: Key,
22  salt: Vec<u8>,
23}
24
25impl Serialize for DerivedKey {
26  fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
27  where
28    S: Serializer,
29  {
30    let mut state = serializer.serialize_struct("DerivedKey", 2)?;
31    state.serialize_field("value", &STANDARD.encode(&self.value.as_slice()))?;
32    state.serialize_field("salt", &STANDARD.encode(&self.salt))?;
33    state.end()
34  }
35}
36
37impl<'de> Deserialize<'de> for DerivedKey {
38  fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
39  where
40    D: Deserializer<'de>,
41  {
42    use serde::de::{self, MapAccess, Visitor};
43    use std::fmt;
44
45    struct DerivedKeyVisitor;
46
47    impl<'de> Visitor<'de> for DerivedKeyVisitor {
48      type Value = DerivedKey;
49
50      fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
51        formatter.write_str("struct DerivedKey")
52      }
53
54      fn visit_map<V>(self, mut map: V) -> Result<DerivedKey, V::Error>
55      where
56        V: MapAccess<'de>,
57      {
58        let mut value = None;
59        let mut salt = None;
60        while let Some(key) = map.next_key::<&str>()? {
61          match key {
62            "value" => {
63              if value.is_some() {
64                return Err(de::Error::duplicate_field("value"));
65              }
66              let value_str: String = map.next_value()?;
67              let value_bytes = base64::engine::general_purpose::STANDARD
68                .decode(&value_str)
69                .map_err(de::Error::custom)?;
70              value = Some(Key::from_slice(&value_bytes).clone());
71            }
72            "salt" => {
73              if salt.is_some() {
74                return Err(de::Error::duplicate_field("salt"));
75              }
76              let salt_str: String = map.next_value()?;
77              let salt_bytes = base64::engine::general_purpose::STANDARD
78                .decode(&salt_str)
79                .map_err(de::Error::custom)?;
80              salt = Some(salt_bytes);
81            }
82            _ => {
83              let _: de::IgnoredAny = map.next_value()?;
84            }
85          }
86        }
87        let value = value.ok_or_else(|| de::Error::missing_field("value"))?;
88        let salt = salt.ok_or_else(|| de::Error::missing_field("salt"))?;
89        Ok(DerivedKey { value, salt })
90      }
91    }
92
93    deserializer.deserialize_struct("DerivedKey", &["value", "salt"], DerivedKeyVisitor)
94  }
95}
96
97impl DerivedKey {
98  pub fn new(passphrase: String, salt: Vec<u8>, key_length: u32) -> Self {
99    let mut output = vec![0; key_length as usize];
100    let kdf_params = KdfParams::new(256u32, 3, 1);
101    let params = Params::new(kdf_params.m_cost, kdf_params.t_cost, kdf_params.p_cost, Some(key_length as usize)).unwrap();
102    Argon2::new(Algorithm::Argon2id, Version::V0x13, params).hash_password_into(passphrase.as_bytes(), &salt, &mut output).unwrap();
103
104    Self { value: Key::from_slice(&output).clone(), salt }
105  }
106
107  pub fn get_value(&self) -> Key {
108    self.value.clone()
109  }
110
111  pub fn get_salt(&self) -> Vec<u8> {
112    self.salt.clone()
113  }
114}
115
116#[cfg(test)]
117mod derived_key_tests {
118  use super::*;
119
120  const SALT: [u8; 12] = [147, 253, 247, 2, 13, 123, 249, 26, 108, 229, 69, 61]; // For testing purposes
121
122  #[test]
123  fn test_argon2_params() {
124    let kdf_params = KdfParams::new(256u32, 3, 1);
125    let params = Params::new(kdf_params.m_cost, kdf_params.t_cost, kdf_params.p_cost, Some(32)).unwrap();
126    assert_eq!(params.m_cost(), kdf_params.m_cost);
127    assert_eq!(params.t_cost(), kdf_params.t_cost);
128    assert_eq!(params.p_cost(), kdf_params.p_cost);
129  }
130
131  #[test]
132  fn test_passphrase_to_derived_key() {
133    let salt = Vec::from(SALT);
134    let derived_key = DerivedKey::new("password".to_string(), salt.clone(), 32);
135
136    assert_eq!(derived_key.value.as_slice().len(), 32);
137    assert_eq!(derived_key.salt, salt);
138  }
139
140  #[test]
141  fn test_the_same_passphrase_should_produce_the_same_derived_key() {
142    let salt = Vec::from(SALT);
143    let derived_key1 = DerivedKey::new("password".to_string(), salt.clone(), 32);
144    let derived_key2 = DerivedKey::new("password".to_string(), salt.clone(), 32);
145    assert_eq!(derived_key1.value, derived_key2.value);
146  }
147
148  #[test]
149  fn test_the_different_passphrase_should_produce_the_different_derived_key() {
150    let salt = Vec::from(SALT);
151    let derived_key1 = DerivedKey::new("password".to_string(), salt.clone(), 32);
152    let derived_key2 = DerivedKey::new("password2".to_string(), salt.clone(), 32);
153    assert_ne!(derived_key1.value, derived_key2.value);
154  }
155
156  #[test]
157  fn test_derived_key_serialize_deserialize() {
158    let salt = Vec::from(SALT);
159    let derived_key = DerivedKey::new("password".to_string(), salt.clone(), 32);
160    let serialized = serde_json::to_string(&derived_key).unwrap();
161    println!("Serialized: {}", serialized);
162    let deserialized: DerivedKey = serde_json::from_str(&serialized).unwrap();
163    println!("Deserialized: {:?}", deserialized);
164
165    assert_eq!(derived_key.value.as_slice(), deserialized.value.as_slice());
166    assert_eq!(derived_key.salt, deserialized.salt);
167  }
168}