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