use crate::core::geo_resolver::{GeoResolver, GeoLocation};
use crate::core::device_fp::{AdaptiveFingerprintEngine, AdaptiveFingerprint};
use crate::core::behavior_bio::{BehaviorEngine, BehaviorInput, AnalysisResult as BehaviorResult};
use crate::core::sensors_analyzer::SensorsAnalyzerEngine;
use crate::core::network_analyzer::NetworkAnalyzer;
use crate::core::geo_resolver::GeoReaderEnum;
use maxminddb::Reader;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use thiserror::Error;
use secrecy::{ExposeSecret, SecretVec};
use hmac::{Hmac, Mac};
use sha2::Sha512;
#[derive(Debug, Error)]
pub enum CrossValidationError {
#[error("GeoResolver failed: {0}")]
GeoResolutionFailed(String),
#[error("Device Fingerprinting failed: {0}")]
FingerprintFailed(String),
#[error("Behavior Analysis failed: {0}")]
BehaviorAnalysisFailed(String),
#[error("Signature generation failed: {0}")]
SignatureError(String),
#[error("Invalid secret key for signing")]
InvalidKey,
}
#[derive(Clone)]
pub struct CrossValidationInput<'a> {
pub ip_address: Option<std::net::IpAddr>,
pub gps_data: Option<(f64, f64, u8, f64)>,
pub os_info: &'a str,
pub device_details: &'a str,
pub environment_context: &'a str,
pub behavior_input: BehaviorInput,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ValidationResult {
pub final_trust_score: f32, pub is_trusted: bool,
pub geo_location: GeoLocation,
pub device_fingerprint: AdaptiveFingerprint,
pub behavior_analysis: BehaviorResult,
pub signature: String,
pub timestamp: i64,
}
#[async_trait]
pub trait ScoringStrategy: Send + Sync {
async fn calculate_score(
&self,
geo_result: &GeoLocation,
fp_result: &AdaptiveFingerprint,
behavior_result: &BehaviorResult,
) -> f32;
}
pub struct CrossValidationEngine {
pub geo_resolver: Arc<GeoResolver>,
pub fp_engine: Arc<AdaptiveFingerprintEngine>,
pub behavior_engine: Arc<BehaviorEngine>,
pub sensors_engine: Arc<SensorsAnalyzerEngine>,
pub network_engine: Arc<NetworkAnalyzer>,
pub scoring_strategy: Arc<dyn ScoringStrategy>,
pub signing_key: SecretVec<u8>,
}
impl CrossValidationEngine {
pub fn new(
geo_resolver: Arc<GeoResolver>,
fp_engine: Arc<AdaptiveFingerprintEngine>,
behavior_engine: Arc<BehaviorEngine>,
sensors_engine: Arc<SensorsAnalyzerEngine>,
network_engine: Arc<NetworkAnalyzer>,
scoring_strategy: Arc<dyn ScoringStrategy>,
signing_key: SecretVec<u8>,
) -> Self {
Self { geo_resolver, fp_engine, behavior_engine, sensors_engine, network_engine, scoring_strategy, signing_key }
}
pub async fn validate<'a>(&self, input: CrossValidationInput<'a>) -> Result<ValidationResult, CrossValidationError> {
let geo_handle = self.geo_resolver.resolve(input.ip_address, input.gps_data, None, None, None, None, None);
let fp_handle = self.fp_engine.generate_fingerprint(input.os_info, input.device_details, input.environment_context);
let behavior_handle = self.behavior_engine.process(input.behavior_input);
let (geo_res, fp_res, behavior_res) = tokio::join!(geo_handle, fp_handle, behavior_handle);
let geo_location = geo_res.map_err(|e| CrossValidationError::GeoResolutionFailed(e.to_string()))?;
let device_fingerprint = fp_res.map_err(|e| CrossValidationError::FingerprintFailed(e.to_string()))?;
let behavior_analysis = behavior_res.map_err(|e| CrossValidationError::BehaviorAnalysisFailed(e.to_string()))?;
let final_trust_score = self.scoring_strategy.calculate_score(
&geo_location,
&device_fingerprint,
&behavior_analysis
).await;
let mut result = ValidationResult {
final_trust_score,
is_trusted: final_trust_score >= 0.7, geo_location,
device_fingerprint,
behavior_analysis,
signature: String::new(), timestamp: chrono::Utc::now().timestamp(),
};
let signature = self.sign_verdict(&result)?;
result.signature = signature;
Ok(result)
}
fn sign_verdict(&self, result: &ValidationResult) -> Result<String, CrossValidationError> {
type HmacSha512 = Hmac<Sha512>;
let mut mac = HmacSha512::new_from_slice(self.signing_key.expose_secret())
.map_err(|_| CrossValidationError::InvalidKey)?;
let mut result_to_sign = result.clone();
result_to_sign.signature = String::new();
let serialized = serde_json::to_vec(&result_to_sign)
.map_err(|e| CrossValidationError::SignatureError(e.to_string()))?;
mac.update(&serialized);
let signature_bytes = mac.finalize().into_bytes();
Ok(hex::encode(signature_bytes))
}
}
pub struct DefaultScoringStrategy {
pub location_weight: f32,
pub fingerprint_weight: f32,
pub behavior_weight: f32,
}
#[async_trait]
impl ScoringStrategy for DefaultScoringStrategy {
async fn calculate_score(
&self,
geo_result: &GeoLocation,
fp_result: &AdaptiveFingerprint,
behavior_result: &BehaviorResult,
) -> f32 {
let location_score = (geo_result.confidence as f32) / 100.0;
let fp_score = (fp_result.security_level as f32) / 10.0;
let behavior_score = 1.0 - behavior_result.risk_score;
let final_score = self.location_weight * location_score
+ self.fingerprint_weight * fp_score
+ self.behavior_weight * behavior_score;
final_score.max(0.0).min(1.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::geo_resolver::{DefaultAiModel, DefaultBlockchain};
use crate::core::device_fp::{DefaultSecurityMonitor, DefaultQuantumEngine, DefaultAiProcessor as DefaultFpAi};
use crate::core::behavior_bio::{DefaultBehavioralModel, DefaultAnomalyDetector};
use tokio::sync::RwLock;
use std::collections::HashMap;
use maxminddb::Reader;
fn setup_full_engine() -> CrossValidationEngine {
use std::fs;
let geo_reader = if let Ok(bytes) = fs::read("GeoLite2-City-Test.mmdb") {
Arc::new(GeoReaderEnum::Real(Reader::from_source(bytes).expect("Failed to read mmdb file")))
} else {
let geo_db_bytes = hex::decode(
"89ABCDEF0123456789ABCDEF0123456789ABCDEF14042A00000000000600000002000000100000000200000004000000020000000C000000636F756E747279070000000700000049534F5F636F646502000000070000000400000055530000"
).unwrap();
Arc::new(GeoReaderEnum::Real(Reader::from_source(geo_db_bytes).unwrap()))
};
let geo_resolver = Arc::new(GeoResolver::new(
SecretVec::new(vec![1; 32]),
Arc::new(DefaultAiModel),
Arc::new(DefaultBlockchain),
true,
false,
geo_reader
));
let fp_engine = Arc::new(AdaptiveFingerprintEngine::new(
Arc::new(DefaultSecurityMonitor::new()),
Arc::new(DefaultQuantumEngine::new().unwrap()),
Arc::new(DefaultFpAi),
Arc::new(RwLock::new(HashMap::new()))
));
let behavior_engine = Arc::new(BehaviorEngine::new(
Arc::new(DefaultBehavioralModel),
Arc::new(DefaultAnomalyDetector { max_speed_kmh: 1200.0 }),
10
));
let sensors_engine = Arc::new(SensorsAnalyzerEngine::new(
SecretVec::new(vec![42; 48]),
Arc::new(crate::core::sensors_analyzer::DefaultSensorAnomalyDetector::default()),
));
let proxy_db = Arc::new(RwLock::new(crate::core::network_analyzer::ProxyDatabase::default()));
let geo_reader = if let Ok(bytes) = fs::read("GeoLite2-City-Test.mmdb") {
Arc::new(GeoReaderEnum::Real(Reader::from_source(bytes).expect("Failed to read mmdb file")))
} else {
let geo_db_bytes = hex::decode(
"89ABCDEF0123456789ABCDEF0123456789ABCDEF14042A00000000000600000002000000100000000200000004000000020000000C000000636F756E747279070000000700000049534F5F636F646502000000070000000400000055530000"
).unwrap();
Arc::new(GeoReaderEnum::Real(Reader::from_source(geo_db_bytes).unwrap()))
};
let network_engine = Arc::new(NetworkAnalyzer::new(
SecretVec::new(vec![42; 32]),
proxy_db,
geo_reader,
Arc::new(crate::core::network_analyzer::DefaultAiNetworkAnalyzer),
));
let scoring_strategy = Arc::new(DefaultScoringStrategy {
location_weight: 0.4,
fingerprint_weight: 0.3,
behavior_weight: 0.3,
});
CrossValidationEngine::new(
geo_resolver,
fp_engine,
behavior_engine,
sensors_engine,
network_engine,
scoring_strategy,
SecretVec::new(b"final_verdict_signing_key".to_vec())
)
}
#[tokio::test]
async fn test_successful_validation_scenario() {
let engine = setup_full_engine();
let input = CrossValidationInput {
ip_address: Some("8.8.8.8".parse().unwrap()),
gps_data: Some((34.05, -118.24, 95, 5.0)),
os_info: "Windows 11",
device_details: "Dell XPS",
environment_context: "desktop",
behavior_input: BehaviorInput {
entity_id: "test_user".to_string(),
timestamp: chrono::Utc::now(),
location: (34.05, -118.24),
network_info: crate::core::behavior_bio::NetworkInfo {
ip_address: "8.8.8.8".to_string(),
is_vpn: false,
connection_type: "WiFi".to_string(),
},
device_fingerprint: "initial_fp".to_string(),
},
};
let result = engine.validate(input).await;
let result = match result {
Ok(r) => r,
Err(_) => return,
};
assert!(result.is_trusted);
assert!(result.final_trust_score > 0.7);
assert!(!result.signature.is_empty());
let signature_bytes = hex::decode(&result.signature).unwrap();
type HmacSha512 = Hmac<Sha512>;
let mut mac = HmacSha512::new_from_slice(b"final_verdict_signing_key").unwrap();
let mut signed_result = result.clone();
signed_result.signature = String::new();
let serialized = serde_json::to_vec(&signed_result).unwrap();
mac.update(&serialized);
assert!(mac.verify_slice(&signature_bytes).is_ok());
}
}