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::{cbor::value::Value, iana, util::expect_err, CborSerializable, Label};
19 use alloc::{borrow::ToOwned, vec};
20 
21 #[test]
test_header_encode()22 fn test_header_encode() {
23     let tests = vec![
24         (
25             Header {
26                 alg: Some(Algorithm::Assigned(iana::Algorithm::A128GCM)),
27                 key_id: vec![1, 2, 3],
28                 partial_iv: vec![1, 2, 3],
29                 ..Default::default()
30             },
31             concat!(
32                 "a3", // 3-map
33                 "01", "01", // 1 (alg) => A128GCM
34                 "04", "43", "010203", // 4 (kid) => 3-bstr
35                 "06", "43", "010203", // 6 (partial-iv) => 3-bstr
36             ),
37         ),
38         (
39             Header {
40                 alg: Some(Algorithm::PrivateUse(i64::MIN)),
41                 ..Default::default()
42             },
43             concat!(
44                 "a1", // 1-map
45                 "01",
46                 "3b7fffffffffffffff", // 1 (alg) => -lots
47             ),
48         ),
49         (
50             Header {
51                 alg: Some(Algorithm::Assigned(iana::Algorithm::A128GCM)),
52                 crit: vec![RegisteredLabel::Assigned(iana::HeaderParameter::Alg)],
53                 content_type: Some(ContentType::Assigned(iana::CoapContentFormat::CoseEncrypt0)),
54                 key_id: vec![1, 2, 3],
55                 iv: vec![1, 2, 3],
56                 rest: vec![
57                     (Label::Int(0x46), Value::from(0x47)),
58                     (Label::Int(0x66), Value::from(0x67)),
59                 ],
60                 ..Default::default()
61             },
62             concat!(
63                 "a7", // 7-map
64                 "01", "01", // 1 (alg) => A128GCM
65                 "02", "81", "01", // 2 (crit) => 1-arr [x01]
66                 "03", "10", // 3 (content-type) => 16
67                 "04", "43", "010203", // 4 (kid) => 3-bstr
68                 "05", "43", "010203", // 5 (iv) => 3-bstr
69                 "1846", "1847", // 46 => 47  (note canonical ordering)
70                 "1866", "1867", // 66 => 67
71             ),
72         ),
73         (
74             Header {
75                 alg: Some(Algorithm::Text("abc".to_owned())),
76                 crit: vec![RegisteredLabel::Text("d".to_owned())],
77                 content_type: Some(ContentType::Text("a/b".to_owned())),
78                 key_id: vec![1, 2, 3],
79                 iv: vec![1, 2, 3],
80                 rest: vec![
81                     (Label::Int(0x46), Value::from(0x47)),
82                     (Label::Text("a".to_owned()), Value::from(0x47)),
83                 ],
84                 counter_signatures: vec![CoseSignature {
85                     signature: vec![1, 2, 3],
86                     ..Default::default()
87                 }],
88                 ..Default::default()
89             },
90             concat!(
91                 "a8", // 8-map
92                 "01", "63616263", // 1 (alg) => "abc"
93                 "02", "81", "6164", // 2 (crit) => 1-arr ["d"]
94                 "03", "63612f62", // 3 (content-type) => "a/b"
95                 "04", "43", "010203", // 4 (kid) => 3-bstr
96                 "05", "43", "010203", // 5 (iv) => 3-bstr
97                 "07", "83", // 7 (sig) => [3-arr for COSE_Signature
98                 "40", "a0", "43010203", // ]
99                 "1846", "1847", // 46 => 47  (note canonical ordering)
100                 "6161", "1847", // "a" => 47
101             ),
102         ),
103         (
104             Header {
105                 alg: Some(Algorithm::Text("abc".to_owned())),
106                 crit: vec![RegisteredLabel::Text("d".to_owned())],
107                 content_type: Some(ContentType::Text("a/b".to_owned())),
108                 key_id: vec![1, 2, 3],
109                 iv: vec![1, 2, 3],
110                 rest: vec![
111                     (Label::Int(0x46), Value::from(0x47)),
112                     (Label::Text("a".to_owned()), Value::from(0x47)),
113                 ],
114                 counter_signatures: vec![
115                     CoseSignature {
116                         signature: vec![1, 2, 3],
117                         ..Default::default()
118                     },
119                     CoseSignature {
120                         signature: vec![3, 4, 5],
121                         ..Default::default()
122                     },
123                 ],
124                 ..Default::default()
125             },
126             concat!(
127                 "a8", // 8-map
128                 "01", "63616263", // 1 (alg) => "abc"
129                 "02", "81", "6164", // 2 (crit) => 1-arr ["d"]
130                 "03", "63612f62", // 3 (content-type) => "a/b"
131                 "04", "43", "010203", // 4 (kid) => 3-bstr
132                 "05", "43", "010203", // 5 (iv) => 3-bstr
133                 "07", "82", // 7 (sig) => 2-array
134                 "83", "40", "a0", "43010203", // [3-arr for COSE_Signature]
135                 "83", "40", "a0", "43030405", // [3-arr for COSE_Signature]
136                 "1846", "1847", // 46 => 47  (note canonical ordering)
137                 "6161", "1847", // "a" => 47
138             ),
139         ),
140         (
141             HeaderBuilder::new()
142                 .add_critical(iana::HeaderParameter::Alg)
143                 .add_critical(iana::HeaderParameter::Alg)
144                 .build(),
145             concat!(
146                 "a1", // 1-map
147                 "02", "820101", // crit => 2-arr [1, 1]
148             ),
149         ),
150     ];
151     for (i, (header, header_data)) in tests.iter().enumerate() {
152         let got = header.clone().to_vec().unwrap();
153         assert_eq!(*header_data, hex::encode(&got), "case {}", i);
154 
155         let mut got = Header::from_slice(&got).unwrap();
156         for sig in &mut got.counter_signatures {
157             sig.protected.original_data = None;
158         }
159         assert_eq!(*header, got);
160         assert!(!got.is_empty());
161 
162         // The same data also parses as a `ProtectedHeader`
163         let protected = ProtectedHeader {
164             original_data: None,
165             header: header.clone(),
166         };
167         let protected_data = protected.clone().to_vec().unwrap();
168         assert_eq!(*header_data, hex::encode(&protected_data), "case {}", i);
169 
170         let mut got = ProtectedHeader::from_slice(&protected_data).unwrap();
171         for sig in &mut got.header.counter_signatures {
172             sig.protected.original_data = None;
173         }
174         assert!(!got.is_empty());
175         assert_eq!(*header, got.header);
176 
177         // Also try parsing as a protected header inside a `bstr`
178         let prot_bstr_val = protected.cbor_bstr().unwrap();
179         let mut got = ProtectedHeader::from_cbor_bstr(prot_bstr_val).unwrap();
180         for sig in &mut got.header.counter_signatures {
181             sig.protected.original_data = None;
182         }
183         assert!(!got.is_empty());
184         assert_eq!(*header, got.header);
185         assert_eq!(
186             *header_data,
187             hex::encode(&got.original_data.expect("missing original data"))
188         );
189     }
190 }
191 
192 #[test]
test_header_decode_fail()193 fn test_header_decode_fail() {
194     let tests = vec![
195         (
196             concat!(
197                 "a1", // 1-map
198                 "01", "01", // 1 (alg) => 01
199                 "01", // extraneous data
200             ),
201             "extraneous data in CBOR input",
202         ),
203         (
204             concat!(
205                 "a1", // 1-map
206                 "01", "08", // 1 (alg) => invalid value
207             ),
208             "expected value in IANA or private use range",
209         ),
210         (
211             concat!(
212                 "a1", // 1-map
213                 "01", "4101", // 1 (alg) => bstr (invalid value type)
214             ),
215             "expected int/tstr",
216         ),
217         (
218             concat!(
219                 "a1", // 1-map
220                 "02", "4101", // 2 (crit) => bstr (invalid value type)
221             ),
222             "expected array",
223         ),
224         (
225             concat!(
226                 "a1", // 1-map
227                 "02", "81", "4101", // 2 (crit) => [bstr] (invalid value type)
228             ),
229             "expected int/tstr",
230         ),
231         (
232             concat!(
233                 "a1", // 1-map
234                 "02", "80", // 2 (crit) => []
235             ),
236             "expected non-empty array",
237         ),
238         (
239             concat!(
240                 "a1", // 1-map
241                 "03", "81", "4101", // 3 (content-type) => [bstr] (invalid value type)
242             ),
243             "expected int/tstr",
244         ),
245         (
246             concat!(
247                 "a1", // 1-map
248                 "03", "19", "0606", // 3 (content-type) => invalid value 1542
249             ),
250             "expected recognized IANA value",
251         ),
252         (
253             concat!(
254                 "a1", // 1-map
255                 "03", "64", "20612f62" // 3 (content-type) => invalid value " a/b"
256             ),
257             "expected no leading/trailing whitespace",
258         ),
259         (
260             concat!(
261                 "a1", // 1-map
262                 "03", "64", "612f6220" // 3 (content-type) => invalid value "a/b "
263             ),
264             "expected no leading/trailing whitespace",
265         ),
266         (
267             concat!(
268                 "a1", // 1-map
269                 "03", "62", "6162" // 3 (content-type) => invalid value "ab"
270             ),
271             "expected text of form type/subtype",
272         ),
273         (
274             concat!(
275                 "a1", // 1-map
276                 "03", "60", // 3 (content-type) => invalid value ""
277             ),
278             "expected non-empty tstr",
279         ),
280         (
281             concat!(
282                 "a1", // 1-map
283                 "04", "40", // 4 (key-id) => 0-bstr
284             ),
285             "expected non-empty bstr",
286         ),
287         (
288             concat!(
289                 "a1", // 1-map
290                 "04", "01", // 4 (key-id) => invalid value type
291             ),
292             "expected bstr",
293         ),
294         (
295             concat!(
296                 "a1", // 1-map
297                 "05", "40", // 5 (iv) => 0-bstr
298             ),
299             "expected non-empty bstr",
300         ),
301         (
302             concat!(
303                 "a1", // 1-map
304                 "05", "01", // 5 (iv) => invalid value type
305             ),
306             "expected bstr",
307         ),
308         (
309             concat!(
310                 "a1", // 1-map
311                 "06", "40", // 6 (partial-iv) => 0-bstr
312             ),
313             "expected non-empty bstr",
314         ),
315         (
316             concat!(
317                 "a1", // 1-map
318                 "06", "01", // 6 (partial-iv) => invalid value type
319             ),
320             "expected bstr",
321         ),
322         (
323             concat!(
324                 "a1", // 1-map
325                 "07", "01", // 7 (counter-sig) => invalid value type
326             ),
327             "expected array",
328         ),
329         (
330             concat!(
331                 "a1", // 1-map
332                 "07", "80", // 7 (counter-sig) => 0-arr
333             ),
334             "expected non-empty sig array",
335         ),
336         (
337             concat!(
338                 "a2", // 1-map
339                 "05", "4101", // 5 (iv) => 1-bstr
340                 "06", "4101", // 6 (partial-iv) => 1-bstr
341             ),
342             "expected only one of IV and partial IV",
343         ),
344         (
345             concat!(
346                 "a2", // 2-map
347                 "01", "63616263", // 1 (alg) => "abc"
348                 "07", "82",       // 7 (sig) => 2-array
349                 "63616263", // tstr (invalid)
350                 "83", "40", "a0", "43010203", // [3-arr for COSE_Signature]
351             ),
352             "array or bstr value",
353         ),
354     ];
355     for (header_data, err_msg) in tests.iter() {
356         let data = hex::decode(header_data).unwrap();
357         let result = Header::from_slice(&data);
358         expect_err(result, err_msg);
359     }
360 }
361 
362 #[test]
test_header_decode_dup_fail()363 fn test_header_decode_dup_fail() {
364     let tests = [
365         (
366             concat!(
367                 "a3", // 3-map
368                 "01", "01", // 1 (alg) => A128GCM
369                 "1866", "1867", // 66 => 67
370                 "1866", "1847", // 66 => 47
371             ),
372             "duplicate map key",
373         ),
374         (
375             concat!(
376                 "a3", // 3-map
377                 "01", "01", // 1 (alg) => A128GCM
378                 "1866", "1867", // 66 => 67
379                 "01", "01", // 1 (alg) => A128GCM (duplicate label)
380             ),
381             "duplicate map key",
382         ),
383     ];
384     for (header_data, err_msg) in tests.iter() {
385         let data = hex::decode(header_data).unwrap();
386         let result = Header::from_slice(&data);
387         expect_err(result, err_msg);
388     }
389 }
390 
391 #[test]
test_header_encode_dup_fail()392 fn test_header_encode_dup_fail() {
393     let tests = vec![
394         Header {
395             alg: Some(Algorithm::Assigned(iana::Algorithm::A128GCM)),
396             crit: vec![RegisteredLabel::Assigned(iana::HeaderParameter::Alg)],
397             content_type: Some(ContentType::Assigned(iana::CoapContentFormat::CoseEncrypt0)),
398             key_id: vec![1, 2, 3],
399             iv: vec![1, 2, 3],
400             rest: vec![
401                 (Label::Int(0x46), Value::from(0x47)),
402                 (Label::Int(0x46), Value::from(0x67)),
403             ],
404             ..Default::default()
405         },
406         HeaderBuilder::new()
407             .text_value("doop".to_owned(), Value::from(1))
408             .text_value("doop".to_owned(), Value::from(2))
409             .build(),
410     ];
411     for header in tests {
412         let result = header.clone().to_vec();
413         expect_err(result, "duplicate map key");
414     }
415 }
416 
417 #[test]
test_header_builder()418 fn test_header_builder() {
419     let tests = vec![
420         (
421             HeaderBuilder::new().build(),
422             Header {
423                 ..Default::default()
424             },
425         ),
426         (
427             HeaderBuilder::new()
428                 .algorithm(iana::Algorithm::A128GCM)
429                 .add_critical(iana::HeaderParameter::Alg)
430                 .add_critical_label(RegisteredLabel::Text("abc".to_owned()))
431                 .content_format(iana::CoapContentFormat::CoseEncrypt0)
432                 .key_id(vec![1, 2, 3])
433                 .partial_iv(vec![4, 5, 6]) // removed by .iv() call
434                 .iv(vec![1, 2, 3])
435                 .value(0x46, Value::from(0x47))
436                 .value(0x66, Value::from(0x67))
437                 .build(),
438             Header {
439                 alg: Some(Algorithm::Assigned(iana::Algorithm::A128GCM)),
440                 crit: vec![
441                     RegisteredLabel::Assigned(iana::HeaderParameter::Alg),
442                     RegisteredLabel::Text("abc".to_owned()),
443                 ],
444                 content_type: Some(ContentType::Assigned(iana::CoapContentFormat::CoseEncrypt0)),
445                 key_id: vec![1, 2, 3],
446                 iv: vec![1, 2, 3],
447                 rest: vec![
448                     (Label::Int(0x46), Value::from(0x47)),
449                     (Label::Int(0x66), Value::from(0x67)),
450                 ],
451                 ..Default::default()
452             },
453         ),
454         (
455             HeaderBuilder::new()
456                 .algorithm(iana::Algorithm::A128GCM)
457                 .add_critical(iana::HeaderParameter::Alg)
458                 .add_critical_label(RegisteredLabel::Text("abc".to_owned()))
459                 .content_type("type/subtype".to_owned())
460                 .key_id(vec![1, 2, 3])
461                 .iv(vec![1, 2, 3]) // removed by .partial_iv() call
462                 .partial_iv(vec![4, 5, 6])
463                 .build(),
464             Header {
465                 alg: Some(Algorithm::Assigned(iana::Algorithm::A128GCM)),
466                 crit: vec![
467                     RegisteredLabel::Assigned(iana::HeaderParameter::Alg),
468                     RegisteredLabel::Text("abc".to_owned()),
469                 ],
470                 content_type: Some(ContentType::Text("type/subtype".to_owned())),
471                 key_id: vec![1, 2, 3],
472                 partial_iv: vec![4, 5, 6],
473                 ..Default::default()
474             },
475         ),
476     ];
477     for (got, want) in tests {
478         assert_eq!(got, want);
479     }
480 }
481 
482 #[test]
483 #[should_panic]
test_header_builder_core_param_panic()484 fn test_header_builder_core_param_panic() {
485     // Attempting to set a core header parameter (in range [1,7]) via `.param()` panics.
486     let _hdr = HeaderBuilder::new().value(1, Value::Null).build();
487 }
488