crypto_ext/symmetric/encryption/
mod.rs

1use aes_gcm::aead::{generic_array::GenericArray, Aead, KeyInit, Payload};
2use aes_gcm::Aes128Gcm;
3use crate::{get_path_relative_to_working_directory, get_static_filepath, read_file, read_or_create_and_write};
4use crate::passphrase::generate_passphrase;
5
6#[cfg(test)]
7mod tests;
8
9/// EncryptionParameters is basically the key and nonce
10pub struct EncryptionParameters {
11    pub key: String,
12    pub nonce: String,
13}
14
15/// DecryptionParameters is basically the key and nonce
16pub struct DecryptionParameters {
17    pub key: String,
18    pub nonce: String,
19}
20
21/// Will read or create EncryptionParameters and DecryptionParameters at the given location which is relative to the working directory
22pub fn setup(path_to_encryption_parameters: Option<&str>) -> Result<(EncryptionParameters, DecryptionParameters), String> {
23    let passphrase_64_bytes = generate_passphrase().unwrap();
24    // key is 16 bytes long
25    let aes_key = passphrase_64_bytes[48..64].to_string();
26    // nonce is 12 bytes long
27    let aes_nonce = passphrase_64_bytes[36..48].to_string();
28
29
30    let relative_path = get_path_relative_to_working_directory(path_to_encryption_parameters, ".aes_key");
31    let boxed_aes_key_path = get_static_filepath(relative_path.as_str());
32    if boxed_aes_key_path.is_err() {
33        return Err(boxed_aes_key_path.err().unwrap());
34    }
35    let aes_key_path = boxed_aes_key_path.unwrap();
36
37    let boxed_aes_key = read_or_create_and_write(aes_key_path.as_str(), aes_key.as_bytes());
38    if boxed_aes_key.is_err() {
39        let message = boxed_aes_key.err().unwrap();
40        return Err(message)
41    }
42
43    let boxed_aes_key = String::from_utf8(boxed_aes_key.unwrap());
44    let aes_key = boxed_aes_key.unwrap();
45
46
47
48
49    let relative_path = get_path_relative_to_working_directory(path_to_encryption_parameters, ".aes_nonce");
50    let boxed_aes_nonce_path = get_static_filepath(relative_path.as_str());
51    if boxed_aes_nonce_path.is_err() {
52        return Err(boxed_aes_nonce_path.err().unwrap());
53    }
54    let aes_nonce_path = boxed_aes_nonce_path.unwrap();
55
56    let boxed_aes_nonce = read_or_create_and_write(aes_nonce_path.as_str(), aes_nonce.as_bytes());
57    if boxed_aes_nonce.is_err() {
58        let message = boxed_aes_nonce.err().unwrap();
59        return Err(message)
60    }
61
62    let boxed_aes_nonce = String::from_utf8(boxed_aes_nonce.unwrap());
63    let aes_nonce = boxed_aes_nonce.unwrap();
64
65    let encryption_params = EncryptionParameters { key: aes_key.to_string(), nonce: aes_nonce.to_string() };
66    let decryption_params = DecryptionParameters { key: aes_key.to_string(), nonce: aes_nonce.to_string() };
67
68    Ok((encryption_params, decryption_params))
69}
70
71/// Returns EncryptionParameters stored at the given location which is relative to the working directory
72pub fn get_encryption_params(path_to_encryption_parameters: Option<&str>) -> Result<EncryptionParameters, String> {
73    let relative_path = get_path_relative_to_working_directory(path_to_encryption_parameters, ".aes_key");
74    let boxed_public_key_path = get_static_filepath(relative_path.as_str());
75    if boxed_public_key_path.is_err() {
76        return Err(boxed_public_key_path.err().unwrap());
77    }
78    let public_key_path = boxed_public_key_path.unwrap();
79
80
81    let boxed_public_key = read_file(public_key_path.as_str());
82    if boxed_public_key.is_err() {
83        let message = boxed_public_key.err().unwrap();
84        return Err(message)
85    }
86    let boxed_public_key = String::from_utf8(boxed_public_key.unwrap());
87    let aes_key = boxed_public_key.unwrap();
88
89
90    let relative_path = get_path_relative_to_working_directory(path_to_encryption_parameters, ".aes_nonce");
91    let boxed_aes_nonce_path = get_static_filepath(relative_path.as_str());
92    if boxed_aes_nonce_path.is_err() {
93        return Err(boxed_aes_nonce_path.err().unwrap());
94    }
95    let aes_nonce_path = boxed_aes_nonce_path.unwrap();
96
97
98    let boxed_aes_nonce = read_file(aes_nonce_path.as_str());
99    if boxed_aes_nonce.is_err() {
100        let message = boxed_aes_nonce.err().unwrap();
101        return Err(message)
102    }
103    let boxed_aes_nonce = String::from_utf8(boxed_aes_nonce.unwrap());
104    let aes_nonce = boxed_aes_nonce.unwrap();
105
106    let encryption_params = EncryptionParameters {
107        key: aes_key.to_string(),
108        nonce: aes_nonce.to_string(),
109    };
110
111    Ok(encryption_params)
112}
113
114/// Returns DecryptionParameters stored at the given location which is relative to the working directory
115pub fn get_decryption_params(path_to_encryption_parameters: Option<&str>) -> Result<DecryptionParameters, String> {
116    // in symmetric encryption same key and nonce used for encryption and decryption
117    let boxed_encryption_params = get_encryption_params(path_to_encryption_parameters);
118    if boxed_encryption_params.is_err() {
119        let message = boxed_encryption_params.err().unwrap().to_string();
120        return Err(message)
121    }
122    let encryption_params = boxed_encryption_params.unwrap();
123    let decryption_params = DecryptionParameters {
124        key: encryption_params.key.to_string(),
125        nonce: encryption_params.nonce.to_string()
126    };
127    Ok(decryption_params)
128}
129
130/// Encrypts given byte array
131///
132/// # Examples
133///
134/// ```
135///     use crypto_ext::symmetric::encryption::{decrypt, encrypt, get_decryption_params, get_encryption_params, setup};
136///
137///     #[test]
138///     fn encryption() {
139///         let data = "some data to encrypt".as_bytes();
140///         let associated_data = "some unencrypted data that needs to be sent along the encrypted data and won't be changed during transmission by a hacker".as_bytes();
141///
142///         // path needs to be accessible by user with write permission for initial setup
143///         // ideally for each encryption you need to setup unique folder for AES key and nonce
144///         // do not reuse same setup with Encryption and Decryption parameters for multiple encryptions
145///         let params_path = "/test/encryption_parameters/";
146///
147///
148///         let (encryption_params, decryption_params) = setup(Some(params_path)).unwrap();
149///
150///         let encrypted = encrypt(encryption_params, data, associated_data).unwrap();
151///
152///         let decrypted = decrypt(decryption_params, encrypted.as_slice(), associated_data).unwrap();
153///
154///         assert_eq!(data, decrypted);
155///
156///     }
157///
158/// ```
159pub fn encrypt(params: EncryptionParameters, data_to_encrypt: &[u8], associated_data: &[u8]) -> Result<Vec<u8>, String> {
160    let payload = Payload {
161        msg: data_to_encrypt,
162        aad: associated_data,
163    };
164
165    let key = GenericArray::from_slice(params.key.as_bytes());
166    let nonce = GenericArray::from_slice(params.nonce.as_bytes());
167
168    let cipher = Aes128Gcm::new(key);
169    let boxed_cipher_text = cipher.encrypt(nonce, payload);
170    if boxed_cipher_text.is_err() {
171        let message = boxed_cipher_text.err().unwrap().to_string();
172        return Err(message)
173    }
174
175    let cipher_text = boxed_cipher_text.unwrap();
176
177    Ok(cipher_text)
178}
179
180
181/// Decrypts given byte array
182///
183/// # Examples
184///
185/// ```
186///     use crypto_ext::symmetric::encryption::{decrypt, encrypt, get_decryption_params, get_encryption_params, setup};
187///
188///     #[test]
189///     fn decryption() {
190///         // to decrypt we need to encrypt first
191///         let data = "some data to encrypt".as_bytes();
192///         let associated_data = "some unencrypted data that needs to be sent along the encrypted data and won't be changed during transmission by a hacker".as_bytes();
193///
194///         // path needs to be accessible by user with write permission for initial setup
195///         // ideally for each encryption you need to setup unique folder for AES key and nonce
196///         // do not reuse same setup with Encryption and Decryption parameters for multiple encryptions
197///         let params_path = "/test/encryption_parameters/";
198///
199///
200///         let _ = setup(Some(params_path)).unwrap();
201///
202///         let encryption_params = get_encryption_params(Some(params_path)).unwrap();
203///         let encrypted = encrypt(encryption_params, data, associated_data).unwrap();
204///
205///         let decryption_params = get_decryption_params(Some(params_path)).unwrap();
206///         let decrypted = decrypt(decryption_params, encrypted.as_slice(), associated_data).unwrap();
207///
208///         assert_eq!(data, decrypted);
209///
210///     }
211/// ```
212pub fn decrypt(params: DecryptionParameters, encrypted_data: &[u8], associated_data: &[u8]) -> Result<Vec<u8>, String> {
213    let payload = Payload {
214        msg: encrypted_data,
215        aad: associated_data,
216    };
217
218    let key = GenericArray::from_slice(params.key.as_bytes());
219    let nonce = GenericArray::from_slice(params.nonce.as_bytes());
220
221    let cipher = Aes128Gcm::new(key);
222    let boxed_decrypted_data = cipher.decrypt(nonce, payload);
223    if boxed_decrypted_data.is_err() {
224        let message = boxed_decrypted_data.err().unwrap().to_string();
225        return Err(message)
226    }
227
228    let decrypted_data = boxed_decrypted_data.unwrap();
229
230    Ok(decrypted_data)
231}