simple_aes256_gcm/
simple_aes256_gcm.rs

1use aes_gcm::Aes256Gcm;
2use aead::{Aead, NewAead, generic_array::GenericArray};
3use std::{fmt, error};
4use std::convert::{TryInto, TryFrom};
5
6#[derive(Debug, Clone)]
7pub enum InvalidKeyError {
8    InvalidKeySizeError,
9    InvalidKeyBase64Error
10}
11
12impl fmt::Display for InvalidKeyError {
13    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
14        match self {
15            InvalidKeyError::InvalidKeySizeError => write!(f, "{}", "Please provide a 32-byte, base64-encoded, key"),
16            InvalidKeyError::InvalidKeyBase64Error => write!(f, "{}", "Please provide a valid base64"),
17        }
18    }
19}
20
21impl error::Error for InvalidKeyError {
22    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
23        // Generic error, underlying cause isn't tracked.
24        None
25    }
26}
27
28pub struct Key {
29    pub u8_array: [u8; 32]
30}
31
32impl TryFrom<&str> for Key {
33    type Error = InvalidKeyError;
34    fn try_from(base64_key: &str) -> Result<Self, InvalidKeyError> {
35        let key = match base64::decode(base64_key) {
36            Ok(data) => data,
37            Err(_) => return Err(InvalidKeyError::InvalidKeyBase64Error)
38        };
39
40        let u8_array: Result<[u8; 32], _> = key.as_slice().try_into();
41        match u8_array {
42            Ok(value) => Ok(Self {
43                u8_array: value
44            }),
45            Err(_) => Err(InvalidKeyError::InvalidKeySizeError)
46        }
47    }
48}
49
50impl TryFrom<String> for Key {
51    type Error = InvalidKeyError;
52    fn try_from(base64_key: String) -> Result<Self, InvalidKeyError> {
53        Self::try_from(&base64_key[..])
54    }
55}
56
57#[derive(Debug, Clone)]
58pub enum InvalidIvError {
59    InvalidIvSizeError,
60    InvalidIvBase64Error
61}
62
63
64impl fmt::Display for InvalidIvError {
65    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
66        match self {
67            InvalidIvError::InvalidIvSizeError => write!(f, "{}", "Please provide a 12-byte, base64-encoded, iv"),
68            InvalidIvError::InvalidIvBase64Error => write!(f, "{}", "Please provide a valid base64"),
69        }
70    }
71}
72
73impl error::Error for InvalidIvError {
74    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
75        // Generic error, underlying cause isn't tracked.
76        None
77    }
78}
79
80pub struct Iv {
81    pub u8_array: [u8; 12]
82}
83
84impl TryFrom<&str> for Iv {
85    type Error = InvalidIvError;
86    fn try_from(base64_iv: &str) -> Result<Iv, InvalidIvError> {
87        let iv = match base64::decode(base64_iv) {
88            Ok(data) => data,
89            Err(_) => return Err(InvalidIvError::InvalidIvBase64Error)
90        };
91
92        let u8_array: Result<[u8; 12], _> = iv.as_slice().try_into();
93        match u8_array {
94            Ok(value) => Ok(Iv {
95                u8_array: value
96            }),
97            Err(_) => Err(InvalidIvError::InvalidIvSizeError)
98        }
99    }
100}
101impl Iv {
102    pub fn generate() -> Iv {
103        Iv {
104            u8_array: rand::random::<[u8; 12]>()
105        }
106    }
107}
108
109impl From<&Iv> for String {
110    fn from(iv: &Iv) -> String {
111        base64::encode(&iv.u8_array)
112    }
113}
114
115impl fmt::Display for Iv {
116    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117        write!(f, "{}", String::from(self))
118    }
119}
120
121
122pub struct Encrypted {
123    pub u8_vec: Vec<u8>
124}
125
126impl TryFrom<&str> for Encrypted {
127    type Error = base64::DecodeError;
128    fn try_from(base64_encrypted: &str) -> Result<Encrypted, base64::DecodeError> {
129        Ok(Encrypted {
130            u8_vec: base64::decode(base64_encrypted)?
131        })
132    }
133}
134
135impl From<&Encrypted> for String {
136    fn from(encrypted: &Encrypted) -> String {
137        base64::encode(&encrypted.u8_vec)
138    }
139}
140
141impl fmt::Display for Encrypted {
142    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143        write!(f, "{}", String::from(self))
144    }
145}
146
147pub struct Decrypted<'a> {
148    value: &'a str
149}
150impl<'a> From<&'a str> for Decrypted<'a> {
151    fn from(value: &'a str) -> Self {
152        Self { value: value }
153    }
154}
155
156impl From<&Decrypted<'_>> for String {
157    fn from(decrypted: &Decrypted<'_>) -> String {
158        String::from(decrypted.value)
159    }
160}
161
162impl<'a> fmt::Display for Decrypted<'a> {
163    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164        write!(f, "{}", String::from(self))
165    }
166}
167
168
169pub struct EncryptedAndIv {
170    pub encrypted: Encrypted,
171    pub iv: Iv
172}
173
174
175#[derive(Debug, Clone)]
176pub enum EncryptionError {
177    GenericEncryptionError
178}
179impl fmt::Display for EncryptionError {
180    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
181        match self {
182            EncryptionError::GenericEncryptionError => write!(f, "{}", "Encryption error"),
183        }
184    }
185}
186
187impl error::Error for EncryptionError {
188    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
189        // Generic error, underlying cause isn't tracked.
190        None
191    }
192}
193
194pub fn encrypt<'a>(key: &Key, decrypted: &Decrypted) -> Result<EncryptedAndIv, EncryptionError> {
195    let iv = Iv::generate();
196    let nonce = GenericArray::from_slice(&iv.u8_array);
197    let client = Aes256Gcm::new(GenericArray::clone_from_slice(&key.u8_array));
198    match client.encrypt(nonce, decrypted.value.as_bytes()) {
199        Ok(ciphertext) => Ok(EncryptedAndIv {
200            iv: iv,
201            encrypted: Encrypted {
202                u8_vec: ciphertext
203            }
204        }),
205        Err(_) => Err(EncryptionError::GenericEncryptionError)
206    }
207}
208
209#[derive(Debug, Clone)]
210pub enum DecryptionError {
211    InvalidUTF8DecryptionError,
212    GenericDecryptionError
213}
214impl fmt::Display for DecryptionError {
215    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
216        match self {
217            DecryptionError::InvalidUTF8DecryptionError => write!(f, "{}", "Decryption error: invalid UTF-8"),
218            DecryptionError::GenericDecryptionError => write!(f, "{}", "Decryption error"),
219        }
220    }
221}
222
223impl error::Error for DecryptionError {
224    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
225        // Generic error, underlying cause isn't tracked.
226        None
227    }
228}
229
230pub fn decrypt(key: &Key, encrypted_and_iv: EncryptedAndIv) -> Result<String, DecryptionError> {
231    let nonce = GenericArray::from_slice(&encrypted_and_iv.iv.u8_array);
232    let client = Aes256Gcm::new(GenericArray::clone_from_slice(&key.u8_array));
233
234    match client.decrypt(nonce, encrypted_and_iv.encrypted.u8_vec.as_ref()) {
235        Ok(decrypted_u8_vec) => match String::from_utf8(decrypted_u8_vec) {
236            Ok(decrypted_string) => Ok(decrypted_string),
237            Err(_) => Err(DecryptionError::InvalidUTF8DecryptionError)
238        },
239        Err(_) => Err(DecryptionError::GenericDecryptionError)
240    }
241}
242
243#[cfg(test)]
244mod tests {
245    use super::*;
246    #[test]
247    fn key_try_from_invalid_base64_fails() {
248        match Key::try_from("012") {
249            Ok(_) => assert!(false),
250            Err(e) => match e {
251                InvalidKeyError::InvalidKeySizeError => assert!(false, "Should err an InvalidKeyError::InvalidKeyBase64Error"),
252                InvalidKeyError::InvalidKeyBase64Error => assert!(true)
253            }
254        }
255    }
256
257    #[test]
258    fn key_try_from_valid_3byte_fails() {
259        match Key::try_from("MDEy") {
260            Ok(_) => assert!(false),
261            Err(e) => match e {
262                InvalidKeyError::InvalidKeyBase64Error => assert!(false, "Should err an InvalidKeyError::InvalidKeySizeError"),
263                InvalidKeyError::InvalidKeySizeError => assert!(true)
264            }
265        }
266    }
267
268    #[test]
269    fn key_try_from_valid_33byte_fails() {
270        match Key::try_from("MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEy") {
271            Ok(_) => assert!(false),
272            Err(e) => match e {
273                InvalidKeyError::InvalidKeyBase64Error => assert!(false, "Should err an InvalidKeyError::InvalidKeySizeError"),
274                InvalidKeyError::InvalidKeySizeError => assert!(true)
275            }
276        }
277    }
278
279    #[test]
280    fn key_try_from_valid_32_bytes_succeeds() {
281        match Key::try_from("MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDE=") {
282            Err(_) => assert!(false, "Should succeed"),
283            Ok(key) => assert_eq!(key.u8_array, [48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49])
284        }
285    }
286
287    #[test]
288    fn iv_try_from_invalid_base64_fails() {
289        match Iv::try_from("012") {
290            Ok(_) => assert!(false),
291            Err(e) => match e {
292                InvalidIvError::InvalidIvSizeError => assert!(false, "Should err an InvalidIvError::InvalidIvBase64Error"),
293                InvalidIvError::InvalidIvBase64Error => assert!(true)
294            }
295        }
296    }
297
298    #[test]
299    fn iv_try_from_valid_3byte_fails() {
300        match Iv::try_from("YWJj") {
301            Ok(_) => assert!(false),
302            Err(e) => match e {
303                InvalidIvError::InvalidIvBase64Error => assert!(false, "Should err an InvalidIvError::InvalidIvSizeError"),
304                InvalidIvError::InvalidIvSizeError => assert!(true)
305            }
306        }
307    }
308
309    #[test]
310    fn iv_try_from_valid_13byte_fails() {
311        match Iv::try_from("MDEyMzQ1Njc4OTAxMg==") {
312            Ok(_) => assert!(false),
313            Err(e) => match e {
314                InvalidIvError::InvalidIvBase64Error => assert!(false, "Should err an InvalidIvError::InvalidIvSizeError"),
315                InvalidIvError::InvalidIvSizeError => assert!(true)
316            }
317        }
318    }
319
320    #[test]
321    fn iv_try_from_valid_12byte_succeeds() {
322        match Iv::try_from("MDEyMzQ1Njc4OTAx") {
323            Err(_) => assert!(false, "Should succeeds"),
324            Ok(iv) => assert_eq!(iv.u8_array, [48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49])
325        }
326    }
327
328    #[test]
329    fn iv_generate() {
330        assert!(
331            Iv::generate().u8_array != Iv::generate().u8_array,
332            "Should generate almost unique values"
333        )
334    }
335
336    #[test]
337    fn iv_format() {
338        assert_eq!(
339            format!("{}", Iv::try_from("MDEyMzQ1Njc4OTAx").unwrap()),
340            "MDEyMzQ1Njc4OTAx"
341        )
342    }
343
344    #[test]
345    fn encrypted_from64_invalid_base64() {
346        match Encrypted::try_from("aaaaaaa") {
347            Ok(_) => assert!(false, "Should err"),
348            Err(_) => assert!(true)
349        }
350    }
351
352    #[test]
353    fn encrypted_from64_valid_base64() {
354        match Encrypted::try_from("YWFhYWFhYQ==") {
355            Err(_) => assert!(false, "Should ok"),
356            Ok(encryped) => assert_eq!(encryped.u8_vec, vec![97, 97, 97, 97, 97, 97, 97])
357        }
358    }
359
360    #[test]
361    fn encrypted_format() {
362        assert_eq!(
363            format!("{}", Encrypted::try_from("YWFhYWFhYQ==").unwrap()),
364            "YWFhYWFhYQ=="
365        )
366    }
367
368    // #[test]
369    // Not able to find any example that would make this err...
370    // fn encrypt_err_when_encryption_error() {
371    //     let key = Key::try_from("12345678901234567890123456789012").unwrap();
372    //     let decrypted = Decrypted::from("???")
373    //     match encrypt(&key, &decrypted) {
374    //         Ok(_) => assert!(false, "Should err"),
375    //         Err(e) => assert!(true)
376    //     }
377    // }
378
379    #[test]
380    fn encrypted_values_are_different_for_same_inputs() {
381        let encrypted_1 = encrypt(
382            &Key::try_from("MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDE=").unwrap(),
383            &Decrypted::from("This is a text.")
384        ).unwrap();
385        let encrypted_2 = encrypt(
386            &Key::try_from("MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDE=").unwrap(),
387            &Decrypted::from("This is a text.")
388        ).unwrap();
389        assert!(encrypted_1.encrypted.u8_vec != encrypted_2.encrypted.u8_vec)
390    }
391
392    #[test]
393    fn encrypted_values_are_different_for_different_inputs() {
394        let encrypted_1 = encrypt(
395            &Key::try_from("MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDE=").unwrap(),
396            &Decrypted::from("This is a text.")
397        ).unwrap();
398        let encrypted_2 = encrypt(
399            &Key::try_from("MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDE=").unwrap(),
400            &Decrypted::from("This is another text.")
401        ).unwrap();
402        assert!(encrypted_1.encrypted.u8_vec != encrypted_2.encrypted.u8_vec)
403    }
404
405    #[test]
406    fn encrypt_decrypt_is_iso() {
407        let key = Key::try_from("MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDE=").unwrap();
408        let encrypted = encrypt(
409            &key,
410            &Decrypted::from("This is a text.")
411        ).unwrap();
412
413        assert_eq!(decrypt(&key, encrypted).unwrap(), String::from("This is a text."))
414    }
415
416    #[test]
417    fn encrypt_decrypt_is_iso_with_string_key() {
418        let key = Key::try_from(String::from("MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDE=")).unwrap();
419        let encrypted = encrypt(
420            &key,
421            &Decrypted::from("This is a text.")
422        ).unwrap();
423
424        assert_eq!(decrypt(&key, encrypted).unwrap(), String::from("This is a text."))
425    }
426
427    #[test]
428    fn decrypt_fails_when_non_utf8() {
429        let key = Key::try_from("MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDE=").unwrap();
430
431        let iv = Iv::generate();
432        let nonce = GenericArray::from_slice(&iv.u8_array);
433        let client = Aes256Gcm::new(GenericArray::clone_from_slice(&key.u8_array));
434        let invalid_utf8_bytes: &[u8] = &[133u8, 133u8];
435        let ciphertext = client.encrypt(nonce, invalid_utf8_bytes).unwrap();
436
437        let encrypted_and_iv = EncryptedAndIv {
438            iv: iv,
439            encrypted: Encrypted {
440                u8_vec: ciphertext
441            }
442        };
443
444        match decrypt(&key, encrypted_and_iv) {
445            Ok(_) => assert!(false, "Should err InvalidUTF8DecryptionError"),
446            Err(e) => match e {
447                DecryptionError::GenericDecryptionError => assert!(false, "Should err InvalidUTF8DecryptionError"),
448                DecryptionError::InvalidUTF8DecryptionError => assert!(true)
449            }
450        }
451    }
452
453    // #[test]
454    // Not able to find any example that would make this err...
455    // fn decrypt_err_when_decryption_error() {
456    //     let key = Key::try_from("12345678901234567890123456789012").unwrap();
457    //     let encrypted_and_iv = EncryptedAndIv {
458    //         encrypted: encrypted,
459    //         iv: iv
460    //     }
461    //     match decrypt(&key, encrypted_and_iv) {
462    //         Ok(_) => assert!(false, "Should err GenericDecryptionError"),
463    //         Err(e) => match e {
464    //             DecryptionError::InvalidUTF8DecryptionError => assert!(false, GenericDecryptionError),
465    //             DecryptionError::GenericDecryptionError => assert!(true)
466    //         }
467    //     }
468    // }
469}