1use core::ptr::{read_volatile, write};
7
8#[macro_export]
54macro_rules! obfstr {
55 ($(let $name:ident = $s:expr;)*) => {$(
56 $crate::obfbytes! { let $name = ::core::convert::identity::<&str>($s).as_bytes(); }
57 let $name = $crate::unsafe_as_str($name);
58 )*};
59 ($name:ident = $s:expr) => {
60 $crate::unsafe_as_str($crate::obfbytes!($name = ::core::convert::identity::<&str>($s).as_bytes()))
61 };
62 ($buf:ident <- $s:expr) => {
63 $crate::unsafe_as_str($crate::obfbytes!($buf <- ::core::convert::identity::<&str>($s).as_bytes()))
64 };
65 ($s:expr) => {
66 $crate::unsafe_as_str($crate::obfbytes!(::core::convert::identity::<&str>($s).as_bytes()))
67 };
68}
69
70#[macro_export]
82macro_rules! obfcstr {
83 ($(let $name:ident = $s:expr;)*) => {$(
84 $crate::obfbytes! { let $name = ::core::ffi::CStr::to_bytes_with_nul($s); }
85 let $name = $crate::unsafe_as_cstr($name);
86 )*};
87 ($name:ident = $s:expr) => {
88 $crate::unsafe_as_cstr($crate::obfbytes!($name = ::core::ffi::CStr::to_bytes_with_nul($s)))
89 };
90 ($buf:ident <- $s:expr) => {
91 $crate::unsafe_as_cstr($crate::obfbytes!($buf <- ::core::ffi::CStr::to_bytes_with_nul($s)))
92 };
93 ($s:expr) => {
94 $crate::unsafe_as_cstr($crate::obfbytes!(::core::ffi::CStr::to_bytes_with_nul($s)))
95 };
96}
97
98#[macro_export]
104macro_rules! obfstring {
105 ($s:expr) => {
106 String::from($crate::obfstr!($s))
107 };
108}
109
110#[macro_export]
112macro_rules! obfbytes {
113 ($(let $name:ident = $s:expr;)*) => {
114 $(let ref $name = $crate::__obfbytes!($s);)*
115 };
116 ($name:ident = $s:expr) => {{
117 $name = $crate::__obfbytes!($s);
118 &$name
119 }};
120 ($buf:ident <- $s:expr) => {{
121 let buf = &mut $buf[..$s.len()];
122 buf.copy_from_slice(&$crate::__obfbytes!($s));
123 buf
124 }};
125 ($s:expr) => {
126 &$crate::__obfbytes!($s)
127 };
128}
129
130#[doc(hidden)]
131#[macro_export]
132macro_rules! __obfbytes {
133 ($s:expr) => {{
134 const _OBFBYTES_STRING: &[u8] = $s;
135 const _OBFBYTES_LEN: usize = _OBFBYTES_STRING.len();
136 const _OBFBYTES_KEYSTREAM: [u8; _OBFBYTES_LEN] = $crate::bytes::keystream::<_OBFBYTES_LEN>($crate::random!(u32, "key", stringify!($s)));
137 static _OBFBYTES_SDATA: [u8; _OBFBYTES_LEN] = $crate::bytes::obfuscate::<_OBFBYTES_LEN>(_OBFBYTES_STRING, &_OBFBYTES_KEYSTREAM);
138 $crate::bytes::deobfuscate::<_OBFBYTES_LEN>(
139 $crate::xref::xref::<_,
140 {$crate::random!(u32, "offset", stringify!($s))},
141 {$crate::random!(u64, "xref", stringify!($s))}>
142 (&_OBFBYTES_SDATA),
143 &_OBFBYTES_KEYSTREAM)
144 }};
145}
146
147#[inline(always)]
150const fn next_round(mut x: u32) -> u32 {
151 x ^= x << 13;
152 x ^= x >> 17;
153 x ^= x << 5;
154 return x;
155}
156
157#[inline(always)]
159pub const fn keystream<const LEN: usize>(key: u32) -> [u8; LEN] {
160 let mut keys = [0u8; LEN];
161 let mut round_key = key;
162 let mut i = 0;
163 while i < LEN & !3 {
165 round_key = next_round(round_key);
166 let kb = round_key.to_ne_bytes();
167 keys[i + 0] = kb[0];
168 keys[i + 1] = kb[1];
169 keys[i + 2] = kb[2];
170 keys[i + 3] = kb[3];
171 i += 4;
172 }
173 round_key = next_round(round_key);
175 let kb = round_key.to_ne_bytes();
176 match LEN % 4 {
177 1 => {
178 keys[i + 0] = kb[0];
179 },
180 2 => {
181 keys[i + 0] = kb[0];
182 keys[i + 1] = kb[1];
183 },
184 3 => {
185 keys[i + 0] = kb[0];
186 keys[i + 1] = kb[1];
187 keys[i + 2] = kb[2];
188 },
189 _ => (),
190 }
191 return keys;
192}
193
194#[inline(always)]
196pub const fn obfuscate<const LEN: usize>(s: &[u8], k: &[u8; LEN]) -> [u8; LEN] {
197 if s.len() != LEN {
198 panic!("input string len not equal to key stream len");
199 }
200 let mut data = [0u8; LEN];
201 let mut i = 0usize;
202 while i < LEN {
203 data[i] = s[i] ^ k[i];
204 i += 1;
205 }
206 return data;
207}
208
209#[inline(always)]
211pub fn deobfuscate<const LEN: usize>(s: &[u8; LEN], k: &[u8; LEN]) -> [u8; LEN] {
212 let mut buf = [0u8; LEN];
213 let mut i = 0;
214 unsafe {
219 let src = s.as_ptr();
220 let dest = buf.as_mut_ptr();
221 #[cfg(target_pointer_width = "64")]
223 while i < LEN & !7 {
224 let ct = read_volatile(src.offset(i as isize) as *const [u8; 8]);
225 let tmp = u64::from_ne_bytes([ct[0], ct[1], ct[2], ct[3], ct[4], ct[5], ct[6], ct[7]]) ^
226 u64::from_ne_bytes([k[i + 0], k[i + 1], k[i + 2], k[i + 3], k[i + 4], k[i + 5], k[i + 6], k[i + 7]]);
227 write(dest.offset(i as isize) as *mut [u8; 8], tmp.to_ne_bytes());
228 i += 8;
229 }
230 while i < LEN & !3 {
232 let ct = read_volatile(src.offset(i as isize) as *const [u8; 4]);
233 let tmp = u32::from_ne_bytes([ct[0], ct[1], ct[2], ct[3]]) ^
234 u32::from_ne_bytes([k[i + 0], k[i + 1], k[i + 2], k[i + 3]]);
235 write(dest.offset(i as isize) as *mut [u8; 4], tmp.to_ne_bytes());
236 i += 4;
237 }
238 match LEN % 4 {
240 1 => {
241 let ct = read_volatile(src.offset(i as isize));
242 write(dest.offset(i as isize), ct ^ k[i]);
243 },
244 2 => {
245 let ct = read_volatile(src.offset(i as isize) as *const [u8; 2]);
246 write(dest.offset(i as isize) as *mut [u8; 2], [
247 ct[0] ^ k[i + 0],
248 ct[1] ^ k[i + 1],
249 ]);
250 },
251 3 => {
252 let ct = read_volatile(src.offset(i as isize) as *const [u8; 3]);
253 write(dest.offset(i as isize) as *mut [u8; 2], [
254 ct[0] ^ k[i + 0],
255 ct[1] ^ k[i + 1],
256 ]);
257 write(dest.offset(i as isize + 2), ct[2] ^ k[i + 2]);
258 },
259 _ => (),
260 }
261 }
262 return buf;
263}
264
265#[inline(always)]
266pub fn equals<const LEN: usize>(s: &[u8; LEN], k: &[u8; LEN], other: &[u8]) -> bool {
267 if other.len() != LEN {
268 return false;
269 }
270 let mut i = 0;
271 unsafe {
276 let src = s.as_ptr();
277 #[cfg(target_pointer_width = "64")]
279 while i < LEN & !7 {
280 let ct = read_volatile(src.offset(i as isize) as *const [u8; 8]);
281 let tmp = u64::from_ne_bytes([ct[0], ct[1], ct[2], ct[3], ct[4], ct[5], ct[6], ct[7]]) ^
282 u64::from_ne_bytes([k[i + 0], k[i + 1], k[i + 2], k[i + 3], k[i + 4], k[i + 5], k[i + 6], k[i + 7]]);
283 let other = u64::from_ne_bytes([other[i + 0], other[i + 1], other[i + 2], other[i + 3], other[i + 4], other[i + 5], other[i + 6], other[i + 7]]);
284 if tmp != other {
285 return false;
286 }
287 i += 8;
288 }
289 while i < LEN & !3 {
291 let ct = read_volatile(src.offset(i as isize) as *const [u8; 4]);
292 let tmp = u32::from_ne_bytes([ct[0], ct[1], ct[2], ct[3]]) ^
293 u32::from_ne_bytes([k[i + 0], k[i + 1], k[i + 2], k[i + 3]]);
294 let other = u32::from_ne_bytes([other[i + 0], other[i + 1], other[i + 2], other[i + 3]]);
295 if tmp != other {
296 return false;
297 }
298 i += 4;
299 }
300 match LEN % 4 {
302 1 => {
303 let ct = read_volatile(src.offset(i as isize));
304 ct ^ k[i] == other[i]
305 },
306 2 => {
307 let ct = read_volatile(src.offset(i as isize) as *const [u8; 2]);
308 u16::from_ne_bytes([ct[0], ct[1]]) ^ u16::from_ne_bytes([k[i + 0], k[i + 1]]) == u16::from_ne_bytes([other[i + 0], other[i + 1]])
309 },
310 3 => {
311 let ct = read_volatile(src.offset(i as isize) as *const [u8; 3]);
312 u32::from_ne_bytes([ct[0], ct[1], ct[2], 0]) ^ u32::from_ne_bytes([k[i + 0], k[i + 1], k[i + 2], 0]) == u32::from_ne_bytes([other[i + 0], other[i + 1], other[i + 2], 0])
313 },
314 _ => true,
315 }
316 }
317}
318
319#[test]
321fn test_remaining_bytes() {
322 const STRING: &[u8] = b"01234567ABCDEFGHI";
323 fn test<const LEN: usize>(key: u32) {
324 let keys = keystream::<LEN>(key);
325 let data = obfuscate::<LEN>(&STRING[..LEN], &keys);
326 let buffer = deobfuscate::<LEN>(&data, &keys);
327 assert_ne!(&data[..], &STRING[..LEN]);
329 assert_eq!(&buffer[..], &STRING[..LEN]);
331 assert!(equals::<LEN>(&data, &keys, &STRING[..LEN]));
333 }
334 test::<8>(0x1111);
335 test::<9>(0x2222);
336 test::<10>(0x3333);
337 test::<11>(0x4444);
338 test::<12>(0x5555);
339 test::<13>(0x6666);
340 test::<14>(0x7777);
341 test::<15>(0x8888);
342 test::<16>(0x9999);
343}
344
345#[test]
346fn test_equals() {
347 const STRING: &str = "Hello ðŸŒ";
348 const LEN: usize = STRING.len();
349 const KEYSTREAM: [u8; LEN] = keystream::<LEN>(0x10203040);
350 const OBFSTRING: [u8; LEN] = obfuscate::<LEN>(STRING.as_bytes(), &KEYSTREAM);
351 assert!(equals::<LEN>(&OBFSTRING, &KEYSTREAM, STRING.as_bytes()));
352}
353
354#[test]
355fn test_obfstr_let() {
356 obfstr! {
357 let abc = "abc";
358 let def = "defdef";
359 }
360 assert_eq!(abc, "abc");
361 assert_eq!(def, "defdef");
362}
363
364#[test]
365fn test_obfstr_const() {
366 assert_eq!(obfstr!("\u{20}\0"), " \0");
367 assert_eq!(obfstr!("\"\n\t\\\'\""), "\"\n\t\\\'\"");
368
369 const LONG_STRING: &str = "This literal is very very very long to see if it correctly handles long strings";
370 assert_eq!(obfstr!(LONG_STRING), LONG_STRING);
371
372 const ABC: &str = "ABC";
373 const WORLD: &str = "🌍";
374
375 assert_eq!(obfbytes!(ABC.as_bytes()), "ABC".as_bytes());
376 assert_eq!(obfbytes!(WORLD.as_bytes()), "🌍".as_bytes());
377}