Files
gyandoot-v2/crates/gyandoot-license/src/anti_tamper.rs
Ishaan Reddy 968710dc46 feat(license): add gyandoot-license crate + CLI subcommand
- New crate: gyandoot-license with:
  - Machine fingerprinting (CPU, MAC, Disk, hostname)
  - Ed25519 JWT signing and verification
  - Agent counting with RAII guards
  - AES-256-GCM encrypted license cache
  - Anti-tamper binary integrity checks
  - Debugger detection
- Added to workspace Cargo.toml
- Added 'gyandoot license' CLI subcommand (activate, status, deactivate, fingerprint)
2026-07-05 10:14:48 +05:30

119 lines
3.3 KiB
Rust

//! Anti-Tamper & Debugger Detection
//!
//! Prevents the binary from being modified or debugged.
//! - Binary integrity hash verification
//! - Debugger detection (ptrace)
//! - String obfuscation helpers
use crate::LicenseError;
use sha2::{Sha256, Digest};
/// Compute SHA-256 hash of the current binary
pub fn current_binary_hash() -> String {
let exe_path = std::env::current_exe().unwrap_or_else(|_| std::path::PathBuf::from("unknown"));
if let Ok(data) = std::fs::read(&exe_path) {
let mut hasher = Sha256::new();
hasher.update(&data);
hex::encode(hasher.finalize())
} else {
"unknown_binary".to_string()
}
}
/// Verify binary integrity against an expected hash
pub fn verify_binary(expected_hash: &str) -> Result<(), LicenseError> {
let actual = current_binary_hash();
if actual != expected_hash {
return Err(LicenseError::BinaryTampered);
}
Ok(())
}
/// Detect if a debugger is attached
pub fn detect_debugger() -> bool {
// Linux: check if being ptraced
#[cfg(target_os = "linux")]
{
// Try ptrace(PTRACE_TRACEME, ...) — if it fails, we're being debugged
let result = unsafe { libc::ptrace(libc::PTRACE_TRACEME, 0, 0, 0) };
if result == -1 {
return true; // Being traced
}
// Detach
unsafe { libc::ptrace(libc::PTRACE_DETACH, 0, 0, 0) };
}
// Check for common debugger environment variables
let debug_env_vars = [
"DEBUG", "DEBUGGER", "JAVA_DEBUG", "PYTHONDEBUG",
"TRACE", "TRACEME", "LD_PRELOAD",
];
for var in &debug_env_vars {
if std::env::var(var).is_ok() {
return true;
}
}
// Check for /proc/self/status TracerPid
#[cfg(target_os = "linux")]
{
if let Ok(content) = std::fs::read_to_string("/proc/self/status") {
for line in content.lines() {
if line.starts_with("TracerPid:") {
let pid = line.trim_start_matches("TracerPid:").trim();
if pid != "0" {
return true;
}
}
}
}
}
false
}
/// Run all anti-tamper checks at startup
pub fn run_startup_checks(expected_binary_hash: &str) -> Result<(), LicenseError> {
// Check 1: Debugger
if detect_debugger() {
return Err(LicenseError::DebuggerDetected);
}
// Check 2: Binary integrity
verify_binary(expected_binary_hash)?;
Ok(())
}
/// Obfuscate a string literal (compile-time)
/// Usage: obfstr!("sensitive_string")
/// In production, use the `obfstr` crate for true compile-time obfuscation
#[macro_export]
macro_rules! obf {
($s:expr) => {{
let bytes: &[u8] = $s.as_bytes();
let mut obfuscated: Vec<u8> = bytes.iter().map(|b| b ^ 0xAA).collect();
let deobf: Vec<u8> = obfuscated.iter().map(|b| b ^ 0xAA).collect();
String::from_utf8(deobf).unwrap_or_default()
}};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_binary_hash_not_empty() {
let hash = current_binary_hash();
assert!(!hash.is_empty());
assert_eq!(hash.len(), 64);
}
#[test]
fn test_obf_macro() {
let original = "license.gyannetra.com";
let result = obf!(original);
assert_eq!(result, original);
}
}