1use alloc::string::String;
11use core::fmt;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum ProfileError {
16 KeyIdLength {
18 actual: usize,
20 },
21 MessageIdLength {
23 actual: usize,
25 },
26 EmptySubject,
28 ContentTypeForm,
31 EmptyPartyIdentity,
33 EmptySignature,
35}
36
37impl fmt::Display for ProfileError {
38 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39 match self {
40 Self::KeyIdLength { actual } => {
41 write!(f, "key id must be 1..=128 bytes, got {actual}")
42 }
43 Self::MessageIdLength { actual } => {
44 write!(f, "message id must be 1..=64 bytes, got {actual}")
45 }
46 Self::EmptySubject => write!(f, "subject string must be non-empty"),
47 Self::ContentTypeForm => write!(
48 f,
49 "content type must be of type/subtype form without surrounding whitespace"
50 ),
51 Self::EmptyPartyIdentity => write!(f, "party identity must be non-empty when present"),
52 Self::EmptySignature => write!(f, "signature must be non-empty"),
53 }
54 }
55}
56
57impl core::error::Error for ProfileError {}
58
59#[derive(Debug, Clone, PartialEq, Eq)]
66pub enum DecodeError {
67 Malformed,
70 NotTagged,
72 WrongTag {
74 expected: u64,
76 actual: u64,
78 },
79 IndefiniteLength,
81 NonMinimalEncoding,
83 NonDeterministicEncoding,
86 DuplicateLabel,
88 UnknownLabel {
90 label: i64,
92 },
93 TextLabel,
95 WrongType {
97 label: i64,
99 },
100 MissingHeader {
102 label: i64,
104 },
105 UnknownAlgorithm {
107 alg: i64,
109 },
110 CritMissing,
112 CritIncomplete {
114 label: i64,
116 },
117 CritUnexpected {
119 label: i64,
121 },
122 ClaimsInUnprotected,
124 UnknownClaim {
126 claim: i64,
128 },
129 FractionalTime,
131 MissingClaim {
133 claim: i64,
135 },
136 RecipientCount {
138 count: usize,
140 },
141 NestedRecipients,
143 RecipientCiphertextPresent,
146 MissingPayload,
149 EmbeddedNotEncrypt,
151 EphemeralKeyShape,
154 InvalidLength {
157 label: i64,
159 expected: usize,
161 actual: usize,
163 },
164 Identifier(ProfileError),
166}
167
168impl fmt::Display for DecodeError {
169 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170 match self {
171 Self::Malformed => write!(f, "malformed CBOR input"),
172 Self::NotTagged => write!(f, "top-level item is not tagged"),
173 Self::WrongTag { expected, actual } => {
174 write!(f, "wrong COSE tag: expected {expected}, got {actual}")
175 }
176 Self::IndefiniteLength => write!(f, "indefinite-length item"),
177 Self::NonMinimalEncoding => write!(f, "non-minimal integer encoding"),
178 Self::NonDeterministicEncoding => write!(f, "non-deterministic encoding"),
179 Self::DuplicateLabel => write!(f, "duplicate map label"),
180 Self::UnknownLabel { label } => write!(f, "unknown label {label}"),
181 Self::TextLabel => write!(f, "text label outside the integer-labelled profile"),
182 Self::WrongType { label } => write!(f, "wrong CBOR type for label {label}"),
183 Self::MissingHeader { label } => write!(f, "missing required header {label}"),
184 Self::UnknownAlgorithm { alg } => write!(f, "algorithm {alg} outside the profile"),
185 Self::CritMissing => write!(f, "missing crit header"),
186 Self::CritIncomplete { label } => write!(f, "label {label} not listed in crit"),
187 Self::CritUnexpected { label } => write!(f, "unexpected crit entry {label}"),
188 Self::ClaimsInUnprotected => write!(f, "claims in unprotected header"),
189 Self::UnknownClaim { claim } => write!(f, "unknown CWT claim {claim}"),
190 Self::FractionalTime => write!(f, "fractional CWT timestamp"),
191 Self::MissingClaim { claim } => write!(f, "missing required CWT claim {claim}"),
192 Self::RecipientCount { count } => {
193 write!(f, "expected exactly one recipient, got {count}")
194 }
195 Self::NestedRecipients => write!(f, "nested recipients are not in the profile"),
196 Self::RecipientCiphertextPresent => {
197 write!(
198 f,
199 "recipient ciphertext must be nil for direct key agreement"
200 )
201 }
202 Self::MissingPayload => write!(f, "payload is absent"),
203 Self::EmbeddedNotEncrypt => {
204 write!(f, "sealed payload is not a tagged COSE_Encrypt")
205 }
206 Self::EphemeralKeyShape => {
207 write!(f, "ephemeral key is not the profile OKP/X25519 shape")
208 }
209 Self::InvalidLength {
210 label,
211 expected,
212 actual,
213 } => write!(f, "field {label} must be {expected} bytes, got {actual}"),
214 Self::Identifier(e) => write!(f, "invalid identifier: {e}"),
215 }
216 }
217}
218
219impl core::error::Error for DecodeError {
220 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
221 match self {
222 Self::Identifier(e) => Some(e),
223 _ => None,
224 }
225 }
226}
227
228impl From<ProfileError> for DecodeError {
229 fn from(e: ProfileError) -> Self {
230 Self::Identifier(e)
231 }
232}
233
234#[derive(Debug, Clone, Copy, PartialEq, Eq)]
236pub enum ClaimsError {
237 IssuedInFuture,
239 Expired,
241 TtlTooLong {
243 seconds: i64,
245 },
246 NonPositiveTtl,
248 AudienceRejected,
250 MissingClaim {
252 label: i64,
254 },
255 ForbiddenClaim {
257 label: i64,
259 },
260}
261
262impl fmt::Display for ClaimsError {
263 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
264 match self {
265 Self::IssuedInFuture => write!(f, "issued-at is in the future beyond allowed skew"),
266 Self::Expired => write!(f, "message expired"),
267 Self::TtlTooLong { seconds } => {
268 write!(f, "explicit ttl of {seconds}s exceeds the maximum")
269 }
270 Self::NonPositiveTtl => write!(f, "expiry is not after issued-at"),
271 Self::AudienceRejected => write!(f, "audience not allowed"),
272 Self::MissingClaim { label } => write!(f, "role requires claim {label}"),
273 Self::ForbiddenClaim { label } => write!(f, "role forbids claim {label}"),
274 }
275 }
276}
277
278impl core::error::Error for ClaimsError {}
279
280#[derive(Debug, Clone, PartialEq, Eq)]
285pub enum SignError {
286 AlgorithmUnsupported,
288 Provider {
291 message: String,
293 },
294}
295
296impl fmt::Display for SignError {
297 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
298 match self {
299 Self::AlgorithmUnsupported => write!(f, "signer does not support the algorithm"),
300 Self::Provider { message } => write!(f, "signing provider failed: {message}"),
301 }
302 }
303}
304
305impl core::error::Error for SignError {}
306
307#[derive(Debug, Clone, PartialEq, Eq)]
309pub enum BuildError {
310 SenderKeyMismatch,
312 RoleShape(ClaimsError),
314 ResponseKeyNotText,
316 Rng,
318 SealFailed,
321 Sign(SignError),
323 Codec,
326}
327
328impl fmt::Display for BuildError {
329 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
330 match self {
331 Self::SenderKeyMismatch => {
332 write!(f, "claims sender key id does not match the signer key id")
333 }
334 Self::RoleShape(e) => write!(f, "claims do not fit the message role: {e}"),
335 Self::ResponseKeyNotText => write!(f, "response key id must be UTF-8"),
336 Self::Rng => write!(f, "randomness unavailable"),
337 Self::SealFailed => write!(f, "seal failed"),
338 Self::Sign(e) => write!(f, "signing failed: {e}"),
339 Self::Codec => write!(f, "structure encoding failed"),
340 }
341 }
342}
343
344impl core::error::Error for BuildError {
345 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
346 match self {
347 Self::RoleShape(e) => Some(e),
348 Self::Sign(e) => Some(e),
349 _ => None,
350 }
351 }
352}
353
354impl From<SignError> for BuildError {
355 fn from(e: SignError) -> Self {
356 Self::Sign(e)
357 }
358}
359
360#[derive(Debug, Clone, PartialEq, Eq)]
362pub enum VerifyError {
363 Decode(DecodeError),
365 SignatureInvalid,
367 UnknownKeyId,
369 AlgorithmMismatch,
371 SenderKeyMismatch,
373 Claims(ClaimsError),
375 ClaimsPresenceMismatch,
378 Provider {
381 message: String,
383 },
384}
385
386impl fmt::Display for VerifyError {
387 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
388 match self {
389 Self::Decode(e) => write!(f, "strict decode failed: {e}"),
390 Self::SignatureInvalid => write!(f, "signature verification failed"),
391 Self::UnknownKeyId => write!(f, "unknown signing key id"),
392 Self::AlgorithmMismatch => write!(f, "algorithm does not match the pinned key"),
393 Self::SenderKeyMismatch => {
394 write!(f, "sender key id claim does not match the outer kid")
395 }
396 Self::Claims(e) => write!(f, "claim validation failed: {e}"),
397 Self::ClaimsPresenceMismatch => write!(
398 f,
399 "claims presence does not match the validation expectation"
400 ),
401 Self::Provider { message } => write!(f, "verification provider failed: {message}"),
402 }
403 }
404}
405
406impl core::error::Error for VerifyError {
407 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
408 match self {
409 Self::Decode(e) => Some(e),
410 Self::Claims(e) => Some(e),
411 _ => None,
412 }
413 }
414}
415
416impl From<DecodeError> for VerifyError {
417 fn from(e: DecodeError) -> Self {
418 Self::Decode(e)
419 }
420}
421
422impl From<ClaimsError> for VerifyError {
423 fn from(e: ClaimsError) -> Self {
424 Self::Claims(e)
425 }
426}
427
428#[derive(Debug, Clone, PartialEq, Eq)]
434pub enum OpenError {
435 Decode(DecodeError),
437 RecipientKeyMismatch,
439 PartyMismatch,
442 OpenFailed,
446 Provider {
449 message: String,
451 },
452}
453
454impl fmt::Display for OpenError {
455 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
456 match self {
457 Self::Decode(e) => write!(f, "strict decode failed: {e}"),
458 Self::RecipientKeyMismatch => write!(f, "message is for a different recipient key"),
459 Self::PartyMismatch => write!(f, "KDF party identities do not match expectation"),
460 Self::OpenFailed => write!(f, "open failed"),
461 Self::Provider { message } => write!(f, "open provider failed: {message}"),
462 }
463 }
464}
465
466impl core::error::Error for OpenError {
467 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
468 match self {
469 Self::Decode(e) => Some(e),
470 _ => None,
471 }
472 }
473}
474
475impl From<DecodeError> for OpenError {
476 fn from(e: DecodeError) -> Self {
477 Self::Decode(e)
478 }
479}