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

namib-project / dcaf-rs / 10516465139

22 Aug 2024 10:16PM UTC coverage: 85.144% (+0.2%) from 84.95%
10516465139

Pull #24

github

web-flow
Merge 0da044a44 into d087ba7ef
Pull Request #24: Add support for AES-CCM

2185 of 4715 branches covered (46.34%)

229 of 266 new or added lines in 16 files covered. (86.09%)

1 existing line in 1 file now uncovered.

3158 of 3709 relevant lines covered (85.14%)

48.86 hits per line

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

70.43
/src/token/cose/encrypted/mod.rs
1
/*
2
 * Copyright (c) 2024 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
use alloc::collections::BTreeSet;
12
use alloc::rc::Rc;
13
use alloc::vec::Vec;
14
use ciborium::Value;
15
use core::cell::RefCell;
16
use core::fmt::Display;
17
use coset::{iana, Algorithm, Header, KeyOperation};
18

19
use crate::error::CoseCipherError;
20
use crate::token::cose::header_util::HeaderParam;
21
use crate::token::cose::key::{CoseParsedKey, CoseSymmetricKey, KeyProvider};
22
use crate::token::cose::{header_util, key, CryptoBackend, KeyParam};
23

24
mod encrypt;
25
mod encrypt0;
26

27
pub use encrypt::{CoseEncryptBuilderExt, CoseEncryptExt};
28
pub use encrypt0::{CoseEncrypt0BuilderExt, CoseEncrypt0Ext};
29

30
/// Authentication tag length to use for AES-GCM (fixed to 128 bits according to
31
/// [RFC 9053, section 4.1](https://datatracker.ietf.org/doc/html/rfc9053#section-4.1)).
32
pub const AES_GCM_TAG_LEN: usize = 16;
33

34
/// Nonce size used for AES-GCM (fixed to 96 bits according to
35
/// [RFC 9053, section 4.1](https://datatracker.ietf.org/doc/html/rfc9053#section-4.1)).
36
pub const AES_GCM_NONCE_SIZE: usize = 12;
37

38
/// Trait for cryptographic backends that can perform encryption and decryption operations for
39
/// algorithms used for COSE.
40
pub trait EncryptCryptoBackend: CryptoBackend {
41
    /// Encrypts the given `payload` using the AES-GCM variant provided as `algorithm` and the given
42
    /// `key`.
43
    ///
44
    /// Note that for all AES-GCM variants defined in RFC 9053, Section 4.1, the authentication tag
45
    /// should be 128 bits/16 bytes long.
46
    ///
47
    /// # Arguments
48
    ///
49
    /// * `algorithm` - The AES-GCM variant to use.
50
    ///           If unsupported by the backend, a [`CoseCipherError::UnsupportedAlgorithm`]
51
    ///           should be returned.
52
    ///           If the given algorithm is an IANA-assigned value that is unknown, the
53
    ///           implementation should return [`CoseCipherError::UnsupportedAlgorithm`] (in case
54
    ///           additional variants of AES-GCM are ever added).
55
    ///           If the algorithm is not an AES-GCM algorithm, the implementation may return
56
    ///           [`CoseCipherError::UnsupportedAlgorithm`] or panic.
57
    /// * `key` - Symmetric key that should be used.
58
    ///           Implementations may assume that the provided key has the right length for the
59
    ///           provided algorithm, and panic if this is not the case.
60
    /// * `plaintext` - Data that should be encrypted.
61
    /// * `aad` - additional authenticated data that should be included in the calculation of the
62
    ///           authentication tag, but not encrypted.
63
    /// * `iv`  - Initialization vector that should be used for the encryption process.
64
    ///           Implementations may assume that `iv` has the correct length for the given AES-GCM
65
    ///           variant and panic if this is not the case.
66
    ///
67
    /// # Returns
68
    ///
69
    /// It is expected that the return value is the computed ciphertext concatenated with the
70
    /// authentication tag as a `Vec`.
71
    ///
72
    /// # Errors
73
    ///
74
    /// In case of errors, the implementation may return any valid [`CoseCipherError`].
75
    /// For backend-specific errors, [`CoseCipherError::Other`] may be used to convey a
76
    /// backend-specific error.
77
    ///
78
    /// # Panics
79
    ///
80
    /// Implementations may panic if the provided algorithm is not an AES-GCM algorithm, the
81
    /// provided key or IV are not of the right length for the provided algorithm or if an
82
    /// unrecoverable backend error occurs that necessitates a panic (at their own discretion).
83
    /// In the last of the above cases, additional panics should be documented on the backend level.
84
    ///
85
    /// For unknown algorithms or key curves, however, the implementation must not panic and return
86
    /// [`CoseCipherError::UnsupportedAlgorithm`] instead (in case new AES-GCM variants are ever
87
    /// defined).
88
    #[allow(unused_variables)]
89
    fn encrypt_aes_gcm(
90
        &mut self,
91
        algorithm: iana::Algorithm,
92
        key: CoseSymmetricKey<'_, Self::Error>,
93
        plaintext: &[u8],
94
        aad: &[u8],
95
        iv: &[u8],
96
    ) -> Result<Vec<u8>, CoseCipherError<Self::Error>> {
97
        Err(CoseCipherError::UnsupportedAlgorithm(Algorithm::Assigned(
98
            algorithm,
99
        )))
100
    }
101

102
    /// Decrypts the given `payload` using the AES-GCM variant provided as `algorithm`, and the given
103
    /// `key` and `iv`.
104
    ///
105
    /// Note that for all AES-GCM variants defined in RFC 9053, Section 4.1, the authentication tag
106
    /// should be 128 bits/16 bytes long.
107
    ///
108
    /// # Arguments
109
    ///
110
    /// * `algorithm` - The AES-GCM variant to use.
111
    ///           If unsupported by the backend, a [`CoseCipherError::UnsupportedAlgorithm`] error
112
    ///           should be returned.
113
    ///           If the given algorithm is an IANA-assigned value that is unknown, the
114
    ///           implementation should return [`CoseCipherError::UnsupportedAlgorithm`] (in case
115
    ///           additional variants of AES-GCM are ever added).
116
    ///           If the algorithm is not an AES-GCM algorithm, the implementation may return
117
    ///           [`CoseCipherError::UnsupportedAlgorithm`] or panic.
118
    /// * `key` - Symmetric key that should be used.
119
    ///           Implementations may assume that the provided key has the right length for the
120
    ///           provided algorithm, and panic if this is not the case.
121
    /// * `ciphertext_with_tag` - The ciphertext that should be decrypted concatenated with the
122
    ///           authentication tag that should be verified.
123
    ///           Is guaranteed to be at least as long as the authentication tag should be.
124
    /// * `aad` - additional authenticated data that should be included in the calculation of the
125
    ///           authentication tag, but not encrypted.
126
    /// * `iv`  - Initialization vector that should be used for the encryption process.
127
    ///           Implementations may assume that `iv` has the correct length for the given AES-GCM
128
    ///           variant and panic if this is not the case.
129
    ///
130
    /// # Returns
131
    ///
132
    /// It is expected that the return value is either the computed plaintext if decryption and
133
    /// authentication are successful, or a [`CoseCipherError::VerificationFailure`] if one of these
134
    /// steps fails even though the input is well-formed.
135
    ///
136
    /// # Errors
137
    ///
138
    /// In case of errors, the implementation may return any valid [`CoseCipherError`].
139
    /// For backend-specific errors, [`CoseCipherError::Other`] may be used to convey a
140
    /// backend-specific error.
141
    ///
142
    /// # Panics
143
    ///
144
    /// Implementations may panic if the provided algorithm is not an AES-GCM algorithm, the
145
    /// provided key or IV are not of the right length for the provided algorithm or if an
146
    /// unrecoverable backend error occurs that necessitates a panic (at their own discretion).
147
    /// In the last of the above cases, additional panics should be documented on the backend level.
148
    ///
149
    /// For unknown algorithms or key curves, however, the implementation must not panic and return
150
    /// [`CoseCipherError::UnsupportedAlgorithm`] instead (in case new AES-GCM variants are ever
151
    /// defined).
152
    #[allow(unused_variables)]
153
    fn decrypt_aes_gcm(
154
        &mut self,
155
        algorithm: iana::Algorithm,
156
        key: CoseSymmetricKey<'_, Self::Error>,
157
        ciphertext_with_tag: &[u8],
158
        aad: &[u8],
159
        iv: &[u8],
160
    ) -> Result<Vec<u8>, CoseCipherError<Self::Error>> {
161
        Err(CoseCipherError::UnsupportedAlgorithm(Algorithm::Assigned(
162
            algorithm,
163
        )))
164
    }
165

166
    /// Encrypts the given `payload` using AES-CCM with the parameters L (size of length field)
167
    /// and M (size of authentication tag) specified for the given `algorithm` in
168
    /// [RFC 9053, section 4.2](https://datatracker.ietf.org/doc/html/rfc9053#section-4.2), the
169
    /// given `key`, and the provided `iv`.
170
    ///
171
    /// # Arguments
172
    ///
173
    /// * `algorithm` - The AES-CCM variant to use.
174
    ///           If unsupported by the backend, a [`CoseCipherError::UnsupportedAlgorithm`]
175
    ///           should be returned.
176
    ///           If the given algorithm is an IANA-assigned value that is unknown, the
177
    ///           implementation should return [`CoseCipherError::UnsupportedAlgorithm`] (in case
178
    ///           additional variants of AES-CCM are ever added).
179
    ///           If the algorithm is not an AES-CCM algorithm, the implementation may return
180
    ///           [`CoseCipherError::UnsupportedAlgorithm`] or panic.
181
    /// * `key` - Symmetric key that should be used.
182
    ///           Implementations may assume that the provided key has the right length for the
183
    ///           provided algorithm, and panic if this is not the case.
184
    /// * `plaintext` - Data that should be encrypted.
185
    /// * `aad` - additional authenticated data that should be included in the calculation of the
186
    ///           authentication tag, but not encrypted.
187
    /// * `iv`  - Initialization vector that should be used for the encryption process.
188
    ///           Implementations may assume that `iv` has the correct length for the given AES-CCM
189
    ///           variant and panic if this is not the case.
190
    ///
191
    /// # Returns
192
    ///
193
    /// It is expected that the return value is the computed output of AES-CCM as specified in
194
    /// [RFC 3610, Section 2.4](https://datatracker.ietf.org/doc/html/rfc3610#section-2.4).
195
    ///
196
    /// # Errors
197
    ///
198
    /// In case of errors, the implementation may return any valid [`CoseCipherError`].
199
    /// For backend-specific errors, [`CoseCipherError::Other`] may be used to convey a
200
    /// backend-specific error.
201
    ///
202
    /// # Panics
203
    ///
204
    /// Implementations may panic if the provided algorithm is not an AES-CCM algorithm, the
205
    /// provided key or IV are not of the right length for the provided algorithm or if an
206
    /// unrecoverable backend error occurs that necessitates a panic (at their own discretion).
207
    /// In the last of the above cases, additional panics should be documented on the backend level.
208
    ///
209
    /// For unknown algorithms or key curves, however, the implementation must not panic and return
210
    /// [`CoseCipherError::UnsupportedAlgorithm`] instead (in case new AES-CCM variants are ever
211
    /// defined).
212
    #[allow(unused_variables)]
NEW
213
    fn encrypt_aes_ccm(
×
214
        &mut self,
215
        algorithm: iana::Algorithm,
216
        key: CoseSymmetricKey<'_, Self::Error>,
217
        plaintext: &[u8],
218
        aad: &[u8],
219
        iv: &[u8],
220
    ) -> Result<Vec<u8>, CoseCipherError<Self::Error>> {
NEW
221
        Err(CoseCipherError::UnsupportedAlgorithm(Algorithm::Assigned(
×
222
            algorithm,
223
        )))
NEW
224
    }
×
225

226
    /// Decrypts the given `payload` using AES-CCM with the parameters L (size of length field)
227
    /// and M (size of authentication tag) specified for the given `algorithm` in
228
    /// [RFC 9053, section 4.2](https://datatracker.ietf.org/doc/html/rfc9053#section-4.2) and the
229
    /// given `key`.
230
    ///
231
    /// # Arguments
232
    ///
233
    /// * `algorithm` - The AES-CCM variant to use.
234
    ///           If unsupported by the backend, a [`CoseCipherError::UnsupportedAlgorithm`] error
235
    ///           should be returned.
236
    ///           If the given algorithm is an IANA-assigned value that is unknown, the
237
    ///           implementation should return [`CoseCipherError::UnsupportedAlgorithm`] (in case
238
    ///           additional variants of AES-CCM are ever added).
239
    ///           If the algorithm is not an AES-CCM algorithm, the implementation may return
240
    ///           [`CoseCipherError::UnsupportedAlgorithm`] or panic.
241
    /// * `key` - Symmetric key that should be used.
242
    ///           Implementations may assume that the provided key has the right length for the
243
    ///           provided algorithm, and panic if this is not the case.
244
    /// * `ciphertext_with_tag` - The ciphertext that should be decrypted concatenated with the
245
    ///           authentication tag that should be verified (if valid, should be the output of a
246
    ///           previous encryption as specified in
247
    ///           [RFC 3610, Section 2.4](https://datatracker.ietf.org/doc/html/rfc3610#section-2.4)).
248
    ///           Is guaranteed to be at least as long as the authentication tag should be.
249
    /// * `aad` - additional authenticated data that should be included in the calculation of the
250
    ///           authentication tag, but not encrypted.
251
    /// * `iv`  - Initialization vector that should be used for the encryption process.
252
    ///           Implementations may assume that `iv` has the correct length for the given AES-CCM
253
    ///           variant and panic if this is not the case.
254
    ///
255
    /// # Returns
256
    ///
257
    /// It is expected that the return value is either the computed plaintext if decryption and
258
    /// authentication are successful, or a [`CoseCipherError::VerificationFailure`] if one of these
259
    /// steps fails even though the input is well-formed.
260
    ///
261
    /// # Errors
262
    ///
263
    /// In case of errors, the implementation may return any valid [`CoseCipherError`].
264
    /// For backend-specific errors, [`CoseCipherError::Other`] may be used to convey a
265
    /// backend-specific error.
266
    ///
267
    /// # Panics
268
    ///
269
    /// Implementations may panic if the provided algorithm is not an AES-CCM algorithm, the
270
    /// provided key or IV are not of the right length for the provided algorithm or if an
271
    /// unrecoverable backend error occurs that necessitates a panic (at their own discretion).
272
    /// In the last of the above cases, additional panics should be documented on the backend level.
273
    ///
274
    /// For unknown algorithms or key curves, however, the implementation must not panic and return
275
    /// [`CoseCipherError::UnsupportedAlgorithm`] instead (in case new AES-CCM variants are ever
276
    /// defined).
277
    #[allow(unused_variables)]
NEW
278
    fn decrypt_aes_ccm(
×
279
        &mut self,
280
        algorithm: iana::Algorithm,
281
        key: CoseSymmetricKey<'_, Self::Error>,
282
        ciphertext_with_tag: &[u8],
283
        aad: &[u8],
284
        iv: &[u8],
285
    ) -> Result<Vec<u8>, CoseCipherError<Self::Error>> {
NEW
286
        Err(CoseCipherError::UnsupportedAlgorithm(Algorithm::Assigned(
×
287
            algorithm,
288
        )))
NEW
289
    }
×
290
}
291

292
/// Returns the IV length expected for the AES variant given as `alg`.
293
///
294
/// # Errors
295
///
296
/// Returns [CoseCipherError::UnsupportedAlgorithm] if the provided algorithm is not a supported
297
/// AES algorithm.
298
pub fn aes_algorithm_iv_len<BE: Display>(
391✔
299
    alg: iana::Algorithm,
300
) -> Result<usize, CoseCipherError<BE>> {
301
    match alg {
391!
302
        // AES-GCM: Nonce is fixed at 96 bits (RFC 9053, Section 4.1)
303
        iana::Algorithm::A128GCM | iana::Algorithm::A192GCM | iana::Algorithm::A256GCM => {
304
            Ok(AES_GCM_NONCE_SIZE)
257✔
305
        }
306
        // AES-CCM: Nonce length is parameterized.
307
        iana::Algorithm::AES_CCM_16_64_128
308
        | iana::Algorithm::AES_CCM_16_128_128
309
        | iana::Algorithm::AES_CCM_16_64_256
310
        | iana::Algorithm::AES_CCM_16_128_256 => Ok(13),
70✔
311
        iana::Algorithm::AES_CCM_64_64_128
312
        | iana::Algorithm::AES_CCM_64_128_128
313
        | iana::Algorithm::AES_CCM_64_64_256
314
        | iana::Algorithm::AES_CCM_64_128_256 => Ok(7),
64✔
NEW
315
        v => Err(CoseCipherError::UnsupportedAlgorithm(Algorithm::Assigned(
×
316
            v,
NEW
317
        ))),
×
318
    }
319
}
391✔
320

321
/// Returns the authentication tag length expected for the AES-CCM variant given as `alg`.
322
///
323
/// # Errors
324
///
325
/// Returns [CoseCipherError::UnsupportedAlgorithm] if the provided algorithm is not a supported
326
/// variant of AES-CCM.
327
pub fn aes_ccm_algorithm_tag_len<BE: Display>(
116✔
328
    algorithm: iana::Algorithm,
329
) -> Result<usize, CoseCipherError<BE>> {
330
    match algorithm {
116!
331
        iana::Algorithm::AES_CCM_16_64_128
332
        | iana::Algorithm::AES_CCM_64_64_128
333
        | iana::Algorithm::AES_CCM_16_64_256
334
        | iana::Algorithm::AES_CCM_64_64_256 => Ok(8),
60✔
335
        iana::Algorithm::AES_CCM_16_128_256
336
        | iana::Algorithm::AES_CCM_64_128_256
337
        | iana::Algorithm::AES_CCM_16_128_128
338
        | iana::Algorithm::AES_CCM_64_128_128 => Ok(16),
56✔
NEW
339
        v => Err(CoseCipherError::UnsupportedAlgorithm(Algorithm::Assigned(
×
340
            v,
NEW
341
        ))),
×
342
    }
343
}
116✔
344

345
/// Determines the key and IV for an AES AEAD operation using the provided `protected` and
346
/// `unprotected` headers, ensuring that the provided `parsed_key` is a valid AES key in the
347
/// process.
348
fn determine_and_check_aes_params<'a, BE: Display>(
289✔
349
    alg: iana::Algorithm,
350
    parsed_key: CoseParsedKey<'a, BE>,
351
    protected: Option<&Header>,
352
    unprotected: Option<&Header>,
353
) -> Result<(CoseSymmetricKey<'a, BE>, Vec<u8>), CoseCipherError<BE>> {
354
    let symm_key = key::ensure_valid_aes_key::<BE>(alg, parsed_key)?;
289!
355

356
    let iv = header_util::determine_header_param(protected, unprotected, |v| {
859✔
357
        (!v.iv.is_empty()).then_some(&v.iv)
570✔
358
    });
570✔
359

360
    let partial_iv = header_util::determine_header_param(protected, unprotected, |v| {
859✔
361
        (!v.partial_iv.is_empty()).then_some(&v.partial_iv)
570✔
362
    });
570✔
363

364
    let expected_iv_len = aes_algorithm_iv_len(alg)?;
289!
365

366
    let iv = match (iv, partial_iv) {
578!
367
        // IV and partial IV must not be set at the same time.
NEW
368
        (Some(_iv), Some(partial_iv)) => Err(CoseCipherError::InvalidHeaderParam(
×
NEW
369
            HeaderParam::Generic(iana::HeaderParameter::PartialIv),
×
NEW
370
            Value::Bytes(partial_iv.clone()),
×
NEW
371
        )),
×
372
        (Some(iv), None) => Ok(iv.clone()),
283✔
373
        // See https://datatracker.ietf.org/doc/html/rfc9052#section-3.1
374
        (None, Some(partial_iv)) => {
6✔
375
            let context_iv = (!symm_key.as_ref().base_iv.is_empty())
6✔
376
                .then(|| &symm_key.as_ref().base_iv)
6✔
377
                .ok_or(CoseCipherError::MissingKeyParam(vec![KeyParam::Common(
12!
378
                    iana::KeyParameter::BaseIv,
6!
NEW
379
                )]))?;
×
380

381
            if partial_iv.len() > expected_iv_len {
6!
NEW
382
                return Err(CoseCipherError::InvalidHeaderParam(
×
NEW
383
                    HeaderParam::Generic(iana::HeaderParameter::PartialIv),
×
NEW
384
                    Value::Bytes(partial_iv.clone()),
×
385
                ));
386
            }
387

388
            if context_iv.len() > expected_iv_len {
6!
NEW
389
                return Err(CoseCipherError::InvalidKeyParam(
×
NEW
390
                    KeyParam::Common(iana::KeyParameter::BaseIv),
×
NEW
391
                    Value::Bytes(context_iv.clone()),
×
392
                ));
393
            }
394

395
            let mut message_iv = vec![0u8; expected_iv_len];
6✔
396

397
            // Left-pad the Partial IV with zeros to the length of IV
398
            message_iv[(expected_iv_len - partial_iv.len())..].copy_from_slice(partial_iv);
6✔
399
            // XOR the padded Partial IV with the Context IV.
400
            message_iv
12✔
401
                .iter_mut()
402
                .zip(context_iv.iter().chain(core::iter::repeat(&0u8)))
6✔
403
                .for_each(|(b1, b2)| *b1 ^= *b2);
72✔
404
            Ok(message_iv)
6✔
405
        }
6✔
NEW
406
        (None, None) => Err(CoseCipherError::MissingHeaderParam(HeaderParam::Generic(
×
NEW
407
            iana::HeaderParameter::Iv,
×
NEW
408
        ))),
×
NEW
409
    }?;
×
410

411
    if iv.len() != expected_iv_len {
289!
NEW
412
        return Err(CoseCipherError::InvalidHeaderParam(
×
NEW
413
            HeaderParam::Generic(iana::HeaderParameter::Iv),
×
NEW
414
            Value::Bytes(iv.clone()),
×
415
        ));
416
    }
417

418
    Ok((symm_key, iv))
289✔
419
}
289✔
420

421
/// Attempts to perform a COSE encryption operation for a [`CoseEncrypt`](coset::CoseEncrypt) or
422
/// [`CoseEncrypt0`](coset::CoseEncrypt0) structure with the given `protected` and `unprotected`
423
/// headers, `plaintext` and `enc_structure` using the given `backend` and `key_provider`.
424
///
425
/// Also performs checks that ensure that the given parameters (esp. headers and keys) are valid and
426
/// are coherent with each other.
427
///
428
/// If the `key_provider` returns multiple keys, all will be tried until one can be successfully
429
/// used for the given operation.
430
fn try_encrypt<B: EncryptCryptoBackend, CKP: KeyProvider>(
109✔
431
    backend: &mut B,
432
    key_provider: &CKP,
433
    protected: Option<&Header>,
434
    unprotected: Option<&Header>,
435
    plaintext: &[u8],
436
    // NOTE: this should be treated as the AAD for the purposes of the cryptographic backend
437
    // (RFC 9052, Section 5.3).
438
    enc_structure: &[u8],
439
) -> Result<Vec<u8>, CoseCipherError<B::Error>> {
440
    header_util::try_cose_crypto_operation(
109✔
441
        key_provider,
442
        protected,
443
        unprotected,
444
        BTreeSet::from_iter(vec![KeyOperation::Assigned(iana::KeyOperation::Encrypt)]),
109!
445
        |key, alg, protected, unprotected| {
216✔
446
            let parsed_key = CoseParsedKey::try_from(key)?;
107!
447
            match alg {
107!
448
                iana::Algorithm::A128GCM | iana::Algorithm::A192GCM | iana::Algorithm::A256GCM => {
449
                    // Check if this is a valid AES key, determine IV.
450
                    let (symm_key, iv) =
73✔
451
                        determine_and_check_aes_params(alg, parsed_key, protected, unprotected)?;
73!
452

453
                    backend.encrypt_aes_gcm(alg, symm_key, plaintext, enc_structure, &iv)
73✔
454
                }
73✔
455
                iana::Algorithm::AES_CCM_16_64_128
456
                | iana::Algorithm::AES_CCM_64_64_128
457
                | iana::Algorithm::AES_CCM_16_128_128
458
                | iana::Algorithm::AES_CCM_64_128_128
459
                | iana::Algorithm::AES_CCM_16_64_256
460
                | iana::Algorithm::AES_CCM_64_64_256
461
                | iana::Algorithm::AES_CCM_16_128_256
462
                | iana::Algorithm::AES_CCM_64_128_256 => {
463
                    // Check if this is a valid AES key, determine IV.
464
                    let (symm_key, iv) =
34✔
465
                        determine_and_check_aes_params(alg, parsed_key, protected, unprotected)?;
34!
466

467
                    backend.encrypt_aes_ccm(alg, symm_key, plaintext, enc_structure, &iv)
34✔
468
                }
34✔
469
                alg => Err(CoseCipherError::UnsupportedAlgorithm(Algorithm::Assigned(
×
470
                    alg,
471
                ))),
×
472
            }
473
        },
107!
474
    )
475
}
109✔
476

477
/// Attempts to perform a COSE decryption operation for a [`CoseEncrypt`](coset::CoseEncrypt) or
478
/// [`CoseEncrypt0`](coset::CoseEncrypt0) structure with the given `protected` and `unprotected`
479
/// headers, `plaintext` and `enc_structure` using the given `backend` and `key_provider`.
480
///
481
/// Also performs checks that ensure that the given parameters (esp. headers and keys) are valid and
482
/// are coherent with each other.
483
///
484
/// If the `key_provider` returns multiple keys, all will be tried until one can be successfully
485
/// used for the given operation.
486
pub(crate) fn try_decrypt<B: EncryptCryptoBackend, CKP: KeyProvider>(
192✔
487
    backend: &Rc<RefCell<&mut B>>,
488
    key_provider: &CKP,
489
    protected: &Header,
490
    unprotected: &Header,
491
    ciphertext: &[u8],
492
    // NOTE: this should be treated as the AAD for the purposes of the cryptographic backend
493
    // (RFC 9052, Section 5.3).
494
    enc_structure: &[u8],
495
) -> Result<Vec<u8>, CoseCipherError<B::Error>> {
496
    header_util::try_cose_crypto_operation(
192✔
497
        key_provider,
498
        Some(protected),
192✔
499
        Some(unprotected),
192✔
500
        BTreeSet::from_iter(vec![KeyOperation::Assigned(iana::KeyOperation::Decrypt)]),
192!
501
        |key, alg, protected, unprotected| {
374✔
502
            let parsed_key = CoseParsedKey::try_from(key)?;
182!
503
            match alg {
182!
504
                iana::Algorithm::A128GCM | iana::Algorithm::A192GCM | iana::Algorithm::A256GCM => {
505
                    // Check if this is a valid AES key, determine IV.
506
                    let (symm_key, iv) =
116✔
507
                        determine_and_check_aes_params(alg, parsed_key, protected, unprotected)?;
116!
508

509
                    // Authentication tag is 16 bytes long and should be included in the ciphertext.
510
                    // Empty payloads are allowed, therefore we check for ciphertext.len() < 16, not <= 16.
511
                    if ciphertext.len() < AES_GCM_TAG_LEN {
116!
512
                        return Err(CoseCipherError::VerificationFailure);
×
513
                    }
514

515
                    (*backend.borrow_mut()).decrypt_aes_gcm(
232✔
516
                        alg,
517
                        symm_key,
518
                        ciphertext,
116✔
519
                        enc_structure,
116✔
520
                        &iv,
116✔
521
                    )
522
                }
116✔
523
                iana::Algorithm::AES_CCM_16_64_128
524
                | iana::Algorithm::AES_CCM_64_64_128
525
                | iana::Algorithm::AES_CCM_16_128_128
526
                | iana::Algorithm::AES_CCM_64_128_128
527
                | iana::Algorithm::AES_CCM_16_64_256
528
                | iana::Algorithm::AES_CCM_64_64_256
529
                | iana::Algorithm::AES_CCM_16_128_256
530
                | iana::Algorithm::AES_CCM_64_128_256 => {
531
                    // Check if this is a valid AES key, determine IV.
532
                    let (symm_key, iv) =
66✔
533
                        determine_and_check_aes_params(alg, parsed_key, protected, unprotected)?;
66!
534

535
                    if ciphertext.len() < aes_ccm_algorithm_tag_len(alg)? {
66!
NEW
536
                        return Err(CoseCipherError::VerificationFailure);
×
537
                    }
538

539
                    (*backend.borrow_mut()).decrypt_aes_ccm(
132✔
540
                        alg,
541
                        symm_key,
542
                        ciphertext,
66✔
543
                        enc_structure,
66✔
544
                        &iv,
66✔
545
                    )
546
                }
66✔
547
                alg => Err(CoseCipherError::UnsupportedAlgorithm(Algorithm::Assigned(
×
548
                    alg,
549
                ))),
×
550
            }
551
        },
182!
552
    )
553
}
192✔
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

© 2025 Coveralls, Inc