Skip to main content

auths_verifier/org_bundle/
error.rs

1//! Typed failures verifying an air-gapped org bundle offline.
2
3use auths_crypto::AuthsErrorInfo;
4
5/// A failure verifying an air-gapped org bundle or off-boarding record.
6///
7/// Every variant is fail-closed: none of these conditions ever yields a
8/// "valid" verdict.
9#[derive(Debug, thiserror::Error)]
10#[non_exhaustive]
11pub enum OrgBundleError {
12    /// A bundled KEL failed integrity or authentication: an event's
13    /// recomputed SAID did not match its stored `d`, the signature
14    /// attachments could not be parsed, or a signature did not verify
15    /// against the controlling key-state (RT-002).
16    #[error("bundle integrity failure for '{id}': {reason}")]
17    Integrity {
18        /// The identifier whose KEL failed integrity.
19        id: String,
20        /// Why integrity failed.
21        reason: String,
22    },
23
24    /// The org KEL delegates a member whose own KEL is not in the bundle —
25    /// the bundle is incomplete and cannot be verified. Fail closed.
26    #[error("bundle is missing the KEL for delegated member '{member}'")]
27    MissingMemberKel {
28        /// The member's `did:keri:`.
29        member: String,
30    },
31
32    /// A queried member has no delegation seal in the org KEL — the org never
33    /// delegated it, so there is no authority to verify. Fail closed.
34    #[error("member '{member}' has no delegation seal in the org KEL")]
35    MissingDelegatorSeal {
36        /// The member's `did:keri:`.
37        member: String,
38    },
39
40    /// Canonical serialization (`json-canon`) of a bundle or record failed.
41    #[error("canonicalization failed: {0}")]
42    Canonicalize(String),
43
44    /// A bundle or record could not be parsed from its JSON form.
45    #[error("parse failed: {0}")]
46    Parse(String),
47
48    /// A signed off-boarding record failed verification: the signature did
49    /// not verify, the curve tag mismatched the org key, or the record is not
50    /// bound to a matching revocation seal on the org KEL.
51    #[error("offboarding record invalid: {0}")]
52    RecordInvalid(String),
53}
54
55impl AuthsErrorInfo for OrgBundleError {
56    fn error_code(&self) -> &'static str {
57        match self {
58            Self::Integrity { .. } => "AUTHS-E2201",
59            Self::MissingMemberKel { .. } => "AUTHS-E2202",
60            Self::MissingDelegatorSeal { .. } => "AUTHS-E2203",
61            Self::Canonicalize(_) => "AUTHS-E2204",
62            Self::Parse(_) => "AUTHS-E2205",
63            Self::RecordInvalid(_) => "AUTHS-E2206",
64        }
65    }
66
67    fn suggestion(&self) -> Option<&'static str> {
68        match self {
69            Self::Integrity { .. } => Some(
70                "The bundle was modified after it was produced; obtain a fresh, untampered bundle",
71            ),
72            Self::MissingMemberKel { .. } | Self::MissingDelegatorSeal { .. } => {
73                Some("The bundle is incomplete; re-produce it with `auths org bundle`")
74            }
75            Self::Canonicalize(_) | Self::Parse(_) => {
76                Some("The file is not a valid air-gapped org bundle; re-export it")
77            }
78            Self::RecordInvalid(_) => Some(
79                "The off-boarding record does not match the org KEL; obtain a fresh bundle from the org",
80            ),
81        }
82    }
83}