crypto_ext/asymmetric/encryption/
mod.rs

1use openssl::rsa::Padding;
2use openssl::rsa::Rsa;
3use openssl::symm::Cipher;
4use crate::{get_path_relative_to_working_directory, get_static_filepath, read_file, read_or_create_and_write};
5use crate::passphrase::generate_passphrase;
6
7#[cfg(test)]
8mod tests;
9
10const RSA_SIZE: u32 = 4096;
11
12/// EncryptionParameters is basically the RSA .pem encoded public key
13///
14pub struct EncryptionParameters {
15    pub rsa_public_key_pem: String,
16}
17
18/// DecryptionParameters is basically the RSA .pem encoded private key and passphrase
19///
20pub struct DecryptionParameters {
21    pub rsa_passphrase: String,
22    pub rsa_private_key_pem: String,
23}
24
25/// Will read or create EncryptionParameters and DecryptionParameters at the given location which is relative to the working directory
26///
27pub fn setup(path_to_encryption_parameters: Option<&str>) -> Result<(EncryptionParameters, DecryptionParameters), String> {
28    let relative_path = get_path_relative_to_working_directory(path_to_encryption_parameters, ".rsa_passphrase");
29    let boxed_passphrase_path = get_static_filepath(relative_path.as_str());
30    if boxed_passphrase_path.is_err() {
31        return Err(boxed_passphrase_path.err().unwrap());
32    }
33    let passphrase_path = boxed_passphrase_path.unwrap();
34
35
36    let boxed_passphrase = get_or_create_passphrase(passphrase_path.as_str());
37    if boxed_passphrase.is_err() {
38        return Err(boxed_passphrase.err().unwrap());
39    }
40    let passphrase = boxed_passphrase.unwrap();
41
42
43    let relative_path = get_path_relative_to_working_directory(path_to_encryption_parameters, ".rsa_public_key");
44    let boxed_public_key_path = get_static_filepath(relative_path.as_str());
45    if boxed_public_key_path.is_err() {
46        return Err(boxed_public_key_path.err().unwrap());
47    }
48    let public_key_path = boxed_public_key_path.unwrap();
49
50
51    let relative_path = get_path_relative_to_working_directory(path_to_encryption_parameters, ".rsa_private_key");
52    let boxed_private_key_path = get_static_filepath(relative_path.as_str());
53    if boxed_private_key_path.is_err() {
54        return Err(boxed_private_key_path.err().unwrap());
55    }
56    let private_key_path = boxed_private_key_path.unwrap();
57
58
59    let boxed_keys = get_or_create_private_public_keys(passphrase.as_str(), public_key_path.as_str(), private_key_path.as_str());
60    if boxed_keys.is_err() {
61        return Err(boxed_keys.err().unwrap());
62    }
63
64    let (private_key, public_key) = boxed_keys.unwrap();
65
66    let encryption_params = EncryptionParameters {
67        rsa_public_key_pem: public_key,
68    };
69
70    let decryption_params = DecryptionParameters {
71        rsa_passphrase: passphrase,
72        rsa_private_key_pem: private_key
73    };
74
75    Ok((encryption_params, decryption_params))
76}
77
78/// Returns EncryptionParameters stored at the given location which is relative to the working directory
79///
80pub fn get_encryption_params(path_to_encryption_parameters: Option<&str>) -> Result<EncryptionParameters, String> {
81    let relative_path = get_path_relative_to_working_directory(path_to_encryption_parameters, ".rsa_public_key");
82    let boxed_public_key_path = get_static_filepath(relative_path.as_str());
83    if boxed_public_key_path.is_err() {
84        return Err(boxed_public_key_path.err().unwrap());
85    }
86    let public_key_path = boxed_public_key_path.unwrap();
87
88
89    let boxed_public_key = read_file(public_key_path.as_str());
90    if boxed_public_key.is_err() {
91        let message = boxed_public_key.err().unwrap();
92        return Err(message)
93    }
94    let boxed_public_key = String::from_utf8(boxed_public_key.unwrap());
95    let public_key = boxed_public_key.unwrap();
96
97    let encryption_params = EncryptionParameters {
98        rsa_public_key_pem: public_key,
99    };
100
101    Ok(encryption_params)
102}
103
104/// Returns DecryptionParameters stored at the given location which is relative to the working directory
105///
106pub fn get_decryption_params(path_to_encryption_parameters: Option<&str>) -> Result<DecryptionParameters, String> {
107    let relative_path = get_path_relative_to_working_directory(path_to_encryption_parameters, ".rsa_passphrase");
108    let boxed_passphrase_path = get_static_filepath(relative_path.as_str());
109    if boxed_passphrase_path.is_err() {
110        return Err(boxed_passphrase_path.err().unwrap());
111    }
112    let passphrase_path = boxed_passphrase_path.unwrap();
113
114
115    let boxed_passphrase = get_or_create_passphrase(passphrase_path.as_str());
116    if boxed_passphrase.is_err() {
117        return Err(boxed_passphrase.err().unwrap());
118    }
119    let passphrase = boxed_passphrase.unwrap();
120
121
122    let relative_path = get_path_relative_to_working_directory(path_to_encryption_parameters, ".rsa_private_key");
123    let boxed_private_key_path = get_static_filepath(relative_path.as_str());
124    if boxed_private_key_path.is_err() {
125        return Err(boxed_private_key_path.err().unwrap());
126    }
127    let private_key_path = boxed_private_key_path.unwrap();
128
129    let boxed_private_key = read_file(private_key_path.as_str());
130    if boxed_private_key.is_err() {
131        let message = boxed_private_key.err().unwrap();
132        return Err(message)
133    }
134    let boxed_private_key = String::from_utf8(boxed_private_key.unwrap());
135    let private_key = boxed_private_key.unwrap();
136
137    let decryption_params = DecryptionParameters {
138        rsa_passphrase: passphrase,
139        rsa_private_key_pem: private_key
140    };
141
142    Ok(decryption_params)
143}
144
145/// Encrypts given byte array of maximum length up to 501 bytes
146///
147/// # Examples
148///
149/// ```
150///    use crypto_ext::asymmetric::encryption::{encrypt, decrypt, setup, get_encryption_params, get_decryption_params};
151///
152///    #[test]
153///    fn encryption() {
154///        //maximum 501 bytes at once to be encrypted
155///        let data_to_encrypt_as_bytes = "Some data to encrypt".as_bytes();
156///
157///        // path needs to be accessible by user with write permission for initial setup
158///        let params_path = "/test/encryption_parameters/";
159///        // it will read encryption params like public, private keys and passphrase or create them
160///        // in this example setup is used to populate the params and used later via get_encryption_params or get_decryption_params
161///        let _ = setup(Some(params_path));
162///
163///        let encryption_params  = get_encryption_params(Some(params_path)).unwrap();
164///        let encrypted_bytes = encrypt(encryption_params, data_to_encrypt_as_bytes).unwrap();
165///
166///        let decryption_params = get_decryption_params(Some(params_path)).unwrap();
167///        let decrypted_bytes = decrypt(decryption_params, encrypted_bytes.as_slice()).unwrap();
168///
169///        assert_eq!(data_to_encrypt_as_bytes, decrypted_bytes);
170///    }
171/// ```
172pub fn encrypt(params: EncryptionParameters, data: &[u8]) -> Result<Vec<u8>, String> {
173    let boxed_rsa = Rsa::public_key_from_pem(params.rsa_public_key_pem.as_bytes());
174    if boxed_rsa.is_err() {
175        let message = boxed_rsa.err().unwrap().to_string();
176        return Err(message)
177    }
178    let rsa = boxed_rsa.unwrap();
179    let mut buffer : Vec<u8> = vec![0; rsa.size() as usize];
180    let boxed_encrypt = rsa.public_encrypt(data, &mut buffer, Padding::PKCS1);
181    if boxed_encrypt.is_err() {
182        let message = boxed_encrypt.err().unwrap().to_string();
183        return Err(message)
184    }
185    let _ = boxed_encrypt.unwrap();
186    Ok(buffer)
187}
188
189
190/// Decrypts given byte array
191///
192/// # Examples
193///
194/// ```
195///    use crypto_ext::asymmetric::encryption::{encrypt, decrypt, setup, get_encryption_params, get_decryption_params};
196///
197///    #[test]
198///    fn decryption() {
199///        // to decrypt first we need to have encrypted data
200///        let data_to_encrypt_as_bytes = "Some data to encrypt".as_bytes();
201///
202///        // path needs to be accessible by user with write permission for initial setup
203///        let params_path = "/test/encryption_parameters/";
204///        // this will create encryption params like public, private keys and passphrase
205///        let (encryption_params, decryption_params) = setup(Some(params_path)).unwrap();
206///
207///        let encrypted_bytes = encrypt(encryption_params, data_to_encrypt_as_bytes).unwrap();
208///        let decrypted_bytes = decrypt(decryption_params, encrypted_bytes.as_slice()).unwrap();
209///
210///        assert_eq!(data_to_encrypt_as_bytes, decrypted_bytes);
211///    }
212/// ```
213pub fn decrypt(params: DecryptionParameters, data: &[u8]) -> Result<Vec<u8>, String> {
214    let boxed_rsa = Rsa::private_key_from_pem_passphrase(params.rsa_private_key_pem.as_bytes(), params.rsa_passphrase.as_bytes());
215    if boxed_rsa.is_err() {
216        let message = boxed_rsa.err().unwrap().to_string();
217        return Err(message)
218    }
219    let rsa = boxed_rsa.unwrap();
220    let mut buffer: Vec<u8> = vec![0; rsa.size() as usize];
221    let boxed_decrypt = rsa.private_decrypt(data, &mut buffer, Padding::PKCS1);
222    if boxed_decrypt.is_err() {
223        let message = boxed_decrypt.err().unwrap().to_string();
224        return Err(message)
225    }
226    let _ = boxed_decrypt.unwrap();
227
228    let as_string = String::from_utf8(buffer).expect("Found invalid UTF-8");
229
230    let as_filtered_string = as_string.trim_end_matches(char::from(0));
231
232    let as_vector = as_filtered_string.as_bytes().to_vec();
233
234    Ok(as_vector)
235}
236
237
238// below are functions not exposed as an api, used for inner implementation
239
240fn get_or_create_passphrase(path: &str) -> Result<String, String> {
241
242    let boxed_passphrase = generate_passphrase();
243    if boxed_passphrase.is_err() {
244        let message = boxed_passphrase.err().unwrap();
245        return Err(message)
246    }
247
248    let passphrase = boxed_passphrase.unwrap();
249
250    let boxed_passphrase = read_or_create_and_write(path, passphrase.as_bytes());
251    if boxed_passphrase.is_err() {
252        let message = boxed_passphrase.err().unwrap();
253        return Err(message)
254    }
255
256    let boxed_passphrase = String::from_utf8(boxed_passphrase.unwrap());
257    let passphrase = boxed_passphrase.unwrap();
258    Ok(passphrase)
259}
260
261fn get_or_create_private_public_keys(passphrase: &str, public_key_path: &str, private_key_path: &str) -> Result<(String, String), String> {
262    let rsa = Rsa::generate(RSA_SIZE).unwrap();
263
264    let boxed_private_key = rsa.private_key_to_pem_passphrase(Cipher::aes_128_cbc(), passphrase.as_bytes());
265    let boxed_private_key = String::from_utf8(boxed_private_key.unwrap());
266    let private_key  = boxed_private_key.unwrap();
267
268    let boxed_private_key = read_or_create_and_write(private_key_path, private_key.as_bytes());
269    if boxed_private_key.is_err() {
270        let message = boxed_private_key.err().unwrap();
271        return Err(message)
272    }
273    let boxed_private_key = String::from_utf8(boxed_private_key.unwrap());
274    let private_key = boxed_private_key.unwrap();
275
276
277    let boxed_public_key = rsa.public_key_to_pem();
278    let boxed_public_key = String::from_utf8(boxed_public_key.unwrap());
279    let public_key = boxed_public_key.unwrap();
280
281    let boxed_public_key = read_or_create_and_write(public_key_path, public_key.as_bytes());
282    if boxed_public_key.is_err() {
283        let message = boxed_public_key.err().unwrap();
284        return Err(message)
285    }
286    let boxed_public_key = String::from_utf8(boxed_public_key.unwrap());
287    let public_key = boxed_public_key.unwrap();
288
289    Ok((private_key.to_string(), public_key.to_string()))
290}
291