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::{HEADER, HEADER_02, encrypt_data};
57
58use error::Error;
59use zeroize::Zeroize;
60
61pub use argon2_rs::Argon2;
62
63const HEADER_LEN: usize = 8;
64const ENCRYPTED_INFO_START: usize = 12;
65pub const RECOMMENDED_SALT_LEN: usize = 64;
66
67pub(crate) fn extract_encrypted_info_and_data(data: &[u8]) -> Result<(Vec<u8>, Vec<u8>), Error> {
68 if &data[0..8] != HEADER_02 {
69 return Err(Error::InvalidFileFormat);
70 }
71
72 if &data[0..8] == HEADER {
73 return Err(Error::VersionMismatch);
74 }
75
76 let encrypted_info_length = u32::from_le_bytes(
77 data[HEADER_LEN..ENCRYPTED_INFO_START]
78 .try_into()
79 .map_err(|_| Error::EncryptedInfo)?,
80 );
81
82 let encrypted_info_end = ENCRYPTED_INFO_START + (encrypted_info_length as usize);
83 let encrypted_info = &data[ENCRYPTED_INFO_START..encrypted_info_end];
84 let encrypted_data = &data[encrypted_info_end..];
85 Ok((encrypted_info.to_vec(), encrypted_data.to_vec()))
86}
87
88#[derive(Default, Clone, Debug)]
89pub struct EncryptedInfo {
90 pub password_salt: Vec<u8>,
91 pub username_salt: Vec<u8>,
92 pub cipher_nonce: Vec<u8>,
93 pub argon2: Argon2,
94}
95
96impl EncryptedInfo {
97 pub fn new(
98 password_salt: Vec<u8>,
99 username_salt: Vec<u8>,
100 cipher_nonce: Vec<u8>,
101 argon2: Argon2,
102 ) -> Self {
103 Self {
104 password_salt,
105 username_salt,
106 cipher_nonce,
107 argon2,
108 }
109 }
110
111 pub fn encode(&self) -> Vec<u8> {
112 let mut data = Vec::new();
113 data.extend_from_slice(&self.password_salt);
114 data.extend_from_slice(&self.username_salt);
115 data.extend_from_slice(&self.cipher_nonce);
116 data.extend_from_slice(&self.argon2.encode());
117 data
118 }
119
120 pub fn from_encrypted_data(data: &[u8]) -> Result<Self, Error> {
121 let (encrypted_info, _) = extract_encrypted_info_and_data(data)?;
122
123 let salt_len = RECOMMENDED_SALT_LEN;
124 let password_salt = encrypted_info[0..salt_len].to_vec();
125 let username_salt = encrypted_info[salt_len..(salt_len * 2)].to_vec();
126 let cipher_nonce_start = salt_len * 2;
127 let cipher_nonce = encrypted_info[cipher_nonce_start..(cipher_nonce_start + 24)].to_vec();
128 let argon2_start = cipher_nonce_start + 24;
129 let argon2 = Argon2::decode(&encrypted_info[argon2_start..])?;
130
131 let info = EncryptedInfo {
132 password_salt,
133 username_salt,
134 cipher_nonce,
135 argon2,
136 };
137
138 Ok(info)
139 }
140}
141
142#[cfg(test)]
143mod tests {
144 use super::*;
145 use secure_types::{SecureBytes, SecureString};
146
147 #[test]
148 fn can_encrypt_decrypt() {
149 let exposed_data: Vec<u8> = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
150 let credentials = Credentials::new(
151 SecureString::from("username"),
152 SecureString::from("password"),
153 SecureString::from("password"),
154 );
155
156 let m_cost = 24_000;
157 let t_cost = 3;
158 let p_cost = 1;
159
160 let argon2 = Argon2::new(m_cost, t_cost, p_cost);
161
162 let secure_data = SecureBytes::from_vec(exposed_data.clone()).unwrap();
163
164 let encrypted_data = encrypt_data(argon2, secure_data, credentials.clone()).unwrap();
165 let decrypted_data = decrypt_data(encrypted_data, credentials).unwrap();
166
167 decrypted_data.unlock_slice(|decrypted_data| {
168 assert_eq!(exposed_data, decrypted_data);
169 });
170 }
171}