use core::ptr::{read_volatile, write};
#[macro_export]
macro_rules! obfstr {
($(let $name:ident = $s:expr;)*) => {$(
$crate::obfbytes! { let $name = ::core::primitive::str::as_bytes($s); }
let $name = $crate::unsafe_as_str($name);
)*};
($name:ident = $s:expr) => {
$crate::unsafe_as_str($crate::obfbytes!($name = ::core::primitive::str::as_bytes($s)))
};
($buf:ident <- $s:expr) => {
$crate::unsafe_as_str($crate::obfbytes!($buf <- ::core::primitive::str::as_bytes($s)))
};
($s:expr) => {
$crate::unsafe_as_str($crate::obfbytes!(::core::primitive::str::as_bytes($s)))
};
}
#[macro_export]
macro_rules! obfcstr {
($(let $name:ident = $s:expr;)*) => {$(
$crate::obfbytes! { let $name = ::core::ffi::CStr::to_bytes_with_nul($s); }
let $name = $crate::unsafe_as_cstr($name);
)*};
($name:ident = $s:expr) => {
$crate::unsafe_as_cstr($crate::obfbytes!($name = ::core::ffi::CStr::to_bytes_with_nul($s)))
};
($buf:ident <- $s:expr) => {
$crate::unsafe_as_cstr($crate::obfbytes!($buf <- ::core::ffi::CStr::to_bytes_with_nul($s)))
};
($s:expr) => {
$crate::unsafe_as_cstr($crate::obfbytes!(::core::ffi::CStr::to_bytes_with_nul($s)))
};
}
#[macro_export]
macro_rules! obfstring {
($s:expr) => {
String::from($crate::obfstr!($s))
};
}
#[macro_export]
macro_rules! obfnum {
($val:expr) => {{
use ::core::primitive::*;
const _OBFNUM_SIZE: usize = ::core::mem::size_of_val(&{ $val });
const _OBFNUM_PLAIN: [u8; _OBFNUM_SIZE] = { $val }.to_ne_bytes();
const _OBFNUM_KEYSTREAM: [u8; _OBFNUM_SIZE] = $crate::bytes::keystream::<_OBFNUM_SIZE>(
$crate::random!(u32, "obfnum_key", stringify!($val)),
);
static _OBFNUM_SDATA: [u8; _OBFNUM_SIZE] =
$crate::bytes::obfuscate::<_OBFNUM_SIZE>(&_OBFNUM_PLAIN, &_OBFNUM_KEYSTREAM);
let _obfnum_deobf: [u8; _OBFNUM_SIZE] = $crate::bytes::deobfuscate::<_OBFNUM_SIZE>(
$crate::xref::xref::<
_,
{ $crate::random!(u32, "obfnum_offset", stringify!($val)) },
{ $crate::random!(u64, "obfnum_xref", stringify!($val)) },
>(&_OBFNUM_SDATA),
&_OBFNUM_KEYSTREAM,
);
unsafe { $crate::bytes::transmute_number_like(|| { $val }, _obfnum_deobf) }
}};
}
#[macro_export]
macro_rules! obfbytes {
($(let $name:ident = $s:expr;)*) => {
$(let ref $name = $crate::__obfbytes!($s);)*
};
($name:ident = $s:expr) => {{
$name = $crate::__obfbytes!($s);
&$name
}};
($buf:ident <- $s:expr) => {{
let buf = &mut $buf[..$s.len()];
buf.copy_from_slice(&$crate::__obfbytes!($s));
buf
}};
($s:expr) => {
&$crate::__obfbytes!($s)
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __obfbytes {
($s:expr) => {{
use ::core::primitive::*;
const _OBFBYTES_STRING: &[u8] = $s;
const _OBFBYTES_LEN: usize = _OBFBYTES_STRING.len();
const _OBFBYTES_KEYSTREAM: [u8; _OBFBYTES_LEN] =
$crate::bytes::keystream::<_OBFBYTES_LEN>($crate::random!(u32, "key", stringify!($s)));
static _OBFBYTES_SDATA: [u8; _OBFBYTES_LEN] =
$crate::bytes::obfuscate::<_OBFBYTES_LEN>(_OBFBYTES_STRING, &_OBFBYTES_KEYSTREAM);
$crate::bytes::deobfuscate::<_OBFBYTES_LEN>(
$crate::xref::xref::<
_,
{ $crate::random!(u32, "offset", stringify!($s)) },
{ $crate::random!(u64, "xref", stringify!($s)) },
>(&_OBFBYTES_SDATA),
&_OBFBYTES_KEYSTREAM,
)
}};
}
#[inline(always)]
const fn next_round(mut x: u32) -> u32 {
x ^= x << 13;
x ^= x >> 17;
x ^= x << 5;
return x;
}
#[inline(always)]
pub const fn keystream<const LEN: usize>(key: u32) -> [u8; LEN] {
let mut keys = [0u8; LEN];
let mut round_key = key;
let mut i = 0;
while i < LEN & !3 {
round_key = next_round(round_key);
let kb = round_key.to_ne_bytes();
keys[i + 0] = kb[0];
keys[i + 1] = kb[1];
keys[i + 2] = kb[2];
keys[i + 3] = kb[3];
i += 4;
}
round_key = next_round(round_key);
let kb = round_key.to_ne_bytes();
match LEN % 4 {
1 => {
keys[i + 0] = kb[0];
}
2 => {
keys[i + 0] = kb[0];
keys[i + 1] = kb[1];
}
3 => {
keys[i + 0] = kb[0];
keys[i + 1] = kb[1];
keys[i + 2] = kb[2];
}
_ => (),
}
return keys;
}
#[inline(always)]
pub const fn obfuscate<const LEN: usize>(s: &[u8], k: &[u8; LEN]) -> [u8; LEN] {
if s.len() != LEN {
panic!("input string len not equal to key stream len");
}
let mut data = [0u8; LEN];
let mut i = 0usize;
while i < LEN {
data[i] = s[i] ^ k[i];
i += 1;
}
return data;
}
#[inline(always)]
pub fn deobfuscate<const LEN: usize>(s: &[u8; LEN], k: &[u8; LEN]) -> [u8; LEN] {
let mut buf = [0u8; LEN];
let mut i = 0;
unsafe {
let src = s.as_ptr();
let dest = buf.as_mut_ptr();
#[cfg(target_pointer_width = "64")]
while i < LEN & !7 {
let ct = read_volatile(src.offset(i as isize) as *const [u8; 8]);
let tmp = u64::from_ne_bytes([ct[0], ct[1], ct[2], ct[3], ct[4], ct[5], ct[6], ct[7]])
^ 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],
]);
write(dest.offset(i as isize) as *mut [u8; 8], tmp.to_ne_bytes());
i += 8;
}
while i < LEN & !3 {
let ct = read_volatile(src.offset(i as isize) as *const [u8; 4]);
let tmp = u32::from_ne_bytes([ct[0], ct[1], ct[2], ct[3]])
^ u32::from_ne_bytes([k[i + 0], k[i + 1], k[i + 2], k[i + 3]]);
write(dest.offset(i as isize) as *mut [u8; 4], tmp.to_ne_bytes());
i += 4;
}
match LEN % 4 {
1 => {
let ct = read_volatile(src.offset(i as isize));
write(dest.offset(i as isize), ct ^ k[i]);
}
2 => {
let ct = read_volatile(src.offset(i as isize) as *const [u8; 2]);
write(
dest.offset(i as isize) as *mut [u8; 2],
[ct[0] ^ k[i + 0], ct[1] ^ k[i + 1]],
);
}
3 => {
let ct = read_volatile(src.offset(i as isize) as *const [u8; 3]);
write(
dest.offset(i as isize) as *mut [u8; 2],
[ct[0] ^ k[i + 0], ct[1] ^ k[i + 1]],
);
write(dest.offset(i as isize + 2), ct[2] ^ k[i + 2]);
}
_ => (),
}
}
return buf;
}
#[doc(hidden)]
#[inline(always)]
pub unsafe fn transmute_number_like<T, F, const LEN: usize>(_: F, bytes: [u8; LEN]) -> T
where
F: FnOnce() -> T,
{
assert!(::core::mem::size_of::<T>() == LEN);
let mut value = ::core::mem::MaybeUninit::<T>::uninit();
unsafe {
::core::ptr::copy_nonoverlapping(bytes.as_ptr(), value.as_mut_ptr() as *mut u8, LEN);
value.assume_init()
}
}
#[inline(always)]
pub fn equals<const LEN: usize>(s: &[u8; LEN], k: &[u8; LEN], other: &[u8]) -> bool {
if other.len() != LEN {
return false;
}
let mut i = 0;
unsafe {
let src = s.as_ptr();
#[cfg(target_pointer_width = "64")]
while i < LEN & !7 {
let ct = read_volatile(src.offset(i as isize) as *const [u8; 8]);
let tmp = u64::from_ne_bytes([ct[0], ct[1], ct[2], ct[3], ct[4], ct[5], ct[6], ct[7]])
^ 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],
]);
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],
]);
if tmp != other {
return false;
}
i += 8;
}
while i < LEN & !3 {
let ct = read_volatile(src.offset(i as isize) as *const [u8; 4]);
let tmp = u32::from_ne_bytes([ct[0], ct[1], ct[2], ct[3]])
^ u32::from_ne_bytes([k[i + 0], k[i + 1], k[i + 2], k[i + 3]]);
let other =
u32::from_ne_bytes([other[i + 0], other[i + 1], other[i + 2], other[i + 3]]);
if tmp != other {
return false;
}
i += 4;
}
match LEN % 4 {
1 => {
let ct = read_volatile(src.offset(i as isize));
ct ^ k[i] == other[i]
}
2 => {
let ct = read_volatile(src.offset(i as isize) as *const [u8; 2]);
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]])
}
3 => {
let ct = read_volatile(src.offset(i as isize) as *const [u8; 3]);
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])
}
_ => true,
}
}
}
#[test]
fn test_remaining_bytes() {
const STRING: &[u8] = b"01234567ABCDEFGHI";
fn test<const LEN: usize>(key: u32) {
let keys = keystream::<LEN>(key);
let data = obfuscate::<LEN>(&STRING[..LEN], &keys);
let buffer = deobfuscate::<LEN>(&data, &keys);
assert_ne!(&data[..], &STRING[..LEN]);
assert_eq!(&buffer[..], &STRING[..LEN]);
assert!(equals::<LEN>(&data, &keys, &STRING[..LEN]));
}
test::<8>(0x1111);
test::<9>(0x2222);
test::<10>(0x3333);
test::<11>(0x4444);
test::<12>(0x5555);
test::<13>(0x6666);
test::<14>(0x7777);
test::<15>(0x8888);
test::<16>(0x9999);
}
#[test]
fn test_equals() {
const STRING: &str = "Hello ðŸŒ";
const LEN: usize = STRING.len();
const KEYSTREAM: [u8; LEN] = keystream::<LEN>(0x10203040);
const OBFSTRING: [u8; LEN] = obfuscate::<LEN>(STRING.as_bytes(), &KEYSTREAM);
assert!(equals::<LEN>(&OBFSTRING, &KEYSTREAM, STRING.as_bytes()));
}
#[test]
fn test_obfstr_let() {
obfstr! {
let abc = "abc";
let def = "defdef";
}
assert_eq!(abc, "abc");
assert_eq!(def, "defdef");
}
#[test]
fn test_obfstr_const() {
assert_eq!(obfstr!("\u{20}\0"), " \0");
assert_eq!(obfstr!("\"\n\t\\\'\""), "\"\n\t\\\'\"");
const LONG_STRING: &str =
"This literal is very very very long to see if it correctly handles long strings";
assert_eq!(obfstr!(LONG_STRING), LONG_STRING);
const ABC: &str = "ABC";
const WORLD: &str = "🌍";
assert_eq!(obfbytes!(ABC.as_bytes()), "ABC".as_bytes());
assert_eq!(obfbytes!(WORLD.as_bytes()), "🌍".as_bytes());
}
#[test]
fn test_obfstring() {
let s = obfstring!("Hello");
assert_eq!(s, "Hello");
assert_eq!(s.len(), 5);
assert_eq!(obfstring!(""), "");
}
#[test]
fn test_obfstr_empty() {
assert_eq!(obfstr!(""), "");
assert_eq!(obfbytes!(b""), b"");
}
#[test]
fn test_zero_lengths() {
let k: [u8; 0] = keystream::<0>(42);
assert_eq!(k.len(), 0);
let s: [u8; 0] = [];
let k: [u8; 0] = [];
let result: [u8; 0] = deobfuscate::<0>(&s, &k);
assert_eq!(result.len(), 0);
}
#[test]
fn test_obfnum_types() {
assert_eq!(
{
let v: u8 = obfnum!(0u8);
v
},
0u8
);
assert_eq!(
{
let v: u16 = obfnum!(0u16);
v
},
0u16
);
assert_eq!(
{
let v: u32 = obfnum!(0u32);
v
},
0u32
);
assert_eq!(
{
let v: u64 = obfnum!(0u64);
v
},
0u64
);
assert_eq!(
{
let v: u128 = obfnum!(0u128);
v
},
0u128
);
assert_eq!(
{
let v: usize = obfnum!(0usize);
v
},
0usize
);
assert_eq!(
{
let v: u8 = obfnum!(255u8);
v
},
255u8
);
assert_eq!(
{
let v: u16 = obfnum!(0xABCDu16);
v
},
0xABCDu16
);
assert_eq!(
{
let v: u32 = obfnum!(0xDEADBEEFu32);
v
},
0xDEADBEEFu32
);
assert_eq!(
{
let v: u64 = obfnum!(u64::MAX);
v
},
u64::MAX
);
assert_eq!(
{
let v: u128 = obfnum!(u128::MAX);
v
},
u128::MAX
);
assert_eq!(
{
let v: i8 = obfnum!(0i8);
v
},
0i8
);
assert_eq!(
{
let v: i16 = obfnum!(0i16);
v
},
0i16
);
assert_eq!(
{
let v: i32 = obfnum!(0i32);
v
},
0i32
);
assert_eq!(
{
let v: i64 = obfnum!(0i64);
v
},
0i64
);
assert_eq!(
{
let v: i128 = obfnum!(0i128);
v
},
0i128
);
assert_eq!(
{
let v: isize = obfnum!(0isize);
v
},
0isize
);
assert_eq!(
{
let v: i8 = obfnum!(-128i8);
v
},
-128i8
);
assert_eq!(
{
let v: i8 = obfnum!(127i8);
v
},
127i8
);
assert_eq!(
{
let v: i32 = obfnum!(-1i32);
v
},
-1i32
);
assert_eq!(
{
let v: i32 = obfnum!(i32::MIN);
v
},
i32::MIN
);
assert_eq!(
{
let v: i32 = obfnum!(i32::MAX);
v
},
i32::MAX
);
assert_eq!(
{
let v: f32 = obfnum!(0.0f32);
v
},
0.0f32
);
assert_eq!(
{
let v: f32 = obfnum!(1.0f32);
v
},
1.0f32
);
assert_eq!(
{
let v: f32 = obfnum!(-1.0f32);
v
},
-1.0f32
);
assert_eq!(
{
let v: f64 = obfnum!(0.0f64);
v
},
0.0f64
);
assert_eq!(
{
let v: f64 = obfnum!(1.0f64);
v
},
1.0f64
);
assert_eq!(
{
let v: f64 = obfnum!(f64::INFINITY);
v
},
f64::INFINITY
);
assert_eq!(
{
let v: f64 = obfnum!(f64::NEG_INFINITY);
v
},
f64::NEG_INFINITY
);
assert_eq!(
{
let x = 50;
x + obfnum!(20_i32)
},
70
);
}