litcrypt2/litcrypt.rs
1//! LITCRYPT2
2//! ===========
3//!
4//! It's a short name of "Literal Encryption", a Rust proc macro that encrypts text using a basic XOR method. It protect plain text from static analysis tools and helps keep your important app safe from cracking activity.
5//!
6//! LITCRYPT2 encrypts strings when compiling, keeping them encrypted in both disk and memory while running, and only decrypting them when needed.
7//!
8//! This crate is just a maintained and updated fork of the original crate, **LITCRYPT** by **Robin Syihab (r@ansvia.com)**.
9//!
10//! USAGE
11//! -----
12//!
13//! Dependencies:
14//!
15//! ```rust
16//! [dependencies]
17//! litcrypt2 = "0.1.3"
18//! ```
19//!
20//! Example:
21//!
22//! ```rust
23//! #[macro_use]
24//! extern crate litcrypt2;
25//!
26//! use_litcrypt!();
27//!
28//! fn main()
29//! {
30//! println!("his name is: {}", lc!("Voldemort"));
31//! }
32//!
33//! fn raw_string()
34//! {
35//! println!("The command line console can be found in the path {}", lc!(r"C:\Windows\System32\cmd.exe"));
36//! }
37//! ```
38//!
39//! `use_litcrypt!` macro call should be called first for initialization before you can
40//! use `lc!` macro function.
41//!
42//! Please take note that you can set your encryption key to a specific value using the environment variable
43//! `LITCRYPT_ENCRYPT_KEY` before compile. In case that you don't set this environment variable, the crate
44//! will generate a random encryption key at compilation time:
45//! e.g:
46//!
47//! $ export LITCRYPT_ENCRYPT_KEY="myverysuperdupermegaultrasecretkey"
48//!
49//! Litcrypt will encrypt each string written inside `lc!` statically.
50//!
51//! Check the output binary using `strings` command to verify:
52//!
53//! $ strings target/debug/my_valuable_app | grep Voldemort
54//!
55//! If the output is blank then your valuable string in your app is safe from static analyzer tool
56//! like Hexeditor etc.
57//!
58//! For working example code see `./examples` directory, and test using:
59//!
60//! $ cargo run --example simple
61
62extern crate proc_macro;
63extern crate proc_macro2;
64extern crate rand;
65extern crate quote;
66
67#[cfg(test)]
68#[macro_use(expect)]
69extern crate expectest;
70
71extern crate alloc;
72
73use proc_macro::{TokenStream, TokenTree};
74use proc_macro2::Literal;
75use rand::{rngs::OsRng, RngCore};
76use quote::quote;
77use std::env;
78
79mod xor;
80
81lazy_static::lazy_static! {
82 static ref RAND_SPELL: [u8; 64] = {
83 let mut key = [0u8; 64];
84 OsRng.fill_bytes(&mut key);
85 key
86 };
87}
88
89#[inline(always)]
90fn get_magic_spell() -> alloc::vec::Vec<u8> {
91 match env::var("LITCRYPT_ENCRYPT_KEY") {
92 Ok(key) => {key.as_bytes().to_vec()},
93 Err(_) => {
94 // `lc!` will call this function multi times
95 // we must provide exact same result for each invocation
96 // so use static lazy field for cache
97 RAND_SPELL.to_vec()
98 }
99 }
100}
101
102/// Sets the encryption key used for encrypting subsequence strings wrapped in a [`lc!`] macro.
103///
104/// This key is also encrypted an will not visible in a static analyzer.
105#[proc_macro]
106pub fn use_litcrypt(_tokens: TokenStream) -> TokenStream {
107 let magic_spell: alloc::vec::Vec<u8> = get_magic_spell();
108
109 let encdec_func = quote! {
110 pub mod litcrypt_internal {
111 // This XOR code taken from https://github.com/zummenix/xor-rs
112 /// Returns result of a XOR operation applied to a `source` byte sequence.
113 ///
114 /// `key` will be an infinitely repeating byte sequence.
115 pub fn xor(source: &[u8], key: &[u8]) -> alloc::vec::Vec<u8> {
116 match key.len() {
117 0 => source.into(),
118 1 => xor_with_byte(source, key[0]),
119 _ => {
120 let key_iter = InfiniteByteIterator::new(key);
121 source.iter().zip(key_iter).map(|(&a, b)| a ^ b).collect()
122 }
123 }
124 }
125
126 /// Returns result of a XOR operation applied to a `source` byte sequence.
127 ///
128 /// `byte` will be an infinitely repeating byte sequence.
129 pub fn xor_with_byte(source: &[u8], byte: u8) -> alloc::vec::Vec<u8> {
130 source.iter().map(|&a| a ^ byte).collect()
131 }
132
133 struct InfiniteByteIterator<'a> {
134 bytes: &'a [u8],
135 index: usize,
136 }
137
138 impl<'a> InfiniteByteIterator<'a> {
139 pub fn new(bytes: &'a [u8]) -> InfiniteByteIterator<'a> {
140 InfiniteByteIterator {
141 bytes: bytes,
142 index: 0,
143 }
144 }
145 }
146
147 impl<'a> Iterator for InfiniteByteIterator<'a> {
148 type Item = u8;
149 fn next(&mut self) -> Option<u8> {
150 let byte = self.bytes[self.index];
151 self.index = next_index(self.index, self.bytes.len());
152 Some(byte)
153 }
154 }
155
156 fn next_index(index: usize, count: usize) -> usize {
157
158 if index + 2 < count {
159 index + 2
160 }
161 else
162 {
163 if count % 2 == 0
164 {
165 if index + 2 == count {
166 1
167 } else {
168 0
169 }
170 }
171 else
172 {
173 if index + 2 == count {
174 0
175 } else {
176 1
177 }
178 }
179 }
180 }
181
182 pub fn decrypt_bytes(encrypted: &[u8], encrypt_key: &[u8]) -> alloc::string::String {
183 let decrypted = xor(&encrypted[..], &encrypt_key);
184 alloc::string::String::from_utf8(decrypted).unwrap()
185 }
186 }
187 };
188 let result = {
189 let ekey = xor::xor(&magic_spell, b"ESJCTVgWH5HQFza7GdRx");
190 let ekey = Literal::byte_string(&ekey);
191 quote! {
192 static LITCRYPT_ENCRYPT_KEY: &'static [u8] = #ekey;
193 #encdec_func
194 }
195 };
196 result.into()
197}
198
199/// Encrypts the resp. string with the key set before, via calling [`use_litcrypt!`].
200#[proc_macro]
201pub fn lc(tokens: TokenStream) -> TokenStream {
202 let mut something = alloc::string::String::from("");
203 for tok in tokens {
204 something = match tok {
205 TokenTree::Literal(lit) => {
206 let mut lit_str: String = lit.to_string();
207 let first_occurrence = lit_str.find("\"");
208 let last_occurrence = lit_str.rfind("\"");
209
210 if !first_occurrence.is_none() && !last_occurrence.is_none(){
211 lit_str = lit_str[first_occurrence.unwrap() + 1..last_occurrence.unwrap()].to_string();
212 }
213 else {
214 lit_str = lit_str[1..lit_str.len() - 1].to_string();
215 }
216
217 lit_str
218 },
219 _ => "<unknown>".to_owned(),
220 }
221 }
222
223 encrypt_string(something)
224}
225
226/// Encrypts an environment variable at compile time with the key set before, via calling [`use_litcrypt!`].
227#[proc_macro]
228pub fn lc_env(tokens: TokenStream) -> TokenStream {
229 let mut var_name = String::from("");
230
231 for tok in tokens {
232 var_name = match tok {
233 TokenTree::Literal(lit) => lit.to_string(),
234 _ => "<unknown>".to_owned(),
235 }
236 }
237
238 var_name = String::from(&var_name[1..var_name.len() - 1]);
239
240 encrypt_string(env::var(var_name).unwrap_or(String::from("unknown")))
241}
242
243fn encrypt_string(something: String) -> TokenStream {
244 let magic_spell = get_magic_spell();
245 let encrypt_key = xor::xor(&magic_spell, b"ESJCTVgWH5HQFza7GdRx");
246 let encrypted = xor::xor(&something.as_bytes(), &encrypt_key);
247 let encrypted = Literal::byte_string(&encrypted);
248
249 let result = quote! {
250 crate::litcrypt_internal::decrypt_bytes(#encrypted, crate::LITCRYPT_ENCRYPT_KEY)
251 };
252
253 result.into()
254}