• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

namib-project / dcaf-rs / 9767273749

02 Jul 2024 08:05PM UTC coverage: 84.68% (-5.5%) from 90.226%
9767273749

Pull #13

github

web-flow
Merge 71cf7efbc into 4a634cd4b
Pull Request #13: Add Cipher implementation using OpenSSL bindings

715 of 929 new or added lines in 9 files covered. (76.96%)

14 existing lines in 3 files now uncovered.

1918 of 2265 relevant lines covered (84.68%)

14.32 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

55.97
/src/error/mod.rs
1
/*
2
 * Copyright (c) 2022 The NAMIB Project Developers.
3
 * Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4
 * https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5
 * <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6
 * option. This file may not be copied, modified, or distributed
7
 * except according to those terms.
8
 *
9
 * SPDX-License-Identifier: MIT OR Apache-2.0
10
 */
11

12
//! Contains error types used across this crate.
13

14
use core::any::type_name;
15
use core::fmt::{Display, Formatter};
16
use std::collections::BTreeSet;
17
use std::convert::Infallible;
18

19
use ciborium::value::Value;
20
use coset::{iana, Algorithm, CoseError, KeyOperation, KeyType, Label};
21
use strum_macros::IntoStaticStr;
22

23
use {alloc::format, alloc::string::String, alloc::string::ToString};
24

25
use crate::token::cose::key::{EllipticCurve, KeyParam};
26
#[cfg(not(feature = "std"))]
27
use {core::num::TryFromIntError, derive_builder::export::core::marker::PhantomData};
28
#[cfg(feature = "std")]
29
use {std::marker::PhantomData, std::num::TryFromIntError};
30

31
/// Error type used when the parameter of the type `T` couldn't be
32
/// converted into [`expected_type`](WrongSourceTypeError::expected_type) because the received
33
/// type was [`actual_type`](WrongSourceTypeError::actual_type) instead.
34
///
35
/// `T` is the general type taken in the [`TryFrom`] conversion.
36
/// Used for [`TryFrom`] conversions from a general enum type to a specific member type.
37
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
6✔
38
pub struct WrongSourceTypeError<T> {
39
    /// The name of the specific type which [`TryFrom`] tried to convert to.
40
    pub expected_type: &'static str,
3✔
41
    /// The name of the actual type which [`TryFrom`] received.
42
    pub actual_type: &'static str,
3✔
43
    /// The general type taken in the [`TryFrom`] conversion.
44
    pub general_type: PhantomData<T>,
3✔
45
}
46

47
impl<T> Display for WrongSourceTypeError<T> {
48
    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
7✔
49
        write!(
7✔
50
            f,
51
            "the given {} is a {} variant (expected {} variant)",
52
            type_name::<T>(),
7✔
53
            self.actual_type,
54
            self.expected_type
55
        )
56
    }
7✔
57
}
58

59
impl<T> WrongSourceTypeError<T> {
60
    /// Creates a new instance of the error, taking `T` as the general type from which
61
    /// the conversion was tried and the `expected_type` as the target type which it was tried to
62
    /// convert it into, but failed because it was actually of the type named by `actual_type`.
63
    #[must_use]
64
    pub fn new(expected_type: &'static str, actual_type: &'static str) -> WrongSourceTypeError<T> {
15✔
65
        WrongSourceTypeError {
15✔
66
            expected_type,
67
            actual_type,
68
            general_type: PhantomData,
69
        }
70
    }
15✔
71
}
72

73
/// Error type used when a given CBOR map can't be converted to a specific type which implements
74
/// the [`ToCborMap`](crate::ToCborMap) trait.
75
///
76
/// **Note: This error type is not expected to be used by library clients!**
77
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
×
78
pub struct TryFromCborMapError {
79
    /// Error message describing why the conversion failed.
80
    message: String,
×
81
}
82

83
impl Display for TryFromCborMapError {
84
    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
5✔
85
        write!(f, "{}", self.message)
5✔
86
    }
5✔
87
}
88

89
impl TryFromCborMapError {
90
    /// Creates a new error with the given custom `message`.
91
    #[must_use]
92
    pub(crate) fn from_message<T>(message: T) -> TryFromCborMapError
9✔
93
    where
94
        T: Into<String>,
95
    {
96
        TryFromCborMapError {
9✔
97
            message: message.into(),
9✔
98
        }
99
    }
9✔
100

101
    /// Creates a new error with a message describing that an unknown field in
102
    /// the CBOR map with the given `key` was encountered.
103
    #[must_use]
104
    pub(crate) fn unknown_field(key: u8) -> TryFromCborMapError {
3✔
105
        TryFromCborMapError {
3✔
106
            message: format!("unknown field with key {key} encountered"),
3✔
107
        }
108
    }
3✔
109

110
    /// Creates a new error with a message describing that the target type could not be built,
111
    /// either due to a missing field or due to a validation error in the builder.
112
    #[must_use]
113
    pub(crate) fn build_failed<T>(name: &'static str, builder_error: T) -> TryFromCborMapError
4✔
114
    where
115
        T: Display,
116
    {
117
        TryFromCborMapError {
4✔
118
            message: format!("couldn't build {name}: {builder_error}"),
4✔
119
        }
120
    }
4✔
121
}
122

123
impl From<TryFromIntError> for TryFromCborMapError {
124
    fn from(e: TryFromIntError) -> Self {
×
125
        TryFromCborMapError::from_message(e.to_string())
×
126
    }
×
127
}
128

129
/// Error type used when a CBOR map does not use integers as its key type, but was expected to.
130
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
×
131
pub struct ValueIsNotIntegerError;
132

133
impl Display for ValueIsNotIntegerError {
134
    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
×
135
        write!(f, "CBOR map key must be an integer")
×
136
    }
×
137
}
138

139
/// Error type used when a [`TextEncodedScope`](crate::common::scope::TextEncodedScope)
140
/// does not conform to the specification given in RFC 6749.
141
#[derive(Debug, PartialEq, Eq, Clone, Hash, IntoStaticStr)]
×
142
pub enum InvalidTextEncodedScopeError {
143
    /// The scope starts with a separator (i.e. space).
144
    StartsWithSeparator,
×
145
    /// The scope ends with a separator (i.e. space).
146
    EndsWithSeparator,
×
147
    /// The scope contains two separators (i.e. spaces).
148
    ConsecutiveSeparators,
×
149
    /// The scope contains an empty element.
150
    EmptyElement,
×
151
    /// The scope is empty.
152
    EmptyScope,
×
153
    /// The scope contains illegal characters (i.e. a backslash (`\\`) or double-quote (`"`)).
154
    IllegalCharacters,
×
155
    /// The scope is invalid for another reason, which is specified in the message contained here.
156
    Other(&'static str),
×
157
}
158

159
impl Display for InvalidTextEncodedScopeError {
160
    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
48✔
161
        let message = match self {
48✔
162
            InvalidTextEncodedScopeError::StartsWithSeparator => "must not start with space",
2✔
163
            InvalidTextEncodedScopeError::EndsWithSeparator => "must not end with space",
7✔
164
            InvalidTextEncodedScopeError::ConsecutiveSeparators => {
165
                "must not contain consecutive spaces"
1✔
166
            }
1✔
167
            InvalidTextEncodedScopeError::EmptyElement => "must not contain empty elements",
2✔
168
            InvalidTextEncodedScopeError::EmptyScope => "must not be empty",
1✔
169
            InvalidTextEncodedScopeError::IllegalCharacters => {
170
                "must not contain illegal character '\\' or '\"'"
35✔
171
            }
35✔
172
            InvalidTextEncodedScopeError::Other(s) => s,
×
173
        };
174
        write!(
48✔
175
            f,
176
            "text-encoded scope must follow format specified in RFC 6749 ({message})"
177
        )
178
    }
48✔
179
}
180

181
/// Error type used when a [`BinaryEncodedScope`](crate::common::scope::BinaryEncodedScope)
182
/// does not conform to the specification given in RFC 6749 and RFC 9200.
183
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
×
184
pub enum InvalidBinaryEncodedScopeError {
185
    /// Scope starts with a separator, which is contained in the field here.
186
    StartsWithSeparator(u8),
×
187
    /// Scope ends with a separator, which is contained in the field here.
188
    EndsWithSeparator(u8),
×
189
    /// Scope contains two consecutive separators, which is contained in the field here.
190
    ConsecutiveSeparators(u8),
×
191
    /// Scope is empty.
192
    EmptyScope,
193
}
194

195
impl Display for InvalidBinaryEncodedScopeError {
196
    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
16✔
197
        match self {
16✔
198
            InvalidBinaryEncodedScopeError::StartsWithSeparator(s) => {
13✔
199
                write!(f, "scope may not start with separator '{s:#x}'")
13✔
200
            }
201
            InvalidBinaryEncodedScopeError::EndsWithSeparator(s) => {
2✔
202
                write!(f, "scope may not end with separator '{s:#x}'")
2✔
203
            }
204
            InvalidBinaryEncodedScopeError::ConsecutiveSeparators(s) => {
1✔
205
                write!(f, "scope may not contain separator '{s:#x}' twice in a row")
1✔
206
            }
207
            InvalidBinaryEncodedScopeError::EmptyScope => write!(f, "scope may not be empty"),
×
208
        }
209
    }
16✔
210
}
211

212
/// Error type used when an [`AifEncodedScope`](crate::common::scope::AifEncodedScope)
213
/// does not conform to the specification given in [RFC 6749](https://datatracker.ietf.org/doc/html/rfc6749) and
214
/// [RFC 9237](https://www.rfc-editor.org/rfc/rfc9237).
215
///
216
/// This is also used when a [`LibdcafEncodedScope`](crate::common::scope::LibdcafEncodedScope)
217
/// does not conform to the format specified in its documentation.
218
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
×
219
#[non_exhaustive]
220
pub enum InvalidAifEncodedScopeError {
221
    /// Scope's bitflags, representing an [AifRestMethodSet](crate::common::scope::AifRestMethodSet)
222
    /// were not valid, i.e., did not represent a valid combination of REST methods.
223
    InvalidRestMethodSet,
224

225
    /// Scope contained a malformed array, i.e., didn't conform to the specification.
226
    MalformedArray,
227
}
228

229
impl Display for InvalidAifEncodedScopeError {
230
    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
10✔
231
        match self {
10✔
232
            InvalidAifEncodedScopeError::InvalidRestMethodSet => {
233
                write!(f, "given REST method bitfield is invalid")
10✔
234
            }
235
            InvalidAifEncodedScopeError::MalformedArray => {
236
                write!(f, "given AIF CBOR array is malformed")
×
237
            }
238
        }
239
    }
10✔
240
}
241

242
/// Error type used when a [`CoseEncryptCipher`](crate::CoseEncryptCipher),
243
/// [`CoseSignCipher`](crate::CoseSignCipher), or [`CoseMacCipher`](crate::CoseMacCipher).
244
/// fails to perform an operation.
245
///
246
/// `T` is the type of the nested error represented by the [`Other`](CoseCipherError::Other) variant.
247
#[derive(Debug, PartialEq, Clone)]
26✔
248
#[non_exhaustive]
249
pub enum CoseCipherError<T>
250
where
251
    T: Display,
252
{
253
    /// A header which the cipher is supposed to set has already been set.
254
    HeaderAlreadySet {
255
        /// The name of the header which has already been set.
256
        existing_header_name: String,
×
257
    },
258
    /// The given signature or MAC tag is either invalid or does not match the given data.
259
    VerificationFailure,
260
    /// The given ciphertext could not be decrypted.
261
    DecryptionFailure,
262
    /// A different error has occurred. Details are provided in the contained error.
263
    Other(T),
8✔
264
    /// Key type is not supported.
NEW
265
    UnsupportedKeyType(KeyType),
×
266
    /// Curve is not supported by coset or the chosen cryptographic backend.
NEW
267
    UnsupportedCurve(EllipticCurve),
×
268
    /// Algorithm is not supported by coset or the chosen cryptographic backend.
269
    UnsupportedAlgorithm(Algorithm),
3✔
270
    /// The cryptographic backend does not support deriving the public key from the private key, and
271
    /// your provided key does not provide the public key parts even though it is required for this
272
    /// operation.
273
    UnsupportedKeyDerivation,
274
    /// The algorithm has not explicitly been specified anywhere (protected headers, unprotected
275
    /// headers or the key itself).
276
    NoAlgorithmDeterminable,
277
    /// Your provided key does not support the given operation.
NEW
278
    KeyOperationNotPermitted(BTreeSet<KeyOperation>, KeyOperation),
×
279
    /// Key in given curve must be in different format.
NEW
280
    KeyTypeCurveMismatch(KeyType, EllipticCurve),
×
281
    /// Provided algorithm requires a different key type.
NEW
282
    KeyTypeAlgorithmMismatch(KeyType, Algorithm),
×
283
    /// Algorithm provided in key does not match algorithm selected for operation.
284
    KeyAlgorithmMismatch(Algorithm, Algorithm),
3✔
NEW
285
    DuplicateHeaders(Vec<Label>),
×
NEW
286
    InvalidKeyId(Vec<u8>),
×
NEW
287
    MissingKeyParam(KeyParam),
×
NEW
288
    InvalidKeyParam(KeyParam, Value),
×
NEW
289
    TypeMismatch(Value),
×
290
    NoKeyFound,
291
    IvRequired,
292
}
293

294
impl<T> CoseCipherError<T>
295
where
296
    T: Display,
297
{
298
    /// Creates a new [`CoseCipherError`] of type
299
    /// [`HeaderAlreadySet`](CoseCipherError::HeaderAlreadySet) where the header
300
    /// that was already set has the name of the given `label`.
301
    #[must_use]
302
    pub fn existing_header_label(label: &Label) -> CoseCipherError<T> {
303
        let existing_header_name = match label {
304
            Label::Int(i) => i.to_string(),
305
            Label::Text(s) => s.to_string(),
306
        };
307
        CoseCipherError::HeaderAlreadySet {
308
            existing_header_name,
309
        }
310
    }
311

312
    /// Creates a new [`CoseCipherError`] of type
313
    /// [`HeaderAlreadySet`](CoseCipherError::HeaderAlreadySet) where the header
314
    /// that was already set has the given `name`.
315
    #[must_use]
316
    pub fn existing_header<S>(name: S) -> CoseCipherError<T>
317
    where
318
        S: Into<String>,
319
    {
320
        CoseCipherError::HeaderAlreadySet {
321
            existing_header_name: name.into(),
322
        }
323
    }
324

325
    /// Creates a new [`CoseCipherError`] of type
326
    /// [`Other`](CoseCipherError::Other) (i.e., an error type that doesn't fit any other
327
    /// [`CoseCipherError`] variant) containing the given nested error `other`.
328
    #[must_use]
329
    pub fn other_error(other: T) -> CoseCipherError<T> {
330
        CoseCipherError::Other(other)
331
    }
332

333
    // TODO: Maybe there's a better way to do the below, parts of this are redundant and duplicated.
334
    pub(crate) fn from_kek_error<C: Display>(
335
        error: CoseCipherError<T>,
336
    ) -> CoseCipherError<MultipleCoseError<T, C>> {
337
        match error {
338
            CoseCipherError::Other(x) => CoseCipherError::Other(MultipleCoseError::KekError(x)),
339
            CoseCipherError::HeaderAlreadySet {
340
                existing_header_name,
341
            } => CoseCipherError::HeaderAlreadySet {
342
                existing_header_name,
343
            },
344
            CoseCipherError::VerificationFailure => CoseCipherError::VerificationFailure,
345
            CoseCipherError::DecryptionFailure => CoseCipherError::DecryptionFailure,
346
            CoseCipherError::UnsupportedKeyType(v) => CoseCipherError::UnsupportedKeyType(v),
347
            CoseCipherError::UnsupportedCurve(v) => CoseCipherError::UnsupportedCurve(v),
348
            CoseCipherError::UnsupportedAlgorithm(v) => CoseCipherError::UnsupportedAlgorithm(v),
349
            CoseCipherError::UnsupportedKeyDerivation => CoseCipherError::UnsupportedKeyDerivation,
350
            CoseCipherError::NoAlgorithmDeterminable => CoseCipherError::NoAlgorithmDeterminable,
351
            CoseCipherError::KeyOperationNotPermitted(v, w) => {
352
                CoseCipherError::KeyOperationNotPermitted(v, w)
353
            }
354
            CoseCipherError::KeyTypeCurveMismatch(v, w) => {
355
                CoseCipherError::KeyTypeCurveMismatch(v, w)
356
            }
357
            CoseCipherError::KeyTypeAlgorithmMismatch(v, w) => {
358
                CoseCipherError::KeyTypeAlgorithmMismatch(v, w)
359
            }
360
            CoseCipherError::KeyAlgorithmMismatch(v, w) => {
361
                CoseCipherError::KeyAlgorithmMismatch(v, w)
362
            }
363
            CoseCipherError::DuplicateHeaders(v) => CoseCipherError::DuplicateHeaders(v),
364
            CoseCipherError::InvalidKeyId(v) => CoseCipherError::InvalidKeyId(v),
365
            CoseCipherError::MissingKeyParam(v) => CoseCipherError::MissingKeyParam(v),
366
            CoseCipherError::InvalidKeyParam(v, w) => CoseCipherError::InvalidKeyParam(v, w),
367
            CoseCipherError::TypeMismatch(v) => CoseCipherError::TypeMismatch(v),
368
            CoseCipherError::NoKeyFound => CoseCipherError::NoKeyFound,
369
            CoseCipherError::IvRequired => CoseCipherError::IvRequired,
370
        }
371
    }
372

373
    pub(crate) fn from_cek_error<K: Display>(
374
        error: CoseCipherError<T>,
375
    ) -> CoseCipherError<MultipleCoseError<K, T>> {
376
        match error {
377
            CoseCipherError::Other(x) => CoseCipherError::Other(MultipleCoseError::CekError(x)),
378
            CoseCipherError::HeaderAlreadySet {
379
                existing_header_name,
380
            } => CoseCipherError::HeaderAlreadySet {
381
                existing_header_name,
382
            },
383
            CoseCipherError::VerificationFailure => CoseCipherError::VerificationFailure,
384
            CoseCipherError::DecryptionFailure => CoseCipherError::DecryptionFailure,
385
            CoseCipherError::UnsupportedKeyType(v) => CoseCipherError::UnsupportedKeyType(v),
386
            CoseCipherError::UnsupportedCurve(v) => CoseCipherError::UnsupportedCurve(v),
387
            CoseCipherError::UnsupportedAlgorithm(v) => CoseCipherError::UnsupportedAlgorithm(v),
388
            CoseCipherError::UnsupportedKeyDerivation => CoseCipherError::UnsupportedKeyDerivation,
389
            CoseCipherError::NoAlgorithmDeterminable => CoseCipherError::NoAlgorithmDeterminable,
390
            CoseCipherError::KeyOperationNotPermitted(v, w) => {
391
                CoseCipherError::KeyOperationNotPermitted(v, w)
392
            }
393
            CoseCipherError::KeyTypeCurveMismatch(v, w) => {
394
                CoseCipherError::KeyTypeCurveMismatch(v, w)
395
            }
396
            CoseCipherError::KeyTypeAlgorithmMismatch(v, w) => {
397
                CoseCipherError::KeyTypeAlgorithmMismatch(v, w)
398
            }
399
            CoseCipherError::KeyAlgorithmMismatch(v, w) => {
400
                CoseCipherError::KeyAlgorithmMismatch(v, w)
401
            }
402
            CoseCipherError::DuplicateHeaders(v) => CoseCipherError::DuplicateHeaders(v),
403
            CoseCipherError::InvalidKeyId(v) => CoseCipherError::InvalidKeyId(v),
404
            CoseCipherError::MissingKeyParam(v) => CoseCipherError::MissingKeyParam(v),
405
            CoseCipherError::InvalidKeyParam(v, w) => CoseCipherError::InvalidKeyParam(v, w),
406
            CoseCipherError::TypeMismatch(v) => CoseCipherError::TypeMismatch(v),
407
            CoseCipherError::NoKeyFound => CoseCipherError::NoKeyFound,
408
            CoseCipherError::IvRequired => CoseCipherError::IvRequired,
409
        }
410
    }
411
}
412

413
impl<T> Display for CoseCipherError<T>
414
where
415
    T: Display,
416
{
417
    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
418
        match self {
419
            // TODO this can probably be done better (use thiserror instead as soon as std::error::Error has been moved to core?)
420
            CoseCipherError::HeaderAlreadySet {
421
                existing_header_name,
422
            } => write!(
423
                f,
424
                "cipher-defined header '{existing_header_name}' already set"
425
            ),
426
            CoseCipherError::VerificationFailure => write!(f, "data verification failed"),
427
            CoseCipherError::DecryptionFailure => write!(f, "decryption failed"),
428
            CoseCipherError::Other(s) => write!(f, "{s}"),
429
            CoseCipherError::UnsupportedKeyType(_) => write!(f, "unsupported key type"),
430
            CoseCipherError::UnsupportedCurve(_) => write!(f, "unsupported curve"),
431
            CoseCipherError::UnsupportedAlgorithm(_) => write!(f, "unsupported alorithm"),
432
            CoseCipherError::UnsupportedKeyDerivation => write!(
433
                f,
434
                "backend does not support public key derivation from private key"
435
            ),
436
            CoseCipherError::NoAlgorithmDeterminable => {
437
                write!(f, "no algorithm was provided in headers or key")
438
            }
439
            CoseCipherError::KeyOperationNotPermitted(_, _) => {
440
                write!(f, "key does not permit the requested operation")
441
            }
442
            CoseCipherError::KeyTypeCurveMismatch(_, _) => {
443
                write!(f, "key type is not supported for the given curve")
444
            }
445
            CoseCipherError::KeyTypeAlgorithmMismatch(_, _) => {
446
                write!(f, "key type is not supported for the given algorithm")
447
            }
448
            CoseCipherError::KeyAlgorithmMismatch(_, _) => {
449
                write!(f, "key does not support the given algorithm")
450
            }
451
            CoseCipherError::DuplicateHeaders(_) => write!(f, "duplicate headers"),
452
            // TODO is this one still needed or maybe misnamed?
453
            CoseCipherError::InvalidKeyId(_) => write!(f, "invalid key ID"),
454
            CoseCipherError::MissingKeyParam(_) => write!(f, "required key parameter missing"),
455
            CoseCipherError::InvalidKeyParam(_, _) => write!(f, "key parameter has invalid value"),
456
            // TODO is this one still needed or maybe misnamed?
457
            CoseCipherError::TypeMismatch(_) => write!(f, "key parameter has invalid type"),
458
            CoseCipherError::NoKeyFound => {
459
                write!(f, "no suitable key was found for this operation")
460
            }
461
            CoseCipherError::IvRequired => {
462
                write!(
463
                    f,
464
                    "chosen algorithm requires an initialization vector, but none was provided"
465
                )
466
            }
467
        }
468
    }
469
}
470

471
/// Error type used when a token for multiple recipients (i.e., `CoseEncrypt`) is decrypted.
472
///
473
/// In that case, the recipients may be encrypted with a different cipher (`K`) than the
474
/// actual content (`C`); hence, this error type differentiates between the two.
475
#[derive(Debug)]
476
pub enum MultipleCoseError<K, C>
477
where
478
    K: Display,
479
    C: Display,
480
{
481
    /// Used when an error occurred in the Key Encryption Key's cipher.
482
    KekError(K),
483

484
    /// Used when an error occurred in the Content Encryption Key's cipher.
485
    CekError(C),
486
}
487

488
impl<K, C> Display for MultipleCoseError<K, C>
489
where
490
    K: Display,
491
    C: Display,
492
{
493
    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
494
        match self {
495
            MultipleCoseError::KekError(k) => k.fmt(f),
496
            MultipleCoseError::CekError(c) => c.fmt(f),
497
        }
498
    }
499
}
500

501
/// Error type when a [`Value`] can't be converted to a Scope.
502
///
503
/// This can be because it isn't a scope, or because the scope is invalid.
504
#[derive(Debug, PartialEq, Clone)]
×
505
#[non_exhaustive]
506
pub enum ScopeFromValueError {
507
    /// The binary scope contained in the [`Value`] is invalid.
508
    ///
509
    /// Details are provided in the given [`InvalidBinaryEncodedScopeError`].
510
    InvalidBinaryEncodedScope(InvalidBinaryEncodedScopeError),
×
511

512
    /// The textual scope contained in the [`Value`] is invalid.
513
    ///
514
    /// Details are provided in the given [`InvalidTextEncodedScopeError`].
515
    InvalidTextEncodedScope(InvalidTextEncodedScopeError),
×
516

517
    /// The AIF-encoded scope contained in the [`Value`] is invalid.
518
    ///
519
    /// Details are provided in the given [`InvalidAifEncodedScopeError`].
520
    InvalidAifEncodedScope(InvalidAifEncodedScopeError),
×
521

522
    /// The [`Value`] isn't a scope, but something else.
523
    ///
524
    /// Details are provided in the given [`WrongSourceTypeError`].
525
    InvalidType(WrongSourceTypeError<Value>),
×
526
}
527

528
fn to_variant_name(value: &Value) -> &'static str {
7✔
529
    match value {
7✔
530
        Value::Integer(_) => "Integer",
1✔
531
        Value::Bytes(_) => "Bytes",
×
532
        Value::Float(_) => "Float",
1✔
533
        Value::Text(_) => "Text",
×
534
        Value::Bool(_) => "Bool",
2✔
535
        Value::Null => "Null",
1✔
536
        Value::Tag(_, _) => "Tag",
1✔
537
        Value::Array(_) => "Array",
×
538
        Value::Map(_) => "Map",
1✔
539
        _ => "Unknown",
540
    }
541
}
7✔
542

543
impl ScopeFromValueError {
544
    /// Creates a new [`InvalidType`](ScopeFromValueError::InvalidType) error from the given
545
    /// `actual` [`Value`].
546
    ///
547
    /// Should be used when a given [`Value`] is not a text or byte string.
548
    #[must_use]
549
    pub fn invalid_type(actual: &Value) -> ScopeFromValueError {
7✔
550
        ScopeFromValueError::from(WrongSourceTypeError::new(
7✔
551
            "Text or Bytes",
552
            to_variant_name(actual),
7✔
553
        ))
554
    }
7✔
555
}
556

557
impl From<InvalidTextEncodedScopeError> for ScopeFromValueError {
558
    fn from(err: InvalidTextEncodedScopeError) -> Self {
×
559
        ScopeFromValueError::InvalidTextEncodedScope(err)
×
560
    }
×
561
}
562

563
impl From<InvalidBinaryEncodedScopeError> for ScopeFromValueError {
564
    fn from(err: InvalidBinaryEncodedScopeError) -> Self {
×
565
        ScopeFromValueError::InvalidBinaryEncodedScope(err)
×
566
    }
×
567
}
568

569
impl From<InvalidAifEncodedScopeError> for ScopeFromValueError {
570
    fn from(err: InvalidAifEncodedScopeError) -> Self {
×
571
        ScopeFromValueError::InvalidAifEncodedScope(err)
×
572
    }
×
573
}
574

575
impl From<WrongSourceTypeError<Value>> for ScopeFromValueError {
576
    fn from(err: WrongSourceTypeError<Value>) -> Self {
7✔
577
        ScopeFromValueError::InvalidType(err)
7✔
578
    }
7✔
579
}
580

581
impl Display for ScopeFromValueError {
582
    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
7✔
583
        match self {
7✔
584
            ScopeFromValueError::InvalidBinaryEncodedScope(s) => {
×
585
                write!(f, "invalid binary-encoded scope: {s}")
×
586
            }
587
            ScopeFromValueError::InvalidTextEncodedScope(s) => {
×
588
                write!(f, "invalid text-encoded scope: {s}")
×
589
            }
590
            ScopeFromValueError::InvalidType(t) => write!(f, "invalid type: {t}"),
7✔
591
            ScopeFromValueError::InvalidAifEncodedScope(a) => {
×
592
                write!(f, "invalid AIF-encoded scope: {a}")
×
593
            }
594
        }
595
    }
7✔
596
}
597

598
/// Error type used when an operation creating or receiving an access token failed.
599
///
600
/// `T` is the type of the nested error possibly contained by the
601
/// [`CoseCipherError`](AccessTokenError::CoseCipherError) variant.
602
#[derive(Debug)]
603
#[non_exhaustive]
604
pub enum AccessTokenError<T>
605
where
606
    T: Display,
607
{
608
    /// A COSE specific error occurred.
609
    ///
610
    /// Details are contained in this field using coset's [`CoseError`].
611
    CoseError(CoseError),
612
    /// A cryptographic CoseCipher operation has failed.
613
    ///
614
    /// Details are contained in this field, represented by a [`CoseCipherError`].
615
    CoseCipherError(CoseCipherError<T>),
616
    /// Headers can't be extracted because the input data is neither a
617
    /// [`CoseEncrypt0`](coset::CoseEncrypt0), [`CoseSign1`](coset::CoseSign1),
618
    /// nor [`CoseMac0`](coset::CoseMac0).
619
    UnknownCoseStructure,
620
    /// No matching recipient was found in the list of COSE_Recipient structures.
621
    /// This means that the given Key Encryption Key could not be used to decrypt any of the
622
    /// recipients, which means no Content Encryption Key could be extracted.
623
    NoMatchingRecipient,
624
    /// Multiple matching recipients were found in the list of COSE_Recipient structures.
625
    /// This means that the given Key Encryption Key could be used to decrypt multiple of the
626
    /// recipients, which means the token is malformed.
627
    MultipleMatchingRecipients,
628
}
629

630
impl<T> Display for AccessTokenError<T>
631
where
632
    T: Display,
633
{
634
    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
635
        match self {
636
            AccessTokenError::CoseError(e) => write!(f, "{e}"),
637
            AccessTokenError::CoseCipherError(e) => write!(f, "cipher error: {e}"),
638
            AccessTokenError::UnknownCoseStructure => write!(
639
                f,
640
                "input is either invalid or none of CoseEncrypt0, CoseSign1 nor CoseMac0"
641
            ),
642
            AccessTokenError::NoMatchingRecipient => {
643
                write!(f, "given KEK doesn't match any recipient")
644
            }
645
            AccessTokenError::MultipleMatchingRecipients => {
646
                write!(f, "given KEK matches multiple recipients")
647
            }
648
        }
649
    }
650
}
651

652
impl<T> From<CoseCipherError<T>> for AccessTokenError<T>
653
where
654
    T: Display,
655
{
656
    #[must_use]
657
    fn from(error: CoseCipherError<T>) -> Self {
658
        AccessTokenError::CoseCipherError(error)
659
    }
660
}
661

662
impl<T> From<CoseError> for AccessTokenError<T>
663
where
664
    T: Display,
665
{
666
    #[must_use]
667
    fn from(error: CoseError) -> Self {
668
        AccessTokenError::CoseError(error)
669
    }
670
}
671

672
#[allow(dead_code)]
673
impl<T> AccessTokenError<T>
674
where
675
    T: Display,
676
{
677
    // TODO: Again, as in CoseCipherError, maybe there's a better way to do the below.
678

679
    pub(crate) fn from_kek_error<C: Display>(
680
        error: AccessTokenError<T>,
681
    ) -> AccessTokenError<MultipleCoseError<T, C>> {
682
        match error {
683
            AccessTokenError::CoseCipherError(x) => {
684
                AccessTokenError::CoseCipherError(CoseCipherError::from_kek_error(x))
685
            }
686
            AccessTokenError::CoseError(x) => AccessTokenError::CoseError(x),
687
            AccessTokenError::UnknownCoseStructure => AccessTokenError::UnknownCoseStructure,
688
            AccessTokenError::NoMatchingRecipient => AccessTokenError::NoMatchingRecipient,
689
            AccessTokenError::MultipleMatchingRecipients => {
690
                AccessTokenError::MultipleMatchingRecipients
691
            }
692
        }
693
    }
694

695
    pub(crate) fn from_cek_error<K: Display>(
696
        error: AccessTokenError<T>,
697
    ) -> AccessTokenError<MultipleCoseError<K, T>> {
698
        match error {
699
            AccessTokenError::CoseCipherError(x) => {
700
                AccessTokenError::CoseCipherError(CoseCipherError::from_cek_error(x))
701
            }
702
            AccessTokenError::CoseError(x) => AccessTokenError::CoseError(x),
703
            AccessTokenError::UnknownCoseStructure => AccessTokenError::UnknownCoseStructure,
704
            AccessTokenError::NoMatchingRecipient => AccessTokenError::NoMatchingRecipient,
705
            AccessTokenError::MultipleMatchingRecipients => {
706
                AccessTokenError::MultipleMatchingRecipients
707
            }
708
        }
709
    }
710
}
711

712
#[cfg(feature = "std")]
713
mod std_error {
714
    use core::fmt::Debug;
715
    use std::error::Error;
716

717
    use crate::endpoints::creation_hint::AuthServerRequestCreationHintBuilderError;
718
    use crate::endpoints::token_req::AccessTokenRequestBuilderError;
719
    use crate::endpoints::token_req::AccessTokenResponseBuilderError;
720
    use crate::endpoints::token_req::ErrorResponseBuilderError;
721

722
    use super::*;
723

724
    impl<T> Error for WrongSourceTypeError<T> where T: Debug {}
725

726
    impl Error for TryFromCborMapError {}
727

728
    impl Error for ValueIsNotIntegerError {}
729

730
    impl Error for InvalidTextEncodedScopeError {}
731

732
    impl Error for InvalidBinaryEncodedScopeError {}
733

734
    impl Error for InvalidAifEncodedScopeError {}
735

736
    impl Error for ScopeFromValueError {}
737

738
    impl<T> Error for CoseCipherError<T> where T: Debug + Display {}
739

740
    impl<T> Error for AccessTokenError<T> where T: Debug + Display {}
741

742
    impl Error for AccessTokenRequestBuilderError {}
743

744
    impl Error for AccessTokenResponseBuilderError {}
745

746
    impl Error for ErrorResponseBuilderError {}
747

748
    impl Error for AuthServerRequestCreationHintBuilderError {}
749
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc