1pub mod log;
2pub mod session;
3
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct AttestationRecord {
9 pub session_id: String,
10 pub timestamp: u64,
11 pub model: ModelAttestation,
12 pub sandbox: SandboxAttestation,
13 pub inference: InferenceAttestation,
14 pub signature: Vec<u8>,
16 pub prev_hash: [u8; 32],
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct ModelAttestation {
22 pub name: String,
23 pub merkle_root: [u8; 32],
24 pub publisher: Option<String>,
25 pub signature_verified: bool,
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct SandboxAttestation {
30 pub platform: String,
31 pub active: bool,
32 pub memory_limit_bytes: u64,
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct InferenceAttestation {
37 pub tokens_generated: u64,
38 pub prompt_hash: [u8; 32],
39 pub output_hash: [u8; 32],
40 pub temperature: f32,
41 pub top_p: f32,
42}
43
44impl AttestationRecord {
45 pub fn hash(&self) -> [u8; 32] {
47 let bytes = serde_json::to_vec(self).unwrap_or_default();
48 blake3::hash(&bytes).into()
49 }
50
51 pub fn signable_bytes(&self) -> Vec<u8> {
53 let content = serde_json::json!({
55 "session_id": self.session_id,
56 "timestamp": self.timestamp,
57 "model": self.model,
58 "sandbox": self.sandbox,
59 "inference": self.inference,
60 });
61 serde_json::to_vec(&content).unwrap_or_default()
62 }
63
64 pub fn verify_signature(&self, public_key: &[u8; 32]) -> bool {
66 if self.signature.len() != 64 {
67 return false;
68 }
69 let verifier = yule_verify::signature::SignatureVerifier::new();
70 let sig: [u8; 64] = self.signature[..64].try_into().unwrap();
71 verifier.verify_ed25519(public_key, &self.signable_bytes(), &sig)
72 .unwrap_or(false)
73 }
74}