Skip to main content

affinidi_data_integrity/
error.rs

1//! Structured error type for data-integrity operations.
2//!
3//! Variants carry enough structure for programmatic matching — callers can
4//! distinguish between "key is the wrong type" and "signature does not
5//! verify", which lead to different remediation paths.
6
7use affinidi_secrets_resolver::secrets::KeyType;
8use thiserror::Error;
9
10use crate::crypto_suites::CryptoSuite;
11
12/// Why a signature failed to verify.
13#[derive(Clone, Debug, PartialEq, Eq)]
14#[non_exhaustive]
15pub enum SignatureFailure {
16    /// The signature bytes are the wrong length or shape for the algorithm.
17    Malformed,
18    /// The signature parsed correctly but did not verify against the data.
19    Invalid,
20}
21
22impl std::fmt::Display for SignatureFailure {
23    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24        match self {
25            Self::Malformed => f.write_str("malformed"),
26            Self::Invalid => f.write_str("invalid"),
27        }
28    }
29}
30
31/// Errors raised by data-integrity signing and verification.
32///
33/// This type is `#[non_exhaustive]`: callers must include a wildcard arm
34/// when matching, so future additions do not constitute breaking changes.
35#[derive(Error, Debug)]
36#[non_exhaustive]
37pub enum DataIntegrityError {
38    /// The cryptosuite identifier is not recognised by this build, either
39    /// because the name is unknown or because the matching Cargo feature is
40    /// disabled.
41    #[error("unsupported cryptosuite: {name}")]
42    UnsupportedCryptoSuite { name: String },
43
44    /// The signer's key type is not compatible with the requested cryptosuite.
45    #[error(
46        "key type {actual:?} is not compatible with cryptosuite {suite:?} (expected {expected:?})"
47    )]
48    KeyTypeMismatch {
49        expected: KeyType,
50        actual: KeyType,
51        suite: CryptoSuite,
52    },
53
54    /// The signature could not be verified.
55    #[error("signature {reason} for cryptosuite {suite:?}")]
56    InvalidSignature {
57        suite: CryptoSuite,
58        reason: SignatureFailure,
59    },
60
61    /// A public key has an unexpected multicodec, length, or encoding.
62    #[error("invalid public key (codec={codec:?}, len={len}): {reason}")]
63    InvalidPublicKey {
64        codec: Option<u64>,
65        len: usize,
66        reason: String,
67    },
68
69    /// Canonicalization (JCS / RDFC / other) of the document or proof config
70    /// failed.
71    #[error("canonicalization failed: {0}")]
72    Canonicalization(String),
73
74    /// The proof document is structurally invalid (missing required fields,
75    /// wrong type, malformed `created` timestamp, mismatched `@context`,
76    /// etc.).
77    #[error("malformed proof: {0}")]
78    MalformedProof(String),
79
80    /// A conformance check against the Data Integrity spec failed for a
81    /// reason other than the signature itself (e.g. missing `proofPurpose`,
82    /// wrong `type`, `created` in the future).
83    #[error("spec conformance check failed: {0}")]
84    Conformance(String),
85
86    /// A signer (local or remote) returned an error while producing a
87    /// signature.
88    #[error("signing failed")]
89    Signing(#[source] Box<dyn std::error::Error + Send + Sync>),
90
91    /// A verification-method resolver (did:key, did:web, etc.) failed to
92    /// locate or decode the requested key.
93    #[error("resolver error: {0}")]
94    Resolver(String),
95}
96
97impl DataIntegrityError {
98    /// Wraps any signer error (local keys, HSM, KMS) in the `Signing`
99    /// variant. Preserves the source chain for debuggers.
100    pub fn signing<E>(e: E) -> Self
101    where
102        E: std::error::Error + Send + Sync + 'static,
103    {
104        Self::Signing(Box::new(e))
105    }
106}