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