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