Skip to main content

lys_core/
error.rs

1//! [`TrustError`] and the crate-wide [`TrustResult`] alias.
2//!
3//! Every fallible public API on the trust primitives returns
4//! `TrustResult<T>`. Each variant names a distinct trust operation, and every
5//! `Display` string carries the operation name so callers can surface a
6//! precise diagnostic without parsing free-form text. Variants that carry a
7//! dynamic cause use a `reason: String` field; signature verification has a
8//! dedicated parameterless variant so callers can match it structurally
9//! rather than by string.
10
11/// Errors returned from the trust primitives.
12///
13/// Variants are grouped by operation: certificate lifecycle (generation,
14/// parsing, verification, revocation), the Merkle transparency log, sealed
15/// payload transport (seal/unseal), key management, signing, and the
16/// dedicated signature-verification failure.
17///
18/// # Why this is `#[non_exhaustive]`
19///
20/// Downstream `match`es must carry a wildcard arm, which makes **every future
21/// variant an additive change instead of a breaking one**. That matters more
22/// here than in a typical library: this crate gains a verification surface with
23/// each new artifact class, and each one wants its own non-oracle failure value
24/// (see [`Self::ReceiptVerification`] and [`Self::BundleVerification`]). Without
25/// this attribute, giving a new artifact class an honest error would mean a
26/// semver break, and the cheap way out — reusing another class's variant —
27/// makes errors mean less over time.
28///
29/// # Non-oracle variants carry no cause, deliberately
30///
31/// The verification failures are deliberately causeless. A caller learns *that*
32/// an artifact failed, never *which check* failed, because these types are
33/// reachable from network-exposed surfaces where a distinguishable error is a
34/// parsing oracle. Callers diagnosing their own artifacts verify the embedded
35/// pieces individually, where the errors are specific and actionable.
36#[derive(Debug, thiserror::Error)]
37#[non_exhaustive]
38pub enum TrustError {
39    /// Generating a certificate failed.
40    #[error("certificate generation failed: {reason}")]
41    CertificateGeneration {
42        /// Human-readable cause of the generation failure.
43        reason: String,
44    },
45
46    /// Parsing a certificate or extracting one of its fields failed.
47    #[error("certificate parsing failed: {reason}")]
48    CertificateParsing {
49        /// Human-readable cause of the parsing failure.
50        reason: String,
51    },
52
53    /// Verifying a certificate chain failed.
54    #[error("certificate verification failed: {reason}")]
55    CertificateVerification {
56        /// Human-readable cause of the verification failure.
57        reason: String,
58    },
59
60    /// A certificate revocation operation failed.
61    ///
62    /// Reserved for consumer-side operations: revocation tracking is
63    /// explicitly a consumer concern (a design non-goal for this crate), so
64    /// this crate never constructs the variant itself. It exists so
65    /// consumers implementing revocation stores can surface failures through
66    /// the shared [`TrustError`] type.
67    #[error("certificate revocation failed: {reason}")]
68    CertificateRevocation {
69        /// Human-readable cause of the revocation failure.
70        reason: String,
71    },
72
73    /// A Merkle transparency-log operation failed.
74    #[error("merkle tree operation failed: {reason}")]
75    MerkleTree {
76        /// Human-readable cause of the Merkle operation failure.
77        reason: String,
78    },
79
80    /// Sealing a payload for a recipient failed.
81    #[error("seal failed: {reason}")]
82    Seal {
83        /// Human-readable cause of the seal failure.
84        reason: String,
85    },
86
87    /// Unsealing failed — deliberately omits the cause so callers cannot
88    /// distinguish wrong-key from tampered-ciphertext (non-oracle).
89    #[error("unseal failed")]
90    UnsealFailed,
91
92    /// Attestation verification failed — the sender's public key did not
93    /// match, or the signature over the sealed payload was invalid.
94    #[error("attestation verification failed")]
95    AttestationFailed,
96
97    /// A key-management operation failed: file I/O, environment loading,
98    /// base64 decoding, or key-material length validation.
99    #[error("key management failed: {reason}")]
100    KeyManagement {
101        /// Human-readable cause of the key-management failure.
102        reason: String,
103    },
104
105    /// Producing a signature failed.
106    ///
107    /// Reserved for consumer-side signing pipelines. This crate's own
108    /// Ed25519 signing is infallible (dalek 2 deterministic signing), so the
109    /// crate never constructs the variant itself; consumers whose signing
110    /// paths can fail (HSMs, remote signers, key lookups) surface those
111    /// failures through it.
112    #[error("signing failed: {reason}")]
113    Signing {
114        /// Human-readable cause of the signing failure.
115        reason: String,
116    },
117
118    /// A signature failed verification, or the supplied signature or public
119    /// key bytes were structurally invalid.
120    ///
121    /// This is also the attestation artifact class's single non-oracle
122    /// failure: a malformed or non-canonical COSE attestation artifact, a
123    /// payload-hash mismatch, and an invalid signature all collapse to this
124    /// one value, so callers cannot distinguish which check rejected the
125    /// artifact.
126    #[error("invalid signature")]
127    InvalidSignature,
128
129    /// Building or encoding a checkpoint or signed note failed (invalid
130    /// origin or key name, malformed body).
131    #[error("checkpoint encoding failed: {reason}")]
132    CheckpointEncoding {
133        /// Human-readable cause of the encoding failure.
134        reason: String,
135    },
136
137    /// Parsing a checkpoint body failed. Used on already-verified body text
138    /// and operator-supplied text; artifact verification collapses it
139    /// (non-oracle).
140    #[error("checkpoint parsing failed: {reason}")]
141    CheckpointParsing {
142        /// Human-readable cause of the parsing failure.
143        reason: String,
144    },
145
146    /// A note verifier key string was malformed or internally inconsistent.
147    /// Trusted operator input — carries an actionable reason.
148    #[error("invalid note verifier key: {reason}")]
149    VerifierKey {
150        /// Human-readable cause of the verifier-key failure.
151        reason: String,
152    },
153
154    /// A signed note failed verification — deliberately omits the cause so
155    /// callers cannot distinguish malformed envelope, unknown key, or bad
156    /// signature (non-oracle).
157    #[error("note verification failed")]
158    NoteVerification,
159
160    /// Building a log proof artifact failed (tree too large for JSON-safe
161    /// integers, size/index invariant violations at build time).
162    #[error("log artifact encoding failed: {reason}")]
163    LogArtifactEncoding {
164        /// Human-readable cause of the encoding failure.
165        reason: String,
166    },
167
168    /// A log proof artifact failed verification — deliberately omits the
169    /// cause (non-oracle): bad checkpoint signature, size mismatch, root
170    /// mismatch, malformed hashes, and kind confusion are indistinguishable.
171    #[error("log artifact verification failed")]
172    LogArtifactVerification,
173
174    /// An anchor receipt failed verification — deliberately omits the cause
175    /// (non-oracle): a bad signature, the wrong anchor key, a tampered
176    /// inclusion path, a malformed or non-canonical artifact, and a leaf the
177    /// receipt does not prove are all indistinguishable.
178    ///
179    /// Distinct from [`Self::InvalidSignature`], which it previously reused: a
180    /// receipt failure is not necessarily a signature failure, and saying so
181    /// misdescribed every other way one can fail. The *class* of artifact is
182    /// already known to the caller from the function they called, so naming it
183    /// leaks nothing a caller did not supply.
184    #[error("receipt verification failed")]
185    ReceiptVerification,
186
187    /// A verification bundle failed verification — deliberately omits the
188    /// cause (non-oracle): a malformed container, a broken inclusion proof, a
189    /// receipt from the wrong anchor, and a chain whose links do not join are
190    /// all indistinguishable.
191    ///
192    /// Distinct from [`Self::LogArtifactVerification`], which it previously
193    /// reused, for the same reason: a bundle is a different artifact class with
194    /// a different set of ways to be wrong.
195    #[error("bundle verification failed")]
196    BundleVerification,
197}
198
199/// Convenience alias for `Result<T, TrustError>`.
200pub type TrustResult<T> = std::result::Result<T, TrustError>;
201
202#[cfg(test)]
203#[path = "error_tests.rs"]
204mod tests;