fencryption_lib/
crypto.rs

1//! Crypto utility.
2
3use aes_gcm::{aead::Aead, Aes256Gcm, KeyInit, Nonce};
4use rand::{rngs::OsRng, RngCore};
5use sha2::{Digest, Sha256};
6use std::io::{self, Read, Write};
7
8/// Default initialization vector length (12b).
9pub const IV_LEN: usize = 96 / 8;
10/// Default authentication tag length (16b).
11pub const TAG_LEN: usize = 128 / 8;
12/// Encryption chunk length: plaintext (128kb).
13pub const ENC_CHUNK_LEN: usize = 128000;
14/// Decryption chunk length: iv (12b) + ciphertext (128kb) + auth tag (16b).
15pub const DEC_CHUNK_LEN: usize = IV_LEN + ENC_CHUNK_LEN + TAG_LEN;
16
17/// Enum of the different possible crypto errors.
18#[derive(Debug)]
19pub enum ErrorKind {
20    AesError(aes_gcm::Error),
21    Io(io::Error),
22}
23
24/// A struct for encrypting/decrypting bytes or io streams.
25#[derive(Clone)]
26pub struct Crypto {
27    cipher: Aes256Gcm,
28}
29
30impl Crypto {
31    /// Create a new [`Crypto`] instance, the given key will be
32    /// used for every operation performed.
33    pub fn new<K>(key: K) -> Result<Crypto, ErrorKind>
34    where
35        K: AsRef<[u8]>,
36    {
37        let key = hash_key(key.as_ref());
38        Ok(Crypto {
39            cipher: Aes256Gcm::new_from_slice(&key).unwrap(),
40        })
41    }
42
43    /// Encrypt bytes with initialisation vector.
44    pub fn encrypt_with_iv(&self, plaintext: &[u8], iv: &[u8]) -> Result<Vec<u8>, ErrorKind> {
45        Ok(self
46            .cipher
47            .encrypt(Nonce::from_slice(iv), plaintext)
48            .map_err(|e| ErrorKind::AesError(e))?)
49    }
50
51    /// Decrypt bytes with initialisation vector.
52    pub fn decrypt_with_iv(&self, ciphertext: &[u8], iv: &[u8]) -> Result<Vec<u8>, ErrorKind> {
53        Ok(self
54            .cipher
55            .decrypt(Nonce::from_slice(iv), ciphertext)
56            .map_err(|e| ErrorKind::AesError(e))?)
57    }
58
59    /// Encrypt a small piece of data.
60    ///
61    /// Example:
62    ///
63    /// ```
64    /// use fencryption_lib::crypto::Crypto;
65    ///
66    /// let my_super_key = "this_is_super_secure".as_bytes();
67    /// let my_super_secret_message = "hello :)".as_bytes();
68    ///
69    /// let crypto = Crypto::new(my_super_key).unwrap();
70    ///
71    /// let enc = crypto.encrypt(my_super_secret_message).unwrap();
72    ///
73    /// assert_ne!(my_super_secret_message, enc);
74    /// ```
75    pub fn encrypt<P>(&self, plain: P) -> Result<Vec<u8>, ErrorKind>
76    where
77        P: AsRef<[u8]>,
78    {
79        let iv = random_iv();
80        Ok([&iv, self.encrypt_with_iv(plain.as_ref(), &iv)?.as_slice()].concat())
81    }
82
83    /// Decrypt a small piece of data.
84    ///
85    /// Example:
86    ///
87    /// ```
88    /// use fencryption_lib::crypto::Crypto;
89    ///
90    /// let my_super_key = "this_is_super_secure".as_bytes();
91    /// let my_super_secret_message = "hello :)".as_bytes();
92    ///
93    /// let crypto = Crypto::new(my_super_key).unwrap();
94    ///
95    /// let enc = crypto.encrypt(my_super_secret_message).unwrap();
96    /// let dec = crypto.decrypt(&enc).unwrap();
97    ///
98    /// assert_eq!(my_super_secret_message, dec);
99    /// ```
100    pub fn decrypt<E>(&self, enc: E) -> Result<Vec<u8>, ErrorKind>
101    where
102        E: AsRef<[u8]>,
103    {
104        let (iv, ciphertext) = enc.as_ref().split_at(IV_LEN);
105        self.decrypt_with_iv(ciphertext, iv)
106    }
107
108    /// Encrypt data from a reader and write it in a writer.
109    /// When working with small pieces of data, use
110    /// [`Crypto::encrypt`].
111    ///
112    /// Example:
113    ///
114    /// (See [`TmpDir`][crate::tmp::TmpDir])
115    ///
116    /// ```
117    /// use fencryption_lib::crypto::Crypto;
118    /// use fencryption_lib::tmp::TmpDir;
119    ///
120    /// let my_super_key = b"this_is_super_secure";
121    /// let my_super_secret_message = b"hello :)";
122    ///
123    /// let tmp_dir = TmpDir::new().unwrap();
124    /// let crypto = Crypto::new(my_super_key).unwrap();
125    ///
126    /// tmp_dir.write_file("plain", my_super_secret_message).unwrap();
127    ///
128    /// crypto
129    ///     .encrypt_io(
130    ///         &mut tmp_dir.open_readable("plain").unwrap(),
131    ///         &mut tmp_dir.create_file("enc").unwrap(),
132    ///     )
133    ///     .unwrap();
134    /// ```
135    pub fn encrypt_io(
136        &self,
137        source: &mut impl Read,
138        dest: &mut impl Write,
139    ) -> Result<(), ErrorKind> {
140        let mut buffer = [0u8; ENC_CHUNK_LEN];
141
142        loop {
143            let read_len = source.read(&mut buffer).map_err(|e| ErrorKind::Io(e))?;
144            dest.write_all(&self.encrypt(&buffer[..read_len])?)
145                .map_err(|e| ErrorKind::Io(e))?;
146            // Stops when the loop reached the end of the file
147            if read_len != ENC_CHUNK_LEN {
148                break;
149            }
150        }
151
152        Ok(())
153    }
154
155    /// Decrypt data from a reader and write it in a writer.
156    /// When working with small pieces of data, use
157    /// [`Crypto::decrypt`].
158    ///
159    /// Example:
160    ///
161    /// (See [`TmpDir`][crate::tmp::TmpDir])
162    ///
163    /// ```
164    /// use fencryption_lib::crypto::Crypto;
165    /// use fencryption_lib::tmp::TmpDir;
166    ///
167    /// let my_super_key = b"this_is_super_secure";
168    /// let my_super_secret_message = b"hello :)";
169    ///
170    /// let tmp_dir = TmpDir::new().unwrap();
171    /// let crypto = Crypto::new(my_super_key).unwrap();
172    ///
173    /// tmp_dir.write_file("plain", my_super_secret_message).unwrap();
174    ///
175    /// crypto
176    ///     .encrypt_io(
177    ///         &mut tmp_dir.open_readable("plain").unwrap(),
178    ///         &mut tmp_dir.create_file("enc").unwrap(),
179    ///     )
180    ///     .unwrap();
181    /// crypto
182    ///     .decrypt_io(
183    ///         &mut tmp_dir.open_readable("enc").unwrap(),
184    ///         &mut tmp_dir.create_file("dec").unwrap(),
185    ///     )
186    ///     .unwrap();
187    ///
188    /// assert_eq!(tmp_dir.read_file("dec").unwrap(), my_super_secret_message[..]);
189    /// ```
190    pub fn decrypt_io(
191        &self,
192        source: &mut impl Read,
193        dest: &mut impl Write,
194    ) -> Result<(), ErrorKind> {
195        let mut buffer = [0u8; DEC_CHUNK_LEN];
196
197        loop {
198            let read_len = source.read(&mut buffer).map_err(|e| ErrorKind::Io(e))?;
199            dest.write_all(&self.decrypt(&buffer[..read_len])?)
200                .map_err(|e| ErrorKind::Io(e))?;
201            // Stops when the loop reached the end of the file.
202            if read_len != DEC_CHUNK_LEN {
203                break;
204            }
205        }
206
207        Ok(())
208    }
209}
210
211fn hash_key<K>(key: K) -> Vec<u8>
212where
213    K: AsRef<[u8]>,
214{
215    let mut hasher = Sha256::new();
216    hasher.update(key.as_ref());
217    hasher.finalize().to_vec()
218}
219
220fn random_iv() -> [u8; IV_LEN] {
221    let mut iv = [0; IV_LEN];
222    OsRng.fill_bytes(&mut iv);
223    iv
224}