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}