pub mod credentials;
pub mod decrypt;
pub mod encrypt;
pub mod error;
pub use secure_types;
pub use zeroize;
pub use credentials::Credentials;
pub use decrypt::decrypt_data;
pub use encrypt::{HEADER, HEADER_02, encrypt_data};
use error::Error;
use zeroize::Zeroize;
pub use argon2_rs::Argon2;
const HEADER_LEN: usize = 8;
const ENCRYPTED_INFO_START: usize = 12;
pub const RECOMMENDED_SALT_LEN: usize = 64;
pub(crate) fn extract_encrypted_info_and_data(data: &[u8]) -> Result<(Vec<u8>, Vec<u8>), Error> {
if &data[0..8] != HEADER_02 {
return Err(Error::InvalidFileFormat);
}
if &data[0..8] == HEADER {
return Err(Error::VersionMismatch);
}
let encrypted_info_length = u32::from_le_bytes(
data[HEADER_LEN..ENCRYPTED_INFO_START]
.try_into()
.map_err(|_| Error::EncryptedInfo)?,
);
let encrypted_info_end = ENCRYPTED_INFO_START + (encrypted_info_length as usize);
let encrypted_info = &data[ENCRYPTED_INFO_START..encrypted_info_end];
let encrypted_data = &data[encrypted_info_end..];
Ok((encrypted_info.to_vec(), encrypted_data.to_vec()))
}
#[derive(Default, Clone, Debug)]
pub struct EncryptedInfo {
pub password_salt: Vec<u8>,
pub username_salt: Vec<u8>,
pub cipher_nonce: Vec<u8>,
pub argon2: Argon2,
}
impl EncryptedInfo {
pub fn new(
password_salt: Vec<u8>,
username_salt: Vec<u8>,
cipher_nonce: Vec<u8>,
argon2: Argon2,
) -> Self {
Self {
password_salt,
username_salt,
cipher_nonce,
argon2,
}
}
pub fn encode(&self) -> Vec<u8> {
let mut data = Vec::new();
data.extend_from_slice(&self.password_salt);
data.extend_from_slice(&self.username_salt);
data.extend_from_slice(&self.cipher_nonce);
data.extend_from_slice(&self.argon2.encode());
data
}
pub fn from_encrypted_data(data: &[u8]) -> Result<Self, Error> {
let (encrypted_info, _) = extract_encrypted_info_and_data(data)?;
let salt_len = RECOMMENDED_SALT_LEN;
let password_salt = encrypted_info[0..salt_len].to_vec();
let username_salt = encrypted_info[salt_len..(salt_len * 2)].to_vec();
let cipher_nonce_start = salt_len * 2;
let cipher_nonce = encrypted_info[cipher_nonce_start..(cipher_nonce_start + 24)].to_vec();
let argon2_start = cipher_nonce_start + 24;
let argon2 = Argon2::decode(&encrypted_info[argon2_start..])?;
let info = EncryptedInfo {
password_salt,
username_salt,
cipher_nonce,
argon2,
};
Ok(info)
}
}
#[cfg(test)]
mod tests {
use super::*;
use secure_types::{SecureBytes, SecureString};
#[test]
fn can_encrypt_decrypt() {
let exposed_data: Vec<u8> = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let credentials = Credentials::new(
SecureString::from("username"),
SecureString::from("password"),
SecureString::from("password"),
);
let m_cost = 24_000;
let t_cost = 3;
let p_cost = 1;
let argon2 = Argon2::new(m_cost, t_cost, p_cost);
let secure_data = SecureBytes::from_vec(exposed_data.clone()).unwrap();
let encrypted_data = encrypt_data(argon2, secure_data, credentials.clone()).unwrap();
let decrypted_data = decrypt_data(encrypted_data, credentials).unwrap();
decrypted_data.unlock_slice(|decrypted_data| {
assert_eq!(exposed_data, decrypted_data);
});
}
}