1use super::{hash::Digest, password::PasswordSalt};
4use chacha20::{
5 cipher::{KeyIvInit, StreamCipher},
6 ChaCha20,
7};
8use generic_array::{sequence::GenericSequence, typenum::Unsigned};
9use hex;
10use ouisync_macros::api;
11use rand::{rngs::OsRng, CryptoRng, Rng};
12use serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer};
13use std::{fmt, sync::Arc};
14use subtle::ConstantTimeEq;
15use thiserror::Error;
16use zeroize::{Zeroize, Zeroizing};
17
18pub(crate) type Nonce = [u8; NONCE_SIZE];
20pub(crate) const NONCE_SIZE: usize =
21 <<chacha20::Nonce as GenericSequence<_>>::Length as Unsigned>::USIZE;
22
23#[derive(Clone)]
33#[api(repr(Bytes), secret)]
34pub struct SecretKey(Arc<Zeroizing<[u8; Self::SIZE]>>);
35
36impl SecretKey {
37 pub const SIZE: usize = <<chacha20::Key as GenericSequence<_>>::Length as Unsigned>::USIZE;
39
40 pub fn parse_hex(hex_str: &str) -> Result<Self, hex::FromHexError> {
42 let mut bytes = [0; Self::SIZE];
43 hex::decode_to_slice(hex_str, &mut bytes)?;
44
45 let mut key = Self::zero();
46 key.as_mut().copy_from_slice(&bytes);
47
48 bytes.zeroize();
49
50 Ok(key)
51 }
52
53 pub fn generate<R: Rng + CryptoRng + ?Sized>(rng: &mut R) -> Self {
59 let mut key = Self::zero();
62 rng.fill(key.as_mut());
63 key
64 }
65
66 pub fn random() -> Self {
68 Self::generate(&mut OsRng)
69 }
70
71 pub fn derive_from_key(master_key: &[u8; Self::SIZE], nonce: &[u8]) -> Self {
73 let mut sub_key = Self::zero();
74
75 let mut hasher = blake3::Hasher::new_keyed(master_key);
76 hasher.update(nonce);
77 hasher.finalize_into(sub_key.as_mut().into());
78
79 sub_key
80 }
81
82 pub fn derive_from_password(user_password: &str, salt: &PasswordSalt) -> Self {
84 use argon2::{Algorithm, Argon2, ParamsBuilder, Version};
85
86 let mut result = Self::zero();
87
88 Argon2::new(
89 Algorithm::default(),
90 Version::default(),
91 ParamsBuilder::new().m_cost(4096).t_cost(3).build().unwrap(),
94 )
95 .hash_password_into(user_password.as_ref(), salt.as_ref(), result.as_mut())
96 .expect("failed to hash password");
100
101 result
102 }
103
104 pub(crate) fn encrypt_no_aead(&self, nonce: &Nonce, buffer: &mut [u8]) {
109 let mut cipher = ChaCha20::new(self.as_ref().into(), nonce.into());
110 cipher.apply_keystream(buffer)
111 }
112
113 pub(crate) fn decrypt_no_aead(&self, nonce: &Nonce, buffer: &mut [u8]) {
115 let mut cipher = ChaCha20::new(self.as_ref().into(), nonce.into());
116 cipher.apply_keystream(buffer)
117 }
118
119 pub fn as_array(&self) -> &[u8; Self::SIZE] {
122 &self.0
123 }
124
125 fn zero() -> Self {
127 Self(Arc::new(Zeroizing::new([0; Self::SIZE])))
128 }
129
130 fn as_mut(&mut self) -> &mut [u8] {
132 &mut **Arc::get_mut(&mut self.0).unwrap()
133 }
134}
135
136impl TryFrom<&[u8]> for SecretKey {
137 type Error = SecretKeyLengthError;
138
139 fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
140 if slice.len() >= Self::SIZE {
141 let mut key = Self::zero();
142 key.as_mut().copy_from_slice(slice);
143 Ok(key)
144 } else {
145 Err(SecretKeyLengthError)
146 }
147 }
148}
149
150impl AsRef<[u8]> for SecretKey {
153 fn as_ref(&self) -> &[u8] {
154 &self.0[..]
155 }
156}
157
158impl PartialEq for SecretKey {
161 fn eq(&self, other: &Self) -> bool {
162 self.as_array().ct_eq(other.as_array()).into()
163 }
164}
165
166impl Eq for SecretKey {}
167
168impl fmt::Debug for SecretKey {
169 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
170 write!(f, "****")
171 }
172}
173
174impl Serialize for SecretKey {
175 fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
176 where
177 S: Serializer,
178 {
179 serde_bytes::Bytes::new(self.as_ref()).serialize(s)
180 }
181}
182
183impl<'de> Deserialize<'de> for SecretKey {
184 fn deserialize<D>(d: D) -> Result<Self, D::Error>
185 where
186 D: Deserializer<'de>,
187 {
188 let bytes: &serde_bytes::Bytes = Deserialize::deserialize(d)?;
189
190 if bytes.len() != Self::SIZE {
191 return Err(D::Error::invalid_length(
192 bytes.len(),
193 &format!("{}", Self::SIZE).as_str(),
194 ));
195 }
196
197 let mut key = Self::zero();
198 key.as_mut().copy_from_slice(bytes);
199
200 Ok(key)
201 }
202}
203
204#[derive(Debug, Error)]
205#[error("invalid secret key length")]
206pub struct SecretKeyLengthError;
207
208#[cfg(test)]
209mod tests {
210
211 use super::*;
212
213 #[test]
214 fn serialize_deserialize_bincode() {
215 let orig = SecretKey::try_from(&b"abcdefghijklmnopqrstuvwxyz012345"[..]).unwrap();
216 let expected_serialized_hex =
217 "20000000000000006162636465666768696a6b6c6d6e6f707172737475767778797a303132333435";
218
219 let serialized = bincode::serialize(&orig).unwrap();
220 assert_eq!(hex::encode(&serialized), expected_serialized_hex);
221
222 let deserialized: SecretKey = bincode::deserialize(&serialized).unwrap();
223 assert_eq!(deserialized.as_ref(), orig.as_ref());
224 }
225
226 #[test]
227 fn serialize_deserialize_msgpack() {
228 let orig = SecretKey::try_from(&b"abcdefghijklmnopqrstuvwxyz012345"[..]).unwrap();
229 let expected_serialized_hex =
230 "c4206162636465666768696a6b6c6d6e6f707172737475767778797a303132333435";
231
232 let serialized = rmp_serde::to_vec(&orig).unwrap();
233 assert_eq!(hex::encode(&serialized), expected_serialized_hex);
234
235 let deserialized: SecretKey = rmp_serde::from_slice(&serialized).unwrap();
236 assert_eq!(deserialized.as_ref(), orig.as_ref());
237 }
238
239 #[test]
240 fn derive_from_password_snapshot() {
241 let test_vectors = [
242 (
243 "jxzBql3QHxENyynvh2SICH9ND",
244 "a2849a63283cbaf0fdbceb1f6479b197",
245 "c7ffa7d05f0898d71839cbd62a00a9616904d795c0372704c22bf76c363b371c",
246 ),
247 (
248 "4oveW4y3uE7KNbT6Yr",
249 "9410973ae328ad92916268128edb4710",
250 "5796c8ff977611fa48009fa690cd4091928c65a5c8587babcd06bdb76f8a4793",
251 ),
252 (
253 "3wQ3KFPW5L4hnaQZQeq",
254 "7d49d2b38763a12b2bbdfa93275aff18",
255 "b358fb33b5aacda99d59cd6b9c1c8e7e2162bea31addc22361220f05781e9736",
256 ),
257 (
258 "DL3NuUTWnjMocBkFMUuP",
259 "8c89c7108fff2095e18ddfef8986b118",
260 "8ebf12a4306b97b4978cac088e80b76a2cf31ae19b3c9c746a53c6d4101ee3be",
261 ),
262 (
263 "9wD4BW85Ji8GjS2XvxC2dLVgpMZGeS",
264 "b2a44461cc0bebb325280ed9130a59bb",
265 "f2847b060d5ffdcd8a1baf0264b12fc7f67e5ea61350defe612f7e9d1a1b29d3",
266 ),
267 (
268 "HFaiwNNJuSULjQlWRSo",
269 "46ece5c682cd598a65eabff63a3572df",
270 "05b17ea51240753a7d7fc707830316017dbf108dda822752ed0a31dd02655ac3",
271 ),
272 (
273 "pA4Zlnc7NQYej1nbQYWERDh1fH",
274 "2e0151573fe9c69df29b830987990985",
275 "186451be15369704049a1237c80306e0a0d6638a97a1e737b59043cc8578d374",
276 ),
277 (
278 "XPrtoJzqdo03fArXYvLCbqxTckLhi",
279 "2b852c5409d6c6813c49d1379cbbc1e9",
280 "77a08ef20a56b62a89c8b66ea6a9a489b863b3fcf06d5a9edf663729ee0ea2cd",
281 ),
282 (
283 "oWeBi4ghF",
284 "277c27b1587751f2af2001be3712ef0d",
285 "16e2b5b01852443672da19b47bacd31e3fb3cda972a9f52440a54ad811f7bcb8",
286 ),
287 (
288 "9FvIzpn7tV0l7lADIsQDbAZ",
289 "f864670399430d1671c31a2431183625",
290 "20ce1c73cfed98a27b8d2b63cb5a06709a92d471188c7398e521c83ea51cb766",
291 ),
292 (
293 "uEqxiIP533EbTK8MK5tEozAsn1nS",
294 "69b52967216f8f3ff5a1fa73e5046315",
295 "40e51fb3f0ecaf38abdcb018bdeb0935a484d9d365a0ce47ce277e72d68ea4ea",
296 ),
297 (
298 "wMybpBIOzN5P",
299 "bca1f48e60bad68798a828d3efd5258a",
300 "833acd5273e323cff011aefd83ff64ac33d72d30d3cdd1b01a03dc16a4a6b1c6",
301 ),
302 (
303 "YQ3Z8mGqp97XGJRV1LT",
304 "b469f80e382214b5f157d1c7d36a2058",
305 "8d1b82de3ed96198378a9a8f9fcffa5203f94e2c344092be4ecc89ccfa7f0173",
306 ),
307 (
308 "otSkYO0uFASqZ",
309 "10b6a29fa50416e276a0e79cbe66534e",
310 "4d3226dc093cbbfa6247c26f317e784f9f3f975fe05b9abb84fd58996e603534",
311 ),
312 (
313 "jWwusg8vdvIQCeC2y9",
314 "956071ee6e80c856f20744a8e5d6ca27",
315 "00eec6c9a868bae430f1f4588b7f2357bf5114fcbb65beb2e65e9e4202c7f75e",
316 ),
317 (
318 "NBNh4UYsHlc",
319 "f61877af4e7f8313ad8234302950b331",
320 "5a5685bf7a635ea8d86967251ebbe029f2782d36f5e0296a9775076002ab34d7",
321 ),
322 ];
323
324 for (password, salt, expected_secret_key) in test_vectors {
325 let salt: [u8; PasswordSalt::SIZE] = hex::decode(salt).unwrap().try_into().unwrap();
326 let salt = PasswordSalt::from(salt);
327
328 let actual_secret_key = SecretKey::derive_from_password(password, &salt);
329 let actual_secret_key = hex::encode(actual_secret_key.as_array());
330
331 assert_eq!(actual_secret_key, expected_secret_key);
332 }
333 }
334}