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