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}