#[cfg(not(feature = "std"))]
use alloc::{boxed::Box, string::String, vec, vec::Vec};
use serde::ser::{SerializeMap, Serializer};
use serde::{Deserialize, Serialize};
pub const PROTOCOL_VERSION: i32 = 1;
pub const MAX_DELEGATION_CHAIN_DEPTH: usize = 3;
pub const CHALLENGE_WINDOW_SECONDS: i64 = 300;
pub const ED25519_PUBLIC_KEY_SIZE: usize = 32;
pub const ED25519_SIGNATURE_SIZE: usize = 64;
pub const MLDSA65_PUBLIC_KEY_SIZE: usize = 1952;
pub const MLDSA65_SIGNATURE_SIZE: usize = 3309;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct HybridPublicKey {
#[serde(with = "crate::canonical::base64_bytes")]
pub ed25519: Vec<u8>, #[serde(with = "crate::canonical::base64_bytes")]
pub ml_dsa_65: Vec<u8>, }
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct HybridSignature {
#[serde(with = "crate::canonical::base64_bytes")]
pub ed25519: Vec<u8>, #[serde(with = "crate::canonical::base64_bytes")]
pub ml_dsa_65: Vec<u8>, }
#[derive(Debug, Clone)]
pub struct HybridPrivateKey {
pub ed25519: Vec<u8>, pub ml_dsa_65: Vec<u8>, }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Anchor {
#[serde(rename = "type")]
pub anchor_type: String,
pub provider: String,
pub reference: String,
pub verified_at: i64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HumanRoot {
pub id: String,
pub public_key: HybridPublicKey,
pub created_at: i64,
#[serde(skip_serializing_if = "Option::is_none")]
pub anchors: Option<Vec<Anchor>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentIdentity {
pub id: String,
pub public_key: HybridPublicKey,
pub name: String,
pub agent_type: String,
pub created_at: i64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DelegationCert {
pub cert_id: String,
pub version: i32,
pub issuer_id: String,
pub issuer_pub_key: HybridPublicKey,
pub subject_id: String,
pub subject_pub_key: HybridPublicKey,
pub scope: Vec<String>,
#[serde(default)]
pub constraints: Vec<Constraint>,
pub issued_at: i64,
pub expires_at: i64,
pub signature: HybridSignature,
}
#[derive(Debug, Clone, Default, Deserialize)]
pub struct Constraint {
#[serde(default)]
pub count: i64,
#[serde(default)]
pub currency: String,
#[serde(default)]
pub end: String,
#[serde(default)]
pub lat: f64,
#[serde(default)]
pub lon: f64,
#[serde(default)]
pub max_alt_m: f64,
#[serde(default)]
pub max_amount: f64,
#[serde(default)]
pub max_lat: f64,
#[serde(default)]
pub max_lon: f64,
#[serde(default)]
pub max_mps: f64,
#[serde(default)]
pub min_alt_m: f64,
#[serde(default)]
pub min_lat: f64,
#[serde(default)]
pub min_lon: f64,
#[serde(default)]
pub points: Vec<[f64; 2]>,
#[serde(default)]
pub radius_m: f64,
#[serde(default)]
pub start: String,
#[serde(default)]
pub tz: String,
#[serde(rename = "type")]
pub kind: String,
#[serde(default)]
pub window_s: i64,
}
impl Serialize for Constraint {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let entries: Vec<(&'static str, FieldValue)> = match self.kind.as_str() {
"geo_circle" => vec![
("lat", FieldValue::F64(self.lat)),
("lon", FieldValue::F64(self.lon)),
("radius_m", FieldValue::F64(self.radius_m)),
("type", FieldValue::Str(self.kind.clone())),
],
"geo_polygon" => vec![
("points", FieldValue::Points(self.points.clone())),
("type", FieldValue::Str(self.kind.clone())),
],
"geo_bbox" => {
let mut v = vec![
("max_lat", FieldValue::F64(self.max_lat)),
("max_lon", FieldValue::F64(self.max_lon)),
("min_lat", FieldValue::F64(self.min_lat)),
("min_lon", FieldValue::F64(self.min_lon)),
];
if self.min_alt_m != 0.0 || self.max_alt_m != 0.0 {
v.insert(0, ("max_alt_m", FieldValue::F64(self.max_alt_m)));
v.insert(3, ("min_alt_m", FieldValue::F64(self.min_alt_m)));
}
v.push(("type", FieldValue::Str(self.kind.clone())));
v
}
"time_window" => vec![
("end", FieldValue::Str(self.end.clone())),
("start", FieldValue::Str(self.start.clone())),
("type", FieldValue::Str(self.kind.clone())),
("tz", FieldValue::Str(self.tz.clone())),
],
"max_speed_mps" => vec![
("max_mps", FieldValue::F64(self.max_mps)),
("type", FieldValue::Str(self.kind.clone())),
],
"max_amount" => vec![
("currency", FieldValue::Str(self.currency.clone())),
("max_amount", FieldValue::F64(self.max_amount)),
("type", FieldValue::Str(self.kind.clone())),
],
"max_rate" => vec![
("count", FieldValue::I64(self.count)),
("type", FieldValue::Str(self.kind.clone())),
("window_s", FieldValue::I64(self.window_s)),
],
_ => vec![("type", FieldValue::Str(self.kind.clone()))],
};
let mut m = serializer.serialize_map(Some(entries.len()))?;
for (k, v) in entries {
match v {
FieldValue::F64(x) => m.serialize_entry(k, &x)?,
FieldValue::I64(x) => m.serialize_entry(k, &x)?,
FieldValue::Str(x) => m.serialize_entry(k, &x)?,
FieldValue::Points(x) => m.serialize_entry(k, &x)?,
}
}
m.end()
}
}
enum FieldValue {
F64(f64),
I64(i64),
Str(String),
Points(Vec<[f64; 2]>),
}
#[derive(Default)]
pub struct VerifierContext<'a> {
pub current_lat: Option<f64>,
pub current_lon: Option<f64>,
pub current_alt_m: Option<f64>,
pub current_speed_mps: Option<f64>,
pub requested_amount: Option<f64>,
pub requested_currency: Option<String>,
pub invocations_in_window: Option<Box<dyn Fn(&str, i64) -> i64 + 'a>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProofBundle {
pub agent_id: String,
pub agent_pub_key: HybridPublicKey,
pub delegations: Vec<DelegationCert>,
#[serde(with = "crate::canonical::base64_bytes")]
pub challenge: Vec<u8>,
pub challenge_at: i64,
pub challenge_sig: HybridSignature,
#[serde(
default,
skip_serializing_if = "Vec::is_empty",
with = "crate::canonical::base64_bytes"
)]
pub session_context: Vec<u8>,
#[serde(
default,
skip_serializing_if = "Vec::is_empty",
with = "crate::canonical::base64_bytes"
)]
pub stream_id: Vec<u8>,
#[serde(default, skip_serializing_if = "is_zero_i64")]
pub stream_seq: i64,
}
fn is_zero_i64(v: &i64) -> bool {
*v == 0
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum IdentityStatus {
VerifiedHuman,
AuthorizedAgent,
Expired,
Revoked,
ScopeDenied,
ConstraintDenied,
ConstraintUnverifiable,
ConstraintUnknown,
DelegationNotAuthorized,
Invalid,
Unauthorized,
}
impl IdentityStatus {
pub fn as_str(&self) -> &'static str {
match self {
Self::VerifiedHuman => "verified_human",
Self::AuthorizedAgent => "authorized_agent",
Self::Expired => "expired",
Self::Revoked => "revoked",
Self::ScopeDenied => "scope_denied",
Self::ConstraintDenied => "constraint_denied",
Self::ConstraintUnverifiable => "constraint_unverifiable",
Self::ConstraintUnknown => "constraint_unknown",
Self::DelegationNotAuthorized => "delegation_not_authorized",
Self::Invalid => "invalid",
Self::Unauthorized => "unauthorized",
}
}
pub fn from_wire(s: &str) -> Option<Self> {
Some(match s {
"verified_human" => Self::VerifiedHuman,
"authorized_agent" => Self::AuthorizedAgent,
"expired" => Self::Expired,
"revoked" => Self::Revoked,
"scope_denied" => Self::ScopeDenied,
"constraint_denied" => Self::ConstraintDenied,
"constraint_unverifiable" => Self::ConstraintUnverifiable,
"constraint_unknown" => Self::ConstraintUnknown,
"delegation_not_authorized" => Self::DelegationNotAuthorized,
"invalid" => Self::Invalid,
"unauthorized" => Self::Unauthorized,
_ => return None,
})
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VerifyResult {
pub valid: bool,
pub identity_status: IdentityStatus,
#[serde(skip_serializing_if = "String::is_empty", default)]
pub human_id: String,
#[serde(skip_serializing_if = "String::is_empty", default)]
pub agent_id: String,
#[serde(skip_serializing_if = "String::is_empty", default)]
pub agent_name: String,
#[serde(skip_serializing_if = "String::is_empty", default)]
pub agent_type: String,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub granted_scope: Vec<String>,
#[serde(skip_serializing_if = "String::is_empty", default)]
pub error_reason: String,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub anchor: Option<Anchor>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RevocationList {
pub issuer_id: String,
pub updated_at: i64,
pub revoked_certs: Vec<String>,
pub signature: HybridSignature,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RevocationPush {
pub issuer_id: String,
pub seq_no: i64,
pub entries: Vec<String>,
pub pushed_at: i64,
pub signature: HybridSignature,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WitnessEntry {
#[serde(with = "crate::canonical::base64_bytes")]
pub prev_hash: Vec<u8>,
#[serde(with = "crate::canonical::base64_bytes")]
pub entry_data: Vec<u8>,
pub timestamp: i64,
pub witness_id: String,
pub signature: HybridSignature,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SessionToken {
pub version: i32,
pub session_id: String,
pub agent_id: String,
pub agent_pub_key: HybridPublicKey,
pub human_id: String,
pub granted_scope: Vec<String>,
pub issued_at: i64,
pub valid_until: i64,
#[serde(with = "crate::canonical::base64_bytes")]
pub chain_hash: Vec<u8>,
#[serde(with = "crate::canonical::base64_bytes")]
pub mac: Vec<u8>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TransactionReceipt {
pub version: i32,
pub transaction_id: String,
pub created_at: i64,
pub terms_schema_uri: String,
#[serde(with = "crate::canonical::base64_bytes")]
pub terms_canonical_json: Vec<u8>,
pub parties: Vec<ReceiptParty>,
pub party_signatures: Vec<ReceiptPartySignature>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReceiptParty {
pub party_id: String,
pub role: String,
pub agent_id: String,
pub agent_pub_key: HybridPublicKey,
pub proof_bundle: ProofBundle,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReceiptPartySignature {
pub party_id: String,
pub signature: HybridSignature,
}
pub struct TransactionReceiptResult {
pub valid: bool,
pub error_reason: String,
pub party_results: Vec<VerifyResult>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KeyRotationStatement {
pub version: i32,
pub old_id: String,
pub old_pub_key: HybridPublicKey,
pub new_id: String,
pub new_pub_key: HybridPublicKey,
pub rotated_at: i64,
pub reason: String,
pub signature_old: HybridSignature,
pub signature_new: HybridSignature,
}
#[derive(Debug, Clone, Default)]
pub struct StreamContext {
pub stream_id: Vec<u8>,
pub last_seen_seq: i64,
}
pub trait RevocationProvider {
fn is_revoked(&self, cert_id: &str) -> Result<bool, String>;
}
pub trait PolicyProvider {
fn evaluate_policy(
&self,
bundle: &ProofBundle,
context: &VerifierContext,
) -> Result<bool, String>;
}
pub trait AuditProvider {
fn log_verification(&self, result: &VerifyResult, bundle: &ProofBundle);
}
pub trait ConstraintEvaluator {
fn evaluate(
&self,
constraint: &Constraint,
cert_id: &str,
context: &VerifierContext,
now: i64,
) -> Result<(), String>;
}
pub trait AnchorResolver {
fn resolve_anchor(&self, human_id: &str) -> Result<Option<Anchor>, String>;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PolicyVerdict {
pub version: i32,
pub verdict_id: String,
pub agent_id: String,
pub scope: String,
pub allow: bool,
#[serde(with = "crate::canonical::base64_bytes")]
pub context_hash: Vec<u8>, pub issued_at: i64,
pub valid_until: i64,
#[serde(with = "crate::canonical::base64_bytes")]
pub mac: Vec<u8>, }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VerificationReceipt {
pub version: i32,
pub verifier_id: String,
pub verifier_pub: HybridPublicKey,
#[serde(with = "crate::canonical::base64_bytes")]
pub bundle_hash: Vec<u8>, pub decision: String,
#[serde(skip_serializing_if = "String::is_empty", default)]
pub human_id: String,
#[serde(skip_serializing_if = "String::is_empty", default)]
pub agent_id: String,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub granted_scope: Vec<String>,
#[serde(skip_serializing_if = "String::is_empty", default)]
pub error_reason: String,
pub verified_at: i64,
#[serde(with = "crate::canonical::base64_bytes")]
pub prev_hash: Vec<u8>, pub signature: HybridSignature,
}
pub struct VerifyOptions<'a> {
pub required_scope: String,
#[deprecated(since = "1.0.0-alpha.7", note = "use `revocation` (SPEC §17.1) instead")]
pub is_revoked: Option<Box<dyn Fn(&str) -> bool + 'a>>,
pub revocation: Option<Box<dyn RevocationProvider + 'a>>,
pub force_revocation_check: bool,
pub now: Option<i64>,
pub session_context: Vec<u8>,
pub stream: Option<StreamContext>,
pub context: VerifierContext<'a>,
pub policy: Option<Box<dyn PolicyProvider + 'a>>,
pub audit: Option<Box<dyn AuditProvider + 'a>>,
pub constraint_evaluators:
Option<alloc::collections::BTreeMap<String, Box<dyn ConstraintEvaluator + 'a>>>,
pub policy_verdict: Option<PolicyVerdict>,
pub policy_secret: Option<Vec<u8>>,
pub anchor_resolver: Option<Box<dyn AnchorResolver + 'a>>,
}
impl<'a> Default for VerifyOptions<'a> {
fn default() -> Self {
#[allow(deprecated)]
Self {
required_scope: String::new(),
is_revoked: None,
revocation: None,
force_revocation_check: false,
now: None,
session_context: Vec::new(),
stream: None,
context: VerifierContext::default(),
policy: None,
audit: None,
constraint_evaluators: None,
policy_verdict: None,
policy_secret: None,
anchor_resolver: None,
}
}
}