obfuse-rs
Compile-time string encryption for Rust with runtime decryption and secure memory wiping.
Security Notice: This library provides string obfuscation, not military-grade encryption. The encryption key is embedded in the binary alongside the ciphertext. A determined attacker with access to your binary can extract both.
Appropriate uses:
- Preventing casual inspection of binaries (
stringscommand, hex editors)- Stopping automated string extraction tools
- Basic protection against unsophisticated reverse engineering
NOT appropriate for:
- Protecting highly sensitive secrets (use proper secrets management)
- Compliance requirements (PCI-DSS, HIPAA, SOC2, etc.)
- Scenarios where key extraction would be catastrophic
Features
- Compile-time encryption: Strings are encrypted during compilation, never stored in plaintext in binaries
- Multiple encryption algorithms: Choose via Cargo features
aes-256-gcm(default) - AES-256 in GCM modeaes-128-gcm- AES-128 in GCM modechacha20-poly1305- ChaCha20-Poly1305 AEADxor- Simple XOR (fast, less secure, good for obfuscation)
- Secure memory handling: Volatile zeroing of sensitive data on drop
- Zero-copy decryption: Decrypt only when accessed
- No runtime dependencies: Encryption happens at compile time
Installation
Add to your Cargo.toml:
[]
= "0.1"
Selecting Encryption Algorithm
By default, aes-256-gcm is used. To use a different algorithm:
# Use AES-128
[]
= { = "0.1", = false, = ["aes-128-gcm"] }
# Use ChaCha20-Poly1305
[]
= { = "0.1", = false, = ["chacha20-poly1305"] }
# Use XOR (fast obfuscation, not cryptographically secure)
[]
= { = "0.1", = false, = ["xor"] }
Usage
Basic Usage
use obfuse;
With Explicit Type
use ;
Lazy Decryption
use obfuse;
Error Handling
For defensive programming, use the fallible API:
use ;
Or with ? operator:
use ;
How It Works
-
Compile Time: The
obfuse!macro:- Generates a random encryption key and nonce
- Encrypts the string literal using the selected algorithm
- Embeds encrypted bytes, key, and nonce in the binary
-
Runtime: The
ObfuseStrtype:- Stores encrypted data until accessed
- Decrypts on first call to
as_str()orDeref - Caches decrypted value for subsequent accesses
-
Drop: When
ObfuseStris dropped:- Uses
std::ptr::write_volatileto zero all sensitive memory - Zeros: encryption key, nonce, and decrypted plaintext
- Prevents compiler from optimizing away the zeroing
- Uses
Build Modes: Random vs Deterministic
This library supports two build modes for different use cases:
Default: Random Key (Recommended for Production)
// Random key generated each compile - different binary every build
let secret = obfuse!;
println!; // Auto-decrypts
Build 1: key = [0xab, 0xcd, ...] (random)
Build 2: key = [0x12, 0x34, ...] (different random)
Build 3: key = [0x9f, 0xe2, ...] (different random)
Benefits:
- Each build produces unique encryption
- Harder for attackers to create universal decryption tools
- Best obfuscation for production binaries
With Seed: Deterministic Key (For Testing/CI)
// Same seed = same key = reproducible output
let secret = obfuse!;
println!; // Auto-decrypts (same as random mode)
Build 1 (seed="test"): key = [0xaa, 0xbb, ...] (deterministic)
Build 2 (seed="test"): key = [0xaa, 0xbb, ...] (same!)
Build 3 (seed="prod"): key = [0xcc, 0xdd, ...] (different seed = different key)
Benefits:
- Reproducible builds for CI/CD pipelines
- Testable encrypted output
- Debugging with known encryption state
Which Mode Should You Use?
| Use Case | Recommended |
|---|---|
| Production builds | obfuse!("...") (random) |
| Unit tests | obfuse!("...", seed = "test") |
| CI/CD pipelines | obfuse!("...", seed = "ci") |
| Debugging encryption issues | obfuse!("...", seed = "debug") |
Important: Both Modes Are Obfuscation
┌─────────────────────────────────────────────────────┐
│ Your Binary (Both Modes) │
├─────────────────────────────────────────────────────┤
│ Encrypted Data: [0x4a, 0x7f, 0x2c, ...] │
│ Encryption Key: [0xab, 0xcd, 0xef, ...] ← HERE │
│ Nonce: [0x11, 0x22, 0x33, ...] │
└─────────────────────────────────────────────────────┘
Key is ALWAYS embedded in binary
This is OBFUSCATION, not real encryption
For real secret protection, use runtime secrets management (environment variables, Vault, AWS Secrets Manager).
Security Considerations
What This Protects Against
- Static binary analysis (strings command, hex editors)
- Simple memory dumps of unaccessed secrets
- Accidental logging of encrypted values
What This Does NOT Protect Against
- Runtime memory inspection while string is in use
- Sophisticated reverse engineering
- Side-channel attacks
- Compromised systems with debugging access
Best Practices
- Minimize lifetime: Keep
ObfuseStrin scope only while needed - Avoid cloning: Don't clone decrypted strings unnecessarily
- Use strong algorithms: Prefer
aes-256-gcmorchacha20-poly1305for real security - Defense in depth: Use as one layer of protection, not the only one
API Reference
obfuse! Macro
// Random key (production)
obfuse! // Deterministic key (testing/CI)
obfuse!
Encrypts a string literal at compile time.
- Without seed: Random key each compile (non-reproducible)
- With seed: Deterministic key derived from seed (reproducible)
ObfuseStr Type
ObfuseStrError Type
/// Errors that can occur during ObfuseStr decryption
Project Structure
obfuse-rs/
├── Cargo.toml # Workspace configuration
├── README.md
├── obfuse/ # Main library crate (re-exports)
│ ├── Cargo.toml
│ └── src/lib.rs
├── obfuse-macros/ # Procedural macro crate
│ ├── Cargo.toml
│ └── src/lib.rs
└── obfuse-core/ # Core encryption/decryption logic
├── Cargo.toml
└── src/
├── lib.rs
├── obfuse_str.rs # ObfuseStr type implementation
├── aes.rs # AES encryption
├── chacha.rs # ChaCha20 encryption
└── xor.rs # XOR encryption
Building
# Build with default features (AES-256-GCM)
# Build with specific algorithm
# Run tests
# Run tests for specific algorithm
License
MIT License - see LICENSE for details.
Contributing
Contributions welcome! Please read the contributing guidelines first.
Acknowledgments
- aes-gcm - AES-GCM implementation
- chacha20poly1305 - ChaCha20-Poly1305 implementation
- zeroize - Secure memory zeroing patterns