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}
363
364#[derive(Debug, Clone, Serialize, Deserialize)]
366pub struct RevocationList {
367 pub issuer_id: String,
368 pub updated_at: i64,
369 pub revoked_certs: Vec<String>,
370 pub signature: HybridSignature,
371}
372
373#[derive(Debug, Clone, Serialize, Deserialize)]
375pub struct RevocationPush {
376 pub issuer_id: String,
377 pub seq_no: i64,
378 pub entries: Vec<String>,
379 pub pushed_at: i64,
380 pub signature: HybridSignature,
381}
382
383#[derive(Debug, Clone, Serialize, Deserialize)]
385pub struct WitnessEntry {
386 #[serde(with = "crate::canonical::base64_bytes")]
387 pub prev_hash: Vec<u8>,
388 #[serde(with = "crate::canonical::base64_bytes")]
389 pub entry_data: Vec<u8>,
390 pub timestamp: i64,
391 pub witness_id: String,
392 pub signature: HybridSignature,
393}
394
395#[derive(Debug, Clone, Serialize, Deserialize)]
399pub struct SessionToken {
400 pub version: i32,
401 pub session_id: String,
402 pub agent_id: String,
403 pub agent_pub_key: HybridPublicKey,
404 pub human_id: String,
405 pub granted_scope: Vec<String>,
406 pub issued_at: i64,
407 pub valid_until: i64,
408 #[serde(with = "crate::canonical::base64_bytes")]
409 pub chain_hash: Vec<u8>,
410 #[serde(with = "crate::canonical::base64_bytes")]
411 pub mac: Vec<u8>,
412}
413
414#[derive(Debug, Clone, Serialize, Deserialize)]
416pub struct TransactionReceipt {
417 pub version: i32,
418 pub transaction_id: String,
419 pub created_at: i64,
420 pub terms_schema_uri: String,
421 #[serde(with = "crate::canonical::base64_bytes")]
422 pub terms_canonical_json: Vec<u8>,
423 pub parties: Vec<ReceiptParty>,
424 pub party_signatures: Vec<ReceiptPartySignature>,
425}
426
427#[derive(Debug, Clone, Serialize, Deserialize)]
429pub struct ReceiptParty {
430 pub party_id: String,
431 pub role: String,
432 pub agent_id: String,
433 pub agent_pub_key: HybridPublicKey,
434 pub proof_bundle: ProofBundle,
435}
436
437#[derive(Debug, Clone, Serialize, Deserialize)]
439pub struct ReceiptPartySignature {
440 pub party_id: String,
441 pub signature: HybridSignature,
442}
443
444pub struct TransactionReceiptResult {
446 pub valid: bool,
447 pub error_reason: String,
448 pub party_results: Vec<VerifyResult>,
449}
450
451#[derive(Debug, Clone, Serialize, Deserialize)]
453pub struct KeyRotationStatement {
454 pub version: i32,
455 pub old_id: String,
456 pub old_pub_key: HybridPublicKey,
457 pub new_id: String,
458 pub new_pub_key: HybridPublicKey,
459 pub rotated_at: i64,
460 pub reason: String,
461 pub signature_old: HybridSignature,
462 pub signature_new: HybridSignature,
463}
464
465#[derive(Debug, Clone, Default)]
471pub struct StreamContext {
472 pub stream_id: Vec<u8>,
473 pub last_seen_seq: i64,
474}
475
476pub struct VerifyOptions<'a> {
478 pub required_scope: String,
480 pub is_revoked: Option<Box<dyn Fn(&str) -> bool + 'a>>,
482 pub force_revocation_check: bool,
486 pub now: Option<i64>,
488 pub session_context: Vec<u8>,
490 pub stream: Option<StreamContext>,
492 pub context: VerifierContext<'a>,
496}
497
498impl<'a> Default for VerifyOptions<'a> {
499 fn default() -> Self {
500 Self {
501 required_scope: String::new(),
502 is_revoked: None,
503 force_revocation_check: false,
504 now: None,
505 session_context: Vec::new(),
506 stream: None,
507 context: VerifierContext::default(),
508 }
509 }
510}