#![allow(deprecated)] use aes_gcm::{ aead::{Aead, KeyInit, OsRng}, Aes256Gcm, Key, Nonce, }; use base64::{engine::general_purpose::STANDARD as BASE64, Engine}; use once_cell::sync::Lazy; use rand::RngCore; use std::env; static MASTER_KEY: Lazy> = Lazy::new(|| { let key_str = env::var("RTIX_MASTER_KEY") .or_else(|_| env::var("SOVEREIGN_MASTER_KEY")) .or_else(|_| env::var("VANTIX_MASTER_KEY")) .unwrap_or_else(|_| "01234567890123456789012345678901".to_string()); key_str.as_bytes().to_vec() }); pub struct CryptoService; impl CryptoService { pub fn encrypt(data: &str) -> String { if data.is_empty() { return String::new(); } let key = Key::::from_slice(&MASTER_KEY); let cipher = Aes256Gcm::new(key); // Institutional Grade: Use unique nonce per encryption let mut nonce_bytes = [0u8; 12]; OsRng.fill_bytes(&mut nonce_bytes); let nonce = Nonce::from(nonce_bytes); match cipher.encrypt(&nonce, data.as_bytes().as_ref()) { Ok(ciphertext) => { // Prepend nonce to ciphertext for decryption let mut combined = nonce_bytes.to_vec(); combined.extend_from_slice(&ciphertext); BASE64.encode(combined) } Err(_) => data.to_string(), } } pub fn decrypt(encrypted_data: &str) -> String { if encrypted_data.is_empty() { return String::new(); } let decoded = match BASE64.decode(encrypted_data) { Ok(d) => d, Err(_) => return encrypted_data.to_string(), }; if decoded.len() < 12 { return encrypted_data.to_string(); } let key = Key::::from_slice(&MASTER_KEY); let cipher = Aes256Gcm::new(key); // Check if it's new-style (with prepended nonce) or old-style (static nonce) // This is a heuristic: if we can't decrypt with prepended nonce, try static. // Try new-style first let (nonce_bytes, ciphertext) = decoded.split_at(12); let nonce = Nonce::clone_from_slice(nonce_bytes); match cipher.decrypt(&nonce, ciphertext.as_ref()) { Ok(plaintext) => { String::from_utf8(plaintext).unwrap_or_else(|_| encrypted_data.to_string()) } Err(_) => { // Fallback to old-style static nonce let static_nonce = Nonce::clone_from_slice(b"unique nonce 12"); match cipher.decrypt(&static_nonce, decoded.as_ref()) { Ok(plaintext) => { String::from_utf8(plaintext).unwrap_or_else(|_| encrypted_data.to_string()) } Err(_) => encrypted_data.to_string(), } } } } pub fn deterministic_hash(data: &str) -> String { use hmac::{Hmac, Mac}; use sha2::Sha256; if data.is_empty() { return String::new(); } type HmacSha256 = Hmac; let mut mac = ::new_from_slice(&MASTER_KEY) .expect("HMAC key initialization error"); mac.update(data.as_bytes()); hex::encode(mac.finalize().into_bytes()) } pub fn generate_zk_telemetry_proof( transaction_id: &str, media_bytes: &[u8], lat: f64, lng: f64, ) -> serde_json::Value { use hmac::{Hmac, Mac}; use sha2::{Digest, Sha256}; // 1. Calculate SHA-256 hash commit of media bytes let mut hasher = Sha256::new(); hasher.update(media_bytes); let hash_commit = hex::encode(hasher.finalize()); // 2. Create the telemetry record let timestamp = chrono::Utc::now().to_rfc3339(); let telemetry_payload = format!( "txnid:{};hash:{};gps:{:.6},{:.6};time:{}", transaction_id, hash_commit, lat, lng, timestamp ); // 3. Sign the telemetry payload using HMAC-SHA256 type HmacSha256 = Hmac; let mut mac = ::new_from_slice(&MASTER_KEY) .expect("HMAC key initialization error"); mac.update(telemetry_payload.as_bytes()); let signature = hex::encode(mac.finalize().into_bytes()); serde_json::json!({ "proof_type": "ZK_TELEMETRY", "hash_commit": hash_commit, "telemetry_payload": telemetry_payload, "telemetry_signature": signature, "gps_coordinates": { "lat": lat, "lng": lng }, "timestamp": timestamp, "is_zero_storage": true }) } }