1use serde::ser::{SerializeMap, Serializer};
7use serde::{Deserialize, Serialize};
8
9pub const PROTOCOL_VERSION: i32 = 1;
10pub const MAX_DELEGATION_CHAIN_DEPTH: usize = 3;
11pub const CHALLENGE_WINDOW_SECONDS: i64 = 300;
12
13pub const ED25519_PUBLIC_KEY_SIZE: usize = 32;
14pub const ED25519_SIGNATURE_SIZE: usize = 64;
15pub const MLDSA65_PUBLIC_KEY_SIZE: usize = 1952;
16pub const MLDSA65_SIGNATURE_SIZE: usize = 3309;
17
18#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
23pub struct HybridPublicKey {
24 #[serde(with = "crate::canonical::base64_bytes")]
25 pub ed25519: Vec<u8>, #[serde(with = "crate::canonical::base64_bytes")]
27 pub ml_dsa_65: Vec<u8>, }
29
30#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
34pub struct HybridSignature {
35 #[serde(with = "crate::canonical::base64_bytes")]
36 pub ed25519: Vec<u8>, #[serde(with = "crate::canonical::base64_bytes")]
38 pub ml_dsa_65: Vec<u8>, }
40
41#[derive(Debug, Clone)]
43pub struct HybridPrivateKey {
44 pub ed25519: Vec<u8>, pub ml_dsa_65: Vec<u8>, }
47
48#[derive(Debug, Clone, Serialize, Deserialize)]
50pub struct Anchor {
51 #[serde(rename = "type")]
52 pub anchor_type: String,
53 pub provider: String,
54 pub reference: String,
55 pub verified_at: i64,
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize)]
60pub struct HumanRoot {
61 pub id: String,
62 pub public_key: HybridPublicKey,
63 pub created_at: i64,
64 #[serde(skip_serializing_if = "Option::is_none")]
65 pub anchors: Option<Vec<Anchor>>,
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize)]
70pub struct AgentIdentity {
71 pub id: String,
72 pub public_key: HybridPublicKey,
73 pub name: String,
74 pub agent_type: String,
75 pub created_at: i64,
76}
77
78#[derive(Debug, Clone, Serialize, Deserialize)]
84pub struct DelegationCert {
85 pub cert_id: String,
86 pub version: i32,
87 pub issuer_id: String,
88 pub issuer_pub_key: HybridPublicKey,
89 pub subject_id: String,
90 pub subject_pub_key: HybridPublicKey,
91 pub scope: Vec<String>,
92 #[serde(default)]
95 pub constraints: Vec<Constraint>,
96 pub issued_at: i64,
97 pub expires_at: i64,
98 pub signature: HybridSignature,
99}
100
101#[derive(Debug, Clone, Default, Deserialize)]
117pub struct Constraint {
118 #[serde(default)]
119 pub count: i64,
120 #[serde(default)]
121 pub currency: String,
122 #[serde(default)]
123 pub end: String,
124 #[serde(default)]
125 pub lat: f64,
126 #[serde(default)]
127 pub lon: f64,
128 #[serde(default)]
129 pub max_alt_m: f64,
130 #[serde(default)]
131 pub max_amount: f64,
132 #[serde(default)]
133 pub max_lat: f64,
134 #[serde(default)]
135 pub max_lon: f64,
136 #[serde(default)]
137 pub max_mps: f64,
138 #[serde(default)]
139 pub min_alt_m: f64,
140 #[serde(default)]
141 pub min_lat: f64,
142 #[serde(default)]
143 pub min_lon: f64,
144 #[serde(default)]
145 pub points: Vec<[f64; 2]>,
146 #[serde(default)]
147 pub radius_m: f64,
148 #[serde(default)]
149 pub start: String,
150 #[serde(default)]
151 pub tz: String,
152 #[serde(rename = "type")]
153 pub kind: String,
154 #[serde(default)]
155 pub window_s: i64,
156}
157
158impl Serialize for Constraint {
162 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
163 let entries: Vec<(&'static str, FieldValue)> = match self.kind.as_str() {
167 "geo_circle" => vec![
168 ("lat", FieldValue::F64(self.lat)),
169 ("lon", FieldValue::F64(self.lon)),
170 ("radius_m", FieldValue::F64(self.radius_m)),
171 ("type", FieldValue::Str(self.kind.clone())),
172 ],
173 "geo_polygon" => vec![
174 ("points", FieldValue::Points(self.points.clone())),
175 ("type", FieldValue::Str(self.kind.clone())),
176 ],
177 "geo_bbox" => {
178 let mut v = vec![
179 ("max_lat", FieldValue::F64(self.max_lat)),
180 ("max_lon", FieldValue::F64(self.max_lon)),
181 ("min_lat", FieldValue::F64(self.min_lat)),
182 ("min_lon", FieldValue::F64(self.min_lon)),
183 ];
184 if self.min_alt_m != 0.0 || self.max_alt_m != 0.0 {
185 v.insert(0, ("max_alt_m", FieldValue::F64(self.max_alt_m)));
187 v.insert(3, ("min_alt_m", FieldValue::F64(self.min_alt_m)));
189 }
190 v.push(("type", FieldValue::Str(self.kind.clone())));
191 v
192 }
193 "time_window" => vec![
194 ("end", FieldValue::Str(self.end.clone())),
195 ("start", FieldValue::Str(self.start.clone())),
196 ("type", FieldValue::Str(self.kind.clone())),
197 ("tz", FieldValue::Str(self.tz.clone())),
198 ],
199 "max_speed_mps" => vec![
200 ("max_mps", FieldValue::F64(self.max_mps)),
201 ("type", FieldValue::Str(self.kind.clone())),
202 ],
203 "max_amount" => vec![
204 ("currency", FieldValue::Str(self.currency.clone())),
205 ("max_amount", FieldValue::F64(self.max_amount)),
206 ("type", FieldValue::Str(self.kind.clone())),
207 ],
208 "max_rate" => vec![
209 ("count", FieldValue::I64(self.count)),
210 ("type", FieldValue::Str(self.kind.clone())),
211 ("window_s", FieldValue::I64(self.window_s)),
212 ],
213 _ => vec![("type", FieldValue::Str(self.kind.clone()))],
215 };
216 let mut m = serializer.serialize_map(Some(entries.len()))?;
217 for (k, v) in entries {
218 match v {
219 FieldValue::F64(x) => m.serialize_entry(k, &x)?,
220 FieldValue::I64(x) => m.serialize_entry(k, &x)?,
221 FieldValue::Str(x) => m.serialize_entry(k, &x)?,
222 FieldValue::Points(x) => m.serialize_entry(k, &x)?,
223 }
224 }
225 m.end()
226 }
227}
228
229enum FieldValue {
232 F64(f64),
233 I64(i64),
234 Str(String),
235 Points(Vec<[f64; 2]>),
236}
237
238#[derive(Default)]
242pub struct VerifierContext<'a> {
243 pub current_lat: Option<f64>,
244 pub current_lon: Option<f64>,
245 pub current_alt_m: Option<f64>,
246 pub current_speed_mps: Option<f64>,
247 pub requested_amount: Option<f64>,
248 pub requested_currency: Option<String>,
249 pub invocations_in_window: Option<Box<dyn Fn(&str, i64) -> i64 + 'a>>,
251}
252
253#[derive(Debug, Clone, Serialize, Deserialize)]
261pub struct ProofBundle {
262 pub agent_id: String,
263 pub agent_pub_key: HybridPublicKey,
264 pub delegations: Vec<DelegationCert>,
265 #[serde(with = "crate::canonical::base64_bytes")]
266 pub challenge: Vec<u8>,
267 pub challenge_at: i64,
268 pub challenge_sig: HybridSignature,
269 #[serde(
270 default,
271 skip_serializing_if = "Vec::is_empty",
272 with = "crate::canonical::base64_bytes"
273 )]
274 pub session_context: Vec<u8>,
275 #[serde(
276 default,
277 skip_serializing_if = "Vec::is_empty",
278 with = "crate::canonical::base64_bytes"
279 )]
280 pub stream_id: Vec<u8>,
281 #[serde(default, skip_serializing_if = "is_zero_i64")]
282 pub stream_seq: i64,
283}
284
285fn is_zero_i64(v: &i64) -> bool {
286 *v == 0
287}
288
289#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
293#[serde(rename_all = "snake_case")]
294pub enum IdentityStatus {
295 VerifiedHuman,
296 AuthorizedAgent,
297 Expired,
298 Revoked,
299 ScopeDenied,
300 ConstraintDenied,
301 ConstraintUnverifiable,
302 ConstraintUnknown,
303 DelegationNotAuthorized,
304 Invalid,
305 Unauthorized,
306}
307
308impl IdentityStatus {
309 pub fn as_str(&self) -> &'static str {
310 match self {
311 Self::VerifiedHuman => "verified_human",
312 Self::AuthorizedAgent => "authorized_agent",
313 Self::Expired => "expired",
314 Self::Revoked => "revoked",
315 Self::ScopeDenied => "scope_denied",
316 Self::ConstraintDenied => "constraint_denied",
317 Self::ConstraintUnverifiable => "constraint_unverifiable",
318 Self::ConstraintUnknown => "constraint_unknown",
319 Self::DelegationNotAuthorized => "delegation_not_authorized",
320 Self::Invalid => "invalid",
321 Self::Unauthorized => "unauthorized",
322 }
323 }
324
325 pub fn from_wire(s: &str) -> Option<Self> {
328 Some(match s {
329 "verified_human" => Self::VerifiedHuman,
330 "authorized_agent" => Self::AuthorizedAgent,
331 "expired" => Self::Expired,
332 "revoked" => Self::Revoked,
333 "scope_denied" => Self::ScopeDenied,
334 "constraint_denied" => Self::ConstraintDenied,
335 "constraint_unverifiable" => Self::ConstraintUnverifiable,
336 "constraint_unknown" => Self::ConstraintUnknown,
337 "delegation_not_authorized" => Self::DelegationNotAuthorized,
338 "invalid" => Self::Invalid,
339 "unauthorized" => Self::Unauthorized,
340 _ => return None,
341 })
342 }
343}
344
345#[derive(Debug, Clone, Serialize, Deserialize)]
347pub struct VerifyResult {
348 pub valid: bool,
349 pub identity_status: IdentityStatus,
350 #[serde(skip_serializing_if = "String::is_empty", default)]
351 pub human_id: String,
352 #[serde(skip_serializing_if = "String::is_empty", default)]
353 pub agent_id: String,
354 #[serde(skip_serializing_if = "String::is_empty", default)]
355 pub agent_name: String,
356 #[serde(skip_serializing_if = "String::is_empty", default)]
357 pub agent_type: String,
358 #[serde(skip_serializing_if = "Vec::is_empty", default)]
359 pub granted_scope: Vec<String>,
360 #[serde(skip_serializing_if = "String::is_empty", default)]
361 pub error_reason: String,
362 #[serde(skip_serializing_if = "Option::is_none", default)]
367 pub anchor: Option<Anchor>,
368}
369
370#[derive(Debug, Clone, Serialize, Deserialize)]
372pub struct RevocationList {
373 pub issuer_id: String,
374 pub updated_at: i64,
375 pub revoked_certs: Vec<String>,
376 pub signature: HybridSignature,
377}
378
379#[derive(Debug, Clone, Serialize, Deserialize)]
381pub struct RevocationPush {
382 pub issuer_id: String,
383 pub seq_no: i64,
384 pub entries: Vec<String>,
385 pub pushed_at: i64,
386 pub signature: HybridSignature,
387}
388
389#[derive(Debug, Clone, Serialize, Deserialize)]
391pub struct WitnessEntry {
392 #[serde(with = "crate::canonical::base64_bytes")]
393 pub prev_hash: Vec<u8>,
394 #[serde(with = "crate::canonical::base64_bytes")]
395 pub entry_data: Vec<u8>,
396 pub timestamp: i64,
397 pub witness_id: String,
398 pub signature: HybridSignature,
399}
400
401#[derive(Debug, Clone, Serialize, Deserialize)]
405pub struct SessionToken {
406 pub version: i32,
407 pub session_id: String,
408 pub agent_id: String,
409 pub agent_pub_key: HybridPublicKey,
410 pub human_id: String,
411 pub granted_scope: Vec<String>,
412 pub issued_at: i64,
413 pub valid_until: i64,
414 #[serde(with = "crate::canonical::base64_bytes")]
415 pub chain_hash: Vec<u8>,
416 #[serde(with = "crate::canonical::base64_bytes")]
417 pub mac: Vec<u8>,
418}
419
420#[derive(Debug, Clone, Serialize, Deserialize)]
422pub struct TransactionReceipt {
423 pub version: i32,
424 pub transaction_id: String,
425 pub created_at: i64,
426 pub terms_schema_uri: String,
427 #[serde(with = "crate::canonical::base64_bytes")]
428 pub terms_canonical_json: Vec<u8>,
429 pub parties: Vec<ReceiptParty>,
430 pub party_signatures: Vec<ReceiptPartySignature>,
431}
432
433#[derive(Debug, Clone, Serialize, Deserialize)]
435pub struct ReceiptParty {
436 pub party_id: String,
437 pub role: String,
438 pub agent_id: String,
439 pub agent_pub_key: HybridPublicKey,
440 pub proof_bundle: ProofBundle,
441}
442
443#[derive(Debug, Clone, Serialize, Deserialize)]
445pub struct ReceiptPartySignature {
446 pub party_id: String,
447 pub signature: HybridSignature,
448}
449
450pub struct TransactionReceiptResult {
452 pub valid: bool,
453 pub error_reason: String,
454 pub party_results: Vec<VerifyResult>,
455}
456
457#[derive(Debug, Clone, Serialize, Deserialize)]
459pub struct KeyRotationStatement {
460 pub version: i32,
461 pub old_id: String,
462 pub old_pub_key: HybridPublicKey,
463 pub new_id: String,
464 pub new_pub_key: HybridPublicKey,
465 pub rotated_at: i64,
466 pub reason: String,
467 pub signature_old: HybridSignature,
468 pub signature_new: HybridSignature,
469}
470
471#[derive(Debug, Clone, Default)]
477pub struct StreamContext {
478 pub stream_id: Vec<u8>,
479 pub last_seen_seq: i64,
480}
481
482pub trait RevocationProvider {
490 fn is_revoked(&self, cert_id: &str) -> Result<bool, String>;
491}
492
493pub trait PolicyProvider {
499 fn evaluate_policy(
500 &self,
501 bundle: &ProofBundle,
502 context: &VerifierContext,
503 ) -> Result<bool, String>;
504}
505
506pub trait AuditProvider {
511 fn log_verification(&self, result: &VerifyResult, bundle: &ProofBundle);
512}
513
514pub trait ConstraintEvaluator {
523 fn evaluate(
524 &self,
525 constraint: &Constraint,
526 cert_id: &str,
527 context: &VerifierContext,
528 now: i64,
529 ) -> Result<(), String>;
530}
531
532pub trait AnchorResolver {
537 fn resolve_anchor(&self, human_id: &str) -> Result<Option<Anchor>, String>;
538}
539
540#[derive(Debug, Clone, Serialize, Deserialize)]
545pub struct PolicyVerdict {
546 pub version: i32,
547 pub verdict_id: String,
548 pub agent_id: String,
549 pub scope: String,
550 pub allow: bool,
551 #[serde(with = "crate::canonical::base64_bytes")]
552 pub context_hash: Vec<u8>, pub issued_at: i64,
554 pub valid_until: i64,
555 #[serde(with = "crate::canonical::base64_bytes")]
556 pub mac: Vec<u8>, }
558
559#[derive(Debug, Clone, Serialize, Deserialize)]
566pub struct VerificationReceipt {
567 pub version: i32,
568 pub verifier_id: String,
569 pub verifier_pub: HybridPublicKey,
570 #[serde(with = "crate::canonical::base64_bytes")]
571 pub bundle_hash: Vec<u8>, pub decision: String,
573 #[serde(skip_serializing_if = "String::is_empty", default)]
574 pub human_id: String,
575 #[serde(skip_serializing_if = "String::is_empty", default)]
576 pub agent_id: String,
577 #[serde(skip_serializing_if = "Vec::is_empty", default)]
578 pub granted_scope: Vec<String>,
579 #[serde(skip_serializing_if = "String::is_empty", default)]
580 pub error_reason: String,
581 pub verified_at: i64,
582 #[serde(with = "crate::canonical::base64_bytes")]
583 pub prev_hash: Vec<u8>, pub signature: HybridSignature,
585}
586
587pub struct VerifyOptions<'a> {
589 pub required_scope: String,
591 #[deprecated(since = "1.0.0-alpha.7", note = "use `revocation` (SPEC §17.1) instead")]
598 pub is_revoked: Option<Box<dyn Fn(&str) -> bool + 'a>>,
599 pub revocation: Option<Box<dyn RevocationProvider + 'a>>,
602 pub force_revocation_check: bool,
606 pub now: Option<i64>,
608 pub session_context: Vec<u8>,
610 pub stream: Option<StreamContext>,
612 pub context: VerifierContext<'a>,
616 pub policy: Option<Box<dyn PolicyProvider + 'a>>,
620 pub audit: Option<Box<dyn AuditProvider + 'a>>,
624 pub constraint_evaluators:
628 Option<std::collections::HashMap<String, Box<dyn ConstraintEvaluator + 'a>>>,
629 pub policy_verdict: Option<PolicyVerdict>,
634 pub policy_secret: Option<Vec<u8>>,
636 pub anchor_resolver: Option<Box<dyn AnchorResolver + 'a>>,
640}
641
642impl<'a> Default for VerifyOptions<'a> {
643 fn default() -> Self {
644 #[allow(deprecated)]
648 Self {
649 required_scope: String::new(),
650 is_revoked: None,
651 revocation: None,
652 force_revocation_check: false,
653 now: None,
654 session_context: Vec::new(),
655 stream: None,
656 context: VerifierContext::default(),
657 policy: None,
658 audit: None,
659 constraint_evaluators: None,
660 policy_verdict: None,
661 policy_secret: None,
662 anchor_resolver: None,
663 }
664 }
665}