use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use crate::error::TelError;
use crate::events::KeriSequence;
use crate::said::{Protocol, compute_said_with_protocol};
use crate::types::{Prefix, Said};
pub const TEL_KERIPY_REVISION: &str = "keripy 1.3.4";
pub const TRAIT_NO_BACKERS: &str = "NB";
const KERI_VERSION_PLACEHOLDER: &str = "KERI10JSON000000_";
const KERI_VERSION_PREFIX: &str = "KERI10JSON";
fn recompute_version_string<T: Serialize>(event: &T) -> Result<String, TelError> {
let bytes = serde_json::to_vec(event)?;
Ok(format!("{KERI_VERSION_PREFIX}{:06x}_", bytes.len()))
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Vcp {
pub v: String,
pub t: String,
pub d: Said,
pub i: Said,
pub ii: Prefix,
pub s: KeriSequence,
pub c: Vec<String>,
pub bt: KeriSequence,
pub b: Vec<Prefix>,
pub n: String,
}
impl Vcp {
pub fn new(issuer: Prefix, nonce: String) -> Self {
Self {
v: KERI_VERSION_PLACEHOLDER.to_string(),
t: "vcp".to_string(),
d: Said::default(),
i: Said::default(),
ii: issuer,
s: KeriSequence::new(0),
c: vec![TRAIT_NO_BACKERS.to_string()],
bt: KeriSequence::new(0),
b: Vec::new(),
n: nonce,
}
}
pub fn saidify(mut self) -> Result<Self, TelError> {
let body = serde_json::to_value(&self)?;
let said = compute_said_with_protocol(&body, Protocol::Keri)?;
self.d = said.clone();
self.i = said;
self.v = recompute_version_string(&self.probe())?;
Ok(self)
}
fn probe(&self) -> Self {
let mut probe = self.clone();
probe.v = KERI_VERSION_PLACEHOLDER.to_string();
probe
}
pub fn registry(&self) -> &Said {
&self.d
}
pub fn verify_said(&self) -> Result<(), TelError> {
verify_event_said(self, &self.d, "vcp")?;
if self.i != self.d {
return Err(TelError::SaidMismatch {
event_type: "vcp",
computed: self.d.as_str().to_string(),
found: self.i.as_str().to_string(),
});
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Iss {
pub v: String,
pub t: String,
pub d: Said,
pub i: Said,
pub s: KeriSequence,
pub ri: Said,
pub dt: String,
}
impl Iss {
pub fn new(credential: Said, registry: Said, dt: String) -> Self {
Self {
v: KERI_VERSION_PLACEHOLDER.to_string(),
t: "iss".to_string(),
d: Said::default(),
i: credential,
s: KeriSequence::new(0),
ri: registry,
dt,
}
}
pub fn saidify(mut self) -> Result<Self, TelError> {
let body = serde_json::to_value(&self)?;
self.d = compute_said_with_protocol(&body, Protocol::Keri)?;
let mut probe = self.clone();
probe.v = KERI_VERSION_PLACEHOLDER.to_string();
self.v = recompute_version_string(&probe)?;
Ok(self)
}
pub fn verify_said(&self) -> Result<(), TelError> {
verify_event_said(self, &self.d, "iss")
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Rev {
pub v: String,
pub t: String,
pub d: Said,
pub i: Said,
pub s: KeriSequence,
pub ri: Said,
pub p: Said,
pub dt: String,
}
impl Rev {
pub fn new(credential: Said, registry: Said, prior: Said, dt: String) -> Self {
Self {
v: KERI_VERSION_PLACEHOLDER.to_string(),
t: "rev".to_string(),
d: Said::default(),
i: credential,
s: KeriSequence::new(1),
ri: registry,
p: prior,
dt,
}
}
pub fn saidify(mut self) -> Result<Self, TelError> {
let body = serde_json::to_value(&self)?;
self.d = compute_said_with_protocol(&body, Protocol::Keri)?;
let mut probe = self.clone();
probe.v = KERI_VERSION_PLACEHOLDER.to_string();
self.v = recompute_version_string(&probe)?;
Ok(self)
}
pub fn verify_said(&self) -> Result<(), TelError> {
verify_event_said(self, &self.d, "rev")
}
}
fn verify_event_said<T: Serialize>(
event: &T,
carried: &Said,
event_type: &'static str,
) -> Result<(), TelError> {
let body = serde_json::to_value(event)?;
let computed = compute_said_with_protocol(&body, Protocol::Keri)?;
if &computed != carried {
return Err(TelError::SaidMismatch {
event_type,
computed: computed.into_inner(),
found: carried.as_str().to_string(),
});
}
Ok(())
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct TelAnchorSeal {
pub i: Prefix,
pub s: KeriSequence,
pub d: Said,
}
impl TelAnchorSeal {
pub fn for_event(aid: Prefix, sequence: KeriSequence, said: Said) -> Self {
Self {
i: aid,
s: sequence,
d: said,
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct TelState {
pub issued: Vec<Said>,
pub revoked: Vec<Said>,
}
impl TelState {
pub fn is_valid(&self, credential: &Said) -> bool {
self.issued.contains(credential) && !self.revoked.contains(credential)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TelEvent {
Vcp(Vcp),
Iss(Iss),
Rev(Rev),
}
impl Serialize for TelEvent {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self {
TelEvent::Vcp(e) => e.serialize(serializer),
TelEvent::Iss(e) => e.serialize(serializer),
TelEvent::Rev(e) => e.serialize(serializer),
}
}
}
impl<'de> Deserialize<'de> for TelEvent {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
use serde::de::Error as _;
let value = serde_json::Value::deserialize(deserializer)?;
let event_type = value
.get("t")
.and_then(|v| v.as_str())
.ok_or_else(|| D::Error::missing_field("t"))?;
match event_type {
"vcp" => serde_json::from_value(value)
.map(TelEvent::Vcp)
.map_err(D::Error::custom),
"iss" => serde_json::from_value(value)
.map(TelEvent::Iss)
.map_err(D::Error::custom),
"rev" => serde_json::from_value(value)
.map(TelEvent::Rev)
.map_err(D::Error::custom),
other => Err(D::Error::custom(format!(
"unknown TEL event type '{other}'"
))),
}
}
}
impl TelEvent {
pub fn from_wire_bytes(bytes: &[u8]) -> Result<Self, TelError> {
let value: serde_json::Value = serde_json::from_slice(bytes)?;
let event_type = value
.get("t")
.and_then(|v| v.as_str())
.ok_or_else(|| TelError::Said("TEL event missing required field 't'".to_string()))?;
match event_type {
"vcp" => Ok(TelEvent::Vcp(serde_json::from_value(value)?)),
"iss" => Ok(TelEvent::Iss(serde_json::from_value(value)?)),
"rev" => Ok(TelEvent::Rev(serde_json::from_value(value)?)),
other => Err(TelError::BrokenChain {
credential: String::new(),
detail: format!("unknown TEL event type '{other}'"),
}),
}
}
}
pub fn validate_tel(events: &[TelEvent]) -> Result<TelState, TelError> {
let mut iter = events.iter();
let registry = match iter.next() {
Some(TelEvent::Vcp(vcp)) => {
vcp.verify_said()?;
vcp.registry().clone()
}
_ => return Err(TelError::MissingInception),
};
let mut state = TelState::default();
let mut issuances: HashMap<String, Issuance> = HashMap::new();
for event in iter {
match event {
TelEvent::Vcp(_) => {
return Err(TelError::BrokenChain {
credential: registry.as_str().to_string(),
detail: "a second vcp inception is not allowed in one TEL".to_string(),
});
}
TelEvent::Iss(iss) => apply_iss(iss, ®istry, &mut state, &mut issuances)?,
TelEvent::Rev(rev) => apply_rev(rev, ®istry, &mut state, &issuances)?,
}
}
Ok(state)
}
struct Issuance {
said: Said,
sequence: u128,
}
fn apply_iss(
iss: &Iss,
registry: &Said,
state: &mut TelState,
issuances: &mut HashMap<String, Issuance>,
) -> Result<(), TelError> {
iss.verify_said()?;
if &iss.ri != registry {
return Err(TelError::IssWithoutRegistry {
registry: iss.ri.as_str().to_string(),
});
}
if state.issued.contains(&iss.i) {
return Err(TelError::DoubleIss {
credential: iss.i.as_str().to_string(),
});
}
issuances.insert(
iss.i.as_str().to_string(),
Issuance {
said: iss.d.clone(),
sequence: iss.s.value(),
},
);
state.issued.push(iss.i.clone());
Ok(())
}
fn apply_rev(
rev: &Rev,
registry: &Said,
state: &mut TelState,
issuances: &HashMap<String, Issuance>,
) -> Result<(), TelError> {
rev.verify_said()?;
if &rev.ri != registry {
return Err(TelError::IssWithoutRegistry {
registry: rev.ri.as_str().to_string(),
});
}
let prior = issuances
.get(rev.i.as_str())
.ok_or_else(|| TelError::RevWithoutIss {
credential: rev.i.as_str().to_string(),
})?;
if state.revoked.contains(&rev.i) {
return Err(TelError::DoubleRev {
credential: rev.i.as_str().to_string(),
});
}
if rev.p != prior.said {
return Err(TelError::BrokenChain {
credential: rev.i.as_str().to_string(),
detail: format!(
"rev back-link p={} does not match issuance SAID {}",
rev.p, prior.said
),
});
}
if rev.s.value() <= prior.sequence {
return Err(TelError::BrokenChain {
credential: rev.i.as_str().to_string(),
detail: format!(
"rev sequence {} must exceed the issuance sequence {}",
rev.s, prior.sequence
),
});
}
state.revoked.push(rev.i.clone());
Ok(())
}
pub fn encode_nonce(raw: &[u8; 16]) -> Result<String, TelError> {
crate::cesr_encode::encode_salt_128(raw)
.map_err(|e| TelError::Said(format!("nonce encoding failed: {e}")))
}
pub fn to_wire_bytes<T: Serialize>(event: &T) -> Result<Vec<u8>, TelError> {
Ok(serde_json::to_vec(event)?)
}