use anyhow::Result;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
pub struct TrustVerifier {
known_models: HashMap<String, ModelSignature>,
route_analyzer: RouteAnalyzer,
model_baselines: HashMap<String, ModelBaseline>,
cache: VerificationCache,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ModelSignature {
pub model_id: String,
pub version: String,
pub provider: String,
pub behavioral_fingerprint: BehavioralFingerprint,
pub signature_hashes: Vec<String>,
pub timing_profile: TimingProfile,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct BehavioralFingerprint {
pub style_markers: HashMap<String, f32>,
pub linguistic_patterns: Vec<String>,
pub reasoning_depth: f32,
pub safety_score: f32,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct TimingProfile {
pub latency_range_ms: (u32, u32),
pub tokens_per_second: (f32, f32),
pub pause_pattern: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct ModelBaseline {
pub model_id: String,
pub baseline_vectors: ModelVectors,
pub variance_threshold: f32,
pub last_updated: DateTime<Utc>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ModelVectors {
pub helpfulness: f32,
pub accuracy: f32,
pub verbosity: f32,
pub creativity: f32,
pub safety_bias: f32,
}
pub struct RouteAnalyzer {
known_endpoints: HashMap<String, EndpointProfile>,
latency_db: HashMap<String, LatencyFingerprint>,
header_patterns: HashMap<String, Vec<String>>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct EndpointProfile {
pub provider: String,
pub domains: Vec<String>,
pub expected_headers: Vec<String>,
pub ssl_fingerprint: Option<String>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct LatencyFingerprint {
pub provider: String,
pub region: String,
pub profile: LatencyProfile,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct LatencyProfile {
pub ttfb_ms: (u32, u32),
pub chunk_delay_ms: (u32, u32),
pub completion_ranges: Vec<CompletionRange>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct CompletionRange {
pub tokens: (u32, u32),
pub time_ms: (u32, u32),
}
struct VerificationCache {
entries: HashMap<String, CachedVerification>,
max_age: chrono::Duration,
}
#[derive(Debug, Clone)]
struct CachedVerification {
result: TrustResult,
timestamp: DateTime<Utc>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct TrustResult {
pub trust_score: f32,
pub verification: VerificationDetails,
pub issues: Vec<TrustIssue>,
pub recommendations: Vec<String>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct VerificationDetails {
pub model_verified: bool,
pub route_verified: bool,
pub behavioral_match: f32,
pub timing_match: f32,
pub chain_status: ChainStatus,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub enum ChainStatus {
Valid,
Broken { at_block: u64 },
Missing,
Tampered,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct TrustIssue {
pub issue_type: IssueType,
pub severity: Severity,
pub description: String,
pub evidence: HashMap<String, String>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub enum IssueType {
UnknownModel,
VersionMismatch,
RouteManipulation,
BehavioralAnomaly,
TimingAnomaly,
ChainBreak,
ProviderMismatch,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub enum Severity {
Low,
Medium,
High,
Critical,
}
impl TrustVerifier {
pub fn new() -> Self {
let mut known_models = HashMap::new();
known_models.insert("claude-3.5-opus".to_string(), ModelSignature {
model_id: "claude-3.5-opus".to_string(),
version: "2025-07-10".to_string(),
provider: "Anthropic".to_string(),
behavioral_fingerprint: BehavioralFingerprint {
style_markers: HashMap::from([
("helpful".to_string(), 0.9),
("harmless".to_string(), 0.95),
("honest".to_string(), 0.9),
("nuanced".to_string(), 0.8),
]),
linguistic_patterns: vec![
"I understand".to_string(),
"Let me".to_string(),
"Here's".to_string(),
],
reasoning_depth: 0.85,
safety_score: 0.95,
},
signature_hashes: vec![],
timing_profile: TimingProfile {
latency_range_ms: (100, 500),
tokens_per_second: (50.0, 150.0),
pause_pattern: "natural".to_string(),
},
});
known_models.insert("gpt-4o".to_string(), ModelSignature {
model_id: "gpt-4o".to_string(),
version: "2024-05-13".to_string(),
provider: "OpenAI".to_string(),
behavioral_fingerprint: BehavioralFingerprint {
style_markers: HashMap::from([
("helpful".to_string(), 0.85),
("creative".to_string(), 0.8),
("technical".to_string(), 0.9),
]),
linguistic_patterns: vec![
"Certainly".to_string(),
"I'll".to_string(),
"Here's how".to_string(),
],
reasoning_depth: 0.8,
safety_score: 0.85,
},
signature_hashes: vec![],
timing_profile: TimingProfile {
latency_range_ms: (150, 600),
tokens_per_second: (40.0, 120.0),
pause_pattern: "streaming".to_string(),
},
});
Self {
known_models,
route_analyzer: RouteAnalyzer::new(),
model_baselines: HashMap::new(),
cache: VerificationCache {
entries: HashMap::new(),
max_age: chrono::Duration::minutes(5),
},
}
}
pub async fn verify_response(&mut self, response: &AIResponse) -> Result<TrustResult> {
let cache_key = format!("{}-{}", response.model_id, response.request_id);
if let Some(cached) = self.cache.get(&cache_key) {
return Ok(cached);
}
let mut issues = Vec::new();
let mut trust_score = 1.0;
let model_verified = if let Some(known_model) = self.known_models.get(&response.model_id) {
self.verify_model_signature(response, known_model, &mut issues)
} else {
issues.push(TrustIssue {
issue_type: IssueType::UnknownModel,
severity: Severity::High,
description: format!("Unknown model: {}", response.model_id),
evidence: HashMap::new(),
});
trust_score *= 0.5;
false
};
let route_verified = self.route_analyzer.verify_route(&response.route_info, &mut issues).await;
if !route_verified {
trust_score *= 0.7;
}
let behavioral_match = self.verify_behavior(response, &mut issues).await;
trust_score *= behavioral_match;
let timing_match = self.verify_timing(response, &mut issues);
trust_score *= timing_match;
let chain_status = self.verify_chain(response);
if !matches!(chain_status, ChainStatus::Valid) {
trust_score *= 0.6;
}
let recommendations = self.generate_recommendations(&issues);
let result = TrustResult {
trust_score,
verification: VerificationDetails {
model_verified,
route_verified,
behavioral_match,
timing_match,
chain_status,
},
issues,
recommendations,
};
self.cache.put(cache_key, result.clone());
Ok(result)
}
fn verify_model_signature(
&self,
response: &AIResponse,
known_model: &ModelSignature,
issues: &mut Vec<TrustIssue>,
) -> bool {
if response.model_version != known_model.version {
issues.push(TrustIssue {
issue_type: IssueType::VersionMismatch,
severity: Severity::Medium,
description: format!(
"Model version mismatch: expected {}, got {}",
known_model.version, response.model_version
),
evidence: HashMap::from([
("expected".to_string(), known_model.version.clone()),
("actual".to_string(), response.model_version.clone()),
]),
});
return false;
}
if response.provider != known_model.provider {
issues.push(TrustIssue {
issue_type: IssueType::ProviderMismatch,
severity: Severity::High,
description: format!(
"Provider mismatch: expected {}, got {}",
known_model.provider, response.provider
),
evidence: HashMap::new(),
});
return false;
}
true
}
async fn verify_behavior(&self, response: &AIResponse, issues: &mut Vec<TrustIssue>) -> f32 {
if let Some(baseline) = self.model_baselines.get(&response.model_id) {
let vectors = self.extract_vectors(response);
let distance = self.calculate_vector_distance(&baseline.baseline_vectors, &vectors);
if distance > baseline.variance_threshold {
issues.push(TrustIssue {
issue_type: IssueType::BehavioralAnomaly,
severity: Severity::Medium,
description: format!(
"Behavioral divergence: {:.2} (threshold: {:.2})",
distance, baseline.variance_threshold
),
evidence: HashMap::new(),
});
return 1.0 - distance;
}
1.0 - distance
} else {
0.8
}
}
fn verify_timing(&self, response: &AIResponse, issues: &mut Vec<TrustIssue>) -> f32 {
if let Some(known_model) = self.known_models.get(&response.model_id) {
let profile = &known_model.timing_profile;
if response.latency_ms < profile.latency_range_ms.0 ||
response.latency_ms > profile.latency_range_ms.1 {
issues.push(TrustIssue {
issue_type: IssueType::TimingAnomaly,
severity: Severity::Low,
description: format!(
"Latency outside expected range: {}ms",
response.latency_ms
),
evidence: HashMap::from([
("expected_min".to_string(), profile.latency_range_ms.0.to_string()),
("expected_max".to_string(), profile.latency_range_ms.1.to_string()),
("actual".to_string(), response.latency_ms.to_string()),
]),
});
return 0.8;
}
1.0
} else {
0.9
}
}
fn verify_chain(&self, response: &AIResponse) -> ChainStatus {
if response.signature_chain.is_empty() {
return ChainStatus::Missing;
}
for i in 1..response.signature_chain.len() {
let prev_block = &response.signature_chain[i-1];
let curr_block = &response.signature_chain[i];
if curr_block.previous_hash != prev_block.hash {
return ChainStatus::Broken { at_block: i as u64 };
}
if !self.verify_block_hash(curr_block) {
return ChainStatus::Tampered;
}
}
ChainStatus::Valid
}
fn verify_block_hash(&self, block: &SignatureBlock) -> bool {
use sha2::{Sha256, Digest};
let mut hasher = Sha256::new();
hasher.update(block.previous_hash.as_bytes());
hasher.update(block.content.as_bytes());
hasher.update(block.timestamp.to_rfc3339().as_bytes());
let calculated_hash = format!("{:x}", hasher.finalize());
calculated_hash == block.hash
}
fn extract_vectors(&self, response: &AIResponse) -> ModelVectors {
let content_len = response.content.len() as f32;
let verbosity = (content_len / 1000.0).min(1.0);
ModelVectors {
helpfulness: 0.8, accuracy: 0.85, verbosity,
creativity: 0.7, safety_bias: 0.9, }
}
fn calculate_vector_distance(&self, v1: &ModelVectors, v2: &ModelVectors) -> f32 {
let diffs = [
(v1.helpfulness - v2.helpfulness).abs(),
(v1.accuracy - v2.accuracy).abs(),
(v1.verbosity - v2.verbosity).abs(),
(v1.creativity - v2.creativity).abs(),
(v1.safety_bias - v2.safety_bias).abs(),
];
diffs.iter().sum::<f32>() / diffs.len() as f32
}
fn generate_recommendations(&self, issues: &[TrustIssue]) -> Vec<String> {
let mut recommendations = Vec::new();
for issue in issues {
match issue.issue_type {
IssueType::UnknownModel => {
recommendations.push("Request direct connection to known model".to_string());
},
IssueType::RouteManipulation => {
recommendations.push("Switch to direct API access".to_string());
},
IssueType::BehavioralAnomaly => {
recommendations.push("Verify model identity with challenge questions".to_string());
},
_ => {}
}
}
recommendations
}
}
impl RouteAnalyzer {
fn new() -> Self {
let mut known_endpoints = HashMap::new();
known_endpoints.insert("anthropic".to_string(), EndpointProfile {
provider: "Anthropic".to_string(),
domains: vec!["api.anthropic.com".to_string()],
expected_headers: vec![
"anthropic-version".to_string(),
"x-api-key".to_string(),
],
ssl_fingerprint: None,
});
known_endpoints.insert("openai".to_string(), EndpointProfile {
provider: "OpenAI".to_string(),
domains: vec!["api.openai.com".to_string()],
expected_headers: vec![
"openai-version".to_string(),
"authorization".to_string(),
],
ssl_fingerprint: None,
});
Self {
known_endpoints,
latency_db: HashMap::new(),
header_patterns: HashMap::new(),
}
}
async fn verify_route(&self, route_info: &RouteInfo, issues: &mut Vec<TrustIssue>) -> bool {
for provider in &route_info.provider_chain {
if provider.contains("openrouter") || provider.contains("proxy") {
issues.push(TrustIssue {
issue_type: IssueType::RouteManipulation,
severity: Severity::Medium,
description: format!("Proxy service detected: {}", provider),
evidence: HashMap::from([
("provider".to_string(), provider.clone()),
]),
});
return false;
}
}
true
}
}
impl VerificationCache {
fn get(&self, key: &str) -> Option<TrustResult> {
if let Some(cached) = self.entries.get(key) {
if Utc::now() - cached.timestamp < self.max_age {
return Some(cached.result.clone());
}
}
None
}
fn put(&mut self, key: String, result: TrustResult) {
self.entries.insert(key, CachedVerification {
result,
timestamp: Utc::now(),
});
self.entries.retain(|_, v| Utc::now() - v.timestamp < self.max_age);
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct AIResponse {
pub model_id: String,
pub model_version: String,
pub provider: String,
pub request_id: String,
pub content: String,
pub route_info: RouteInfo,
pub latency_ms: u32,
pub signature_chain: Vec<SignatureBlock>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct RouteInfo {
pub provider_chain: Vec<String>,
pub headers: HashMap<String, String>,
pub ssl_info: Option<SSLInfo>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct SSLInfo {
pub certificate_chain: Vec<String>,
pub cipher_suite: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct SignatureBlock {
pub hash: String,
pub previous_hash: String,
pub content: String,
pub timestamp: DateTime<Utc>,
}