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}