Skip to content

Commit

Permalink
Accept string constants (#18)
Browse files Browse the repository at this point in the history
Using const fn the obfuscation can accept string constants, not just string literals.
  • Loading branch information
CasualX authored Sep 11, 2020
1 parent a571513 commit 944ef97
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 50 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ edition = "2018"
license = "MIT"

authors = ["Casper <CasualX@users.noreply.github.com>"]
description = "Compiletime string literal obfuscation for Rust"
description = "Compiletime string constant obfuscation for Rust"
documentation = "https://docs.rs/obfstr"
repository = "https://github.com/CasualX/obfstr"
readme = "readme.md"
Expand Down
29 changes: 11 additions & 18 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,39 +6,32 @@ String Obfuscation
[![docs.rs](https://docs.rs/obfstr/badge.svg)](https://docs.rs/obfstr)
[![Build Status](https://travis-ci.org/CasualX/obfstr.svg?branch=master)](https://travis-ci.org/CasualX/obfstr)

Compiletime string literal obfuscation for Rust.
Compiletime string constant obfuscation for Rust.

Examples
--------

The `obfstr!` macro returns a borrowed temporary and may not escape the statement it was used in:
The `obfstr!` macro returns the deobfuscated string constant:

```rust
assert_eq!(obfstr::obfstr!("Hello 🌍"), "Hello 🌍");
```

The `obflocal!` macro returns the `ObfBuffer` with the deobfuscated string and is more flexible but less ergonomic:

```rust
let str_buf = obfstr::obflocal!("Hello 🌍");
assert_eq!(str_buf.as_str(), "Hello 🌍");
```

The `obfconst!` macro returns the encrypted `ObfString` for use in constant expressions:
The string constant itself is embedded in obfuscated form and deobfuscated locally.
This reference to a temporary value must be used in the same statement it was generated.
See the documentation for more advanced use cases.

```rust
static GSTR: obfstr::ObfString<[u8; 10]> = obfstr::obfconst!("Hello 🌍");
assert_eq!(GSTR.deobfuscate(0).as_str(), "Hello 🌍");
assert_eq!(obfstr::obfstr!("Hello 🌍"), "Hello 🌍");
```

The `wide!` macro provides compile time utf16 string literals:
The `wide!` macro provides compiletime utf16 string constants:

```rust
let expected = &['W' as u16, 'i' as u16, 'd' as u16, 'e' as u16, 0];
assert_eq!(obfstr::wide!("Wide\0"), expected);
```

The `random!` macro provides compile time random values:
The `random!` macro provides compiletime random values:

Based on `file!()`, `line!()`, `column!()` and a fixed seed to ensure reproducibility.
This fixed seed is stored in the environment variable `OBFSTR_SEED` and can be changed as desired.

```rust
const RND: i32 = obfstr::random!(u8) as i32;
Expand Down
48 changes: 24 additions & 24 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*!
Compiletime string literal obfuscation.
Compiletime string constant obfuscation.
*/

#![allow(incomplete_features)]
Expand Down Expand Up @@ -59,7 +59,7 @@ pub const fn splitmix(seed: u64) -> u64 {
return z ^ (z >> 31);
}

/// Compiletime string hash.
/// Compiletime string constant hash.
///
/// Implemented using the [DJB2 hash function](http://www.cse.yorku.ca/~oz/hash.html#djb2).
#[inline(always)]
Expand All @@ -74,17 +74,17 @@ pub const fn hash(s: &str) -> u32 {
return result;
}

/// Compiletime string hash.
/// Compiletime string constant hash.
///
/// Helper macro guarantees compiletime evaluation of the string literal hash.
/// Helper macro guarantees compiletime evaluation of the string constant hash.
///
/// ```
/// const STRING: &str = "Hello World";
/// assert_eq!(obfstr::hash!(STRING), 1481604729);
/// ```
#[macro_export]
macro_rules! hash {
($string:expr) => {{ const HASH: u32 = $crate::hash($string); HASH }};
($s:expr) => {{ const HASH: u32 = $crate::hash($s); HASH }};
}

/// Produces pseudorandom entropy given the file, line and column information.
Expand Down Expand Up @@ -113,17 +113,17 @@ const fn next_round(mut x: u32) -> u32 {

//----------------------------------------------------------------

/// Wide string literal, returns an array of words.
/// Wide string constant, returns an array of words.
///
/// The type of the returned literal is `&'static [u16; LEN]`.
/// The type of the returned constant is `&'static [u16; LEN]`.
///
/// ```
/// let expected = &['W' as u16, 'i' as u16, 'd' as u16, 'e' as u16, 0];
/// assert_eq!(obfstr::wide!("Wide\0"), expected);
/// ```
#[macro_export]
macro_rules! wide {
($s:literal) => { &$crate::wide::<{$crate::wide_len($s)}>($s) };
($s:expr) => { &$crate::wide::<{$crate::wide_len($s)}>($s) };
}

#[doc(hidden)]
Expand Down Expand Up @@ -294,7 +294,7 @@ impl<const LEN: usize> ObfBuffer<[u8; LEN]> {
}
#[inline]
pub fn as_str(&self) -> &str {
// This should be safe as it can only be constructed from a string literal...
// This should be safe as it can only be constructed from a string constant...
#[cfg(debug_assertions)]
return str::from_utf8(&self.0).unwrap();
#[cfg(not(debug_assertions))]
Expand Down Expand Up @@ -408,54 +408,54 @@ impl<const LEN: usize> fmt::Debug for ObfBuffer<[u16; LEN]> {

//----------------------------------------------------------------

/// Compiletime string literal obfuscation.
/// Compiletime string constant obfuscation.
///
/// Returns a borrowed temporary and may not escape the statement it was used in.
///
/// Prefix the string literal with `L` to get an UTF-16 obfuscated string.
/// Prefix the string with `L` to get an UTF-16 obfuscated string.
///
/// ```
/// assert_eq!(obfstr::obfstr!("Hello 🌍"), "Hello 🌍");
/// ```
#[macro_export]
macro_rules! obfstr {
($s:literal) => { $crate::obflocal!($s).as_str() };
(L$s:literal) => { $crate::obflocal!(L$s).as_ref() };
($s:expr) => { $crate::obflocal!($s).as_str() };
(L$s:expr) => { $crate::obflocal!(L$s).as_ref() };
}

/// Compiletime string literal obfuscation.
/// Compiletime string constant obfuscation.
///
/// Returns the deobfuscated [`ObfBuffer`](struct.ObfBuffer.html) for assignment to local variable.
///
/// Prefix the string literal with `L` to get an UTF-16 obfuscated string.
/// Prefix the string with `L` to get an UTF-16 obfuscated string.
///
/// ```
/// let str_buf = obfstr::obflocal!("Hello 🌍");
/// assert_eq!(str_buf.as_str(), "Hello 🌍");
/// ```
#[macro_export]
macro_rules! obflocal {
($s:literal) => { $crate::obfconst!($s).deobfuscate($crate::random!(usize) & 0xffff) };
(L$s:literal) => { $crate::obfconst!(L$s).deobfuscate($crate::random!(usize) & 0xffff) };
($s:expr) => { $crate::obfconst!($s).deobfuscate($crate::random!(usize) & 0xffff) };
(L$s:expr) => { $crate::obfconst!(L$s).deobfuscate($crate::random!(usize) & 0xffff) };
}

/// Compiletime string literal obfuscation.
/// Compiletime string constant obfuscation.
///
/// Returns the obfuscated [`ObfString`](struct.ObfString.html) for use in constant expressions.
///
/// Prefix the string literal with `L` to get an UTF-16 obfuscated string.
/// Prefix the string with `L` to get an UTF-16 obfuscated string.
///
/// ```
/// static GSTR: obfstr::ObfString<[u8; 10]> = obfstr::obfconst!("Hello 🌍");
/// assert_eq!(GSTR.deobfuscate(0).as_str(), "Hello 🌍");
/// ```
#[macro_export]
macro_rules! obfconst {
($s:literal) => {{ const STRING: $crate::ObfString<[u8; {$s.len()}]> = $crate::ObfString::<[u8; {$s.len()}]>::obfuscate($crate::random!(u32), $s); STRING }};
(L$s:literal) => {{ const STRING: $crate::ObfString<[u16; {$crate::wide_len($s)}]> = $crate::ObfString::<[u16; {$crate::wide_len($s)}]>::obfuscate($crate::random!(u32), $s); STRING }};
($s:expr) => {{ const STRING: $crate::ObfString<[u8; {$s.len()}]> = $crate::ObfString::<[u8; {$s.len()}]>::obfuscate($crate::random!(u32), $s); STRING }};
(L$s:expr) => {{ const STRING: $crate::ObfString<[u16; {$crate::wide_len($s)}]> = $crate::ObfString::<[u16; {$crate::wide_len($s)}]>::obfuscate($crate::random!(u32), $s); STRING }};
}

/// Check if string equals specific string literal.
/// Check if string equals specific string constant.
///
/// This does not need to decrypt the string before comparison and the comparison is not constant-time.
///
Expand All @@ -465,6 +465,6 @@ macro_rules! obfconst {
/// ```
#[macro_export]
macro_rules! obfeq {
($e:expr, $s:literal) => { $crate::obfconst!($s).eq(&$e, $crate::random!(usize) & 0xffff) };
($e:expr, L$s:literal) => { $crate::obfconst!(L$s).eq($e, $crate::random!(usize) & 0xffff) };
($e:expr, $s:expr) => { $crate::obfconst!($s).eq(&$e, $crate::random!(usize) & 0xffff) };
($e:expr, L$s:expr) => { $crate::obfconst!(L$s).eq($e, $crate::random!(usize) & 0xffff) };
}
16 changes: 9 additions & 7 deletions tests/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,18 @@ fn main() {
let (a, b) = (random!(u64), random!(u64));
assert_ne!(a, b);

assert_eq!(
obfstr!("This literal is very very very long to see if it correctly handles long string"),
"This literal is very very very long to see if it correctly handles long string");
const LONG_STRING: &str = "This literal is very very very long to see if it correctly handles long string";
assert_eq!(obfstr!(LONG_STRING), LONG_STRING);

assert_eq!(obfstr!("\u{20}\0"), " \0");
assert_eq!(obfstr!("\"\n\t\\\'\""), "\"\n\t\\\'\"");

assert_eq!(obfstr!(L"ABC"), &[b'A' as u16, b'B' as u16, b'C' as u16]);
assert_eq!(obfstr!(L"🌍"), &[0xd83c, 0xdf0d]);
const ABC: &str = "ABC";
const WORLD: &str = "🌍";

assert!(obfeq!(wide!("ABC"), L"ABC"));
assert!(obfeq!(wide!("🌍"), L"🌍"));
assert_eq!(obfstr!(L ABC), &[b'A' as u16, b'B' as u16, b'C' as u16]);
assert_eq!(obfstr!(L WORLD), &[0xd83c, 0xdf0d]);

assert!(obfeq!(wide!(ABC), L ABC));
assert!(obfeq!(wide!(WORLD), L WORLD));
}

0 comments on commit 944ef97

Please sign in to comment.