Spaces:
Running
Running
| 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<Vec<u8>> = 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::<Aes256Gcm>::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::<Aes256Gcm>::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<Sha256>; | |
| let mut mac = <HmacSha256 as 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<Sha256>; | |
| let mut mac = <HmacSha256 as 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 | |
| }) | |
| } | |
| } | |