otplus_core/cipher/
derived_key.rs1use 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]; #[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}