include_crypt/
lib.rs

1#[doc(hidden)] pub use include_crypt_codegen as codegen;
2#[doc(hidden)] pub use include_crypt_crypto as crypto;
3#[doc(hidden)] pub use obfstr;
4
5use crate::obfstr::{random, ObfString};
6use crypto::{
7    aes::{aes_decrypt, AES_KEY_LEN, AES_NONCE_LEN},
8    key::EncryptionKey,
9    xor::{xor, XOR_KEY_LEN},
10};
11use std::{
12    collections::hash_map::DefaultHasher,
13    hash::{Hash, Hasher},
14    string::FromUtf8Error,
15};
16
17/// The different encryption types with their encryption keys. The obfuscated
18/// strings have double the size because of the hex encoding.
19pub enum EncryptionType {
20    /// The xor encryption type with the key.
21    Xor(ObfString<[u8; XOR_KEY_LEN * 2]>),
22
23    /// The aes encryption type with the key and nonce.
24    Aes(ObfString<[u8; AES_KEY_LEN * 2]>, ObfString<[u8; AES_NONCE_LEN * 2]>),
25}
26
27/// The structure which is used to store the encrypted buffer and the decryption
28/// keys.
29pub struct EncryptedFile {
30    /// The buffer that contains the encrypted bytes.
31    buffer: &'static [u8],
32
33    /// The type of the encryption that has been used.
34    enc_type: EncryptionType,
35}
36
37impl EncryptedFile {
38    /// Creates a new instance with the specified encrypted buffer and
39    /// encryption type. The encryption type also stores the decryption keys
40    /// which can be used to access the original data.
41    ///
42    /// # Parameters
43    ///
44    /// - `buffer`: The buffer with the encrypted bytes. This will be the output
45    ///   of the `encrypt_xor` / `encrypt_aes` proc macros.
46    /// - `enc_type`: The type of the encryption. This will be used to decrypt
47    ///   the buffer as it also stores the decryption keys for the different
48    ///   algorithms. If the key is randomly generated it will also be returned
49    ///   by the proc macro and saved.
50    ///
51    /// # Returns
52    ///
53    /// Returns a `EncryptedFile` instance which can be used to decrypt the
54    /// internal buffer.
55    pub const fn new(buffer: &'static [u8], enc_type: EncryptionType) -> Self { Self { buffer, enc_type } }
56
57    /// Decrypts the internal buffer and returns it.
58    ///
59    /// # Returns
60    ///
61    /// Returns the decrypted buffer.
62    #[inline(always)]
63    pub fn decrypt(&self) -> Vec<u8> {
64        let buffer = match &self.enc_type {
65            EncryptionType::Xor(key) => {
66                let mut buffer = self.buffer.to_vec();
67
68                // By using `map` instead of `unwrap` we are getting rid of the panic strings in
69                // the binary.
70                //
71                let _ = EncryptionKey::new(key.deobfuscate(random!(u16) as usize).as_str(), XOR_KEY_LEN)
72                    .map(|key| xor(buffer.as_mut_slice(), key));
73
74                buffer.to_vec()
75            }
76            EncryptionType::Aes(key, nonce) => {
77                let mut buffer = self.buffer.to_vec();
78
79                // By using `map` instead of `unwrap` we are getting rid of the panic strings in
80                // the binary.
81                //
82                let _ = EncryptionKey::new(key.deobfuscate(random!(u16) as usize).as_str(), AES_KEY_LEN).map(|key| {
83                    EncryptionKey::new(nonce.deobfuscate(random!(u16) as usize).as_str(), AES_NONCE_LEN).map(|nonce| {
84                        // This should never fail anyways because the keys have a fixed size.
85                        //
86                        let _ = aes_decrypt(buffer.as_mut_slice(), key, nonce);
87                    })
88                });
89
90                buffer
91            }
92        };
93
94        // Decompress the file if the feature is set
95        //
96        #[cfg(feature = "compression")]
97        {
98            use std::io::Read;
99
100            let mut decompressed = Vec::new();
101
102            let mut decoder = libflate::deflate::Decoder::new(std::io::Cursor::new(buffer));
103            decoder
104                .read_to_end(&mut decompressed)
105                .expect("The embedded deflate buffer was corrupted");
106
107            decompressed
108        }
109
110        #[cfg(not(feature = "compression"))]
111        {
112            buffer
113        }
114    }
115
116    /// Decrypts the internal buffer and returns it as a string.
117    ///
118    /// # Returns
119    ///
120    /// If the decrypted buffer is not a valid utf-8 string, an error will be
121    /// returned. If it is a valid utf-8 string, it will be returned.
122    #[inline(always)]
123    pub fn decrypt_str(&self) -> Result<String, FromUtf8Error> { String::from_utf8(self.decrypt()) }
124}
125
126/// Macro that can be used to safely embed files into the binary.
127///
128/// # Parameters
129///
130/// The macro can be used with different encryption algorithms.
131///
132/// ```ignore
133/// include_crypt!($encryption_type, $file_path, $optional_key)
134/// ```
135///
136/// - `$encryption_type`: The type of the encryption. Either `XOR` or `AES`. If
137///   you don't specify an encryption type, `XOR` will be used.
138/// - `$file_path`: The path to the file that should be embedded. If the path is
139///   relative, the `CARGO_MANIFEST_DIR` will be used as a starting point.
140/// - `$optional_key`: The optional encryption key. If specified, it has to be
141///   decodable by [hex](https://crates.io/crates/hex) crate.
142///
143/// # Returns
144///
145/// The macro expands to a `encrypt_xor` or `encrypt_aes` proc macro call. The
146/// return value will then be used to create a new `EncryptedFile` instance.
147///
148/// # Examples
149///
150/// More examples can be found in the `tests` and `examples` directory.
151///
152/// ```
153/// # use include_crypt::{EncryptedFile,include_crypt};
154/// #
155/// // Encrypt using XOR with random key
156/// let file: EncryptedFile = include_crypt!("src/lib.rs");
157///
158/// // Encrypt using XOR with custom key
159/// let file: EncryptedFile = include_crypt!("src/lib.rs", 0xdeadbeef);
160///
161/// // Encrypt using XOR with random key
162/// let file: EncryptedFile = include_crypt!(XOR, "src/lib.rs");
163///
164/// // Encrypt using XOR with custom key
165/// let file: EncryptedFile = include_crypt!(XOR, "src/lib.rs", 0xdeadbeef);
166///
167/// // Encrypt using AES with random key
168/// let file: EncryptedFile = include_crypt!(AES, "src/lib.rs");
169///
170/// // Encrypt using AES with custom key
171/// let file: EncryptedFile = include_crypt!(AES, "src/lib.rs", 0xdeadbeef);
172/// ```
173///
174/// You can also use absolute paths:
175/// ```ignore
176/// let file: EncryptedFile = include_crypt!("D:/file.txt");
177/// ```
178#[macro_export]
179macro_rules! include_crypt {
180    (XOR, $path:expr) => {{
181        let (key, data) = $crate::codegen::encrypt_xor!($path);
182
183        $crate::EncryptedFile::new(data, $crate::EncryptionType::Xor(key))
184    }};
185    (XOR, $path:expr, $key:expr) => {{
186        let (key, data) = $crate::codegen::encrypt_xor!($path, $key);
187
188        $crate::EncryptedFile::new(data, $crate::EncryptionType::Xor(key))
189    }};
190
191    (AES, $path:expr) => {{
192        let (key, nonce, data) = $crate::codegen::encrypt_aes!($path);
193
194        $crate::EncryptedFile::new(data, $crate::EncryptionType::Aes(key, nonce))
195    }};
196    (AES, $path:expr, $key:expr) => {{
197        let (key, nonce, data) = $crate::codegen::encrypt_aes!($path, $key);
198
199        $crate::EncryptedFile::new(data, $crate::EncryptionType::Aes(key, nonce))
200    }};
201
202    ($path:expr) => {
203        $crate::include_crypt!(XOR, $path)
204    };
205    ($path:expr, $key:expr) => {
206        $crate::include_crypt!(XOR, $path, $key)
207    };
208}
209
210/// The folder with all the encrypted files.
211#[derive(Clone)]
212pub struct EncryptedFolder<'a> {
213    #[doc(hidden)]
214    pub files: &'a [(&'static str, EncryptedFile)],
215}
216
217impl<'a> EncryptedFolder<'a> {
218    /// Tries to find the file in the folder.
219    ///
220    /// # Parameters
221    ///
222    /// - `path`: The relative path to the file in the folder.
223    ///
224    /// # Returns
225    ///
226    /// If the file could be found, it will be returned. If it couldn't be
227    /// found, `None` will be returned.
228    ///
229    /// # Examples
230    ///
231    /// ```
232    /// # use include_crypt::{include_dir, EncryptedFile, EncryptedFolder};
233    /// let folder: EncryptedFolder = include_dir!(".");
234    ///
235    /// println!("{}", folder.files.len());
236    ///
237    /// assert!(folder.get("src\\lib.rs").is_some());
238    /// assert!(folder.get("src/lib.rs").is_some());
239    /// ```
240    pub fn get(&self, file_path: &str) -> Option<&EncryptedFile> {
241        // We have to normalize the slashes first so that there's no difference
242        // between `\` and `/`. After that we can hash the file and compare it later in
243        // the loop.
244        //
245        let file_path = {
246            let path = file_path.replace("\\", "/");
247
248            let mut hasher = DefaultHasher::new();
249            path.hash(&mut hasher);
250            hasher.finish().to_string()
251        };
252
253        for (path, file) in self.files {
254            if *path == file_path {
255                return Some(file);
256            }
257        }
258
259        None
260    }
261}
262
263/// Macro that can be used to safely embed a folder into the binary.
264///
265/// # Parameters
266///
267/// The macro can be used with different encryption algorithms.
268///
269/// ```ignore
270/// include_dir!($encryption_type, $folder_path)
271/// ```
272///
273/// - `$encryption_type`: The type of the encryption. Either `XOR` or `AES`. If
274///   you don't specify an encryption type, `XOR` will be used.
275/// - `$folder_path`: The path to the folder that should be embedded. If the
276///   path is relative, the `CARGO_MANIFEST_DIR` will be used as a starting
277///   point.
278///
279/// # Returns
280///
281/// The macro expands to a `include_files` proc macro call. The return value
282/// will then be used to create a new `EncryptedFolder` instance.
283///
284/// # Examples
285///
286/// ```
287/// # use include_crypt::{EncryptedFolder, include_dir};
288/// #
289/// // Encrypt using XOR with random key
290/// let folder: EncryptedFolder = include_dir!("./src");
291///
292/// // Encrypt using XOR with random key
293/// let folder: EncryptedFolder = include_dir!(XOR, "./src");
294///
295/// // Encrypt using AES with random key
296/// let folder: EncryptedFolder = include_dir!(AES, "./src");
297/// ```
298///
299/// You can also use absolute paths:
300/// ```ignore
301/// let folder: EncryptedFolder = include_dir!("D:/assets");
302/// ```
303#[macro_export]
304macro_rules! include_dir {
305    (XOR, $path:expr) => {
306        $crate::EncryptedFolder {
307            files: &$crate::codegen::include_files!("XOR", $path),
308        }
309    };
310
311    (AES, $path:expr) => {
312        $crate::EncryptedFolder {
313            files: &$crate::codegen::include_files!("AES", $path),
314        }
315    };
316
317    ($path:expr) => {
318        $crate::include_dir!(XOR, $path)
319    };
320}