1#![warn(missing_docs)]
39
40use serde::{Deserialize, Serialize};
41use std::collections::HashMap;
42use std::sync::Arc;
43use thiserror::Error;
44use tokio::sync::RwLock;
45
46#[derive(Debug, Error)]
48#[allow(missing_docs)]
49pub enum TrustError {
50 #[error("Key change detected for peer {peer_id}")]
52 KeyChanged { peer_id: String },
53 #[error("Unknown peer: {peer_id}")]
55 UnknownPeer { peer_id: String },
56 #[error("Attestation verification failed: {0}")]
58 AttestationFailed(String),
59 #[error("Key mismatch during verification")]
61 KeyMismatch,
62}
63
64pub type TrustResult<T> = Result<T, TrustError>;
66
67#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
69pub enum TrustLevel {
70 Unknown,
72 FirstContact,
74 Trusted,
76 Vouched,
78 Verified,
80 KeyChanged,
82 Blocked,
84}
85
86impl TrustLevel {
87 pub fn score(&self) -> f32 {
89 match self {
90 Self::Unknown => 0.0,
91 Self::FirstContact => 0.3,
92 Self::Trusted => 0.6,
93 Self::Vouched => 0.7,
94 Self::Verified => 1.0,
95 Self::KeyChanged => -1.0,
96 Self::Blocked => -2.0,
97 }
98 }
99
100 pub fn is_usable(&self) -> bool {
102 !matches!(self, Self::Blocked | Self::KeyChanged)
103 }
104}
105
106#[derive(Debug, Clone, Serialize, Deserialize)]
108pub struct Attestation {
109 pub attester_id: String,
111 pub subject_id: String,
113 pub subject_pub_key: Vec<u8>,
115 pub signature: Vec<u8>,
117 pub timestamp: u64,
119 pub note: Option<String>,
121}
122
123#[derive(Debug, Clone, Serialize, Deserialize)]
125#[allow(missing_docs)]
126pub struct PeerRecord {
127 pub peer_id: String,
128 pub pinned_key: Vec<u8>,
130 pub current_key: Vec<u8>,
132 pub trust_level: TrustLevel,
133 pub first_seen: u64,
134 pub last_seen: u64,
135 pub attestations: Vec<Attestation>,
136 pub interaction_count: u32,
137 pub display_name: Option<String>,
138}
139
140impl PeerRecord {
141 pub fn has_key_changed(&self) -> bool {
143 self.pinned_key != self.current_key
144 }
145
146 pub fn computed_trust_score(&self, our_attesters: &HashMap<String, f32>) -> f32 {
148 let base = self.trust_level.score();
149 if base < 0.0 {
150 return base;
151 }
152
153 let attestation_bonus: f32 = self
154 .attestations
155 .iter()
156 .filter_map(|a| our_attesters.get(&a.attester_id))
157 .map(|&attester_score| attester_score * 0.3)
158 .sum::<f32>()
159 .min(0.3);
160
161 let interaction_bonus = (self.interaction_count as f32 / 100.0).min(0.1);
162
163 (base + attestation_bonus + interaction_bonus).min(1.0)
164 }
165}
166
167pub struct TrustStore {
169 peers: Arc<RwLock<HashMap<String, PeerRecord>>>,
170 our_trust_scores: Arc<RwLock<HashMap<String, f32>>>,
171}
172
173impl TrustStore {
174 pub fn new() -> Self {
176 Self {
177 peers: Arc::new(RwLock::new(HashMap::new())),
178 our_trust_scores: Arc::new(RwLock::new(HashMap::new())),
179 }
180 }
181
182 pub async fn verify_or_pin(&self, peer_id: &str, public_key: &[u8]) -> TrustResult<bool> {
187 let mut peers = self.peers.write().await;
188
189 if let Some(record) = peers.get_mut(peer_id) {
190 record.current_key = public_key.to_vec();
191 record.last_seen = now_secs();
192 record.interaction_count += 1;
193
194 if record.pinned_key != public_key {
195 record.trust_level = TrustLevel::KeyChanged;
196 return Err(TrustError::KeyChanged {
197 peer_id: peer_id.to_string(),
198 });
199 }
200
201 if record.trust_level == TrustLevel::FirstContact {
202 record.trust_level = TrustLevel::Trusted;
203 }
204
205 Ok(false)
206 } else {
207 peers.insert(
208 peer_id.to_string(),
209 PeerRecord {
210 peer_id: peer_id.to_string(),
211 pinned_key: public_key.to_vec(),
212 current_key: public_key.to_vec(),
213 trust_level: TrustLevel::FirstContact,
214 first_seen: now_secs(),
215 last_seen: now_secs(),
216 attestations: vec![],
217 interaction_count: 1,
218 display_name: None,
219 },
220 );
221 Ok(true)
222 }
223 }
224
225 pub async fn add_attestation(&self, attestation: Attestation) -> TrustResult<()> {
227 self.verify_attestation_signature(&attestation)
228 .await
229 .map_err(TrustError::AttestationFailed)?;
230
231 let mut peers = self.peers.write().await;
232 let record = peers
233 .entry(attestation.subject_id.clone())
234 .or_insert_with(|| PeerRecord {
235 peer_id: attestation.subject_id.clone(),
236 pinned_key: attestation.subject_pub_key.clone(),
237 current_key: attestation.subject_pub_key.clone(),
238 trust_level: TrustLevel::Vouched,
239 first_seen: now_secs(),
240 last_seen: now_secs(),
241 attestations: vec![],
242 interaction_count: 0,
243 display_name: None,
244 });
245
246 if !record
247 .attestations
248 .iter()
249 .any(|a| a.attester_id == attestation.attester_id)
250 {
251 record.attestations.push(attestation);
252
253 if record.trust_level == TrustLevel::Unknown
254 || record.trust_level == TrustLevel::FirstContact
255 {
256 record.trust_level = TrustLevel::Vouched;
257 }
258 }
259
260 Ok(())
261 }
262
263 pub async fn mark_verified(&self, peer_id: &str, verified_key: &[u8]) -> TrustResult<()> {
265 let mut peers = self.peers.write().await;
266 let record = peers
267 .get_mut(peer_id)
268 .ok_or_else(|| TrustError::UnknownPeer {
269 peer_id: peer_id.to_string(),
270 })?;
271
272 if record.pinned_key != verified_key {
273 return Err(TrustError::KeyMismatch);
274 }
275
276 record.trust_level = TrustLevel::Verified;
277 Ok(())
278 }
279
280 pub async fn block(&self, peer_id: &str) {
282 let mut peers = self.peers.write().await;
283 if let Some(record) = peers.get_mut(peer_id) {
284 record.trust_level = TrustLevel::Blocked;
285 }
286 }
287
288 pub async fn get(&self, peer_id: &str) -> Option<PeerRecord> {
290 self.peers.read().await.get(peer_id).cloned()
291 }
292
293 pub async fn trust_score(&self, peer_id: &str) -> f32 {
295 let peers = self.peers.read().await;
296 let scores = self.our_trust_scores.read().await;
297 peers
298 .get(peer_id)
299 .map(|r| r.computed_trust_score(&scores))
300 .unwrap_or(0.0)
301 }
302
303 pub async fn all_peers(&self) -> Vec<PeerRecord> {
305 let peers = self.peers.read().await;
306 let scores = self.our_trust_scores.read().await;
307 let mut list: Vec<_> = peers.values().cloned().collect();
308 list.sort_by(|a, b| {
309 b.computed_trust_score(&scores)
310 .partial_cmp(&a.computed_trust_score(&scores))
311 .unwrap_or(std::cmp::Ordering::Equal)
312 });
313 list
314 }
315
316 pub async fn set_attester_trust(&self, peer_id: String, score: f32) {
318 self.our_trust_scores
319 .write()
320 .await
321 .insert(peer_id, score.clamp(0.0, 1.0));
322 }
323
324 pub async fn blocked_count(&self) -> usize {
326 self.peers
327 .read()
328 .await
329 .values()
330 .filter(|r| r.trust_level == TrustLevel::Blocked)
331 .count()
332 }
333
334 pub async fn average_trust_score(&self) -> f32 {
336 let peers = self.peers.read().await;
337 if peers.is_empty() {
338 return 0.0;
339 }
340 let sum: f32 = peers.values().map(|r| r.trust_level.score()).sum();
341 sum / peers.len() as f32
342 }
343
344 async fn verify_attestation_signature(&self, att: &Attestation) -> Result<(), String> {
345 use ring::signature::{UnparsedPublicKey, ED25519};
346
347 if att.signature.is_empty() {
348 return Err(format!(
349 "Attestation from {} has empty signature",
350 att.attester_id
351 ));
352 }
353 if att.subject_pub_key.is_empty() {
354 return Err(format!(
355 "Attestation from {} has empty subject public key",
356 att.attester_id
357 ));
358 }
359
360 let attester_pub_key = {
361 let peers = self.peers.read().await;
362 peers
363 .get(&att.attester_id)
364 .map(|r| r.current_key.clone())
365 .ok_or_else(|| {
366 format!(
367 "Cannot verify attestation from unknown peer: {}",
368 att.attester_id
369 )
370 })?
371 };
372
373 let payload = [
374 att.subject_id.as_bytes(),
375 &att.subject_pub_key,
376 &att.timestamp.to_le_bytes(),
377 ]
378 .concat();
379
380 let pub_key = UnparsedPublicKey::new(&ED25519, &attester_pub_key);
381 pub_key.verify(&payload, &att.signature).map_err(|e| {
382 format!(
383 "Attestation signature verification failed for {} → {}: {}",
384 att.attester_id, att.subject_id, e
385 )
386 })
387 }
388}
389
390impl Default for TrustStore {
391 fn default() -> Self {
392 Self::new()
393 }
394}
395
396fn now_secs() -> u64 {
397 std::time::SystemTime::now()
398 .duration_since(std::time::UNIX_EPOCH)
399 .unwrap_or_default()
400 .as_secs()
401}
402
403#[cfg(test)]
404mod tests {
405 use super::*;
406
407 #[tokio::test]
408 async fn tofu_pin_on_first_contact() {
409 let store = TrustStore::new();
410 let key = vec![0x01u8; 32];
411 let is_new = store.verify_or_pin("peer1", &key).await.unwrap();
412 assert!(is_new);
413 let record = store.get("peer1").await.unwrap();
414 assert_eq!(record.trust_level, TrustLevel::FirstContact);
415 }
416
417 #[tokio::test]
418 async fn second_contact_with_same_key_is_trusted() {
419 let store = TrustStore::new();
420 let key = vec![0x02u8; 32];
421 store.verify_or_pin("peer1", &key).await.unwrap();
422 let is_new = store.verify_or_pin("peer1", &key).await.unwrap();
423 assert!(!is_new);
424 let record = store.get("peer1").await.unwrap();
425 assert_eq!(record.trust_level, TrustLevel::Trusted);
426 }
427
428 #[tokio::test]
429 async fn key_change_returns_error() {
430 let store = TrustStore::new();
431 let key1 = vec![0x03u8; 32];
432 let key2 = vec![0x04u8; 32];
433 store.verify_or_pin("peer1", &key1).await.unwrap();
434 let result = store.verify_or_pin("peer1", &key2).await;
435 assert!(result.is_err());
436 let record = store.get("peer1").await.unwrap();
437 assert_eq!(record.trust_level, TrustLevel::KeyChanged);
438 }
439
440 #[tokio::test]
441 async fn block_sets_level() {
442 let store = TrustStore::new();
443 let key = vec![0x05u8; 32];
444 store.verify_or_pin("peer1", &key).await.unwrap();
445 store.block("peer1").await;
446 let record = store.get("peer1").await.unwrap();
447 assert_eq!(record.trust_level, TrustLevel::Blocked);
448 assert!(!record.trust_level.is_usable());
449 }
450}