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;