1pub mod credentials;
47pub mod decrypt;
48pub mod encrypt;
49pub mod error;
50
51pub use secure_types;
52pub use zeroize;
53
54pub use credentials::Credentials;
55pub use decrypt::decrypt_data;
56pub use encrypt::encrypt_data;
57
58use bincode::{Decode, Encode, config::standard, decode_from_slice};
59use error::{Error, map_argon2_error};
60use secure_types::{SecureBytes, SecureString};
61use zeroize::Zeroize;
62
63use argon2_sys::{ARGON2_FLAG_CLEAR_PASSWORD, argon2_context, argon2_ctx};
64
65const HEADER_LEN: usize = 8;
66const ENCRYPTED_INFO_START: usize = 12;
67pub const RECOMMENDED_SALT_LEN: usize = 64;
68pub const RECOMMENDED_HASH_LENGTH: u64 = 64;
69
70#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Default, Ord, Encode, Decode)]
72pub enum Algorithm {
73 Argon2d = 0,
78
79 Argon2i = 1,
84
85 #[default]
94 Argon2id = 2,
95}
96
97#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, PartialOrd, Ord, Encode, Decode)]
99#[repr(u32)]
100pub enum Version {
101 V0x10 = 0x10,
105
106 #[default]
110 V0x13 = 0x13,
111}
112
113pub(crate) fn extract_encrypted_info_and_data(
114 encrypted_data: &[u8],
115) -> Result<(Vec<u8>, Vec<u8>), Error> {
116 let encrypted_info_length = u32::from_le_bytes(
117 encrypted_data[HEADER_LEN..ENCRYPTED_INFO_START]
118 .try_into()
119 .map_err(|_| Error::EncryptedInfo)?,
120 );
121
122 let encrypted_info_end = ENCRYPTED_INFO_START + (encrypted_info_length as usize);
123 let encrypted_info = &encrypted_data[ENCRYPTED_INFO_START..encrypted_info_end];
124 let encrypted_data = &encrypted_data[encrypted_info_end..];
125 Ok((encrypted_info.to_vec(), encrypted_data.to_vec()))
126}
127
128#[derive(Default, Clone, Debug, Encode, Decode)]
129pub struct EncryptedInfo {
130 pub password_salt: Vec<u8>,
131 pub username_salt: Vec<u8>,
132 pub cipher_nonce: Vec<u8>,
133 pub argon2: Argon2,
134}
135
136impl EncryptedInfo {
137 pub fn new(
138 password_salt: Vec<u8>,
139 username_salt: Vec<u8>,
140 cipher_nonce: Vec<u8>,
141 argon2: Argon2,
142 ) -> Self {
143 Self {
144 password_salt,
145 username_salt,
146 cipher_nonce,
147 argon2,
148 }
149 }
150
151 pub fn from_encrypted_data(data: &[u8]) -> Result<Self, Error> {
152 let (encrypted_info, _) = extract_encrypted_info_and_data(data)?;
153
154 let info: (EncryptedInfo, usize) = decode_from_slice(&encrypted_info, standard())
155 .map_err(|e| Error::DecodingFailed(e.to_string()))?;
156
157 Ok(info.0)
158 }
159}
160
161#[derive(Default, Clone, Debug, Encode, Decode)]
163pub struct Argon2 {
164 pub m_cost: u32,
165 pub t_cost: u32,
166 pub p_cost: u32,
167 pub hash_length: u64,
168 pub algorithm: Algorithm,
170 pub version: Version,
172}
173
174impl Argon2 {
175 pub fn new(m_cost: u32, t_cost: u32, p_cost: u32) -> Self {
176 Self {
177 m_cost,
178 t_cost,
179 p_cost,
180 hash_length: RECOMMENDED_HASH_LENGTH,
181 ..Default::default()
182 }
183 }
184
185 pub fn with_algorithm(mut self, algorithm: Algorithm) -> Self {
186 self.algorithm = algorithm;
187 self
188 }
189
190 pub fn with_version(mut self, version: Version) -> Self {
191 self.version = version;
192 self
193 }
194
195 pub fn with_hash_length(mut self, hash_length: u64) -> Self {
196 self.hash_length = hash_length;
197 self
198 }
199
200 pub fn hash_password(
213 &self,
214 password: &SecureString,
215 mut salt: Vec<u8>,
216 ) -> Result<SecureBytes, Error> {
217 let mut hash_buffer = vec![0u8; self.hash_length as usize];
218
219 let code = password.str_scope(|password_str| {
220 let mut context = argon2_context {
221 out: hash_buffer.as_mut_ptr(),
222 outlen: self.hash_length as u32,
223 pwd: password_str.as_bytes().as_ptr() as *mut u8,
224 pwdlen: password_str.len() as u32,
225 salt: salt.as_mut_ptr(),
226 saltlen: salt.len() as u32,
227 secret: std::ptr::null_mut(),
228 secretlen: 0,
229 ad: std::ptr::null_mut(),
230 adlen: 0,
231 t_cost: self.t_cost,
232 m_cost: self.m_cost,
233 lanes: self.p_cost,
234 threads: self.p_cost,
235 version: self.version as u32,
236 allocate_cbk: None,
237 free_cbk: None,
238 flags: ARGON2_FLAG_CLEAR_PASSWORD,
239 };
240
241 unsafe {
242 argon2_ctx(&mut context, self.algorithm as u32)
243 }
244 });
245
246 salt.zeroize();
247
248 if code != 0 {
249 return Err(Error::Argon2(map_argon2_error(code)));
250 }
251
252 let secured_buffer =
253 SecureBytes::from_vec(hash_buffer).map_err(|e| Error::Custom(e.to_string()))?;
254 Ok(secured_buffer)
255 }
256}
257
258impl Argon2 {
260 pub fn very_fast() -> Self {
262 Self {
263 m_cost: 128_000,
264 t_cost: 16,
265 p_cost: 4,
266 hash_length: RECOMMENDED_HASH_LENGTH,
267 ..Default::default()
268 }
269 }
270
271 pub fn fast() -> Self {
273 Self {
274 m_cost: 256_000,
275 t_cost: 8,
276 p_cost: 4,
277 hash_length: RECOMMENDED_HASH_LENGTH,
278 ..Default::default()
279 }
280 }
281
282 pub fn balanced() -> Self {
283 Self {
284 m_cost: 1024_000,
285 t_cost: 16,
286 p_cost: 4,
287 hash_length: RECOMMENDED_HASH_LENGTH,
288 ..Default::default()
289 }
290 }
291
292 pub fn slow() -> Self {
293 Self {
294 m_cost: 2048_000,
295 t_cost: 16,
296 p_cost: 4,
297 hash_length: RECOMMENDED_HASH_LENGTH,
298 ..Default::default()
299 }
300 }
301
302 pub fn very_slow() -> Self {
303 Self {
304 m_cost: 3072_000,
305 t_cost: 24,
306 p_cost: 4,
307 hash_length: RECOMMENDED_HASH_LENGTH,
308 ..Default::default()
309 }
310 }
311}
312
313#[cfg(test)]
314mod tests {
315 use super::*;
316 use secure_types::{SecureBytes, SecureString};
317
318 #[test]
319 fn can_encrypt_decrypt() {
320 let exposed_data: Vec<u8> = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
321 let credentials = Credentials::new(
322 SecureString::from("username"),
323 SecureString::from("password"),
324 SecureString::from("password"),
325 );
326
327 let m_cost = 24_000;
328 let t_cost = 3;
329 let p_cost = 1;
330
331 let argon2 = Argon2::new(m_cost, t_cost, p_cost);
332
333 let secure_data = SecureBytes::from_vec(exposed_data.clone()).unwrap();
334
335 let encrypted_data = encrypt_data(argon2, secure_data, credentials.clone()).unwrap();
336 let decrypted_data = decrypt_data(encrypted_data, credentials).unwrap();
337
338 decrypted_data.slice_scope(|decrypted_data| {
339 assert_eq!(exposed_data, decrypted_data);
340 });
341 }
342}