use crate::primitives::{AgentDid, CtxId};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum RevocationTrustClass {
ProducerSigned,
RegistryAttested,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
#[non_exhaustive]
pub enum RevocationSource {
Known,
Discovered {
authority: String,
},
Cached {
origin: String,
},
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct SourcedRevocation {
pub revoked_key_fingerprint: String,
pub compromised_since: DateTime<Utc>,
pub ctx_id: Option<CtxId>,
pub issuer: AgentDid,
pub revoked_key_controller: AgentDid,
pub trust_class: RevocationTrustClass,
pub source: RevocationSource,
}
impl SourcedRevocation {
#[must_use]
pub fn new(
revoked_key_fingerprint: String,
compromised_since: DateTime<Utc>,
ctx_id: Option<CtxId>,
issuer: AgentDid,
revoked_key_controller: AgentDid,
trust_class: RevocationTrustClass,
source: RevocationSource,
) -> Self {
Self {
revoked_key_fingerprint,
compromised_since,
ctx_id,
issuer,
revoked_key_controller,
trust_class,
source,
}
}
#[must_use]
pub fn speaks_for(&self, producer: &AgentDid) -> bool {
match self.trust_class {
RevocationTrustClass::ProducerSigned => self.issuer == *producer,
RevocationTrustClass::RegistryAttested => self.revoked_key_controller == *producer,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum RevocationEffect {
PreCompromise,
Revoked,
Unplaceable,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct RevocationRefusal {
pub effect: RevocationEffect,
pub signing_key_fingerprint: String,
pub boundary: DateTime<Utc>,
pub revocation: SourcedRevocation,
pub placement: Option<DateTime<Utc>>,
pub detail: String,
}
impl RevocationRefusal {
#[must_use]
pub fn new(
effect: RevocationEffect,
signing_key_fingerprint: String,
boundary: DateTime<Utc>,
revocation: SourcedRevocation,
placement: Option<DateTime<Utc>>,
detail: String,
) -> Self {
Self {
effect,
signing_key_fingerprint,
boundary,
revocation,
placement,
detail,
}
}
}
impl std::fmt::Display for RevocationRefusal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let effect = match self.effect {
RevocationEffect::PreCompromise => "pre-compromise",
RevocationEffect::Revoked => "revoked",
RevocationEffect::Unplaceable => "unplaceable",
};
write!(
f,
"{effect}: key {} revoked as of {} by {} ({:?}",
self.signing_key_fingerprint,
crate::time::fmt_rfc3339_ms(self.boundary),
self.revocation.issuer,
self.revocation.trust_class,
)?;
if let Some(ctx) = &self.revocation.ctx_id {
write!(f, ", {ctx}")?;
}
write!(f, "): {}", self.detail)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum UnknownReason {
Truncated,
Unanswerable,
Transient,
UnverifiableCandidate,
}
impl UnknownReason {
#[must_use]
pub fn as_str(self) -> &'static str {
match self {
UnknownReason::Truncated => "truncated",
UnknownReason::Unanswerable => "unanswerable",
UnknownReason::Transient => "transient",
UnknownReason::UnverifiableCandidate => "unverifiable_candidate",
}
}
}
impl std::fmt::Display for UnknownReason {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}