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