1use anyhow::Result;
14use rand::{prelude::StdRng, Rng, SeedableRng};
15use zeroize::Zeroize;
16
17use crate::cipher::Ciphers;
18use crate::header::{Header, HeaderVersion};
19use crate::primitives::{MASTER_KEY_LEN, SALT_LEN};
20use crate::protected::Protected;
21
22pub fn argon2id_hash(
42 raw_key: Protected<Vec<u8>>,
43 salt: &[u8; SALT_LEN],
44 version: &HeaderVersion,
45) -> Result<Protected<[u8; 32]>> {
46 use argon2::Argon2;
47 use argon2::Params;
48
49 let params = match version {
50 HeaderVersion::V1 => {
51 Params::new(8192, 8, 4, Some(Params::DEFAULT_OUTPUT_LEN))
53 .map_err(|_| anyhow::anyhow!("Error initialising argon2id parameters"))?
54 }
55 HeaderVersion::V2 => {
56 Params::new(262_144, 8, 4, Some(Params::DEFAULT_OUTPUT_LEN))
58 .map_err(|_| anyhow::anyhow!("Error initialising argon2id parameters"))?
59 }
60 HeaderVersion::V3 => {
61 Params::new(262_144, 10, 4, Some(Params::DEFAULT_OUTPUT_LEN))
63 .map_err(|_| anyhow::anyhow!("Error initialising argon2id parameters"))?
64 }
65 HeaderVersion::V4 | HeaderVersion::V5 => {
66 return Err(anyhow::anyhow!(
67 "argon2id is not supported on header versions above V3."
68 ))
69 }
70 };
71
72 let mut key = [0u8; 32];
73 let argon2 = Argon2::new(argon2::Algorithm::Argon2id, argon2::Version::V0x13, params);
74 let result = argon2.hash_password_into(raw_key.expose(), salt, &mut key);
75 drop(raw_key);
76
77 if result.is_err() {
78 return Err(anyhow::anyhow!("Error while hashing your key"));
79 }
80
81 Ok(Protected::new(key))
82}
83
84pub fn balloon_hash(
106 raw_key: Protected<Vec<u8>>,
107 salt: &[u8; SALT_LEN],
108 version: &HeaderVersion,
109) -> Result<Protected<[u8; 32]>> {
110 use balloon_hash::Balloon;
111
112 let params = match version {
113 HeaderVersion::V1 | HeaderVersion::V2 | HeaderVersion::V3 => {
114 return Err(anyhow::anyhow!(
115 "Balloon hashing is not supported in header versions below V4."
116 ));
117 }
118 HeaderVersion::V4 => balloon_hash::Params::new(262_144, 1, 1)
119 .map_err(|_| anyhow::anyhow!("Error initialising balloon hashing parameters"))?,
120 HeaderVersion::V5 => balloon_hash::Params::new(278_528, 1, 1)
121 .map_err(|_| anyhow::anyhow!("Error initialising balloon hashing parameters"))?,
122 };
123
124 let mut key = [0u8; 32];
125 let balloon = Balloon::<blake3::Hasher>::new(balloon_hash::Algorithm::Balloon, params, None);
126 let result = balloon.hash_into(raw_key.expose(), salt, &mut key);
127 drop(raw_key);
128
129 if result.is_err() {
130 return Err(anyhow::anyhow!("Error while hashing your key"));
131 }
132
133 Ok(Protected::new(key))
134}
135
136
137#[allow(clippy::module_name_repetitions)]
145pub fn decrypt_master_key(
146 raw_key: Protected<Vec<u8>>,
147 header: &Header,
148 ) -> Result<Protected<[u8; MASTER_KEY_LEN]>> {
150 match header.header_type.version {
151 HeaderVersion::V1 | HeaderVersion::V2 | HeaderVersion::V3 => {
152 argon2id_hash(raw_key, &header.salt.ok_or_else(|| anyhow::anyhow!("Missing salt within the header!"))?, &header.header_type.version)
153 }
154 HeaderVersion::V4 => {
155 let keyslots = header.keyslots.as_ref().ok_or_else(|| anyhow::anyhow!("Unable to find a keyslot!"))?;
156 let keyslot = keyslots.first().ok_or_else(|| anyhow::anyhow!("Unable to find a match with the key you provided (maybe you supplied the wrong key?)"))?;
157 let key = keyslot.hash_algorithm.hash(raw_key, &keyslot.salt)?;
158
159 let cipher = Ciphers::initialize(key, &header.header_type.algorithm)?;
160 cipher
161 .decrypt(&keyslot.nonce, keyslot.encrypted_key.as_slice())
162 .map(vec_to_arr)
163 .map(Protected::new)
164 .map_err(|_| anyhow::anyhow!("Cannot decrypt master key"))
165 }
166 HeaderVersion::V5 => {
167 header
168 .keyslots
169 .as_ref()
170 .ok_or_else(|| anyhow::anyhow!("Unable to find a keyslot!"))?
171 .iter()
172 .find_map(|keyslot| {
173 let key = keyslot.hash_algorithm.hash(raw_key.clone(), &keyslot.salt).ok()?;
174
175 let cipher = Ciphers::initialize(key, &header.header_type.algorithm).ok()?;
176 cipher
177 .decrypt(&keyslot.nonce, keyslot.encrypted_key.as_slice())
178 .map(vec_to_arr)
179 .map(Protected::new)
180 .ok()
181 })
182 .ok_or_else(|| anyhow::anyhow!("Unable to find a match with the key you provided (maybe you supplied the wrong key?)"))
183 }
184 }
185}
186
187#[must_use]
190pub fn vec_to_arr<const N: usize>(mut master_key_vec: Vec<u8>) -> [u8; N] {
191 let mut master_key = [0u8; N];
192 let len = N.min(master_key_vec.len());
193 master_key[..len].copy_from_slice(&master_key_vec[..len]);
194 master_key_vec.zeroize();
195 master_key
196}
197
198#[must_use]
206pub fn generate_passphrase() -> Protected<String> {
207 let collection = include_str!("wordlist.lst");
208 let words = collection.lines().collect::<Vec<_>>();
209
210 let mut passphrase = String::new();
211
212 for _ in 0..3 {
213 let index = StdRng::from_entropy().gen_range(0..=words.len());
214 let word = words[index];
215 let capitalized_word = word
216 .char_indices()
217 .map(|(i, ch)| match i {
218 0 => ch.to_ascii_uppercase(),
219 _ => ch,
220 })
221 .collect::<String>();
222 passphrase.push_str(&capitalized_word);
223 passphrase.push('-');
224 }
225
226 for _ in 0..6 {
227 let number: i64 = StdRng::from_entropy().gen_range(0..=9);
228 passphrase.push_str(&number.to_string());
229 }
230
231 Protected::new(passphrase)
232}