use serde::{Serialize, Deserialize};
use std::collections::HashMap;
use std::env;
use std::fs;
use std::time::{SystemTime, UNIX_EPOCH};
use sha2::{Sha256, Digest};
use hmac::{Hmac, Mac};
use aes_gcm::{Aes256Gcm, Key, Nonce};
use aes_gcm::aead::{Aead, NewAead};
use rand::Rng;
#[derive(Debug, Serialize, Deserialize)]
pub struct TamperDetection {
pub timestamp: u64,
pub file: Option<String>,
pub function: Option<String>,
pub expected: String,
pub actual: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct TamperingReport {
pub file_tampering: bool,
pub function_tampering: bool,
pub environment_tampering: bool,
pub debugger_detected: bool,
pub details: Vec<String>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct IntegrityReport {
pub self_check_passed: bool,
pub tampering_detected: TamperingReport,
pub protected_functions: Vec<String>,
pub integrity_checks: usize,
pub tamper_detections: usize,
pub last_self_check: u64,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct ObfuscationCache {
pub original: String,
pub obfuscated: String,
pub hash: String,
}
pub struct TuskAntiTamper {
secret_key: String,
encryption_key: Vec<u8>,
integrity_checks: HashMap<String, String>,
tamper_detections: Vec<TamperDetection>,
obfuscation_cache: HashMap<String, ObfuscationCache>,
self_check_interval: u64,
last_self_check: u64,
}
impl TuskAntiTamper {
pub fn new(secret_key: String) -> Self {
let encryption_key = Self::derive_key(&secret_key);
Self {
secret_key,
encryption_key,
integrity_checks: HashMap::new(),
tamper_detections: Vec::new(),
obfuscation_cache: HashMap::new(),
self_check_interval: 300, last_self_check: 0,
}
}
fn derive_key(password: &str) -> Vec<u8> {
let salt = b"tusklang_antitamper_salt";
let mut hasher = Sha256::new();
hasher.update(salt);
hasher.update(password.as_bytes());
hasher.finalize().to_vec()
}
pub fn calculate_file_hash(&self, file_path: &str) -> String {
match fs::read(file_path) {
Ok(content) => {
let mut hasher = Sha256::new();
hasher.update(&content);
format!("{:x}", hasher.finalize())
}
Err(_) => String::new(),
}
}
pub fn verify_file_integrity(&self, file_path: &str, expected_hash: &str) -> bool {
let actual_hash = self.calculate_file_hash(file_path);
actual_hash == expected_hash
}
pub fn obfuscate_code(&self, code: &str, level: u8) -> String {
if level == 0 {
return code.to_string();
}
let mut obfuscated = code.to_string();
if level >= 1 {
use base64::{Engine as _, engine::general_purpose};
let encoded = general_purpose::STANDARD.encode(code.as_bytes());
obfuscated = format!("// Obfuscated code\nlet _decoded = base64::engine::general_purpose::STANDARD.decode(\"{}\").unwrap();\nlet _code = String::from_utf8(_decoded).unwrap();\n// Execute: {}", encoded, code);
}
if level >= 2 {
let junk_vars: Vec<String> = (0..10).map(|i| format!("let _junk_{} = None;", i)).collect();
obfuscated = format!("{}\n{}", junk_vars.join("\n"), obfuscated);
}
if level >= 3 {
let key = Key::from_slice(&self.encryption_key);
let cipher = Aes256Gcm::new(key);
let nonce_bytes: [u8; 12] = rand::thread_rng().gen();
let nonce = Nonce::from_slice(&nonce_bytes);
if let Ok(encrypted) = cipher.encrypt(nonce, code.as_bytes()) {
let mut result = nonce_bytes.to_vec();
result.extend(encrypted);
let encoded = general_purpose::STANDARD.encode(result);
obfuscated = format!("// Encrypted code\nlet _key = [{}];\nlet _nonce = [{}];\nlet _encrypted = general_purpose::STANDARD.decode(\"{}\").unwrap();\n// Decrypt and execute",
self.encryption_key.iter().map(|b| b.to_string()).collect::<Vec<_>>().join(", "),
nonce_bytes.iter().map(|b| b.to_string()).collect::<Vec<_>>().join(", "),
encoded);
}
}
obfuscated
}
pub fn deobfuscate_code(&self, obfuscated_code: &str) -> String {
if obfuscated_code.contains("// Obfuscated code") {
if let Some(start) = obfuscated_code.find("\"") {
if let Some(end) = obfuscated_code[start + 1..].find("\"") {
let encoded = &obfuscated_code[start + 1..start + 1 + end];
if let Ok(decoded) = general_purpose::STANDARD.decode(encoded) {
if let Ok(decoded_str) = String::from_utf8(decoded) {
return decoded_str;
}
}
}
}
}
obfuscated_code.to_string()
}
pub fn protect_function<F, Args, Ret>(&mut self, func: F, name: &str, obfuscation_level: u8) -> impl Fn(Args) -> Ret
where
F: Fn(Args) -> Ret + 'static,
Args: 'static,
Ret: 'static,
{
let func_signature = format!("function:{}", name);
let mut hasher = Sha256::new();
hasher.update(func_signature.as_bytes());
let hash = format!("{:x}", hasher.finalize());
self.obfuscation_cache.insert(name.to_string(), ObfuscationCache {
original: func_signature.clone(),
obfuscated: self.obfuscate_code(&func_signature, obfuscation_level),
hash: hash.clone(),
});
move |args| {
if !self.self_check() {
panic!("Tampering detected - function execution blocked");
}
func(args)
}
}
pub fn self_check(&mut self) -> bool {
let current_time = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs();
if current_time - self.last_self_check < self.self_check_interval {
return true;
}
self.last_self_check = current_time;
if let Ok(current_exe) = env::current_exe() {
if let Some(current_path) = current_exe.to_str() {
let current_hash = self.calculate_file_hash(current_path);
if !self.integrity_checks.contains_key(current_path) {
self.integrity_checks.insert(current_path.to_string(), current_hash);
} else if let Some(stored_hash) = self.integrity_checks.get(current_path) {
if stored_hash != ¤t_hash {
self.tamper_detections.push(TamperDetection {
timestamp: current_time,
file: Some(current_path.to_string()),
function: None,
expected: stored_hash.clone(),
actual: current_hash,
});
return false;
}
}
}
}
for (func_name, cache_data) in &self.obfuscation_cache {
let mut hasher = Sha256::new();
hasher.update(cache_data.original.as_bytes());
let current_hash = format!("{:x}", hasher.finalize());
if cache_data.hash != current_hash {
self.tamper_detections.push(TamperDetection {
timestamp: current_time,
file: None,
function: Some(func_name.clone()),
expected: cache_data.hash.clone(),
actual: current_hash,
});
return false;
}
}
true
}
pub fn detect_tampering(&mut self) -> TamperingReport {
let mut report = TamperingReport {
file_tampering: false,
function_tampering: false,
environment_tampering: false,
debugger_detected: false,
details: Vec::new(),
};
if self.detect_debugger() {
report.debugger_detected = true;
report.details.push("Debugger detected".to_string());
}
if self.detect_environment_tampering() {
report.environment_tampering = true;
report.details.push("Environment tampering detected".to_string());
}
if !self.self_check() {
report.file_tampering = true;
report.details.push("File integrity check failed".to_string());
}
for (func_name, cache_data) in &self.obfuscation_cache {
let mut hasher = Sha256::new();
hasher.update(cache_data.original.as_bytes());
let current_hash = format!("{:x}", hasher.finalize());
if cache_data.hash != current_hash {
report.function_tampering = true;
report.details.push(format!("Function {} tampering detected", func_name));
}
}
report
}
fn detect_debugger(&self) -> bool {
if let Ok(debug) = env::var("RUST_BACKTRACE") {
if debug == "1" {
return true;
}
}
if let Ok(profile) = env::var("CARGO_PROFILE") {
if profile == "debug" {
return true;
}
}
false
}
fn detect_environment_tampering(&self) -> bool {
let suspicious_vars = ["RUST_BACKTRACE", "RUST_LOG", "CARGO_PROFILE"];
for var_name in &suspicious_vars {
if let Ok(value) = env::var(var_name) {
let value_lower = value.to_lowercase();
if value_lower.contains("debug") || value_lower.contains("test") || value_lower.contains("dev") {
return true;
}
}
}
for arg in env::args() {
let arg_lower = arg.to_lowercase();
if arg_lower.contains("debug") || arg_lower.contains("test") {
return true;
}
}
false
}
pub fn get_tamper_detections(&self) -> Vec<TamperDetection> {
self.tamper_detections.clone()
}
pub fn clear_tamper_detections(&mut self) {
self.tamper_detections.clear();
}
pub fn get_integrity_report(&mut self) -> IntegrityReport {
IntegrityReport {
self_check_passed: self.self_check(),
tampering_detected: self.detect_tampering(),
protected_functions: self.obfuscation_cache.keys().cloned().collect(),
integrity_checks: self.integrity_checks.len(),
tamper_detections: self.tamper_detections.len(),
last_self_check: self.last_self_check,
}
}
}
use std::sync::Mutex;
use once_cell::sync::Lazy;
static ANTI_TAMPER_INSTANCE: Lazy<Mutex<Option<TuskAntiTamper>>> = Lazy::new(|| Mutex::new(None));
pub fn initialize_anti_tamper(secret_key: String) -> TuskAntiTamper {
let anti_tamper = TuskAntiTamper::new(secret_key);
let mut instance = ANTI_TAMPER_INSTANCE.lock().unwrap();
*instance = Some(anti_tamper.clone());
anti_tamper
}
pub fn get_anti_tamper() -> TuskAntiTamper {
let instance = ANTI_TAMPER_INSTANCE.lock().unwrap();
instance.as_ref()
.cloned()
.expect("Anti-tamper not initialized. Call initialize_anti_tamper() first.")
}
impl Clone for TuskAntiTamper {
fn clone(&self) -> Self {
Self {
secret_key: self.secret_key.clone(),
encryption_key: self.encryption_key.clone(),
integrity_checks: self.integrity_checks.clone(),
tamper_detections: self.tamper_detections.clone(),
obfuscation_cache: self.obfuscation_cache.clone(),
self_check_interval: self.self_check_interval,
last_self_check: self.last_self_check,
}
}
}