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, iana, util::expect_err, Algorithm, CborSerializable, ContentType,
20 HeaderBuilder, RegisteredLabel, TaggedCborSerializable,
21 };
22 use alloc::{
23 format,
24 string::{String, ToString},
25 vec,
26 };
27
28 #[test]
test_cose_signature_encode()29 fn test_cose_signature_encode() {
30 let tests = vec![
31 (
32 CoseSignature::default(),
33 concat!(
34 "83", // 3-tuple
35 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
36 "a0", // 0-map
37 "40", // 0-bstr
38 ),
39 ),
40 (
41 CoseSignature {
42 signature: vec![1, 2, 3],
43 ..Default::default()
44 },
45 concat!(
46 "83", // 3-tuple
47 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
48 "a0", // 0-map
49 "43010203", // 3-bstr
50 ),
51 ),
52 (
53 CoseSignature {
54 protected: ProtectedHeader {
55 original_data: None,
56 header: Header {
57 alg: Some(Algorithm::Assigned(iana::Algorithm::A128GCM)),
58 key_id: vec![1, 2, 3],
59 partial_iv: vec![1, 2, 3],
60 ..Default::default()
61 },
62 },
63 signature: vec![1, 2, 3],
64 ..Default::default()
65 },
66 concat!(
67 "83", // 3-tuple
68 "4d", // 13-bstr
69 "a3", // 3-map
70 "01", "01", // 1 (alg) => A128GCM
71 "04", "43", "010203", // 4 (kid) => 3-bstr
72 "06", "43", "010203", // 6 (partial-iv) => 3-bstr
73 "a0", // 0-map
74 "43010203", // 3-bstr
75 ),
76 ),
77 (
78 CoseSignature {
79 unprotected: Header {
80 alg: Some(Algorithm::Assigned(iana::Algorithm::A128GCM)),
81 key_id: vec![1, 2, 3],
82 partial_iv: vec![1, 2, 3],
83 ..Default::default()
84 },
85 signature: vec![1, 2, 3],
86 ..Default::default()
87 },
88 concat!(
89 "83", // 3-tuple
90 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
91 "a3", // 3-map
92 "01", "01", // 1 (alg) => A128GCM
93 "04", "43", "010203", // 4 (kid) => 3-bstr
94 "06", "43", "010203", // 6 (partial-iv) => 3-bstr
95 "43010203", // 3-bstr
96 ),
97 ),
98 ];
99 for (i, (sig, sig_data)) in tests.iter().enumerate() {
100 let got = sig.clone().to_vec().unwrap();
101 assert_eq!(*sig_data, hex::encode(&got), "case {}", i);
102
103 let mut got = CoseSignature::from_slice(&got).unwrap();
104 got.protected.original_data = None;
105 assert_eq!(*sig, got);
106 }
107 }
108
109 #[test]
test_cose_signature_decode_noncanonical()110 fn test_cose_signature_decode_noncanonical() {
111 // RFC8152 section 3: "Recipients MUST accept both a zero-length binary value and a zero-length
112 // map encoded in the binary value."
113 let sig_data = hex::decode(concat!(
114 "83", // 3-tuple
115 "41a0", // 1-bstr holding 0-map (not a 0-bstr)
116 "a0", // 0-map
117 "40", // 0-bstr
118 ))
119 .unwrap();
120 let sig = CoseSignature::default();
121 let mut got = CoseSignature::from_slice(&sig_data).unwrap();
122 got.protected.original_data = None;
123 assert_eq!(sig, got);
124 }
125
126 #[test]
test_cose_signature_decode_fail()127 fn test_cose_signature_decode_fail() {
128 let tests = vec![
129 (
130 concat!(
131 "a2", // 2-map
132 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
133 "a0", // 0-map
134 "43010203", // 3-bstr
135 "40", // 0-bstr
136 ),
137 "expected array",
138 ),
139 (
140 concat!(
141 "83", // 3-tuple
142 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
143 "40", // 0-bstr (invalid: should be map)
144 "43010203", // 3-bstr
145 ),
146 "expected map",
147 ),
148 (
149 concat!(
150 "83", // 3-tuple
151 "a0", // 0-map (invalid: should be bstr)
152 "a0", // 0-map
153 "43010203", // 3-bstr
154 ),
155 "expected bstr",
156 ),
157 (
158 concat!(
159 "84", // 4-tuple
160 "40", // 0-bstr
161 "a0", // 0-map
162 "43010203", // 3-bstr
163 "43010203", // 3-bstr
164 ),
165 "expected array with 3 items",
166 ),
167 (
168 concat!(
169 "82", // 4-tuple
170 "40", // 0-bstr
171 "a0", // 0-map
172 ),
173 "expected array with 3 items",
174 ),
175 (
176 concat!(
177 "83", // 3-tuple
178 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
179 "40", // 0-bstr (invalid: should be map)
180 "63616263", // 3-tstr
181 ),
182 "expected bstr",
183 ),
184 (
185 concat!(
186 "83", // 3-tuple
187 "45", // 0-bstr (special case for empty protected headers, rather than 41a0)
188 "a1", // 1-map
189 "03", "81", "4101", // 3 (content-type) => [bstr] (invalid value type)
190 "a0", // 0-map
191 "43616263", // 0-bstr
192 ),
193 "expected int/tstr",
194 ),
195 ];
196 for (sig_data, err_msg) in tests.iter() {
197 let data = hex::decode(sig_data).unwrap();
198 let result = CoseSignature::from_slice(&data);
199 expect_err(result, err_msg);
200 }
201 }
202
203 #[test]
test_cose_signature_builder()204 fn test_cose_signature_builder() {
205 let tests = vec![
206 (
207 CoseSignatureBuilder::new().build(),
208 CoseSignature::default(),
209 ),
210 (
211 CoseSignatureBuilder::new().signature(vec![1, 2, 3]).build(),
212 CoseSignature {
213 signature: vec![1, 2, 3],
214 ..Default::default()
215 },
216 ),
217 (
218 CoseSignatureBuilder::new()
219 .signature(vec![1, 2, 3])
220 .protected(
221 HeaderBuilder::new()
222 .algorithm(iana::Algorithm::A128GCM)
223 .key_id(vec![1, 2, 3])
224 .iv(vec![1, 2, 3])
225 .build(),
226 )
227 .build(),
228 CoseSignature {
229 protected: ProtectedHeader {
230 original_data: None,
231 header: Header {
232 alg: Some(Algorithm::Assigned(iana::Algorithm::A128GCM)),
233 key_id: vec![1, 2, 3],
234 iv: vec![1, 2, 3],
235 ..Default::default()
236 },
237 },
238 signature: vec![1, 2, 3],
239 ..Default::default()
240 },
241 ),
242 (
243 CoseSignatureBuilder::new()
244 .signature(vec![1, 2, 3])
245 .unprotected(
246 HeaderBuilder::new()
247 .algorithm(iana::Algorithm::A128GCM)
248 .key_id(vec![1, 2, 3])
249 .partial_iv(vec![1, 2, 3])
250 .build(),
251 )
252 .build(),
253 CoseSignature {
254 unprotected: Header {
255 alg: Some(Algorithm::Assigned(iana::Algorithm::A128GCM)),
256 key_id: vec![1, 2, 3],
257 partial_iv: vec![1, 2, 3],
258 ..Default::default()
259 },
260 signature: vec![1, 2, 3],
261 ..Default::default()
262 },
263 ),
264 ];
265 for (got, want) in tests {
266 assert_eq!(got, want);
267 }
268 }
269
270 #[test]
test_cose_sign_encode()271 fn test_cose_sign_encode() {
272 let tests = vec![
273 (
274 CoseSign::default(),
275 concat!(
276 "84", // 4-tuple
277 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
278 "a0", // 0-map
279 "f6", // null
280 "80", // 0-tuple
281 ),
282 ),
283 (
284 CoseSignBuilder::new()
285 .add_signature(CoseSignature::default())
286 .build(),
287 concat!(
288 "84", // 4-tuple
289 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
290 "a0", // 0-map
291 "f6", // null
292 "81", // 1-tuple
293 "83", "40a040", // 3-tuple
294 ),
295 ),
296 (
297 CoseSignBuilder::new()
298 .protected(
299 HeaderBuilder::new()
300 .algorithm(iana::Algorithm::A128GCM)
301 .key_id(vec![1, 2, 3])
302 .build(),
303 )
304 .payload(vec![4, 5, 6])
305 .add_signature(
306 CoseSignatureBuilder::new()
307 .signature(vec![1, 2, 3])
308 .protected(
309 HeaderBuilder::new()
310 .algorithm(iana::Algorithm::A128GCM)
311 .key_id(vec![1, 2, 3])
312 .iv(vec![1, 2, 3])
313 .build(),
314 )
315 .build(),
316 )
317 .build(),
318 concat!(
319 "84", // 4-tuple
320 "48", // 8-bstr (protected)
321 "a2", // 2-map
322 "01", "01", // 1 (alg) => A128GCM
323 "04", "43", "010203", // 4 (kid) => 3-bstr
324 "a0", // 0-map (unprotected)
325 "43", "040506", // 3-bstr (payload)
326 "81", // 1-tuple (signatures)
327 "83", // 3-tuple (COSE_Signature)
328 "4d", // 14-bstr (protected)
329 "a3", // 3-map
330 "01", "01", // 1 (alg) => A128GCM
331 "04", "43", "010203", // 4 (kid) => 3-bstr
332 "05", "43", "010203", // 5 (iv) => 3-bstr
333 "a0", // 0-map (unprotected)
334 "43", "010203", // 0-bstr (signature)
335 ),
336 ),
337 (
338 CoseSignBuilder::new()
339 .unprotected(
340 HeaderBuilder::new()
341 .algorithm(iana::Algorithm::A128GCM)
342 .key_id(vec![1, 2, 3])
343 .build(),
344 )
345 .payload(vec![4, 5, 6])
346 .add_signature(
347 CoseSignatureBuilder::new()
348 .signature(vec![1, 2, 3])
349 .protected(
350 HeaderBuilder::new()
351 .algorithm(iana::Algorithm::A128GCM)
352 .key_id(vec![1, 2, 3])
353 .iv(vec![1, 2, 3])
354 .build(),
355 )
356 .build(),
357 )
358 .build(),
359 concat!(
360 "84", // 4-tuple
361 "40", // 0-bstr (protected)
362 "a2", // 2-map (unprotected)
363 "01", "01", // 1 (alg) => A128GCM
364 "04", "43", "010203", // 4 (kid) => 3-bstr
365 "43", "040506", // 3-bstr (payload)
366 "81", // 1-tuple (signatures)
367 "83", // 3-tuple (COSE_Signature)
368 "4d", // 14-bstr (protected)
369 "a3", // 3-map
370 "01", "01", // 1 (alg) => A128GCM
371 "04", "43", "010203", // 4 (kid) => 3-bstr
372 "05", "43", "010203", // 5 (iv) => 3-bstr
373 "a0", // 0-map (unprotected)
374 "43", "010203", // 0-bstr (signature)
375 ),
376 ),
377 ];
378 for (i, (sign, sign_data)) in tests.iter().enumerate() {
379 let got = sign.clone().to_vec().unwrap();
380 assert_eq!(*sign_data, hex::encode(&got), "case {}", i);
381
382 let mut got = CoseSign::from_slice(&got).unwrap();
383 got.protected.original_data = None;
384 for sig in &mut got.signatures {
385 sig.protected.original_data = None;
386 }
387 assert_eq!(*sign, got);
388
389 // Repeat with tagged variant.
390 let got = sign.clone().to_tagged_vec().unwrap();
391 let tagged_sign_data = format!("d862{}", sign_data);
392 assert_eq!(tagged_sign_data, hex::encode(&got), "tagged case {}", i);
393
394 let mut got = CoseSign::from_tagged_slice(&got).unwrap();
395 got.protected.original_data = None;
396 for sig in &mut got.signatures {
397 sig.protected.original_data = None;
398 }
399 assert_eq!(*sign, got);
400 }
401 }
402
403 #[test]
test_cose_sign_decode_fail()404 fn test_cose_sign_decode_fail() {
405 let tests = vec![
406 (
407 concat!(
408 "a2", // 2-map
409 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
410 "40", // 0-bstr (invalid: should be map)
411 "43010203", // 3-bstr
412 "80", // 0-tuple
413 ),
414 "expected array",
415 ),
416 (
417 concat!(
418 "84", // 4-tuple
419 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
420 "40", // 0-bstr (invalid: should be map)
421 "43010203", // 3-bstr
422 "80", // 0-tuple
423 ),
424 "expected map",
425 ),
426 (
427 concat!(
428 "84", // 4-tuple
429 "a0", // 0-map (invalid: should be bstr)
430 "a0", // 0-map
431 "43010203", // 3-bstr
432 "80", // 0-tuple
433 ),
434 "expected bstr",
435 ),
436 (
437 concat!(
438 "85", // 5-tuple
439 "40", // 0-bstr
440 "a0", // 0-map
441 "43010203", // 3-bstr
442 "80", // 0-tuple
443 "43010203", // 3-bstr
444 ),
445 "expected array with 4 items",
446 ),
447 (
448 concat!(
449 "83", // 3-tuple
450 "40", // 0-bstr
451 "a0", // 0-map
452 "43010203", // 3-bstr
453 ),
454 "expected array with 4 items",
455 ),
456 (
457 concat!(
458 "84", // 4-tuple
459 "40", // 0-bstr
460 "a0", // 0-map
461 "43010203", // 3-bstr
462 "43010203", // 3-bstr
463 ),
464 "expected array",
465 ),
466 (
467 concat!(
468 "84", // 4-tuple
469 "40", // 0-bstr
470 "a0", // 0-map
471 "63616263", // 3-tstr
472 "80", // 0-tuple
473 ),
474 "expected bstr",
475 ),
476 (
477 concat!(
478 "84", // 4-tuple
479 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
480 "a0", // 0-map
481 "43010203", // 3-bstr
482 "81", // 1-tuple
483 "83", // 3-tuple
484 "a0", // 0-map (invalid: should be bstr)
485 "a0", // 0-map
486 "43010203", // 3-bstr
487 ),
488 "expected map for COSE_Signature",
489 ),
490 (
491 concat!(
492 "84", // 4-tuple
493 "45", // 5-bstr
494 "a1", // 1-map
495 "03", "81", "4101", // 3 (content-type) => [bstr] (invalid value type)
496 "a0", // 0-map
497 "43616263", // 3-bstr
498 "80", // 0-tuple
499 ),
500 "expected int/tstr",
501 ),
502 ];
503 for (sign_data, err_msg) in tests.iter() {
504 let data = hex::decode(sign_data).unwrap();
505 let result = CoseSign::from_slice(&data);
506 expect_err(result, err_msg);
507 }
508 }
509
510 #[test]
test_cose_sign_tagged_decode_fail()511 fn test_cose_sign_tagged_decode_fail() {
512 let tests = vec![
513 (
514 concat!(
515 "d862", // tag(98)
516 "84", // 4-tuple
517 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
518 "40", // 0-bstr (invalid: should be map)
519 "43010203", // 3-bstr
520 "80", // 0-tuple
521 ),
522 "expected map",
523 ),
524 (
525 concat!(
526 "d862", // tag(98)
527 "84", // 4-tuple
528 "a0", // 0-map (invalid: should be bstr)
529 "a0", // 0-map
530 "43010203", // 3-bstr
531 "80", // 0-tuple
532 ),
533 "expected bstr",
534 ),
535 (
536 concat!(
537 "d862", // tag(98)
538 "85", // 5-tuple
539 "40", // 0-bstr
540 "a0", // 0-map
541 "43010203", // 3-bstr
542 "80", // 0-tuple
543 "43010203", // 3-bstr
544 ),
545 "expected array with 4 items",
546 ),
547 (
548 concat!(
549 "d862", // tag(98)
550 "83", // 3-tuple
551 "40", // 0-bstr
552 "a0", // 0-map
553 "43010203", // 3-bstr
554 ),
555 "expected array with 4 items",
556 ),
557 (
558 concat!(
559 "1862", // int instead of tag
560 ),
561 "expected tag",
562 ),
563 (
564 concat!(
565 "d861", // tag(97) : wrong tag
566 "84", // 4-tuple
567 "40", // 0-bstr
568 "a0", // 0-map
569 "43010203", // 3-bstr
570 "80", // 0-tuple
571 ),
572 "expected other tag",
573 ),
574 (
575 concat!(
576 "1861", // int (97) : not a tag
577 "84", // 4-tuple
578 "40", // 0-bstr
579 "a0", // 0-map
580 "43010203", // 3-bstr
581 "80", // 0-tuple
582 ),
583 "extraneous data",
584 ),
585 (
586 concat!(
587 "18", // incomplete int
588 ),
589 "decode CBOR failure: Io(EndOfFile",
590 ),
591 ];
592 for (sign_data, err_msg) in tests.iter() {
593 let data = hex::decode(sign_data).unwrap();
594 let result = CoseSign::from_tagged_slice(&data);
595 expect_err(result, err_msg);
596 }
597 }
598
599 #[test]
test_rfc8152_cose_sign_decode()600 fn test_rfc8152_cose_sign_decode() {
601 // COSE_Sign structures from RFC 8152 section C.1.
602 let tests = vec![
603 (
604 // C.1.1: Single Signature
605 CoseSignBuilder::new()
606 .payload(b"This is the content.".to_vec())
607 .add_signature(
608 CoseSignatureBuilder::new()
609 .protected(HeaderBuilder::new().algorithm(iana::Algorithm::ES256).build())
610 .unprotected(HeaderBuilder::new().key_id(b"11".to_vec()).build())
611 .signature(hex::decode(
612 "e2aeafd40d69d19dfe6e52077c5d7ff4e408282cbefb5d06cbf414af2e19d982ac45ac98b8544c908b4507de1e90b717c3d34816fe926a2b98f53afd2fa0f30a"
613 ).unwrap())
614 .build()
615 )
616 .build(),
617 concat!(
618 "d862",
619 "84",
620 "40",
621 "a0",
622 "54", "546869732069732074686520636f6e74656e742e",
623 "81", "83",
624 "43", "a10126",
625 "a1", "04", "42", "3131",
626 "5840", "e2aeafd40d69d19dfe6e52077c5d7ff4e408282cbefb5d06cbf414af2e19d982ac45ac98b8544c908b4507de1e90b717c3d34816fe926a2b98f53afd2fa0f30a"
627 ),
628 ),
629 (
630 // C.1.2: Multiple Signers
631 CoseSignBuilder::new()
632 .payload(b"This is the content.".to_vec())
633 .add_signature(
634 CoseSignatureBuilder::new()
635 .protected(HeaderBuilder::new().algorithm(iana::Algorithm::ES256).build())
636 .unprotected(HeaderBuilder::new().key_id(b"11".to_vec()).build())
637 .signature(hex::decode(
638 "e2aeafd40d69d19dfe6e52077c5d7ff4e408282cbefb5d06cbf414af2e19d982ac45ac98b8544c908b4507de1e90b717c3d34816fe926a2b98f53afd2fa0f30a"
639 ).unwrap())
640 .build()
641 )
642 .add_signature(
643 CoseSignatureBuilder::new()
644 .protected(HeaderBuilder::new().algorithm(iana::Algorithm::ES512).build())
645 .unprotected(HeaderBuilder::new().key_id(b"[email protected]".to_vec()).build())
646 .signature(hex::decode(
647 "00a2d28a7c2bdb1587877420f65adf7d0b9a06635dd1de64bb62974c863f0b160dd2163734034e6ac003b01e8705524c5c4ca479a952f0247ee8cb0b4fb7397ba08d009e0c8bf482270cc5771aa143966e5a469a09f613488030c5b07ec6d722e3835adb5b2d8c44e95ffb13877dd2582866883535de3bb03d01753f83ab87bb4f7a0297"
648 ).unwrap())
649 .build()
650 )
651 .build(),
652 concat!(
653 "d862",
654 "84",
655 "40",
656 "a0", "54", "546869732069732074686520636f6e74656e742e",
657 "82",
658 "83",
659 "43", "a10126",
660 "a1", "04", "42", "3131",
661 "5840", "e2aeafd40d69d19dfe6e52077c5d7ff4e408282cbefb5d06cbf414af2e19d982ac45ac98b8544c908b4507de1e90b717c3d34816fe926a2b98f53afd2fa0f30a",
662 "83",
663 "44", "a1013823",
664 "a1", "04", "581e", "62696c626f2e62616767696e7340686f626269746f6e2e6578616d706c65",
665 "5884", "00a2d28a7c2bdb1587877420f65adf7d0b9a06635dd1de64bb62974c863f0b160dd2163734034e6ac003b01e8705524c5c4ca479a952f0247ee8cb0b4fb7397ba08d009e0c8bf482270cc5771aa143966e5a469a09f613488030c5b07ec6d722e3835adb5b2d8c44e95ffb13877dd2582866883535de3bb03d01753f83ab87bb4f7a0297",
666 )
667 ),
668 (
669 // C.1.3: Counter Signature
670 CoseSignBuilder::new()
671 .unprotected(HeaderBuilder::new()
672 .add_counter_signature(
673 CoseSignatureBuilder::new()
674 .protected(HeaderBuilder::new().algorithm(iana::Algorithm::ES256).build())
675 .unprotected(HeaderBuilder::new().key_id(b"11".to_vec()).build())
676 .signature(hex::decode(
677 "5ac05e289d5d0e1b0a7f048a5d2b643813ded50bc9e49220f4f7278f85f19d4a77d655c9d3b51e805a74b099e1e085aacd97fc29d72f887e8802bb6650cceb2c"
678 ).unwrap())
679 .build()
680 )
681 .build())
682 .payload(b"This is the content.".to_vec())
683 .add_signature(
684 CoseSignatureBuilder::new()
685 .protected(HeaderBuilder::new().algorithm(iana::Algorithm::ES256).build())
686 .unprotected(HeaderBuilder::new().key_id(b"11".to_vec()).build())
687 .signature(hex::decode(
688 "e2aeafd40d69d19dfe6e52077c5d7ff4e408282cbefb5d06cbf414af2e19d982ac45ac98b8544c908b4507de1e90b717c3d34816fe926a2b98f53afd2fa0f30a"
689 ).unwrap())
690 .build()
691 )
692 .build(),
693 concat!(
694 "d862",
695 "84",
696 "40",
697 "a1", "07",
698 "83",
699 "43", "a10126",
700 "a1", "04", "42", "3131",
701 "5840", "5ac05e289d5d0e1b0a7f048a5d2b643813ded50bc9e49220f4f7278f85f19d4a77d655c9d3b51e805a74b099e1e085aacd97fc29d72f887e8802bb6650cceb2c",
702 "54", "546869732069732074686520636f6e74656e742e",
703 "81",
704 "83",
705 "43", "a10126",
706 "a1", "04", "42", "3131",
707 "5840", "e2aeafd40d69d19dfe6e52077c5d7ff4e408282cbefb5d06cbf414af2e19d982ac45ac98b8544c908b4507de1e90b717c3d34816fe926a2b98f53afd2fa0f30a",
708 ),
709 ),
710 (
711 // C.1.4: Signature with Criticality
712 CoseSignBuilder::new()
713 .protected(HeaderBuilder::new()
714 .text_value("reserved".to_owned(), Value::Bool(false))
715 .add_critical_label(RegisteredLabel::Text("reserved".to_owned()))
716 .build())
717 .payload(b"This is the content.".to_vec())
718 .add_signature(
719 CoseSignatureBuilder::new()
720 .protected(HeaderBuilder::new().algorithm(iana::Algorithm::ES256).build())
721 .unprotected(HeaderBuilder::new().key_id(b"11".to_vec()).build())
722 .signature(hex::decode(
723 "3fc54702aa56e1b2cb20284294c9106a63f91bac658d69351210a031d8fc7c5ff3e4be39445b1a3e83e1510d1aca2f2e8a7c081c7645042b18aba9d1fad1bd9c"
724 ).unwrap())
725 .build()
726 )
727 .build(),
728 // Note: contents of protected header changed from RFC to be put in canonical order.
729 concat!(
730 "d862",
731 "84",
732 "56",
733 "a2",
734 "02", "81687265736572766564",
735 "687265736572766564", "f4",
736 "a0",
737 "54", "546869732069732074686520636f6e74656e742e",
738 "81", "83",
739 "43", "a10126",
740 "a1", "04", "42", "3131",
741 "5840", "3fc54702aa56e1b2cb20284294c9106a63f91bac658d69351210a031d8fc7c5ff3e4be39445b1a3e83e1510d1aca2f2e8a7c081c7645042b18aba9d1fad1bd9c",
742 ),
743 ),
744 ];
745
746 for (i, (sign, sign_data)) in tests.iter().enumerate() {
747 let got = sign.clone().to_tagged_vec().unwrap();
748 assert_eq!(
749 *sign_data,
750 hex::encode(&got),
751 "case {}: encode {:?}",
752 i,
753 sign
754 );
755
756 let mut got = CoseSign::from_tagged_slice(&got).unwrap();
757 got.protected.original_data = None;
758 for sig in &mut got.signatures {
759 sig.protected.original_data = None;
760 }
761 for sig in &mut got.unprotected.counter_signatures {
762 sig.protected.original_data = None;
763 }
764 assert_eq!(*sign, got);
765 }
766 }
767
768 #[test]
test_cose_sign1_encode()769 fn test_cose_sign1_encode() {
770 let tests = vec![
771 (
772 CoseSign1Builder::new().payload(vec![]).build(),
773 concat!(
774 "84", // 4-tuple
775 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
776 "a0", // 0-map
777 "40", // 0-bstr
778 "40", // 0-bstr
779 ),
780 ),
781 (
782 CoseSign1Builder::new().signature(vec![1, 2, 3]).build(),
783 concat!(
784 "84", // 4-tuple
785 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
786 "a0", // 0-map
787 "f6", // null
788 "43", "010203", // 3-bstr
789 ),
790 ),
791 (
792 CoseSign1Builder::new()
793 .protected(
794 HeaderBuilder::new()
795 .algorithm(iana::Algorithm::A128GCM)
796 .key_id(vec![1, 2, 3])
797 .build(),
798 )
799 .payload(vec![])
800 .signature(vec![1, 2, 3])
801 .build(),
802 concat!(
803 "84", // 4-tuple
804 "48", // 8-bstr (protected)
805 "a2", // 2-map
806 "01", "01", // 1 (alg) => A128GCM
807 "04", "43", "010203", // 4 (kid) => 3-bstr
808 "a0", // 0-map
809 "40", // 0-bstr
810 "43", "010203", // 3-bstr
811 ),
812 ),
813 (
814 CoseSign1Builder::new()
815 .unprotected(
816 HeaderBuilder::new()
817 .algorithm(iana::Algorithm::A128GCM)
818 .key_id(vec![1, 2, 3])
819 .build(),
820 )
821 .payload(vec![])
822 .signature(vec![1, 2, 3])
823 .build(),
824 concat!(
825 "84", // 4-tuple
826 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
827 "a2", // 2-map
828 "01", "01", // 1 (alg) => A128GCM
829 "04", "43", "010203", // 4 (kid) => 3-bstr
830 "40", // 0-bstr
831 "43", "010203", // 3-bstr
832 ),
833 ),
834 ];
835 for (i, (sign, sign_data)) in tests.iter().enumerate() {
836 let got = sign.clone().to_vec().unwrap();
837 assert_eq!(*sign_data, hex::encode(&got), "case {}", i);
838
839 let mut got = CoseSign1::from_slice(&got).unwrap();
840 got.protected.original_data = None;
841 assert_eq!(*sign, got);
842
843 // Repeat with tagged variant.
844 let got = sign.clone().to_tagged_vec().unwrap();
845 let want_hex = format!("d2{}", sign_data);
846 assert_eq!(want_hex, hex::encode(&got), "tagged case {}", i);
847
848 let mut got = CoseSign1::from_tagged_slice(&got).unwrap();
849 got.protected.original_data = None;
850 assert_eq!(*sign, got);
851 }
852 }
853
854 #[test]
test_cose_sign1_decode_fail()855 fn test_cose_sign1_decode_fail() {
856 let tests = vec![
857 (
858 concat!(
859 "a2", // 2-map
860 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
861 "a0", // 0-map
862 "43010203", // 3-bstr
863 "40", // 0-bstr
864 ),
865 "expected array",
866 ),
867 (
868 concat!(
869 "84", // 4-tuple
870 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
871 "40", // 0-bstr (invalid: should be map)
872 "43010203", // 3-bstr
873 "40", // 0-bstr
874 ),
875 "expected map",
876 ),
877 (
878 concat!(
879 "84", // 4-tuple
880 "a0", // 0-map (invalid: should be bstr)
881 "a0", // 0-map
882 "43010203", // 3-bstr
883 "40", // 0-bstr
884 ),
885 "expected bstr",
886 ),
887 (
888 concat!(
889 "84", // 4-tuple
890 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
891 "a0", // 0-map
892 "63616263", // 3-tstr
893 "40", // 0-bstr
894 ),
895 "expected bstr",
896 ),
897 (
898 concat!(
899 "84", // 4-tuple
900 "40", // 0-bstr
901 "a0", // 0-map
902 "43010203", // 3-bstr
903 "80", // 0-arr (invalid: should be bstr)
904 ),
905 "expected bstr",
906 ),
907 (
908 concat!(
909 "85", // 5-tuple
910 "40", // 0-bstr
911 "a0", // 0-map
912 "43010203", // 3-bstr
913 "40", // 0-bstr
914 "43010203", // 3-bstr
915 ),
916 "expected array with 4 items",
917 ),
918 (
919 concat!(
920 "83", // 3-tuple
921 "40", // 0-bstr
922 "a0", // 0-map
923 "43010203", // 3-bstr
924 ),
925 "expected array with 4 items",
926 ),
927 (
928 concat!(
929 "84", // 4-tuple
930 "45", // 5-bstr
931 "a1", // 1-map
932 "03", "81", "4101", // 3 (content-type) => [bstr] (invalid value type)
933 "a0", // 0-map
934 "43616263", // 3-bstr
935 "40", // 0-bstr
936 ),
937 "expected int/tstr",
938 ),
939 ];
940 for (sign_data, err_msg) in tests.iter() {
941 let data = hex::decode(sign_data).unwrap();
942 let result = CoseSign1::from_slice(&data);
943 expect_err(result, err_msg);
944 }
945 }
946
947 #[test]
test_cose_sign1_decode_noncanonical()948 fn test_cose_sign1_decode_noncanonical() {
949 let tests = vec![(
950 CoseSign1Builder::new()
951 .protected(
952 HeaderBuilder::new()
953 .algorithm(iana::Algorithm::ES256)
954 .key_id(vec![0x31, 0x31])
955 .build(),
956 )
957 .payload(vec![0x61, 0x61])
958 .build(),
959 concat!(
960 "84", // 4-tuple
961 "47", // bstr len 7 (protected)
962 concat!(
963 "a2", // 2-map
964 // The contents of the bstr-encoded header are not in canonical order.
965 "04", "42", "3131", // 4 (kid) => 2-bstr "11"
966 "01", "26", // 1 (alg) => ES256
967 ),
968 "a0", // 0-map (unprotected)
969 "42", // 2-bstr (payload)
970 "6161", // "aa"
971 "40", // 0-bstr
972 ),
973 )];
974 for (sign, sign_data) in tests.iter() {
975 let data = hex::decode(sign_data).unwrap();
976 let mut got = CoseSign1::from_slice(&data).unwrap();
977 got.protected.original_data = None;
978 assert_eq!(*sign, got);
979
980 // Repeat with tagged variant.
981 let mut tagged_data = vec![0xd2];
982 tagged_data.extend_from_slice(&data);
983 let mut got = CoseSign1::from_tagged_slice(&tagged_data).unwrap();
984 got.protected.original_data = None;
985 assert_eq!(*sign, got);
986 }
987 }
988
989 #[test]
test_cose_sign1_tagged_decode_fail()990 fn test_cose_sign1_tagged_decode_fail() {
991 let tests = vec![
992 (
993 concat!(
994 "d2", // tag(18)
995 "84", // 4-tuple
996 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
997 "40", // 0-bstr (invalid: should be map)
998 "43010203", // 3-bstr
999 "40", // 0-bstr
1000 ),
1001 "expected map",
1002 ),
1003 (
1004 concat!(
1005 "d2", // tag(18)
1006 "84", // 4-tuple
1007 "a0", // 0-map (invalid: should be bstr)
1008 "a0", // 0-map
1009 "43010203", // 3-bstr
1010 "40", // 0-bstr
1011 ),
1012 "expected bstr",
1013 ),
1014 (
1015 concat!(
1016 "d2", // tag(18)
1017 "85", // 5-tuple
1018 "40", // 0-bstr
1019 "a0", // 0-map
1020 "43010203", // 3-bstr
1021 "80", // 0-tuple
1022 "40", // 0-bstr
1023 ),
1024 "expected array with 4 items",
1025 ),
1026 (
1027 concat!(
1028 "d2", // tag(18)
1029 "83", // 3-tuple
1030 "40", // 0-bstr
1031 "a0", // 0-map
1032 "43010203", // 3-bstr
1033 ),
1034 "expected array with 4 items",
1035 ),
1036 (
1037 concat!(
1038 "d1", // tag(17) : wrong tag
1039 "84", // 4-tuple
1040 "40", // 0-bstr
1041 "a0", // 0-map
1042 "43010203", // 3-bstr
1043 "40", // 0-bstr
1044 ),
1045 "expected other tag",
1046 ),
1047 (
1048 concat!(
1049 "12", // int (18) : not a tag
1050 "84", // 4-tuple
1051 "40", // 0-bstr
1052 "a0", // 0-map
1053 "43010203", // 3-bstr
1054 "40", // 0-bstr
1055 ),
1056 "extraneous data",
1057 ),
1058 (
1059 concat!(
1060 "12", // incomplete int
1061 ),
1062 "expected tag",
1063 ),
1064 ];
1065 for (sign_data, err_msg) in tests.iter() {
1066 let data = hex::decode(sign_data).unwrap();
1067 let result = CoseSign1::from_tagged_slice(&data);
1068 expect_err(result, err_msg);
1069 }
1070 }
1071
1072 #[test]
test_rfc8152_cose_sign1_decode()1073 fn test_rfc8152_cose_sign1_decode() {
1074 // COSE_Sign1 structures from RFC 8152 section C.2.
1075 let tests = vec![
1076 (
1077 CoseSign1Builder::new()
1078 .protected(HeaderBuilder::new().algorithm(iana::Algorithm::ES256).build())
1079 .unprotected(HeaderBuilder::new().key_id(b"11".to_vec()).build())
1080 .payload(b"This is the content.".to_vec())
1081 .signature(hex::decode(
1082 "8eb33e4ca31d1c465ab05aac34cc6b23d58fef5c083106c4d25a91aef0b0117e2af9a291aa32e14ab834dc56ed2a223444547e01f11d3b0916e5a4c345cacb36",
1083 ).unwrap())
1084 .build(),
1085 concat!(
1086 "d2",
1087 "84",
1088 "43", "a10126",
1089 "a1", "04", "42", "3131",
1090 "54", "546869732069732074686520636f6e74656e742e",
1091 "5840", "8eb33e4ca31d1c465ab05aac34cc6b23d58fef5c083106c4d25a91aef0b0117e2af9a291aa32e14ab834dc56ed2a223444547e01f11d3b0916e5a4c345cacb36",
1092 ),
1093 ),
1094 ];
1095
1096 for (i, (sign, sign_data)) in tests.iter().enumerate() {
1097 let got = sign.clone().to_tagged_vec().unwrap();
1098 assert_eq!(*sign_data, hex::encode(&got), "case {}", i);
1099
1100 let mut got = CoseSign1::from_tagged_slice(&got).unwrap();
1101 got.protected.original_data = None;
1102 assert_eq!(*sign, got);
1103 }
1104 }
1105
1106 #[derive(Copy, Clone)]
1107 struct FakeSigner {}
1108
1109 extern crate std;
1110 impl FakeSigner {
sign(&self, data: &[u8]) -> Vec<u8>1111 fn sign(&self, data: &[u8]) -> Vec<u8> {
1112 data.to_vec()
1113 }
1114
verify(&self, sig: &[u8], data: &[u8]) -> Result<(), String>1115 fn verify(&self, sig: &[u8], data: &[u8]) -> Result<(), String> {
1116 if sig != self.sign(data) {
1117 Err("failed to verify".to_owned())
1118 } else {
1119 Ok(())
1120 }
1121 }
try_sign(&self, data: &[u8]) -> Result<Vec<u8>, String>1122 fn try_sign(&self, data: &[u8]) -> Result<Vec<u8>, String> {
1123 Ok(self.sign(data))
1124 }
1125
fail_sign(&self, _data: &[u8]) -> Result<Vec<u8>, String>1126 fn fail_sign(&self, _data: &[u8]) -> Result<Vec<u8>, String> {
1127 Err("failed".to_string())
1128 }
1129 }
1130
1131 #[test]
test_sign_roundtrip()1132 fn test_sign_roundtrip() {
1133 let signer = FakeSigner {};
1134 let verifier = signer;
1135
1136 let pt = b"This is the content";
1137 let aad = b"this is additional data";
1138
1139 let protected = HeaderBuilder::new()
1140 .algorithm(iana::Algorithm::ES256)
1141 .key_id(b"11".to_vec())
1142 .build();
1143 let sign = CoseSignBuilder::new()
1144 .protected(protected.clone())
1145 .payload(pt.to_vec())
1146 .add_created_signature(
1147 CoseSignatureBuilder::new().protected(protected).build(),
1148 aad,
1149 |pt| signer.sign(pt),
1150 )
1151 .build();
1152
1153 let sign_data = sign.to_vec().unwrap();
1154 let mut sign = CoseSign::from_slice(&sign_data).unwrap();
1155
1156 assert!(sign
1157 .verify_signature(0, aad, |sig, data| verifier.verify(sig, data))
1158 .is_ok());
1159
1160 // Changing an unprotected header leaves the signature valid.
1161 sign.unprotected.content_type = Some(ContentType::Text("text/plain".to_owned()));
1162 assert!(sign
1163 .verify_signature(0, aad, |sig, data| verifier.verify(sig, data))
1164 .is_ok());
1165
1166 // Providing a different `aad` means the signature won't validate.
1167 assert!(sign
1168 .verify_signature(0, b"not aad", |sig, data| verifier.verify(sig, data))
1169 .is_err());
1170
1171 // Changing a protected header invalidates the signature.
1172 let mut sign2 = sign.clone();
1173 sign2.protected = ProtectedHeader::default();
1174 assert!(sign2
1175 .verify_signature(0, aad, |sig, data| verifier.verify(sig, data))
1176 .is_err());
1177 let mut sign3 = sign;
1178 sign3.signatures[0].protected = ProtectedHeader::default();
1179 assert!(sign2
1180 .verify_signature(0, aad, |sig, data| verifier.verify(sig, data))
1181 .is_err());
1182 }
1183
1184 #[test]
test_sign_detached_roundtrip()1185 fn test_sign_detached_roundtrip() {
1186 let signer = FakeSigner {};
1187 let verifier = signer;
1188
1189 let pt = b"This is the content";
1190 let aad = b"this is additional data";
1191
1192 let protected = HeaderBuilder::new()
1193 .algorithm(iana::Algorithm::ES256)
1194 .key_id(b"11".to_vec())
1195 .build();
1196 let sign = CoseSignBuilder::new()
1197 .protected(protected.clone())
1198 .add_detached_signature(
1199 CoseSignatureBuilder::new().protected(protected).build(),
1200 pt,
1201 aad,
1202 |pt| signer.sign(pt),
1203 )
1204 .build();
1205
1206 let sign_data = sign.to_vec().unwrap();
1207 let mut sign = CoseSign::from_slice(&sign_data).unwrap();
1208
1209 assert!(sign
1210 .verify_detached_signature(0, pt, aad, |sig, data| verifier.verify(sig, data))
1211 .is_ok());
1212
1213 // Changing an unprotected header leaves the signature valid.
1214 sign.unprotected.content_type = Some(ContentType::Text("text/plain".to_owned()));
1215 assert!(sign
1216 .verify_detached_signature(0, pt, aad, |sig, data| verifier.verify(sig, data))
1217 .is_ok());
1218
1219 // Providing a different `payload` means the signature won't validate.
1220 assert!(sign
1221 .verify_detached_signature(0, b"not payload", aad, |sig, data| verifier
1222 .verify(sig, data))
1223 .is_err());
1224
1225 // Providing a different `aad` means the signature won't validate.
1226 assert!(sign
1227 .verify_detached_signature(0, pt, b"not aad", |sig, data| verifier.verify(sig, data))
1228 .is_err());
1229
1230 // Changing a protected header invalidates the signature.
1231 let mut sign2 = sign.clone();
1232 sign2.protected = ProtectedHeader::default();
1233 assert!(sign2
1234 .verify_detached_signature(0, pt, aad, |sig, data| verifier.verify(sig, data))
1235 .is_err());
1236 let mut sign3 = sign;
1237 sign3.signatures[0].protected = ProtectedHeader::default();
1238 assert!(sign2
1239 .verify_detached_signature(0, pt, aad, |sig, data| verifier.verify(sig, data))
1240 .is_err());
1241 }
1242
1243 #[test]
test_sign_noncanonical()1244 fn test_sign_noncanonical() {
1245 let signer = FakeSigner {};
1246 let verifier = signer;
1247 let pt = b"aa";
1248 let aad = b"bb";
1249
1250 let tests = vec![
1251 // Non-canonical: empty map can just be an empty bstr, not a bstr holding an empty map.
1252 ("a0", Header::default()),
1253 // Non-canonical: the map length (of 0) is non-minimally encoded as the 0x00 following
1254 // 0xb8; it is short enough that it would normally be folded into the type byte
1255 // (0xa0).
1256 ("b800", Header::default()),
1257 // Non-canonical: map not in canonical order.
1258 (
1259 concat!(
1260 "a2", // 2-map
1261 // The contents of the bstr-encoded header are not in canonical order.
1262 "04", "42", "3131", // 4 (kid) => 2-bstr "11"
1263 "01", "26", // 1 (alg) => ES256
1264 ),
1265 HeaderBuilder::new()
1266 .algorithm(iana::Algorithm::ES256)
1267 .key_id(vec![0x31, 0x31])
1268 .build(),
1269 ),
1270 ];
1271
1272 for (protected_data, header) in tests {
1273 // Build a protected header from a non-canonically encoded input.
1274 let protected_data = hex::decode(protected_data).unwrap();
1275 let protected =
1276 ProtectedHeader::from_cbor_bstr(Value::Bytes(protected_data.clone())).unwrap();
1277 assert_eq!(protected.header, header);
1278 assert_eq!(protected.original_data, Some(protected_data));
1279
1280 // Build a signature whose inputs include the non-canonically encoded protected header.
1281 let mut sign = CoseSign {
1282 payload: Some(pt.to_vec()),
1283 protected: protected.clone(),
1284 ..Default::default()
1285 };
1286 let mut sig = CoseSignature {
1287 protected: protected.clone(),
1288 ..Default::default()
1289 };
1290 sig.protected = protected.clone();
1291 sig.signature = signer.sign(&sign.tbs_data(aad, &sig));
1292 sign.signatures.push(sig.clone());
1293 let sign_data = sign.to_vec().unwrap();
1294
1295 // Parsing and verifying this signature should still succeed, because the `ProtectedHeader`
1296 // includes the wire data and uses it for building the signature input.
1297 let sign = CoseSign::from_slice(&sign_data).unwrap();
1298 assert!(sign
1299 .verify_signature(0, aad, |sig, data| verifier.verify(sig, data))
1300 .is_ok());
1301
1302 // However, if we attempt to build the same signature inputs by hand (thus not including the
1303 // non-canonical wire data)...
1304 let recreated_sign = CoseSignBuilder::new()
1305 .protected(protected.header)
1306 .payload(pt.to_vec())
1307 .add_signature(sig)
1308 .build();
1309
1310 // ...then the transplanted signature will not verify, because the re-building of the
1311 // signature inputs will use the canonical encoding of the protected header, which
1312 // is not what was originally used for the signature input.
1313 assert!(recreated_sign
1314 .verify_signature(0, aad, |sig, data| verifier.verify(sig, data))
1315 .is_err());
1316 }
1317 }
1318
1319 #[test]
test_sign_create_result()1320 fn test_sign_create_result() {
1321 let signer = FakeSigner {};
1322
1323 let pt = b"This is the content";
1324 let aad = b"this is additional data";
1325
1326 let protected = HeaderBuilder::new()
1327 .algorithm(iana::Algorithm::ES256)
1328 .key_id(b"11".to_vec())
1329 .build();
1330 let _sign = CoseSignBuilder::new()
1331 .protected(protected.clone())
1332 .payload(pt.to_vec())
1333 .try_add_created_signature(
1334 CoseSignatureBuilder::new()
1335 .protected(protected.clone())
1336 .build(),
1337 aad,
1338 |pt| signer.try_sign(pt),
1339 )
1340 .unwrap()
1341 .build();
1342
1343 let result = CoseSignBuilder::new()
1344 .protected(protected.clone())
1345 .payload(pt.to_vec())
1346 .try_add_created_signature(
1347 CoseSignatureBuilder::new().protected(protected).build(),
1348 aad,
1349 |pt| signer.fail_sign(pt),
1350 );
1351 expect_err(result, "failed");
1352 }
1353
1354 #[test]
test_sign_detached_create_result()1355 fn test_sign_detached_create_result() {
1356 let signer = FakeSigner {};
1357
1358 let pt = b"This is the content";
1359 let aad = b"this is additional data";
1360
1361 let protected = HeaderBuilder::new()
1362 .algorithm(iana::Algorithm::ES256)
1363 .key_id(b"11".to_vec())
1364 .build();
1365 let _sign = CoseSignBuilder::new()
1366 .protected(protected.clone())
1367 .try_add_detached_signature(
1368 CoseSignatureBuilder::new()
1369 .protected(protected.clone())
1370 .build(),
1371 pt,
1372 aad,
1373 |pt| signer.try_sign(pt),
1374 )
1375 .unwrap()
1376 .build();
1377
1378 let result = CoseSignBuilder::new()
1379 .protected(protected.clone())
1380 .try_add_detached_signature(
1381 CoseSignatureBuilder::new().protected(protected).build(),
1382 pt,
1383 aad,
1384 |pt| signer.fail_sign(pt),
1385 );
1386 expect_err(result, "failed");
1387 }
1388
1389 #[test]
1390 #[should_panic]
test_sign_sig_index_invalid()1391 fn test_sign_sig_index_invalid() {
1392 let signer = FakeSigner {};
1393 let verifier = signer;
1394
1395 let pt = b"This is the content";
1396 let aad = b"this is additional data";
1397
1398 let protected = HeaderBuilder::new()
1399 .algorithm(iana::Algorithm::ES256)
1400 .key_id(b"11".to_vec())
1401 .build();
1402 let sign = CoseSignBuilder::new()
1403 .protected(protected)
1404 .payload(pt.to_vec())
1405 .add_created_signature(CoseSignatureBuilder::new().build(), aad, |pt| {
1406 signer.sign(pt)
1407 })
1408 .build();
1409
1410 // Attempt to verify an out-of-range signature
1411 let _ = sign.verify_signature(sign.signatures.len(), aad, |sig, data| {
1412 verifier.verify(sig, data)
1413 });
1414 }
1415
1416 #[test]
1417 #[should_panic]
test_sign_detached_sig_index_invalid()1418 fn test_sign_detached_sig_index_invalid() {
1419 let signer = FakeSigner {};
1420 let verifier = signer;
1421
1422 let pt = b"This is the content";
1423 let aad = b"this is additional data";
1424
1425 let protected = HeaderBuilder::new()
1426 .algorithm(iana::Algorithm::ES256)
1427 .key_id(b"11".to_vec())
1428 .build();
1429 let sign = CoseSignBuilder::new()
1430 .protected(protected)
1431 .add_detached_signature(CoseSignatureBuilder::new().build(), pt, aad, |pt| {
1432 signer.sign(pt)
1433 })
1434 .build();
1435
1436 // Attempt to verify an out-of-range signature
1437 let _ = sign.verify_detached_signature(sign.signatures.len(), pt, aad, |sig, data| {
1438 verifier.verify(sig, data)
1439 });
1440 }
1441
1442 #[test]
1443 #[should_panic]
test_sign1_create_detached_signature_embeddedpayload()1444 fn test_sign1_create_detached_signature_embeddedpayload() {
1445 let signer = FakeSigner {};
1446
1447 let payload = b"this is the content";
1448 let aad = b"this is additional data";
1449
1450 // Attempt to create a detached signature for a message with an embedded payload
1451 let _ = CoseSign1Builder::new()
1452 .protected(
1453 HeaderBuilder::new()
1454 .algorithm(iana::Algorithm::ES256)
1455 .build(),
1456 )
1457 .payload(payload.to_vec())
1458 .create_detached_signature(payload, aad, |pt| signer.sign(pt))
1459 .build();
1460 }
1461
1462 #[test]
1463 #[should_panic]
test_sign1_try_create_detached_signature_embeddedpayload()1464 fn test_sign1_try_create_detached_signature_embeddedpayload() {
1465 let signer = FakeSigner {};
1466
1467 let payload = b"this is the content";
1468 let aad = b"this is additional data";
1469
1470 // Attempt to create a detached signature for a message with an embedded payload
1471 let _ = CoseSign1Builder::new()
1472 .protected(
1473 HeaderBuilder::new()
1474 .algorithm(iana::Algorithm::ES256)
1475 .build(),
1476 )
1477 .payload(payload.to_vec())
1478 .try_create_detached_signature(payload, aad, |pt| Ok::<Vec<u8>, String>(signer.sign(pt)))
1479 .unwrap()
1480 .build();
1481 }
1482
1483 #[test]
1484 #[should_panic]
test_sign1_verify_detached_signature_embeddedpayload()1485 fn test_sign1_verify_detached_signature_embeddedpayload() {
1486 let signer = FakeSigner {};
1487
1488 let payload = b"this is the content";
1489 let aad = b"this is additional data";
1490
1491 let mut sign1 = CoseSign1Builder::new()
1492 .protected(
1493 HeaderBuilder::new()
1494 .algorithm(iana::Algorithm::ES256)
1495 .build(),
1496 )
1497 .create_detached_signature(payload, aad, |pt| signer.sign(pt))
1498 .build();
1499
1500 // Attempt to verify a detached signature for a message with an embedded payload
1501 sign1.payload = Some(payload.to_vec());
1502 sign1
1503 .verify_detached_signature(payload, aad, |sig, data| signer.verify(sig, data))
1504 .unwrap()
1505 }
1506
1507 #[test]
1508 #[should_panic]
test_sign_add_detached_signature_embeddedpayload()1509 fn test_sign_add_detached_signature_embeddedpayload() {
1510 let signer = FakeSigner {};
1511
1512 let payload = b"this is the content";
1513 let aad = b"this is additional data";
1514
1515 // Attempt to add a detached signature to a message with an embedded payload
1516 let _ = CoseSignBuilder::new()
1517 .protected(
1518 HeaderBuilder::new()
1519 .algorithm(iana::Algorithm::ES256)
1520 .build(),
1521 )
1522 .payload(payload.to_vec())
1523 .add_detached_signature(CoseSignatureBuilder::new().build(), payload, aad, |pt| {
1524 signer.sign(pt)
1525 })
1526 .build();
1527 }
1528
1529 #[test]
1530 #[should_panic]
test_sign_try_add_detached_signature_embeddedpayload()1531 fn test_sign_try_add_detached_signature_embeddedpayload() {
1532 let signer = FakeSigner {};
1533
1534 let payload = b"this is the content";
1535 let aad = b"this is additional data";
1536
1537 // Attempt to create a detached signature for a message with an embedded payload
1538 let _ = CoseSignBuilder::new()
1539 .protected(
1540 HeaderBuilder::new()
1541 .algorithm(iana::Algorithm::ES256)
1542 .build(),
1543 )
1544 .payload(payload.to_vec())
1545 .try_add_detached_signature(CoseSignatureBuilder::new().build(), payload, aad, |pt| {
1546 Ok::<Vec<u8>, String>(signer.sign(pt))
1547 })
1548 .unwrap()
1549 .build();
1550 }
1551
1552 #[test]
1553 #[should_panic]
test_sign_verify_detached_signature_embeddedpayload()1554 fn test_sign_verify_detached_signature_embeddedpayload() {
1555 let signer = FakeSigner {};
1556
1557 let payload = b"this is the content";
1558 let aad = b"this is additional data";
1559
1560 let mut sign = CoseSignBuilder::new()
1561 .protected(
1562 HeaderBuilder::new()
1563 .algorithm(iana::Algorithm::ES256)
1564 .build(),
1565 )
1566 .add_detached_signature(CoseSignatureBuilder::new().build(), payload, aad, |pt| {
1567 signer.sign(pt)
1568 })
1569 .build();
1570
1571 // Attempt to verify a detached signature for a message with an embedded payload
1572 sign.payload = Some(payload.to_vec());
1573 sign.verify_detached_signature(0, payload, aad, |sig, data| signer.verify(sig, data))
1574 .unwrap()
1575 }
1576
1577 #[test]
test_sign1_roundtrip()1578 fn test_sign1_roundtrip() {
1579 let signer = FakeSigner {};
1580 let verifier = signer;
1581
1582 let pt = b"This is the content";
1583 let aad = b"this is additional data";
1584
1585 let protected = HeaderBuilder::new()
1586 .algorithm(iana::Algorithm::ES256)
1587 .key_id(b"11".to_vec())
1588 .build();
1589 let sign1 = CoseSign1Builder::new()
1590 .protected(protected)
1591 .payload(pt.to_vec())
1592 .create_signature(aad, |pt| signer.sign(pt))
1593 .build();
1594
1595 let sign1_data = sign1.to_vec().unwrap();
1596 let mut sign1 = CoseSign1::from_slice(&sign1_data).unwrap();
1597
1598 assert!(sign1
1599 .verify_signature(aad, |sig, data| verifier.verify(sig, data))
1600 .is_ok());
1601
1602 // Changing an unprotected header leaves the signature valid.
1603 sign1.unprotected.content_type = Some(ContentType::Text("text/plain".to_owned()));
1604 assert!(sign1
1605 .verify_signature(aad, |sig, data| verifier.verify(sig, data))
1606 .is_ok());
1607
1608 // Providing a different `aad` means the signature won't validate.
1609 assert!(sign1
1610 .verify_signature(b"not aad", |sig, data| verifier.verify(sig, data))
1611 .is_err());
1612
1613 // Changing a protected header invalidates the signature.
1614 sign1.protected.original_data = None;
1615 sign1.protected.header.content_type = Some(ContentType::Text("text/plain".to_owned()));
1616 assert!(sign1
1617 .verify_signature(aad, |sig, data| verifier.verify(sig, data))
1618 .is_err());
1619 }
1620
1621 #[test]
test_sign1_detached_roundtrip()1622 fn test_sign1_detached_roundtrip() {
1623 let signer = FakeSigner {};
1624 let verifier = signer;
1625
1626 let pt = b"This is the content";
1627 let aad = b"this is additional data";
1628
1629 let protected = HeaderBuilder::new()
1630 .algorithm(iana::Algorithm::ES256)
1631 .key_id(b"11".to_vec())
1632 .build();
1633 let sign1 = CoseSign1Builder::new()
1634 .protected(protected)
1635 .create_detached_signature(pt, aad, |pt| signer.sign(pt))
1636 .build();
1637
1638 let sign1_data = sign1.to_vec().unwrap();
1639 let mut sign1 = CoseSign1::from_slice(&sign1_data).unwrap();
1640
1641 assert!(sign1
1642 .verify_detached_signature(pt, aad, |sig, data| verifier.verify(sig, data))
1643 .is_ok());
1644
1645 // Changing an unprotected header leaves the signature valid.
1646 sign1.unprotected.content_type = Some(ContentType::Text("text/plain".to_owned()));
1647 assert!(sign1
1648 .verify_detached_signature(pt, aad, |sig, data| verifier.verify(sig, data))
1649 .is_ok());
1650
1651 // Providing a different 'payload' means the signature won't validate.
1652 assert!(sign1
1653 .verify_detached_signature(b"not payload", aad, |sig, data| verifier.verify(sig, data))
1654 .is_err());
1655
1656 // Providing a different `aad` means the signature won't validate.
1657 assert!(sign1
1658 .verify_detached_signature(pt, b"not aad", |sig, data| verifier.verify(sig, data))
1659 .is_err());
1660
1661 // Changing a protected header invalidates the signature.
1662 sign1.protected.original_data = None;
1663 sign1.protected.header.content_type = Some(ContentType::Text("text/plain".to_owned()));
1664 assert!(sign1
1665 .verify_detached_signature(pt, aad, |sig, data| verifier.verify(sig, data))
1666 .is_err());
1667 }
1668
1669 #[test]
test_sign1_create_result()1670 fn test_sign1_create_result() {
1671 let signer = FakeSigner {};
1672
1673 let pt = b"This is the content";
1674 let aad = b"this is additional data";
1675
1676 let protected = HeaderBuilder::new()
1677 .algorithm(iana::Algorithm::ES256)
1678 .key_id(b"11".to_vec())
1679 .build();
1680 let _sign = CoseSign1Builder::new()
1681 .protected(protected.clone())
1682 .payload(pt.to_vec())
1683 .try_create_signature(aad, |pt| signer.try_sign(pt))
1684 .unwrap()
1685 .build();
1686
1687 let result = CoseSign1Builder::new()
1688 .protected(protected)
1689 .payload(pt.to_vec())
1690 .try_create_signature(aad, |pt| signer.fail_sign(pt));
1691 expect_err(result, "failed");
1692 }
1693
1694 #[test]
test_sign1_create_detached_result()1695 fn test_sign1_create_detached_result() {
1696 let signer = FakeSigner {};
1697
1698 let pt = b"This is the content";
1699 let aad = b"this is additional data";
1700
1701 let protected = HeaderBuilder::new()
1702 .algorithm(iana::Algorithm::ES256)
1703 .key_id(b"11".to_vec())
1704 .build();
1705 let _sign = CoseSign1Builder::new()
1706 .protected(protected.clone())
1707 .try_create_detached_signature(pt, aad, |pt| signer.try_sign(pt))
1708 .unwrap()
1709 .build();
1710
1711 let result = CoseSign1Builder::new()
1712 .protected(protected)
1713 .try_create_detached_signature(pt, aad, |pt| signer.fail_sign(pt));
1714 expect_err(result, "failed");
1715 }
1716
1717 #[test]
test_sign1_noncanonical()1718 fn test_sign1_noncanonical() {
1719 let signer = FakeSigner {};
1720 let verifier = signer;
1721 let pt = b"aa";
1722 let aad = b"bb";
1723
1724 // Build an empty protected header from a non-canonical input of 41a0 rather than 40.
1725 let protected = ProtectedHeader::from_cbor_bstr(Value::Bytes(vec![0xa0])).unwrap();
1726 assert_eq!(protected.header, Header::default());
1727 assert_eq!(protected.original_data, Some(vec![0xa0]));
1728
1729 // Build a signature whose inputs include the non-canonically encoded protected header.
1730 let mut sign1 = CoseSign1::default();
1731 sign1.payload = Some(pt.to_vec());
1732 sign1.protected = protected.clone();
1733 sign1.signature = signer.sign(&sign1.tbs_data(aad));
1734 let sign1_data = sign1.to_vec().unwrap();
1735
1736 // Parsing and verifying this signature should still succeed, because the `ProtectedHeader`
1737 // includes the wire data and uses it for building the signature input.
1738 let sign1 = CoseSign1::from_slice(&sign1_data).unwrap();
1739 assert!(sign1
1740 .verify_signature(aad, |sig, data| verifier.verify(sig, data))
1741 .is_ok());
1742
1743 // However, if we attempt to build the same signature inputs by hand (thus not including the
1744 // non-canonical wire data)...
1745 let recreated_sign1 = CoseSign1Builder::new()
1746 .protected(protected.header)
1747 .payload(pt.to_vec())
1748 .signature(sign1.signature)
1749 .build();
1750
1751 // ...then the transplanted signature will not verify, because the re-building of the signature
1752 // inputs will use the canonical encoding of the protected header, which is not what was
1753 // originally used for the signature input.
1754 assert!(recreated_sign1
1755 .verify_signature(aad, |sig, data| verifier.verify(sig, data))
1756 .is_err());
1757 }
1758
1759 #[test]
test_sig_structure_data()1760 fn test_sig_structure_data() {
1761 let protected = ProtectedHeader {
1762 original_data: None,
1763 header: Header {
1764 alg: Some(Algorithm::Assigned(iana::Algorithm::A128GCM)),
1765 key_id: vec![1, 2, 3],
1766 partial_iv: vec![4, 5, 6],
1767 ..Default::default()
1768 },
1769 };
1770 let got = hex::encode(sig_structure_data(
1771 SignatureContext::CounterSignature,
1772 protected,
1773 None,
1774 &[0x01, 0x02],
1775 &[0x11, 0x12],
1776 ));
1777 assert_eq!(
1778 got,
1779 concat!(
1780 "84", // 4-arr
1781 "70", // 16-tstr
1782 "436f756e7465725369676e6174757265", // "CounterSignature"
1783 "4d", // 13-bstr for protected
1784 "a3", // 3-map
1785 "0101", // 1 (alg) => A128GCM
1786 "0443010203", // 4 (kid) => 3-bstr
1787 "0643040506", // 6 (partial-iv) => 3-bstr
1788 "420102", // bstr for aad
1789 "421112", // bstr for payload
1790 )
1791 );
1792 }
1793