1use auths_verifier::{CanonicalDid, IdentityDID, Role};
2use serde::{Deserialize, Serialize};
3
4use crate::checkpoint::SignedCheckpoint;
5use crate::entry::{Entry, EntryType};
6use crate::proof::InclusionProof;
7
8#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
30#[allow(missing_docs)]
31pub struct OfflineBundle {
32 pub entry: Entry,
33 pub inclusion_proof: InclusionProof,
34 pub signed_checkpoint: SignedCheckpoint,
35 #[serde(default, skip_serializing_if = "Vec::is_empty")]
36 pub delegation_chain: Vec<DelegationChainLink>,
37}
38
39#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
59#[allow(missing_docs)]
60pub struct DelegationChainLink {
61 pub link_type: EntryType,
62 pub entry: Entry,
63 pub inclusion_proof: InclusionProof,
64}
65
66#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
94#[allow(missing_docs)]
95pub struct BundleVerificationReport {
96 pub signature: SignatureStatus,
97 pub inclusion: InclusionStatus,
98 pub checkpoint: CheckpointStatus,
99 pub witnesses: WitnessStatus,
100 pub namespace: NamespaceStatus,
101 pub delegation: DelegationStatus,
102 pub warnings: Vec<String>,
103}
104
105impl BundleVerificationReport {
106 pub fn is_valid(&self) -> bool {
117 let sig_ok = matches!(self.signature, SignatureStatus::Verified);
118 let inc_ok = matches!(self.inclusion, InclusionStatus::Verified);
119 let chk_ok = matches!(
120 self.checkpoint,
121 CheckpointStatus::Verified | CheckpointStatus::NotProvided
122 );
123 let wit_ok = matches!(
124 self.witnesses,
125 WitnessStatus::Quorum { .. } | WitnessStatus::NotProvided
126 );
127 let ns_ok = matches!(
128 self.namespace,
129 NamespaceStatus::Authorized | NamespaceStatus::Owned
130 );
131 let del_ok = !matches!(self.delegation, DelegationStatus::ChainBroken { .. });
132
133 sig_ok && inc_ok && chk_ok && wit_ok && ns_ok && del_ok
134 }
135}
136
137#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
139#[serde(tag = "status", rename_all = "snake_case")]
140#[non_exhaustive]
141pub enum SignatureStatus {
142 Verified,
144 Failed {
146 reason: String,
148 },
149 NotProvided,
151}
152
153#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
155#[serde(tag = "status", rename_all = "snake_case")]
156#[non_exhaustive]
157pub enum InclusionStatus {
158 Verified,
160 Failed {
162 reason: String,
164 },
165 NotProvided,
167}
168
169#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
171#[serde(tag = "status", rename_all = "snake_case")]
172#[non_exhaustive]
173pub enum CheckpointStatus {
174 Verified,
176 InvalidSignature,
178 MissingEcdsaSignature,
183 MissingEcdsaKey,
186 NotProvided,
188}
189
190#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
192#[serde(tag = "status", rename_all = "snake_case")]
193#[non_exhaustive]
194pub enum WitnessStatus {
195 Quorum {
197 verified: usize,
199 required: usize,
201 },
202 Insufficient {
204 verified: usize,
206 required: usize,
208 },
209 NotIndependent {
214 verified: usize,
216 required: usize,
218 shortfalls: Vec<String>,
220 },
221 NotProvided,
223}
224
225#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
227#[serde(tag = "status", rename_all = "snake_case")]
228#[non_exhaustive]
229pub enum NamespaceStatus {
230 Authorized,
232 Owned,
234 Unowned,
236 Unauthorized,
238}
239
240#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
242#[serde(tag = "status", rename_all = "snake_case")]
243#[non_exhaustive]
244pub enum DelegationStatus {
245 Direct,
247 ChainVerified {
249 org_did: IdentityDID,
251 member_did: IdentityDID,
253 member_role: Role,
255 device_did: CanonicalDid,
257 },
258 ChainBroken {
260 reason: String,
262 },
263 NoDelegationData,
265}
266
267#[cfg(test)]
268#[allow(clippy::disallowed_methods)]
269mod tests {
270 use super::*;
271
272 #[test]
273 fn signature_status_serializes() {
274 let status = SignatureStatus::Verified;
275 let json = serde_json::to_string(&status).unwrap();
276 let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
277 assert_eq!(parsed["status"], "verified");
278 }
279
280 #[test]
281 fn witness_status_quorum_serializes() {
282 let status = WitnessStatus::Quorum {
283 verified: 3,
284 required: 2,
285 };
286 let json = serde_json::to_string(&status).unwrap();
287 let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
288 assert_eq!(parsed["status"], "quorum");
289 assert_eq!(parsed["verified"], 3);
290 assert_eq!(parsed["required"], 2);
291 }
292
293 #[test]
294 fn witness_status_insufficient_serializes() {
295 let status = WitnessStatus::Insufficient {
296 verified: 1,
297 required: 3,
298 };
299 let json = serde_json::to_string(&status).unwrap();
300 let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
301 assert_eq!(parsed["status"], "insufficient");
302 assert_eq!(parsed["verified"], 1);
303 assert_eq!(parsed["required"], 3);
304 }
305
306 #[test]
307 fn report_is_valid_all_verified() {
308 let report = BundleVerificationReport {
309 signature: SignatureStatus::Verified,
310 inclusion: InclusionStatus::Verified,
311 checkpoint: CheckpointStatus::Verified,
312 witnesses: WitnessStatus::Quorum {
313 verified: 2,
314 required: 2,
315 },
316 namespace: NamespaceStatus::Authorized,
317 delegation: DelegationStatus::Direct,
318 warnings: vec![],
319 };
320 assert!(report.is_valid());
321 }
322
323 #[test]
324 fn report_is_valid_with_not_provided_optionals() {
325 let report = BundleVerificationReport {
326 signature: SignatureStatus::Verified,
327 inclusion: InclusionStatus::Verified,
328 checkpoint: CheckpointStatus::NotProvided,
329 witnesses: WitnessStatus::NotProvided,
330 namespace: NamespaceStatus::Owned,
331 delegation: DelegationStatus::NoDelegationData,
332 warnings: vec![],
333 };
334 assert!(report.is_valid());
335 }
336
337 #[test]
338 fn report_invalid_on_failed_signature() {
339 let report = BundleVerificationReport {
340 signature: SignatureStatus::Failed {
341 reason: "bad sig".into(),
342 },
343 inclusion: InclusionStatus::Verified,
344 checkpoint: CheckpointStatus::Verified,
345 witnesses: WitnessStatus::NotProvided,
346 namespace: NamespaceStatus::Authorized,
347 delegation: DelegationStatus::Direct,
348 warnings: vec![],
349 };
350 assert!(!report.is_valid());
351 }
352
353 #[test]
354 fn report_invalid_on_broken_delegation() {
355 let report = BundleVerificationReport {
356 signature: SignatureStatus::Verified,
357 inclusion: InclusionStatus::Verified,
358 checkpoint: CheckpointStatus::Verified,
359 witnesses: WitnessStatus::NotProvided,
360 namespace: NamespaceStatus::Authorized,
361 delegation: DelegationStatus::ChainBroken {
362 reason: "missing link".into(),
363 },
364 warnings: vec![],
365 };
366 assert!(!report.is_valid());
367 }
368
369 #[test]
370 fn delegation_status_chain_verified_serializes() {
371 let status = DelegationStatus::ChainVerified {
372 org_did: IdentityDID::new_unchecked("did:keri:EOrg123"),
373 member_did: IdentityDID::new_unchecked("did:keri:EMember456"),
374 member_role: Role::Admin,
375 device_did: CanonicalDid::new_unchecked("did:key:z6MkDevice789"),
376 };
377 let json = serde_json::to_string(&status).unwrap();
378 let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
379 assert_eq!(parsed["status"], "chain_verified");
380 assert_eq!(parsed["org_did"], "did:keri:EOrg123");
381 assert_eq!(parsed["member_did"], "did:keri:EMember456");
382 }
383}