• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 ////////////////////////////////////////////////////////////////////////////////
16 
17 use super::*;
18 use crate::{
19     cbor::value::Value, util::expect_err, CborSerializable, ContentType, CoseKeyBuilder,
20     CoseRecipientBuilder, HeaderBuilder, TaggedCborSerializable,
21 };
22 use alloc::{
23     string::{String, ToString},
24     vec,
25     vec::Vec,
26 };
27 
28 #[test]
test_cose_mac_decode()29 fn test_cose_mac_decode() {
30     let tests: Vec<(CoseMac, &'static str)> = vec![
31         (
32             CoseMacBuilder::new().build(),
33             concat!(
34                 "85", // 5-tuple
35                 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
36                 "a0", // 0-map
37                 "f6", // null
38                 "40", // 0-bstr
39                 "80", // 0-arr
40             ),
41         ),
42         (
43             CoseMacBuilder::new().payload(vec![]).build(),
44             concat!(
45                 "85", // 5-tuple
46                 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
47                 "a0", // 0-map
48                 "40", // 0-bstr
49                 "40", // 0-bstr
50                 "80", // 0-arr
51             ),
52         ),
53     ];
54     for (i, (mac, mac_data)) in tests.iter().enumerate() {
55         let got = mac.clone().to_vec().unwrap();
56         assert_eq!(*mac_data, hex::encode(&got), "case {}", i);
57 
58         let mut got = CoseMac::from_slice(&got).unwrap();
59         got.protected.original_data = None;
60         assert_eq!(*mac, got);
61     }
62 }
63 
64 #[test]
test_cose_mac_decode_fail()65 fn test_cose_mac_decode_fail() {
66     let tests = vec![
67         (
68             concat!(
69                 "a2",   // 2-map (should be tuple)
70                 "40",   // 0-bstr (special case for empty protected headers, rather than 41a0)
71                 "a0",   // 0-map
72                 "4100", // 1-bstr
73                 "40",   // 0-bstr
74             ),
75             "expected array",
76         ),
77         (
78             concat!(
79                 "84", // 4-tuple (should be 5-tuple)
80                 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
81                 "a0", // 0-map
82                 "40", // 0-bstr
83                 "40", // 0-bstr
84             ),
85             "expected array with 5 items",
86         ),
87         (
88             concat!(
89                 "85", // 5-tuple
90                 "80", // 0-tuple (should be bstr)
91                 "a0", // 0-map
92                 "40", // 0-bstr
93                 "40", // 0-bstr
94                 "80", // 0-arr
95             ),
96             "expected bstr",
97         ),
98         (
99             concat!(
100                 "85", // 5-tuple
101                 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
102                 "40", // 0-bstr (should be map)
103                 "40", // 0-bstr
104                 "40", // 0-bstr
105                 "80", // 0-arr
106             ),
107             "expected map",
108         ),
109         (
110             concat!(
111                 "85", // 5-tuple
112                 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
113                 "a0", // 0-map
114                 "60", // 0-tstr (should be bstr)
115                 "40", // 0-bstr
116                 "80", // 0-arr
117             ),
118             "expected bstr",
119         ),
120         (
121             concat!(
122                 "85", // 5-tuple
123                 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
124                 "a0", // 0-map
125                 "40", // 0-bstr
126                 "60", // 0-tstr
127                 "80", // 0-arr
128             ),
129             "expected bstr",
130         ),
131         (
132             concat!(
133                 "85", // 5-tuple
134                 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
135                 "a0", // 0-map
136                 "40", // 0-bstr
137                 "40", // 0-bstr
138                 "40", // 0-bstr
139             ),
140             "expected array",
141         ),
142     ];
143     for (mac_data, err_msg) in tests.iter() {
144         let data = hex::decode(mac_data).unwrap();
145         let result = CoseMac::from_slice(&data);
146         expect_err(result, err_msg);
147     }
148 }
149 
150 #[test]
test_rfc8152_cose_mac_decode()151 fn test_rfc8152_cose_mac_decode() {
152     // COSE_Mac structures from RFC 8152 section C.5.
153     let tests: Vec<(CoseMac, &'static str)> = vec![
154         (
155             CoseMacBuilder::new()
156                 .protected(
157                     HeaderBuilder::new()
158                         .algorithm(iana::Algorithm::AES_MAC_256_64)
159                         .build(),
160                 )
161                 .payload(b"This is the content.".to_vec())
162                 .tag(hex::decode("9e1226ba1f81b848").unwrap())
163                 .add_recipient(
164                     CoseRecipientBuilder::new()
165                         .unprotected(
166                             HeaderBuilder::new()
167                                 .algorithm(iana::Algorithm::Direct)
168                                 .key_id(b"our-secret".to_vec())
169                                 .build(),
170                         )
171                         .ciphertext(vec![])
172                         .build(),
173                 )
174                 .build(),
175             concat!(
176                 "d861",
177                 "85",
178                 "43",
179                 "a1010f",
180                 "a0",
181                 "54",
182                 "546869732069732074686520636f6e74656e742e",
183                 "48",
184                 "9e1226ba1f81b848",
185                 "81",
186                 "83",
187                 "40",
188                 "a2",
189                 "01",
190                 "25",
191                 "04",
192                 "4a",
193                 "6f75722d736563726574",
194                 "40",
195             ),
196         ),
197         (
198             CoseMacBuilder::new()
199                 .protected(HeaderBuilder::new().algorithm(iana::Algorithm::HMAC_256_256).build())
200                 .payload(b"This is the content.".to_vec())
201                 .tag(hex::decode("81a03448acd3d305376eaa11fb3fe416a955be2cbe7ec96f012c994bc3f16a41").unwrap())
202                 .add_recipient(
203                     CoseRecipientBuilder::new()
204                         .protected(HeaderBuilder::new().algorithm(iana::Algorithm::ECDH_SS_HKDF_256).build())
205                         .unprotected(
206                             HeaderBuilder::new()
207                                 .key_id(b"meriadoc.brandybuck@buckland.example".to_vec())
208                                 .value(
209                                     iana::HeaderAlgorithmParameter::StaticKeyId as i64,
210                                     Value::Bytes(b"peregrin.took@tuckborough.example".to_vec())
211                                 )
212                                 .value(
213                                     iana::HeaderAlgorithmParameter::PartyUNonce as i64,
214                                     Value::Bytes(hex::decode("4d8553e7e74f3c6a3a9dd3ef286a8195cbf8a23d19558ccfec7d34b824f42d92bd06bd2c7f0271f0214e141fb779ae2856abf585a58368b017e7f2a9e5ce4db5").unwrap())
215                                 )
216                                 .build(),
217                         )
218                         .ciphertext(vec![])
219                         .build(),
220                 )
221                 .build(),
222             // Note: contents of maps have been re-ordered from the RFC to canonical ordering.
223             concat!(
224                 "d861",
225                 "85",
226                 "43", "a10105",
227                 "a0",
228                 "54", "546869732069732074686520636f6e74656e742e",
229                 "5820", "81a03448acd3d305376eaa11fb3fe416a955be2cbe7ec96f012c994bc3f16a41",
230                 "81",
231                 "83",
232                 "44", "a101381a",
233                 "a3",
234                 "04",
235                 "5824", "6d65726961646f632e6272616e64796275636b406275636b6c616e642e6578616d706c65",
236                 "22",
237                 "5821", "706572656772696e2e746f6f6b407475636b626f726f7567682e6578616d706c65",
238                 "35",
239                 "5840", "4d8553e7e74f3c6a3a9dd3ef286a8195cbf8a23d19558ccfec7d34b824f42d92bd06bd2c7f0271f0214e141fb779ae2856abf585a58368b017e7f2a9e5ce4db5",
240                 "40",
241             ),
242         ),
243         (
244             CoseMacBuilder::new()
245                 .protected(HeaderBuilder::new().algorithm(iana::Algorithm::AES_MAC_128_64).build())
246                 .payload(b"This is the content.".to_vec())
247                 .tag(hex::decode("36f5afaf0bab5d43").unwrap())
248                 .add_recipient(
249                     CoseRecipientBuilder::new()
250                         .unprotected(
251                             HeaderBuilder::new()
252                                 .algorithm(iana::Algorithm::A256KW)
253                                 .key_id(b"018c0ae5-4d9b-471b-bfd6-eef314bc7037".to_vec())
254                                 .build(),
255                         )
256                         .ciphertext(hex::decode("711ab0dc2fc4585dce27effa6781c8093eba906f227b6eb0").unwrap())
257                         .build(),
258                 )
259                 .build(),
260             concat!(
261                 "d861",
262                 "85",
263                 "43", "a1010e",
264                 "a0",
265                 "54", "546869732069732074686520636f6e74656e742e",
266                 "48", "36f5afaf0bab5d43",
267                 "81",
268                 "83",
269                 "40",
270                 "a2",
271                 "01",
272                 "24",
273                 "04",
274                 "5824", "30313863306165352d346439622d343731622d626664362d656566333134626337303337",
275                 "5818", "711ab0dc2fc4585dce27effa6781c8093eba906f227b6eb0",
276             ),
277         ),
278         (
279             CoseMacBuilder::new()
280                 .protected(HeaderBuilder::new().algorithm(iana::Algorithm::HMAC_256_256).build())
281                 .payload(b"This is the content.".to_vec())
282                 .tag(hex::decode("bf48235e809b5c42e995f2b7d5fa13620e7ed834e337f6aa43df161e49e9323e").unwrap())
283                 .add_recipient(
284                     CoseRecipientBuilder::new()
285                         .protected(HeaderBuilder::new().algorithm(iana::Algorithm::ECDH_ES_A128KW).build())
286                         .unprotected(
287                             HeaderBuilder::new()
288                                 .value(iana::HeaderAlgorithmParameter::EphemeralKey as i64,
289                                        CoseKeyBuilder::new_ec2_pub_key_y_sign(iana::EllipticCurve::P_521,
290                                                                               hex::decode("0043b12669acac3fd27898ffba0bcd2e6c366d53bc4db71f909a759304acfb5e18cdc7ba0b13ff8c7636271a6924b1ac63c02688075b55ef2d613574e7dc242f79c3").unwrap(),
291                                                                               true)
292                                        .build().to_cbor_value().unwrap())
293                                 .key_id(b"bilbo.baggins@hobbiton.example".to_vec())
294                                 .build(),
295                         )
296                         .ciphertext(hex::decode("339bc4f79984cdc6b3e6ce5f315a4c7d2b0ac466fcea69e8c07dfbca5bb1f661bc5f8e0df9e3eff5").unwrap())
297                         .build(),
298                 )
299                 .add_recipient(
300                     CoseRecipientBuilder::new()
301                         .unprotected(
302                             HeaderBuilder::new()
303                                 .algorithm(iana::Algorithm::A256KW)
304                                 .key_id(b"018c0ae5-4d9b-471b-bfd6-eef314bc7037".to_vec())
305                                 .build(),
306                         )
307                         .ciphertext(hex::decode("0b2c7cfce04e98276342d6476a7723c090dfdd15f9a518e7736549e998370695e6d6a83b4ae507bb").unwrap())
308                         .build(),
309                 )
310                 .build(),
311             // Note: contents of maps have been re-ordered from the RFC to canonical ordering.
312             concat!(
313                 "d861",
314                 "85",
315                 "43", "a10105",
316                 "a0",
317                 "54", "546869732069732074686520636f6e74656e742e",
318                 "5820", "bf48235e809b5c42e995f2b7d5fa13620e7ed834e337f6aa43df161e49e9323e",
319                 "82",
320                 "83",
321                 "44", "a101381c",
322                 "a2",
323                 "04",
324                 "581e", "62696c626f2e62616767696e7340686f626269746f6e2e6578616d706c65",
325                 "20",
326                 "a4",
327                 "01",
328                 "02",
329                 "20",
330                 "03",
331                 "21",
332                 "5842", "0043b12669acac3fd27898ffba0bcd2e6c366d53bc4db71f909a759304acfb5e18cdc7ba0b13ff8c7636271a6924b1ac63c02688075b55ef2d613574e7dc242f79c3",
333                 "22",
334                 "f5",
335                 "5828", "339bc4f79984cdc6b3e6ce5f315a4c7d2b0ac466fcea69e8c07dfbca5bb1f661bc5f8e0df9e3eff5",
336                 "83",
337                 "40",
338                 "a2",
339                 "01",
340                 "24",
341                 "04",
342                 "5824", "30313863306165352d346439622d343731622d626664362d656566333134626337303337",
343                 "5828", "0b2c7cfce04e98276342d6476a7723c090dfdd15f9a518e7736549e998370695e6d6a83b4ae507bb",
344             ),
345         ),
346     ];
347 
348     for (i, (mac, mac_data)) in tests.iter().enumerate() {
349         let got = mac.clone().to_tagged_vec().unwrap();
350         assert_eq!(*mac_data, hex::encode(&got), "case {}", i);
351 
352         let mut got = CoseMac::from_tagged_slice(&got).unwrap();
353         got.protected.original_data = None;
354         for mut recip in &mut got.recipients {
355             recip.protected.original_data = None;
356         }
357         for mut sig in &mut got.unprotected.counter_signatures {
358             sig.protected.original_data = None;
359         }
360         assert_eq!(*mac, got);
361     }
362 }
363 
364 #[test]
test_cose_mac0_decode()365 fn test_cose_mac0_decode() {
366     let tests: Vec<(CoseMac0, &'static str)> = vec![
367         (
368             CoseMac0Builder::new().build(),
369             concat!(
370                 "84", // 4-tuple
371                 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
372                 "a0", // 0-map
373                 "f6", // null
374                 "40", // 0-bstr
375             ),
376         ),
377         (
378             CoseMac0Builder::new().payload(vec![]).build(),
379             concat!(
380                 "84", // 4-tuple
381                 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
382                 "a0", // 0-map
383                 "40", // 0-bstr
384                 "40", // 0-bstr
385             ),
386         ),
387     ];
388     for (i, (mac, mac_data)) in tests.iter().enumerate() {
389         let got = mac.clone().to_vec().unwrap();
390         assert_eq!(*mac_data, hex::encode(&got), "case {}", i);
391 
392         let mut got = CoseMac0::from_slice(&got).unwrap();
393         got.protected.original_data = None;
394         assert_eq!(*mac, got);
395     }
396 }
397 #[test]
test_cose_mac0_decode_fail()398 fn test_cose_mac0_decode_fail() {
399     let tests = vec![
400         (
401             concat!(
402                 "a2",   // 2-map (should be tuple)
403                 "40",   // 0-bstr (special case for empty protected headers, rather than 41a0)
404                 "a0",   // 0-map
405                 "4100", // 0-bstr
406                 "40",   // 0-bstr
407             ),
408             "expected array",
409         ),
410         (
411             concat!(
412                 "83", // 3-tuple (should be 4-tuple)
413                 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
414                 "a0", // 0-map
415                 "40", // 0-bstr
416             ),
417             "expected array with 4 items",
418         ),
419         (
420             concat!(
421                 "84", // 4-tuple
422                 "80", // 0-tuple (should be bstr)
423                 "a0", // 0-map
424                 "40", // 0-bstr
425                 "40", // 0-bstr
426             ),
427             "expected bstr",
428         ),
429         (
430             concat!(
431                 "84", // 4-tuple
432                 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
433                 "40", // 0-bstr (should be map)
434                 "40", // 0-bstr
435                 "40", // 0-bstr
436             ),
437             "expected map",
438         ),
439         (
440             concat!(
441                 "84", // 4-tuple
442                 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
443                 "a0", // 0-map
444                 "60", // 0-tstr (should be bstr)
445                 "40", // 0-bstr
446             ),
447             "expected bstr",
448         ),
449         (
450             concat!(
451                 "84", // 4-tuple
452                 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
453                 "a0", // 0-map
454                 "40", // 0-bstr
455                 "60", // 0-tstr
456             ),
457             "expected bstr",
458         ),
459     ];
460     for (mac_data, err_msg) in tests.iter() {
461         let data = hex::decode(mac_data).unwrap();
462         let result = CoseMac0::from_slice(&data);
463         expect_err(result, err_msg);
464     }
465 }
466 
467 #[test]
test_rfc8152_cose_mac0_decode()468 fn test_rfc8152_cose_mac0_decode() {
469     // COSE_Mac0 structures from RFC 8152 section C.5.
470     let tests: Vec<(CoseMac0, &'static str)> = vec![(
471         CoseMac0Builder::new()
472             .protected(
473                 HeaderBuilder::new()
474                     .algorithm(iana::Algorithm::AES_MAC_256_64)
475                     .build(),
476             )
477             .payload(b"This is the content.".to_vec())
478             .tag(hex::decode("726043745027214f").unwrap())
479             .build(),
480         concat!(
481             "d1",
482             "84",
483             "43",
484             "a1010f",
485             "a0",
486             "54",
487             "546869732069732074686520636f6e74656e742e",
488             "48",
489             "726043745027214f",
490         ),
491     )];
492 
493     for (i, (mac, mac_data)) in tests.iter().enumerate() {
494         let got = mac.clone().to_tagged_vec().unwrap();
495         assert_eq!(*mac_data, hex::encode(&got), "case {}", i);
496 
497         let mut got = CoseMac0::from_tagged_slice(&got).unwrap();
498         got.protected.original_data = None;
499         assert_eq!(*mac, got);
500     }
501 }
502 
503 struct FakeMac {}
504 impl FakeMac {
compute(&self, data: &[u8]) -> Vec<u8>505     fn compute(&self, data: &[u8]) -> Vec<u8> {
506         let mut val = 0u8;
507         for b in data {
508             val ^= b;
509         }
510         vec![val]
511     }
verify(&self, tag: &[u8], data: &[u8]) -> Result<(), String>512     fn verify(&self, tag: &[u8], data: &[u8]) -> Result<(), String> {
513         if self.compute(data) == tag {
514             Ok(())
515         } else {
516             Err("mismatch".to_owned())
517         }
518     }
try_compute(&self, data: &[u8]) -> Result<Vec<u8>, String>519     fn try_compute(&self, data: &[u8]) -> Result<Vec<u8>, String> {
520         Ok(self.compute(data))
521     }
fail_compute(&self, _data: &[u8]) -> Result<Vec<u8>, String>522     fn fail_compute(&self, _data: &[u8]) -> Result<Vec<u8>, String> {
523         Err("failed".to_string())
524     }
525 }
526 
527 #[test]
test_cose_mac_roundtrip()528 fn test_cose_mac_roundtrip() {
529     let tagger = FakeMac {};
530     let external_aad = b"This is the external aad";
531     let mut mac = CoseMacBuilder::new()
532         .protected(HeaderBuilder::new().key_id(b"11".to_vec()).build())
533         .payload(b"This is the data".to_vec())
534         .create_tag(external_aad, |data| tagger.compute(data))
535         .build();
536 
537     assert!(mac
538         .verify_tag(external_aad, |tag, data| tagger.verify(tag, data))
539         .is_ok());
540 
541     // Changing an unprotected header leaves a correct tag.
542     mac.unprotected.content_type = Some(ContentType::Text("text/plain".to_owned()));
543     assert!(mac
544         .verify_tag(external_aad, |tag, data| tagger.verify(tag, data))
545         .is_ok());
546 
547     // Providing a different `aad` means the tag won't validate
548     assert!(mac
549         .verify_tag(b"not aad", |tag, data| tagger.verify(tag, data))
550         .is_err());
551 
552     // Changing a protected header invalidates the tag.
553     mac.protected = ProtectedHeader::default();
554     assert!(mac
555         .verify_tag(external_aad, |tag, data| tagger.verify(tag, data))
556         .is_err());
557 }
558 
559 #[test]
test_cose_mac_noncanonical()560 fn test_cose_mac_noncanonical() {
561     let tagger = FakeMac {};
562     let external_aad = b"aad";
563 
564     // Build an empty protected header from a non-canonical input of 41a0 rather than 40.
565     let protected = ProtectedHeader::from_cbor_bstr(Value::Bytes(vec![0xa0])).unwrap();
566     assert_eq!(protected.header, Header::default());
567     assert_eq!(protected.original_data, Some(vec![0xa0]));
568 
569     let mut mac = CoseMac {
570         protected: protected.clone(),
571         payload: Some(b"data".to_vec()),
572         ..Default::default()
573     };
574     let tbm = mac.tbm(external_aad);
575     mac.tag = tagger.compute(&tbm);
576 
577     // Checking the MAC should still succeed, because the `ProtectedHeader`
578     // includes the wire data and uses it for building the input.
579     assert!(mac
580         .verify_tag(external_aad, |tag, data| tagger.verify(tag, data))
581         .is_ok());
582 
583     // However, if we attempt to build the same decryption inputs by hand (thus not including the
584     // non-canonical wire data)...
585     let recreated_mac = CoseMacBuilder::new()
586         .protected(protected.header)
587         .payload(b"data".to_vec())
588         .tag(mac.tag)
589         .build();
590 
591     // ...then the transplanted tag will not verify, because the re-building of the
592     // inputs will use the canonical encoding of the protected header, which is not what was
593     // originally used for the input.
594     assert!(recreated_mac
595         .verify_tag(external_aad, |tag, data| tagger.verify(tag, data))
596         .is_err());
597 }
598 
599 #[test]
test_cose_mac_tag_result()600 fn test_cose_mac_tag_result() {
601     let tagger = FakeMac {};
602     let external_aad = b"This is the external aad";
603     let mut _mac = CoseMacBuilder::new()
604         .protected(HeaderBuilder::new().key_id(b"11".to_vec()).build())
605         .payload(b"This is the data".to_vec())
606         .try_create_tag(external_aad, |data| tagger.try_compute(data))
607         .unwrap()
608         .build();
609 
610     // Cope with MAC creation failure.
611     let result = CoseMacBuilder::new()
612         .protected(HeaderBuilder::new().key_id(b"11".to_vec()).build())
613         .payload(b"This is the data".to_vec())
614         .try_create_tag(external_aad, |data| tagger.fail_compute(data));
615     expect_err(result, "failed");
616 }
617 
618 #[test]
619 #[should_panic]
test_cose_mac_create_tag_no_payload()620 fn test_cose_mac_create_tag_no_payload() {
621     let tagger = FakeMac {};
622     let external_aad = b"This is the external aad";
623     let _mac = CoseMacBuilder::new()
624         .protected(HeaderBuilder::new().key_id(b"11".to_vec()).build())
625         // Creating a tag before a payload has been set will panic.
626         .create_tag(external_aad, |data| tagger.compute(data))
627         .build();
628 }
629 
630 #[test]
631 #[should_panic]
test_cose_mac_verify_tag_no_payload()632 fn test_cose_mac_verify_tag_no_payload() {
633     let tagger = FakeMac {};
634     let external_aad = b"This is the external aad";
635     let mut mac = CoseMacBuilder::new()
636         .protected(HeaderBuilder::new().key_id(b"11".to_vec()).build())
637         .payload(b"This is the data".to_vec())
638         .create_tag(external_aad, |data| tagger.compute(data))
639         .build();
640 
641     mac.payload = None;
642     // Trying to verify with no payload available panics.
643     let _result = mac.verify_tag(external_aad, |tag, data| tagger.verify(tag, data));
644 }
645 
646 #[test]
test_cose_mac0_roundtrip()647 fn test_cose_mac0_roundtrip() {
648     let tagger = FakeMac {};
649     let external_aad = b"This is the external aad";
650     let mut mac = CoseMac0Builder::new()
651         .protected(HeaderBuilder::new().key_id(b"11".to_vec()).build())
652         .payload(b"This is the data".to_vec())
653         .create_tag(external_aad, |data| tagger.compute(data))
654         .build();
655 
656     assert!(mac
657         .verify_tag(external_aad, |tag, data| tagger.verify(tag, data))
658         .is_ok());
659 
660     // Changing an unprotected header leaves a correct tag.
661     mac.unprotected.content_type = Some(ContentType::Text("text/plain".to_owned()));
662     assert!(mac
663         .verify_tag(external_aad, |tag, data| tagger.verify(tag, data))
664         .is_ok());
665 
666     // Providing a different `aad` means the tag won't validate
667     assert!(mac
668         .verify_tag(b"not aad", |tag, data| tagger.verify(tag, data))
669         .is_err());
670 
671     // Changing a protected header invalidates the tag.
672     mac.protected = ProtectedHeader::default();
673     assert!(mac
674         .verify_tag(external_aad, |tag, data| tagger.verify(tag, data))
675         .is_err());
676 }
677 
678 #[test]
test_cose_mac0_noncanonical()679 fn test_cose_mac0_noncanonical() {
680     let tagger = FakeMac {};
681     let external_aad = b"aad";
682 
683     // Build an empty protected header from a non-canonical input of 41a0 rather than 40.
684     let protected = ProtectedHeader::from_cbor_bstr(Value::Bytes(vec![0xa0])).unwrap();
685     assert_eq!(protected.header, Header::default());
686     assert_eq!(protected.original_data, Some(vec![0xa0]));
687 
688     let mut mac = CoseMac0 {
689         protected: protected.clone(),
690         payload: Some(b"data".to_vec()),
691         ..Default::default()
692     };
693     let tbm = mac.tbm(external_aad);
694     mac.tag = tagger.compute(&tbm);
695 
696     // Checking the MAC should still succeed, because the `ProtectedHeader`
697     // includes the wire data and uses it for building the input.
698     assert!(mac
699         .verify_tag(external_aad, |tag, data| tagger.verify(tag, data))
700         .is_ok());
701 
702     // However, if we attempt to build the same decryption inputs by hand (thus not including the
703     // non-canonical wire data)...
704     let recreated_mac = CoseMac0Builder::new()
705         .protected(protected.header)
706         .payload(b"data".to_vec())
707         .tag(mac.tag)
708         .build();
709 
710     // ...then the transplanted tag will not verify, because the re-building of the
711     // inputs will use the canonical encoding of the protected header, which is not what was
712     // originally used for the input.
713     assert!(recreated_mac
714         .verify_tag(external_aad, |tag, data| tagger.verify(tag, data))
715         .is_err());
716 }
717 
718 #[test]
test_cose_mac0_tag_result()719 fn test_cose_mac0_tag_result() {
720     let tagger = FakeMac {};
721     let external_aad = b"This is the external aad";
722     let mut _mac = CoseMac0Builder::new()
723         .protected(HeaderBuilder::new().key_id(b"11".to_vec()).build())
724         .payload(b"This is the data".to_vec())
725         .try_create_tag(external_aad, |data| tagger.try_compute(data))
726         .unwrap()
727         .build();
728 
729     // Cope with MAC creation failure.
730     let result = CoseMac0Builder::new()
731         .protected(HeaderBuilder::new().key_id(b"11".to_vec()).build())
732         .payload(b"This is the data".to_vec())
733         .try_create_tag(external_aad, |data| tagger.fail_compute(data));
734     expect_err(result, "failed");
735 }
736 
737 #[test]
738 #[should_panic]
test_cose_mac0_create_tag_no_payload()739 fn test_cose_mac0_create_tag_no_payload() {
740     let tagger = FakeMac {};
741     let external_aad = b"This is the external aad";
742     let _mac = CoseMac0Builder::new()
743         .protected(HeaderBuilder::new().key_id(b"11".to_vec()).build())
744         // Creating a tag before a payload has been set will panic.
745         .create_tag(external_aad, |data| tagger.compute(data))
746         .build();
747 }
748 
749 #[test]
750 #[should_panic]
test_cose_mac0_verify_tag_no_payload()751 fn test_cose_mac0_verify_tag_no_payload() {
752     let tagger = FakeMac {};
753     let external_aad = b"This is the external aad";
754     let mut mac = CoseMac0Builder::new()
755         .protected(HeaderBuilder::new().key_id(b"11".to_vec()).build())
756         .payload(b"This is the data".to_vec())
757         .create_tag(external_aad, |data| tagger.compute(data))
758         .build();
759 
760     mac.payload = None;
761     // Trying to verify with no payload available panics.
762     let _result = mac.verify_tag(external_aad, |tag, data| tagger.verify(tag, data));
763 }
764