• 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

99.19
/src/token/cose/encrypted/encrypt/tests.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 core::convert::Infallible;
12
use std::path::PathBuf;
13

14
use coset::{
15
    iana, Algorithm, CoseEncrypt, CoseEncryptBuilder, CoseError, CoseKey, CoseKeyBuilder,
16
    CoseRecipientBuilder, EncryptionContext, HeaderBuilder,
17
};
18
use rstest::rstest;
19

20
use crate::token::cose::encrypted::encrypt::{CoseEncryptBuilderExt, CoseEncryptExt};
21
use crate::token::cose::encrypted::EncryptCryptoBackend;
22
use crate::token::cose::header_util::{determine_algorithm, HeaderBuilderExt};
23
use crate::token::cose::key::CoseSymmetricKey;
24
use crate::token::cose::recipient::{CoseRecipientBuilderExt, KeyDistributionCryptoBackend};
25
use crate::token::cose::test_helper::{
26
    apply_attribute_failures, apply_header_failures, calculate_base_iv,
27
    perform_cose_reference_output_test, perform_cose_self_signed_test,
28
    serialize_cose_with_failures, CoseStructTestHelper, TestCase,
29
};
30
use crate::token::cose::{determine_header_param, CryptoBackend};
31

32
#[cfg(feature = "openssl")]
33
use crate::token::cose::test_helper::openssl_ctx;
34
#[cfg(all(
35
    any(feature = "rustcrypto-aes-gcm", feature = "rustcrypto-aes-ccm"),
36
    feature = "rustcrypto-aes-kw"
37
))]
38
use crate::token::cose::test_helper::rustcrypto_ctx;
39

40
impl<B: CryptoBackend + EncryptCryptoBackend + KeyDistributionCryptoBackend> CoseStructTestHelper<B>
41
    for CoseEncrypt
42
{
43
    fn from_test_case(case: &TestCase, backend: &mut B) -> Self {
62✔
44
        let encrypt_cfg = case
62✔
45
            .input
46
            .enveloped
47
            .as_ref()
48
            .expect("expected a CoseEncrypt test case, but it was not found");
49

50
        let encrypt = CoseEncryptBuilder::new();
62✔
51

52
        let recipient = encrypt_cfg
62✔
53
            .recipients
54
            .first()
55
            .expect("test case has no recipient");
56

57
        let mut recipient_struct_builder = CoseRecipientBuilder::from(recipient.clone());
62✔
58
        let mut enc_key: CoseKey;
59
        if recipient.alg == Some(Algorithm::Assigned(iana::Algorithm::Direct))
124!
60
            || determine_algorithm::<Infallible>(
62✔
61
                None,
62✔
62
                recipient.protected.as_ref(),
62✔
63
                recipient.unprotected.as_ref(),
62✔
64
            ) == Ok(iana::Algorithm::Direct)
62✔
65
        {
66
            enc_key = recipient.key.clone();
48✔
67
        } else {
68
            enc_key = CoseKeyBuilder::new_symmetric_key(
14✔
69
                case.intermediates
14✔
70
                    .as_ref()
71
                    .expect("CoseEncrypt test case should have intermediates")
72
                    .cek
73
                    .clone(),
74
            )
75
            .build();
76
            let parsed_key = CoseSymmetricKey::<Infallible>::try_from(&enc_key)
14✔
77
                .expect("unable to parse CEK input as symmetric key");
78
            recipient_struct_builder = recipient_struct_builder
28✔
79
                .try_encrypt(
80
                    backend,
81
                    &recipient.key,
14✔
82
                    EncryptionContext::EncRecipient,
14✔
83
                    recipient.protected.clone(),
14✔
84
                    recipient.unprotected.clone(),
14✔
85
                    parsed_key.k,
14✔
86
                    &[] as &[u8],
87
                )
88
                .expect("unable to create CoseRecipient structure");
89
        }
90

91
        // Need to generate an IV. Have to do this quite uglily, because we have implemented our IV
92
        // generation on the header builder only.
93
        let alg = if let Algorithm::Assigned(alg) = encrypt_cfg
124✔
94
            .protected
95
            .as_ref()
96
            .or(encrypt_cfg.unprotected.as_ref())
62!
97
            .unwrap()
98
            .alg
99
            .as_ref()
100
            .unwrap()
101
        {
102
            alg
103
        } else {
NEW
104
            panic!("unknown/invalid algorithm in test case")
×
105
        };
106

107
        let mut unprotected = encrypt_cfg.unprotected.clone().unwrap_or_default();
62✔
108
        if let Some(partial_iv) = determine_header_param(
62✔
109
            encrypt_cfg.protected.as_ref(),
62✔
110
            encrypt_cfg.unprotected.as_ref(),
62✔
111
            |v| (!v.partial_iv.is_empty()).then_some(&v.partial_iv),
64✔
112
        ) {
113
            enc_key.base_iv = calculate_base_iv(
2✔
114
                encrypt_cfg
2✔
115
                    .unsent
116
                    .as_ref()
117
                    .expect("test case has partial IV but no full IV defined"),
118
                partial_iv,
119
            );
120
        } else {
121
            let iv_generator = HeaderBuilder::new()
120✔
122
                .gen_iv(backend, *alg)
60✔
123
                .expect("unable to generate IV")
124
                .build();
125
            unprotected.iv = iv_generator.iv;
60✔
126
        }
60✔
127

128
        encrypt
124✔
129
            .add_recipient(recipient_struct_builder.build())
62✔
130
            .try_encrypt(
131
                backend,
132
                &enc_key,
133
                encrypt_cfg.protected.clone(),
62✔
134
                Some(unprotected),
62✔
135
                &case.input.plaintext.clone().into_bytes(),
62✔
136
                encrypt_cfg.external.as_slice(),
62✔
137
            )
138
            .expect("unable to encrypt Encrypt object")
139
            .build()
140
    }
62✔
141

142
    fn serialize_and_apply_failures(mut self, case: &TestCase) -> Result<Vec<u8>, CoseError> {
62✔
143
        let failures = &case.input.failures;
62✔
144
        if let Some(1) = &failures.change_tag {
62!
145
            let byte = self.ciphertext.as_mut().unwrap().first_mut().unwrap();
4✔
146
            *byte = byte.wrapping_add(1);
4✔
147
        }
148

149
        apply_header_failures(&mut self.protected.header, failures);
62✔
150

151
        apply_attribute_failures(&mut self.unprotected, failures)?;
66✔
152
        Ok(serialize_cose_with_failures(self, failures))
60✔
153
    }
62✔
154

155
    fn check_against_test_case(&self, case: &TestCase, backend: &mut B) {
110✔
156
        let test_case = case
110✔
157
            .input
158
            .enveloped
159
            .as_ref()
160
            .expect("expected CoseEncrypt test case");
161
        let keys: Vec<CoseKey> = test_case
110✔
162
            .recipients
163
            .iter()
164
            .map(|v| {
110✔
165
                let mut key_with_alg = v.key.clone();
110✔
166
                if key_with_alg.alg.is_none() {
110!
167
                    key_with_alg.alg.clone_from(&v.alg);
110✔
168
                }
169
                if let Some(partial_iv) = determine_header_param(
110✔
170
                    test_case.protected.as_ref(),
110✔
171
                    test_case.unprotected.as_ref(),
110✔
172
                    |v| (!v.partial_iv.is_empty()).then_some(&v.partial_iv),
114✔
173
                ) {
174
                    key_with_alg.base_iv = calculate_base_iv(
4✔
175
                        test_case
4✔
176
                            .unsent
177
                            .as_ref()
178
                            .expect("test case has partial IV but no full IV defined"),
179
                        partial_iv,
180
                    );
181
                }
182
                key_with_alg
110✔
183
            })
110✔
184
            .collect();
185
        let aad = test_case.external.as_slice();
110✔
186

187
        let verify_result = self.try_decrypt_with_recipients(backend, &keys, aad);
110✔
188

189
        if case.fail {
110✔
190
            verify_result.expect_err("invalid token was successfully verified");
20✔
191
        } else {
192
            let plaintext = verify_result.expect("unable to verify token");
90✔
193

194
            assert_eq!(case.input.plaintext.as_bytes(), plaintext.as_slice());
90!
195
            // IV is apparently taken from rng_stream field, not header field, but still implicitly
196
            // added to header. ugh...
197
            let mut unprotected = test_case.unprotected.clone().unwrap_or_default();
90✔
198
            let mut protected = test_case.protected.clone().unwrap_or_default();
90✔
199
            unprotected.iv.clone_from(&self.unprotected.iv);
90✔
200
            protected.iv.clone_from(&self.protected.header.iv);
90✔
201
            assert_eq!(&unprotected, &self.unprotected);
90!
202
            assert_eq!(&protected, &self.protected.header);
90!
203
        }
90✔
204
    }
110✔
205
}
206

207
#[rstest]
90✔
208
#[cfg_attr(feature = "openssl", case::openssl(openssl_ctx()))]
9✔
209
#[cfg_attr(
210
    all(feature = "rustcrypto-aes-kw", feature = "rustcrypto-aes-gcm"),
211
    case::rustcrypto(rustcrypto_ctx())
9✔
212
)]
213
fn cose_examples_enveloped_reference_output<
214
    B: EncryptCryptoBackend + KeyDistributionCryptoBackend,
215
>(
216
    #[files("tests/cose_examples/enveloped-tests/env-*.json")] test_path: PathBuf,
217
    #[case] backend: B,
218
) {
219
    perform_cose_reference_output_test::<CoseEncrypt, B>(test_path, backend);
18✔
220
}
221

222
#[rstest]
90✔
223
#[cfg_attr(feature = "openssl", case::openssl(openssl_ctx()))]
9✔
224
#[cfg_attr(
225
    all(feature = "rustcrypto-aes-kw", feature = "rustcrypto-aes-gcm"),
226
    case::rustcrypto(rustcrypto_ctx())
9✔
227
)]
228
fn cose_examples_enveloped_self_signed<B: EncryptCryptoBackend + KeyDistributionCryptoBackend>(
229
    #[files("tests/cose_examples/enveloped-tests/env-*.json")] test_path: PathBuf,
230
    #[case] backend: B,
231
) {
232
    perform_cose_self_signed_test::<CoseEncrypt, B>(test_path, backend);
18✔
233
}
234

235
#[rstest]
60✔
236
#[cfg_attr(feature = "openssl", case::openssl(openssl_ctx()))]
6✔
237
#[cfg_attr(
238
    all(feature = "rustcrypto-aes-kw", feature = "rustcrypto-aes-gcm"),
239
    case::rustcrypto(rustcrypto_ctx())
6✔
240
)]
241
fn cose_examples_aes_wrap_reference_output<
242
    B: EncryptCryptoBackend + KeyDistributionCryptoBackend,
243
>(
244
    #[files("tests/cose_examples/aes-wrap-examples/aes-wrap-*-0[45].json")] test_path: PathBuf, // The other tests use (as of now) unsupported algorithms
245
    #[case] backend: B,
246
) {
247
    perform_cose_reference_output_test::<CoseEncrypt, B>(test_path, backend);
12✔
248
}
249

250
#[rstest]
60✔
251
#[cfg_attr(feature = "openssl", case::openssl(openssl_ctx()))]
6✔
252
#[cfg_attr(
253
    all(feature = "rustcrypto-aes-kw", feature = "rustcrypto-aes-gcm"),
254
    case::rustcrypto(rustcrypto_ctx())
6✔
255
)]
256
fn cose_examples_aes_wrap_self_signed<B: EncryptCryptoBackend + KeyDistributionCryptoBackend>(
257
    #[files("tests/cose_examples/aes-wrap-examples/aes-wrap-*-0[45].json")] test_path: PathBuf,
258
    #[case] backend: B,
259
) {
260
    perform_cose_self_signed_test::<CoseEncrypt, B>(test_path, backend);
12✔
261
}
262

263
#[rstest]
50✔
264
#[cfg_attr(feature = "openssl", case::openssl(openssl_ctx()))]
5✔
265
#[cfg_attr(
266
    all(feature = "rustcrypto-aes-kw", feature = "rustcrypto-aes-gcm"),
267
    case::rustcrypto(rustcrypto_ctx())
5✔
268
)]
269
fn cose_examples_aes_gcm_reference_output<
270
    B: EncryptCryptoBackend + KeyDistributionCryptoBackend,
271
>(
272
    #[files("tests/cose_examples/aes-gcm-examples/aes-gcm-0[0-9].json")] test_path: PathBuf,
273
    #[case] backend: B,
274
) {
275
    perform_cose_reference_output_test::<CoseEncrypt, B>(test_path, backend);
10✔
276
}
277

278
#[rstest]
50✔
279
#[cfg_attr(feature = "openssl", case::openssl(openssl_ctx()))]
5✔
280
#[cfg_attr(
281
    all(feature = "rustcrypto-aes-kw", feature = "rustcrypto-aes-gcm"),
282
    case::rustcrypto(rustcrypto_ctx())
5✔
283
)]
284
fn cose_examples_aes_gcm_self_signed<B: EncryptCryptoBackend + KeyDistributionCryptoBackend>(
285
    #[files("tests/cose_examples/aes-gcm-examples/aes-gcm-0[0-9].json")] test_path: PathBuf,
286
    #[case] backend: B,
287
) {
288
    perform_cose_self_signed_test::<CoseEncrypt, B>(test_path, backend);
10✔
289
}
290

291
#[rstest]
80✔
292
#[cfg_attr(feature = "openssl", case::openssl(openssl_ctx()))]
8✔
293
#[cfg_attr(
294
    all(feature = "rustcrypto-aes-kw", feature = "rustcrypto-aes-ccm"),
295
    case::rustcrypto(rustcrypto_ctx())
8✔
296
)]
297
fn cose_examples_aes_ccm_reference_output<
298
    B: EncryptCryptoBackend + KeyDistributionCryptoBackend,
299
>(
300
    #[files("tests/cose_examples/aes-ccm-examples/aes-ccm-0[0-9].json")] test_path: PathBuf,
301
    #[case] backend: B,
302
) {
303
    perform_cose_reference_output_test::<CoseEncrypt, B>(test_path, backend);
16✔
304
}
305

306
#[rstest]
80✔
307
#[cfg_attr(feature = "openssl", case::openssl(openssl_ctx()))]
8✔
308
#[cfg_attr(
309
    all(feature = "rustcrypto-aes-kw", feature = "rustcrypto-aes-ccm"),
310
    case::rustcrypto(rustcrypto_ctx())
8✔
311
)]
312
fn cose_examples_aes_ccm_self_signed<B: EncryptCryptoBackend + KeyDistributionCryptoBackend>(
313
    #[files("tests/cose_examples/aes-ccm-examples/aes-ccm-0[0-9].json")] test_path: PathBuf,
314
    #[case] backend: B,
315
) {
316
    perform_cose_self_signed_test::<CoseEncrypt, B>(test_path, backend);
16✔
317
}
318

319
#[rstest]
10✔
320
#[cfg_attr(feature = "openssl", case::openssl(openssl_ctx()))]
1✔
321
#[cfg_attr(
322
    all(feature = "rustcrypto-aes-kw", feature = "rustcrypto-aes-gcm"),
323
    case::rustcrypto(rustcrypto_ctx())
1✔
324
)]
325
fn aes_wrap_tests<B: EncryptCryptoBackend + KeyDistributionCryptoBackend>(
326
    #[files("tests/dcaf_cose_examples/aes-kw/*.json")] test_path: PathBuf,
327
    #[case] backend: B,
328
) {
329
    perform_cose_self_signed_test::<CoseEncrypt, B>(test_path, backend);
2✔
330
}
331

332
#[rstest]
10✔
333
#[cfg_attr(feature = "openssl", case::openssl(openssl_ctx()))]
1✔
334
#[cfg_attr(
335
    all(feature = "rustcrypto-aes-kw", feature = "rustcrypto-aes-gcm"),
336
    case::rustcrypto(rustcrypto_ctx())
1✔
337
)]
338
fn aes_gcm_tests<B: EncryptCryptoBackend + KeyDistributionCryptoBackend>(
339
    #[files("tests/dcaf_cose_examples/aes-gcm/*.json")] test_path: PathBuf,
340
    #[case] backend: B,
341
) {
342
    perform_cose_self_signed_test::<CoseEncrypt, B>(test_path, backend);
2✔
343
}
344

345
#[rstest]
10✔
346
#[cfg_attr(feature = "openssl", case::openssl(openssl_ctx()))]
1✔
347
#[cfg_attr(
348
    all(feature = "rustcrypto-aes-kw", feature = "rustcrypto-aes-ccm"),
349
    case::rustcrypto(rustcrypto_ctx())
1✔
350
)]
351
fn aes_ccm_tests<B: EncryptCryptoBackend + KeyDistributionCryptoBackend>(
352
    #[files("tests/dcaf_cose_examples/aes-ccm/*.json")] test_path: PathBuf,
353
    #[case] backend: B,
354
) {
355
    perform_cose_self_signed_test::<CoseEncrypt, B>(test_path, backend);
2✔
356
}
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