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, CborSerializable, ContentType, CoseKeyBuilder,
20     CoseRecipientBuilder, CoseSignatureBuilder, HeaderBuilder, TaggedCborSerializable,
21 };
22 use alloc::{
23     string::{String, ToString},
24     vec,
25     vec::Vec,
26 };
27 
28 #[test]
test_cose_recipient_decode()29 fn test_cose_recipient_decode() {
30     let tests: Vec<(CoseRecipient, &'static str)> = vec![
31         (
32             CoseRecipientBuilder::new().build(),
33             concat!(
34                 "83", // 3-tuple
35                 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
36                 "a0", // 0-map
37                 "f6", // null
38             ),
39         ),
40         (
41             CoseRecipientBuilder::new().ciphertext(vec![]).build(),
42             concat!(
43                 "83", // 3-tuple
44                 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
45                 "a0", // 0-map
46                 "40", // 0-bstr
47             ),
48         ),
49         (
50             CoseRecipientBuilder::new()
51                 .ciphertext(vec![])
52                 .add_recipient(CoseRecipientBuilder::new().build())
53                 .build(),
54             concat!(
55                 "84", // 4-tuple
56                 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
57                 "a0", // 0-map
58                 "40", // 0-bstr
59                 "81", // 1-tuple
60                 "83", // 3-tuple
61                 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
62                 "a0", // 0-map
63                 "f6", // null
64             ),
65         ),
66     ];
67 
68     for (i, (recipient, recipient_data)) in tests.iter().enumerate() {
69         let got = recipient.clone().to_vec().unwrap();
70         assert_eq!(*recipient_data, hex::encode(&got), "case {}", i);
71 
72         let mut got = CoseRecipient::from_slice(&got).unwrap();
73         got.protected.original_data = None;
74         for recip in &mut got.recipients {
75             recip.protected.original_data = None;
76         }
77         assert_eq!(*recipient, got);
78     }
79 }
80 
81 #[test]
test_cose_recipient_decode_fail()82 fn test_cose_recipient_decode_fail() {
83     let tests = [
84         (
85             concat!(
86                 "a2",   // 2-map (should be tuple)
87                 "40",   // 0-bstr (special case for empty protected headers, rather than 41a0)
88                 "a0",   // 0-map
89                 "4161", // 1-bstr
90                 "40",   // 0-bstr
91             ),
92             "expected array",
93         ),
94         (
95             concat!(
96                 "82", // 2-tuple (should be 4-tuple)
97                 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
98                 "a0", // 0-map
99             ),
100             "expected array with 3 or 4 items",
101         ),
102         (
103             concat!(
104                 "84", // 4-tuple
105                 "80", // 0-tuple (should be bstr)
106                 "a0", // 0-map
107                 "40", // 0-bstr
108                 "80", // 0-arr
109             ),
110             "expected bstr",
111         ),
112         (
113             concat!(
114                 "84", // 4-tuple
115                 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
116                 "40", // 0-bstr (should be map)
117                 "40", // 0-bstr
118                 "80", // 0-arr
119             ),
120             "expected map",
121         ),
122         (
123             concat!(
124                 "84", // 4-tuple
125                 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
126                 "a0", // 0-map
127                 "60", // 0-tstr (should be bstr)
128                 "80", // 0-arr
129             ),
130             "expected bstr",
131         ),
132         (
133             concat!(
134                 "84", // 4-tuple
135                 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
136                 "a0", // 0-map
137                 "40", // 0-bstr
138                 "40", // 0-bstr
139             ),
140             "expected array",
141         ),
142     ];
143     for (recipient_data, err_msg) in tests.iter() {
144         let data = hex::decode(recipient_data).unwrap();
145         let result = CoseRecipient::from_slice(&data);
146         expect_err(result, err_msg);
147     }
148 }
149 
150 #[test]
test_cose_encrypt_decode()151 fn test_cose_encrypt_decode() {
152     let tests: Vec<(CoseEncrypt, &'static str)> = vec![
153         (
154             CoseEncryptBuilder::new().build(),
155             concat!(
156                 "84", // 4-tuple
157                 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
158                 "a0", // 0-map
159                 "f6", // null
160                 "80", // 0-tuple
161             ),
162         ),
163         (
164             CoseEncryptBuilder::new().ciphertext(vec![]).build(),
165             concat!(
166                 "84", // 4-tuple
167                 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
168                 "a0", // 0-map
169                 "40", // 0-bstr
170                 "80", // 0-tuple
171             ),
172         ),
173     ];
174 
175     for (i, (encrypt, encrypt_data)) in tests.iter().enumerate() {
176         let got = encrypt.clone().to_vec().unwrap();
177         assert_eq!(*encrypt_data, hex::encode(&got), "case {}", i);
178 
179         let mut got = CoseEncrypt::from_slice(&got).unwrap();
180         got.protected.original_data = None;
181         assert_eq!(*encrypt, got);
182     }
183 }
184 
185 #[test]
test_cose_encrypt_decode_fail()186 fn test_cose_encrypt_decode_fail() {
187     let tests = [
188         (
189             concat!(
190                 "a2",   // 2-map (should be tuple)
191                 "40",   // 0-bstr (special case for empty protected headers, rather than 41a0)
192                 "a0",   // 0-map
193                 "4161", // 1-bstr
194                 "40",   // 0-bstr
195             ),
196             "expected array",
197         ),
198         (
199             concat!(
200                 "83", // 3-tuple (should be 4-tuple)
201                 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
202                 "a0", // 0-map
203                 "40", // 0-bstr
204             ),
205             "expected array with 4 items",
206         ),
207         (
208             concat!(
209                 "84", // 4-tuple
210                 "80", // 0-tuple (should be bstr)
211                 "a0", // 0-map
212                 "40", // 0-bstr
213                 "80", // 0-arr
214             ),
215             "expected bstr",
216         ),
217         (
218             concat!(
219                 "84", // 4-tuple
220                 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
221                 "40", // 0-bstr (should be map)
222                 "40", // 0-bstr
223                 "80", // 0-arr
224             ),
225             "expected map",
226         ),
227         (
228             concat!(
229                 "84", // 4-tuple
230                 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
231                 "a0", // 0-map
232                 "60", // 0-tstr (should be bstr)
233                 "80", // 0-arr
234             ),
235             "expected bstr",
236         ),
237         (
238             concat!(
239                 "84", // 4-tuple
240                 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
241                 "a0", // 0-map
242                 "40", // 0-bstr
243                 "40", // 0-bstr
244             ),
245             "expected array",
246         ),
247     ];
248     for (encrypt_data, err_msg) in tests.iter() {
249         let data = hex::decode(encrypt_data).unwrap();
250         let result = CoseEncrypt::from_slice(&data);
251         expect_err(result, err_msg);
252     }
253 }
254 #[test]
test_rfc8152_cose_encrypt_decode()255 fn test_rfc8152_cose_encrypt_decode() {
256     // COSE_Encrypt structures from RFC 8152 section C.3.
257     let tests: Vec<(CoseEncrypt, &'static str)> = vec![
258         (
259             CoseEncryptBuilder::new()
260                 .protected(
261                     HeaderBuilder::new()
262                         .algorithm(iana::Algorithm::A128GCM)
263                         .build(),
264                 )
265                 .unprotected(
266                     HeaderBuilder::new()
267                         .iv(hex::decode("c9cf4df2fe6c632bf7886413").unwrap())
268                         .build(),
269                 )
270                 .ciphertext(
271                     hex::decode(
272                         "7adbe2709ca818fb415f1e5df66f4e1a51053ba6d65a1a0c52a357da7a644b8070a151b0",
273                     )
274                     .unwrap(),
275                 )
276                 .add_recipient(
277                     CoseRecipientBuilder::new()
278                         .protected(
279                             HeaderBuilder::new()
280                                 .algorithm(iana::Algorithm::ECDH_ES_HKDF_256)
281                                 .build(),
282                         )
283                         .unprotected(
284                             HeaderBuilder::new()
285                                 .value(iana::HeaderAlgorithmParameter::EphemeralKey as i64,
286                                        CoseKeyBuilder::new_ec2_pub_key_y_sign(iana::EllipticCurve::P_256,
287                                                                               hex::decode("98f50a4ff6c05861c8860d13a638ea56c3f5ad7590bbfbf054e1c7b4d91d6280").unwrap(),
288                                                                               true)
289                                        .build().to_cbor_value().unwrap())
290                                 .key_id(b"[email protected]".to_vec())
291                                 .build(),
292                         )
293                         .ciphertext(vec![])
294                         .build(),
295                 )
296                 .build(),
297             // Note: contents of maps have been re-ordered from the RFC to canonical ordering.
298             concat!(
299                 "d860",
300                 "84", "43", "a10101",
301                 "a1", "05", "4c", "c9cf4df2fe6c632bf7886413",
302                 "5824", "7adbe2709ca818fb415f1e5df66f4e1a51053ba6d65a1a0c52a357da7a644b8070a151b0",
303                 "81",
304                 "83",
305                 "44", "a1013818",
306                 "a2",
307                 "04",
308                 "5824", "6d65726961646f632e6272616e64796275636b406275636b6c616e642e6578616d706c65",
309                 "20",
310                 "a4",
311                 "01", "02",
312                 "20", "01",
313                 "21", "5820", "98f50a4ff6c05861c8860d13a638ea56c3f5ad7590bbfbf054e1c7b4d91d6280",
314                 "22", "f5",
315                 "40",
316             ),
317         ),
318         (
319             CoseEncryptBuilder::new()
320                 .protected(HeaderBuilder::new().algorithm(iana::Algorithm::AES_CCM_16_64_128).build())
321                 .unprotected(HeaderBuilder::new().iv(hex::decode("89f52f65a1c580933b5261a76c").unwrap()).build())
322                 .ciphertext(hex::decode("753548a19b1307084ca7b2056924ed95f2e3b17006dfe931b687b847").unwrap())
323                 .add_recipient(CoseRecipientBuilder::new()
324                                .protected(HeaderBuilder::new().algorithm(iana::Algorithm::Direct_HKDF_SHA_256).build())
325                                .unprotected(
326                                    HeaderBuilder::new()
327                                        .key_id(b"our-secret".to_vec())
328                                        .value(iana::HeaderAlgorithmParameter::Salt as i64,
329                                               Value::Bytes(b"aabbccddeeffgghh".to_vec()))
330                                        .build())
331                                .ciphertext(vec![])
332                                .build())
333                 .build(),
334             // Note: contents of maps have been re-ordered from the RFC to canonical ordering.
335             concat!(
336                 "d860",
337                 "84",
338                 "43",
339                 "a1010a",
340                 "a1",
341                 "05",
342                 "4d", "89f52f65a1c580933b5261a76c",
343                 "581c", "753548a19b1307084ca7b2056924ed95f2e3b17006dfe931b687b847",
344                 "81",
345                 "83",
346                 "43",
347                 "a10129",
348                 "a2",
349                 "04", "4a", "6f75722d736563726574",
350                 "33", "50", "61616262636364646565666667676868",
351                 "40",
352             ),
353         ),
354 
355         (
356             CoseEncryptBuilder::new()
357                 .protected(HeaderBuilder::new().algorithm(iana::Algorithm::A128GCM).build())
358                 .unprotected(HeaderBuilder::new()
359                              .iv(hex::decode("c9cf4df2fe6c632bf7886413").unwrap())
360                              .add_counter_signature(CoseSignatureBuilder::new()
361                                                     .protected(HeaderBuilder::new().algorithm(iana::Algorithm::ES512).build())
362                                                     .unprotected(HeaderBuilder::new().key_id(b"[email protected]".to_vec()).build())
363                                                     .signature(hex::decode("00929663c8789bb28177ae28467e66377da12302d7f9594d2999afa5dfa531294f8896f2b6cdf1740014f4c7f1a358e3a6cf57f4ed6fb02fcf8f7aa989f5dfd07f0700a3a7d8f3c604ba70fa9411bd10c2591b483e1d2c31de003183e434d8fba18f17a4c7e3dfa003ac1cf3d30d44d2533c4989d3ac38c38b71481cc3430c9d65e7ddff").unwrap())
364                                                     .build())
365                              .build())
366                 .ciphertext(hex::decode("7adbe2709ca818fb415f1e5df66f4e1a51053ba6d65a1a0c52a357da7a644b8070a151b0").unwrap())
367                 .add_recipient(CoseRecipientBuilder::new()
368                                .protected(
369                                    HeaderBuilder::new()
370                                        .algorithm(iana::Algorithm::ECDH_ES_HKDF_256)
371                                        .build())
372                                .unprotected(
373                                    HeaderBuilder::new()
374                                        .value(iana::HeaderAlgorithmParameter::EphemeralKey as i64,
375                                               CoseKeyBuilder::new_ec2_pub_key_y_sign(iana::EllipticCurve::P_256,
376                                                                                      hex::decode("98f50a4ff6c05861c8860d13a638ea56c3f5ad7590bbfbf054e1c7b4d91d6280").unwrap(),
377                                                                                      true)
378                                               .build().to_cbor_value().unwrap())
379                                        .key_id(b"[email protected]".to_vec())
380                                        .build())
381                         .ciphertext(vec![])
382                                .build())
383                 .build(),
384             // Note: contents of maps have been re-ordered from the RFC to canonical ordering.
385             concat!(
386                 "d860",
387                 "84",
388                 "43",
389                 "a10101",
390                 "a2",
391                 "05",
392                 "4c", "c9cf4df2fe6c632bf7886413",
393                 "07",
394                 "83",
395                 "44",
396                 "a1013823",
397                 "a1",
398                 "04",
399                 "581e", "62696c626f2e62616767696e7340686f626269746f6e2e6578616d706c65",
400                 "5884",
401                 "00929663c8789bb28177ae28467e66377da12302d7f9594d2999afa5dfa531294f8896f2b6cdf1740014f4c7f1a358e3a6cf57f4ed6fb02fcf8f7aa989f5dfd07f0700a3a7d8f3c604ba70fa9411bd10c2591b483e1d2c31de003183e434d8fba18f17a4c7e3dfa003ac1cf3d30d44d2533c4989d3ac38c38b71481cc3430c9d65e7ddff",
402                 "5824",
403                 "7adbe2709ca818fb415f1e5df66f4e1a51053ba6d65a1a0c52a357da7a644b8070a151b0",
404                 "81",
405                 "83",
406                 "44", "a1013818",
407                 "a2",
408                 "04",
409                 "5824", "6d65726961646f632e6272616e64796275636b406275636b6c616e642e6578616d706c65",
410                 "20",
411                 "a4",
412                 "01",
413                 "02",
414                 "20",
415                 "01",
416                 "21",
417                 "5820", "98f50a4ff6c05861c8860d13a638ea56c3f5ad7590bbfbf054e1c7b4d91d6280",
418                 "22",
419                 "f5",
420                 "40",
421             ),
422         ),
423         (
424             CoseEncryptBuilder::new()
425                 .protected(HeaderBuilder::new().algorithm(iana::Algorithm::A128GCM).build())
426                 .unprotected(HeaderBuilder::new().iv(hex::decode("02d1f7e6f26c43d4868d87ce").unwrap()).build())
427                 .ciphertext(hex::decode("64f84d913ba60a76070a9a48f26e97e863e28529d8f5335e5f0165eee976b4a5f6c6f09d").unwrap())
428                 .add_recipient(CoseRecipientBuilder::new()
429                                .protected(HeaderBuilder::new().algorithm(iana::Algorithm::ECDH_SS_A128KW).build())
430                                .unprotected(HeaderBuilder::new()
431                                             .key_id(b"[email protected]".to_vec())
432                                             .value(
433                                                 iana::HeaderAlgorithmParameter::StaticKeyId as i64,
434                                                 Value::Bytes(b"[email protected]".to_vec())
435                                             )
436                                             .value(
437                                                 iana::HeaderAlgorithmParameter::PartyUNonce as i64,
438                                                 Value::Bytes(hex::decode("0101").unwrap())
439                                             )
440                                             .build())
441                                .ciphertext(hex::decode("41e0d76f579dbd0d936a662d54d8582037de2e366fde1c62").unwrap())
442                                .build())
443                 .build(),
444             // Note: contents of maps have been re-ordered from the RFC to canonical ordering.
445             concat!(
446                 "d860",
447                 "84",
448                 "43",
449                 "a10101",
450                 "a1",
451                 "05",
452                 "4c", "02d1f7e6f26c43d4868d87ce",
453                 "5824", "64f84d913ba60a76070a9a48f26e97e863e28529d8f5335e5f0165eee976b4a5f6c6f09d",
454                 "81",
455                 "83",
456                 "44", "a101381f",
457                 "a3",
458                 "04",
459                 "5824", "6d65726961646f632e6272616e64796275636b406275636b6c616e642e6578616d706c65",
460                 "22",
461                 "5821", "706572656772696e2e746f6f6b407475636b626f726f7567682e6578616d706c65",
462                 "35",
463                 "42",
464                 "0101",
465                 "5818", "41e0d76f579dbd0d936a662d54d8582037de2e366fde1c62",
466             ),
467         ),
468     ];
469 
470     for (i, (encrypt, encrypt_data)) in tests.iter().enumerate() {
471         let got = encrypt.clone().to_tagged_vec().unwrap();
472         assert_eq!(*encrypt_data, hex::encode(&got), "case {}", i);
473 
474         let mut got = CoseEncrypt::from_tagged_slice(&got).unwrap();
475         got.protected.original_data = None;
476         for recip in &mut got.recipients {
477             recip.protected.original_data = None;
478         }
479         for sig in &mut got.unprotected.counter_signatures {
480             sig.protected.original_data = None;
481         }
482         assert_eq!(*encrypt, got);
483     }
484 }
485 
486 #[test]
test_cose_encrypt0_decode()487 fn test_cose_encrypt0_decode() {
488     let tests: Vec<(CoseEncrypt0, &'static str)> = vec![
489         (
490             CoseEncrypt0Builder::new().build(),
491             concat!(
492                 "83", // 3-tuple
493                 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
494                 "a0", // 0-map
495                 "f6", // null
496             ),
497         ),
498         (
499             CoseEncrypt0Builder::new().ciphertext(vec![]).build(),
500             concat!(
501                 "83", // 3-tuple
502                 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
503                 "a0", // 0-map
504                 "40", // 0-bstr
505             ),
506         ),
507     ];
508 
509     for (i, (encrypt, encrypt_data)) in tests.iter().enumerate() {
510         let got = encrypt.clone().to_vec().unwrap();
511         assert_eq!(*encrypt_data, hex::encode(&got), "case {}", i);
512 
513         let mut got = CoseEncrypt0::from_slice(&got).unwrap();
514         got.protected.original_data = None;
515         assert_eq!(*encrypt, got);
516     }
517 }
518 
519 #[test]
test_cose_encrypt0_decode_fail()520 fn test_cose_encrypt0_decode_fail() {
521     let tests = [
522         (
523             concat!(
524                 "a2",   // 2-map (should be tuple)
525                 "40",   // 0-bstr (special case for empty protected headers, rather than 41a0)
526                 "a0",   // 0-map
527                 "4100", // 1-bstr
528                 "40",   // 0-bstr
529             ),
530             "expected array",
531         ),
532         (
533             concat!(
534                 "82", // 2-tuple (should be 3-tuple)
535                 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
536                 "a0", // 0-map
537             ),
538             "expected array with 3 items",
539         ),
540         (
541             concat!(
542                 "83", // 3-tuple
543                 "80", // 0-tuple (should be bstr)
544                 "a0", // 0-map
545                 "40", // 0-bstr
546             ),
547             "expected bstr",
548         ),
549         (
550             concat!(
551                 "83", // 3-tuple
552                 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
553                 "40", // 0-bstr (should be map)
554                 "40", // 0-bstr
555             ),
556             "expected map",
557         ),
558         (
559             concat!(
560                 "83", // 3-tuple
561                 "40", // 0-bstr (special case for empty protected headers, rather than 41a0)
562                 "a0", // 0-map
563                 "60", // 0-tstr (should be bstr)
564             ),
565             "expected bstr",
566         ),
567     ];
568     for (encrypt_data, err_msg) in tests.iter() {
569         let data = hex::decode(encrypt_data).unwrap();
570         let result = CoseEncrypt0::from_slice(&data);
571         expect_err(result, err_msg);
572     }
573 }
574 
575 #[test]
test_rfc8152_cose_encrypt0_decode()576 fn test_rfc8152_cose_encrypt0_decode() {
577     // COSE_Encrypt0 structures from RFC 8152 section C.4.
578     let tests: Vec<(CoseEncrypt0, &'static str)> = vec![
579         (
580             CoseEncrypt0Builder::new()
581                 .protected(
582                     HeaderBuilder::new()
583                         .algorithm(iana::Algorithm::AES_CCM_16_64_128)
584                         .build(),
585                 )
586                 .unprotected(
587                     HeaderBuilder::new()
588                         .iv(hex::decode("89f52f65a1c580933b5261a78c").unwrap())
589                         .build(),
590                 )
591                 .ciphertext(
592                     hex::decode("5974e1b99a3a4cc09a659aa2e9e7fff161d38ce71cb45ce460ffb569")
593                         .unwrap(),
594                 )
595                 .build(),
596             concat!(
597                 "d0",
598                 "83",
599                 "43",
600                 "a1010a",
601                 "a1",
602                 "05",
603                 "4d",
604                 "89f52f65a1c580933b5261a78c",
605                 "581c",
606                 "5974e1b99a3a4cc09a659aa2e9e7fff161d38ce71cb45ce460ffb569",
607             ),
608         ),
609         (
610             CoseEncrypt0Builder::new()
611                 .protected(
612                     HeaderBuilder::new()
613                         .algorithm(iana::Algorithm::AES_CCM_16_64_128)
614                         .build(),
615                 )
616                 .unprotected(
617                     HeaderBuilder::new()
618                         .partial_iv(hex::decode("61a7").unwrap())
619                         .build(),
620                 )
621                 .ciphertext(
622                     hex::decode("252a8911d465c125b6764739700f0141ed09192de139e053bd09abca")
623                         .unwrap(),
624                 )
625                 .build(),
626             concat!(
627                 "d0",
628                 "83",
629                 "43",
630                 "a1010a",
631                 "a1",
632                 "06",
633                 "42",
634                 "61a7",
635                 "581c",
636                 "252a8911d465c125b6764739700f0141ed09192de139e053bd09abca",
637             ),
638         ),
639     ];
640 
641     for (i, (encrypt, encrypt_data)) in tests.iter().enumerate() {
642         let got = encrypt.clone().to_tagged_vec().unwrap();
643         assert_eq!(*encrypt_data, hex::encode(&got), "case {}", i);
644 
645         let mut got = CoseEncrypt0::from_tagged_slice(&got).unwrap();
646         got.protected.original_data = None;
647         assert_eq!(*encrypt, got);
648     }
649 }
650 
651 struct FakeCipher {}
652 
653 impl FakeCipher {
encrypt(&self, plaintext: &[u8], additional_data: &[u8]) -> Result<Vec<u8>, String>654     fn encrypt(&self, plaintext: &[u8], additional_data: &[u8]) -> Result<Vec<u8>, String> {
655         let mut result = vec![];
656         result.extend_from_slice(&(plaintext.len() as u32).to_be_bytes());
657         result.extend_from_slice(plaintext);
658         result.extend_from_slice(additional_data);
659         Ok(result)
660     }
661 
decrypt(&self, ciphertext: &[u8], additional_data: &[u8]) -> Result<Vec<u8>, String>662     fn decrypt(&self, ciphertext: &[u8], additional_data: &[u8]) -> Result<Vec<u8>, String> {
663         if ciphertext.len() < 4 {
664             return Err("not long enough".to_owned());
665         }
666         let pt_len =
667             u32::from_be_bytes([ciphertext[0], ciphertext[1], ciphertext[2], ciphertext[3]])
668                 as usize;
669         let pt = &ciphertext[4..4 + pt_len];
670         let recovered_aad = &ciphertext[4 + pt_len..];
671         if recovered_aad != additional_data {
672             return Err("aad doesn't match".to_owned());
673         }
674         Ok(pt.to_vec())
675     }
fail_encrypt(&self, _plaintext: &[u8], _additional_data: &[u8]) -> Result<Vec<u8>, String>676     fn fail_encrypt(&self, _plaintext: &[u8], _additional_data: &[u8]) -> Result<Vec<u8>, String> {
677         Err("failed".to_string())
678     }
679 }
680 
681 #[test]
test_cose_recipient_roundtrip()682 fn test_cose_recipient_roundtrip() {
683     let pt = b"This is the plaintext";
684     let external_aad = b"This is the external aad";
685     let cipher = FakeCipher {};
686 
687     for context in &[
688         EncryptionContext::EncRecipient,
689         EncryptionContext::MacRecipient,
690         EncryptionContext::RecRecipient,
691     ] {
692         let protected = HeaderBuilder::new()
693             .algorithm(iana::Algorithm::ES256)
694             .key_id(b"11".to_vec())
695             .build();
696 
697         let mut recipient = CoseRecipientBuilder::new()
698             .protected(protected)
699             .create_ciphertext(*context, pt, external_aad, |pt, aad| {
700                 cipher.encrypt(pt, aad).unwrap()
701             })
702             .build();
703 
704         let recovered_pt = recipient
705             .decrypt(*context, external_aad, |ct, aad| cipher.decrypt(ct, aad))
706             .unwrap();
707         assert_eq!(&pt[..], recovered_pt);
708 
709         // Changing an unprotected header leaves the ciphertext decipherable.
710         recipient.unprotected.content_type = Some(ContentType::Text("text/plain".to_owned()));
711         assert!(recipient
712             .decrypt(*context, external_aad, |ct, aad| {
713                 cipher.decrypt(ct, aad)
714             })
715             .is_ok());
716 
717         // Providing a different `aad` means the ciphertext won't validate.
718         assert!(recipient
719             .decrypt(*context, b"not aad", |ct, aad| { cipher.decrypt(ct, aad) })
720             .is_err());
721 
722         // Changing a protected header invalidates the ciphertext.
723         recipient.protected = ProtectedHeader::default();
724         assert!(recipient
725             .decrypt(*context, external_aad, |ct, aad| {
726                 cipher.decrypt(ct, aad)
727             })
728             .is_err());
729     }
730 }
731 
732 #[test]
test_cose_recipient_noncanonical()733 fn test_cose_recipient_noncanonical() {
734     let pt = b"aa";
735     let aad = b"bb";
736     let cipher = FakeCipher {};
737     let context = EncryptionContext::EncRecipient;
738 
739     // Build an empty protected header from a non-canonical input of 41a0 rather than 40.
740     let protected = ProtectedHeader::from_cbor_bstr(Value::Bytes(vec![0xa0])).unwrap();
741     assert_eq!(protected.header, Header::default());
742     assert_eq!(protected.original_data, Some(vec![0xa0]));
743 
744     let mut recipient = CoseRecipient {
745         protected: protected.clone(),
746         ..Default::default()
747     };
748     let internal_aad = crate::encrypt::enc_structure_data(context, protected, aad);
749     recipient.ciphertext = Some(cipher.encrypt(pt, &internal_aad).unwrap());
750 
751     // Deciphering the ciphertext should still succeed, because the `ProtectedHeader`
752     // includes the wire data and uses it for building the decryption input.
753     let recovered_pt = recipient
754         .decrypt(context, aad, |ct, aad| cipher.decrypt(ct, aad))
755         .unwrap();
756     assert_eq!(&pt[..], recovered_pt);
757 
758     // However, if we attempt to build the same decryption inputs by hand (thus not including the
759     // non-canonical wire data)...
760     let recreated_recipient = CoseRecipientBuilder::new()
761         .ciphertext(recipient.ciphertext.unwrap())
762         .build();
763 
764     // ...then the transplanted cipher text will not decipher, because the re-building of the
765     // inputs will use the canonical encoding of the protected header, which is not what was
766     // originally used for the input.
767     assert!(recreated_recipient
768         .decrypt(context, aad, |ct, aad| cipher.decrypt(ct, aad))
769         .is_err());
770 }
771 
772 #[test]
test_cose_recipient_result()773 fn test_cose_recipient_result() {
774     let pt = b"This is the plaintext";
775     let external_aad = b"This is the external aad";
776     let cipher = FakeCipher {};
777 
778     let protected = HeaderBuilder::new()
779         .algorithm(iana::Algorithm::ES256)
780         .key_id(b"11".to_vec())
781         .build();
782     let _recipient = CoseRecipientBuilder::new()
783         .protected(protected.clone())
784         .try_create_ciphertext(
785             EncryptionContext::EncRecipient,
786             pt,
787             external_aad,
788             |pt, aad| cipher.encrypt(pt, aad),
789         )
790         .unwrap()
791         .build();
792     let status = CoseRecipientBuilder::new()
793         .protected(protected)
794         .try_create_ciphertext(
795             EncryptionContext::EncRecipient,
796             pt,
797             external_aad,
798             |pt, aad| cipher.fail_encrypt(pt, aad),
799         );
800     expect_err(status, "failed");
801 }
802 
803 #[test]
804 #[should_panic]
test_cose_recipient_missing_ciphertext()805 fn test_cose_recipient_missing_ciphertext() {
806     let external_aad = b"This is the external aad";
807     let cipher = FakeCipher {};
808 
809     let recipient = CoseRecipient::default();
810 
811     // No ciphertext has been set, do decryption will panic.
812     let _result = recipient.decrypt(EncryptionContext::EncRecipient, external_aad, |ct, aad| {
813         cipher.decrypt(ct, aad)
814     });
815 }
816 
817 #[test]
818 #[should_panic]
test_cose_recipient_builder_invalid_context()819 fn test_cose_recipient_builder_invalid_context() {
820     let pt = b"This is the plaintext";
821     let external_aad = b"This is the external aad";
822     let cipher = FakeCipher {};
823 
824     // Can't use a non-recipient context.
825     let _recipient = CoseRecipientBuilder::new()
826         .create_ciphertext(
827             EncryptionContext::CoseEncrypt,
828             pt,
829             external_aad,
830             |pt, aad| cipher.encrypt(pt, aad).unwrap(),
831         )
832         .build();
833 }
834 
835 #[test]
836 #[should_panic]
test_cose_recipient_decrypt_invalid_context()837 fn test_cose_recipient_decrypt_invalid_context() {
838     let pt = b"This is the plaintext";
839     let external_aad = b"This is the external aad";
840     let cipher = FakeCipher {};
841 
842     let recipient = CoseRecipientBuilder::new()
843         .create_ciphertext(
844             EncryptionContext::EncRecipient,
845             pt,
846             external_aad,
847             |pt, aad| cipher.encrypt(pt, aad).unwrap(),
848         )
849         .build();
850 
851     // Can't use a non-recipient context.
852     let _result = recipient.decrypt(EncryptionContext::CoseEncrypt, external_aad, |ct, aad| {
853         cipher.decrypt(ct, aad)
854     });
855 }
856 
857 #[test]
test_cose_encrypt_roundtrip()858 fn test_cose_encrypt_roundtrip() {
859     let pt = b"This is the plaintext";
860     let external_aad = b"This is the external aad";
861     let cipher = FakeCipher {};
862 
863     let protected = HeaderBuilder::new()
864         .algorithm(iana::Algorithm::ES256)
865         .key_id(b"11".to_vec())
866         .build();
867     let mut encrypt = CoseEncryptBuilder::new()
868         .protected(protected)
869         .create_ciphertext(pt, external_aad, |pt, aad| cipher.encrypt(pt, aad).unwrap())
870         .build();
871 
872     let recovered_pt = encrypt
873         .decrypt(external_aad, |ct, aad| cipher.decrypt(ct, aad))
874         .unwrap();
875     assert_eq!(&pt[..], recovered_pt);
876 
877     // Changing an unprotected header leaves the ciphertext decipherable.
878     encrypt.unprotected.content_type = Some(ContentType::Text("text/plain".to_owned()));
879     assert!(encrypt
880         .decrypt(external_aad, |ct, aad| cipher.decrypt(ct, aad))
881         .is_ok());
882 
883     // Providing a different `aad` means the signature won't validate.
884     assert!(encrypt
885         .decrypt(b"not aad", |ct, aad| cipher.decrypt(ct, aad))
886         .is_err());
887 
888     // Changing a protected header invalidates the ciphertext.
889     encrypt.protected = ProtectedHeader::default();
890     assert!(encrypt
891         .decrypt(external_aad, |ct, aad| cipher.decrypt(ct, aad))
892         .is_err());
893 }
894 
895 #[test]
test_cose_encrypt_noncanonical()896 fn test_cose_encrypt_noncanonical() {
897     let pt = b"aa";
898     let external_aad = b"bb";
899     let cipher = FakeCipher {};
900 
901     // Build an empty protected header from a non-canonical input of 41a0 rather than 40.
902     let protected = ProtectedHeader::from_cbor_bstr(Value::Bytes(vec![0xa0])).unwrap();
903     assert_eq!(protected.header, Header::default());
904     assert_eq!(protected.original_data, Some(vec![0xa0]));
905 
906     let mut encrypt = CoseEncrypt {
907         protected: protected.clone(),
908         ..Default::default()
909     };
910     let aad = enc_structure_data(
911         EncryptionContext::CoseEncrypt,
912         protected.clone(),
913         external_aad,
914     );
915     encrypt.ciphertext = Some(cipher.encrypt(pt, &aad).unwrap());
916 
917     // Deciphering the ciphertext should still succeed, because the `ProtectedHeader`
918     // includes the wire data and uses it for building the decryption input.
919     let recovered_pt = encrypt
920         .decrypt(external_aad, |ct, aad| cipher.decrypt(ct, aad))
921         .unwrap();
922     assert_eq!(&pt[..], recovered_pt);
923 
924     // However, if we attempt to build the same decryption inputs by hand (thus not including the
925     // non-canonical wire data)...
926     let recreated_encrypt = CoseEncryptBuilder::new()
927         .protected(protected.header)
928         .ciphertext(encrypt.ciphertext.unwrap())
929         .build();
930 
931     // ...then the transplanted cipher text will not decipher, because the re-building of the
932     // inputs will use the canonical encoding of the protected header, which is not what was
933     // originally used for the input.
934     assert!(recreated_encrypt
935         .decrypt(external_aad, |ct, aad| cipher.decrypt(ct, aad))
936         .is_err());
937 }
938 
939 #[test]
test_cose_encrypt_status()940 fn test_cose_encrypt_status() {
941     let pt = b"This is the plaintext";
942     let external_aad = b"This is the external aad";
943     let cipher = FakeCipher {};
944 
945     let protected = HeaderBuilder::new()
946         .algorithm(iana::Algorithm::ES256)
947         .key_id(b"11".to_vec())
948         .build();
949     let _encrypt = CoseEncryptBuilder::new()
950         .protected(protected.clone())
951         .try_create_ciphertext(pt, external_aad, |pt, aad| cipher.encrypt(pt, aad))
952         .unwrap()
953         .build();
954     let status = CoseEncryptBuilder::new()
955         .protected(protected)
956         .try_create_ciphertext(pt, external_aad, |pt, aad| cipher.fail_encrypt(pt, aad));
957     expect_err(status, "failed");
958 }
959 
960 #[test]
961 #[should_panic]
test_cose_encrypt_missing_ciphertext()962 fn test_cose_encrypt_missing_ciphertext() {
963     let external_aad = b"This is the external aad";
964     let cipher = FakeCipher {};
965 
966     let encrypt = CoseEncrypt::default();
967 
968     // No ciphertext has been set, do decryption will panic.
969     let _result = encrypt.decrypt(external_aad, |ct, aad| cipher.decrypt(ct, aad));
970 }
971 
972 #[test]
test_cose_encrypt0_roundtrip()973 fn test_cose_encrypt0_roundtrip() {
974     let pt = b"This is the plaintext";
975     let external_aad = b"This is the external aad";
976     let cipher = FakeCipher {};
977 
978     let protected = HeaderBuilder::new()
979         .algorithm(iana::Algorithm::ES256)
980         .key_id(b"11".to_vec())
981         .build();
982     let mut encrypt = CoseEncrypt0Builder::new()
983         .protected(protected)
984         .create_ciphertext(pt, external_aad, |pt, aad| cipher.encrypt(pt, aad).unwrap())
985         .build();
986 
987     let recovered_pt = encrypt
988         .decrypt(external_aad, |ct, aad| cipher.decrypt(ct, aad))
989         .unwrap();
990     assert_eq!(&pt[..], recovered_pt);
991 
992     // Changing an unprotected header leaves the ciphertext decipherable.
993     encrypt.unprotected.content_type = Some(ContentType::Text("text/plain".to_owned()));
994     assert!(encrypt
995         .decrypt(external_aad, |ct, aad| cipher.decrypt(ct, aad))
996         .is_ok());
997 
998     // Providing a different `aad` means the ciphertext won't decrypt.
999     assert!(encrypt
1000         .decrypt(b"not aad", |ct, aad| cipher.decrypt(ct, aad))
1001         .is_err());
1002 
1003     // Changing a protected header invalidates the ciphertext.
1004     encrypt.protected = ProtectedHeader::default();
1005     assert!(encrypt
1006         .decrypt(external_aad, |ct, aad| cipher.decrypt(ct, aad))
1007         .is_err());
1008 }
1009 
1010 #[test]
test_cose_encrypt0_noncanonical()1011 fn test_cose_encrypt0_noncanonical() {
1012     let pt = b"aa";
1013     let external_aad = b"bb";
1014     let cipher = FakeCipher {};
1015 
1016     // Build an empty protected header from a non-canonical input of 41a0 rather than 40.
1017     let protected = ProtectedHeader::from_cbor_bstr(Value::Bytes(vec![0xa0])).unwrap();
1018     assert_eq!(protected.header, Header::default());
1019     assert_eq!(protected.original_data, Some(vec![0xa0]));
1020 
1021     let mut encrypt = CoseEncrypt0 {
1022         protected: protected.clone(),
1023         ..Default::default()
1024     };
1025     let aad = enc_structure_data(
1026         EncryptionContext::CoseEncrypt0,
1027         protected.clone(),
1028         external_aad,
1029     );
1030     encrypt.ciphertext = Some(cipher.encrypt(pt, &aad).unwrap());
1031 
1032     // Deciphering the ciphertext should still succeed, because the `ProtectedHeader`
1033     // includes the wire data and uses it for building the decryption input.
1034     let recovered_pt = encrypt
1035         .decrypt(external_aad, |ct, aad| cipher.decrypt(ct, aad))
1036         .unwrap();
1037     assert_eq!(&pt[..], recovered_pt);
1038 
1039     // However, if we attempt to build the same decryption inputs by hand (thus not including the
1040     // non-canonical wire data)...
1041     let recreated_encrypt = CoseEncrypt0Builder::new()
1042         .protected(protected.header)
1043         .ciphertext(encrypt.ciphertext.unwrap())
1044         .build();
1045 
1046     // ...then the transplanted cipher text will not decipher, because the re-building of the
1047     // inputs will use the canonical encoding of the protected header, which is not what was
1048     // originally used for the input.
1049     assert!(recreated_encrypt
1050         .decrypt(external_aad, |ct, aad| cipher.decrypt(ct, aad))
1051         .is_err());
1052 }
1053 #[test]
test_cose_encrypt0_status()1054 fn test_cose_encrypt0_status() {
1055     let pt = b"This is the plaintext";
1056     let external_aad = b"This is the external aad";
1057     let cipher = FakeCipher {};
1058 
1059     let protected = HeaderBuilder::new()
1060         .algorithm(iana::Algorithm::ES256)
1061         .key_id(b"11".to_vec())
1062         .build();
1063     let _encrypt = CoseEncrypt0Builder::new()
1064         .protected(protected.clone())
1065         .try_create_ciphertext(pt, external_aad, |pt, aad| cipher.encrypt(pt, aad))
1066         .unwrap()
1067         .build();
1068     let status = CoseEncrypt0Builder::new()
1069         .protected(protected)
1070         .try_create_ciphertext(pt, external_aad, |pt, aad| cipher.fail_encrypt(pt, aad));
1071     expect_err(status, "failed");
1072 }
1073 
1074 #[test]
1075 #[should_panic]
test_cose_encrypt0_missing_ciphertext()1076 fn test_cose_encrypt0_missing_ciphertext() {
1077     let external_aad = b"This is the external aad";
1078     let cipher = FakeCipher {};
1079 
1080     let encrypt = CoseEncrypt0::default();
1081 
1082     // No ciphertext has been set, do decryption will panic.
1083     let _result = encrypt.decrypt(external_aad, |ct, aad| cipher.decrypt(ct, aad));
1084 }
1085