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}