epan/dissectors/pidl/ C99 drsuapi
[wireshark-sm.git] / epan / dissectors / packet-cbor.c
blob11fccd928fcace3bf5e68a8d68126c5b88dbd90b
1 /* packet-cbor.c
2 * Routines for Concise Binary Object Representation (CBOR) (RFC 7049) dissection
3 * References:
4 * RFC 7049: https://tools.ietf.org/html/rfc7049
5 * RFC 8742: https://tools.ietf.org/html/rfc8742
7 * Copyright 2015, Hauke Mehrtens <hauke@hauke-m.de>
8 * Copyright 2022, Stig Bjorlykke <stig@bjorlykke.org>
10 * Wireshark - Network traffic analyzer
11 * By Gerald Combs <gerald@wireshark.org>
12 * Copyright 1998 Gerald Combs
14 * SPDX-License-Identifier: GPL-2.0-or-later
17 #include "config.h"
19 #include <math.h>
21 #include <epan/packet.h>
22 #include <epan/expert.h>
23 #include <epan/proto_data.h>
24 #include <wsutil/str_util.h>
26 void proto_register_cbor(void);
27 void proto_reg_handoff_cbor(void);
29 static int proto_cbor;
31 static int hf_cbor_item_major_type;
32 static int hf_cbor_item_integer_size;
33 static int hf_cbor_item_length_size;
34 static int hf_cbor_item_length5;
35 static int hf_cbor_item_length;
36 static int hf_cbor_item_items5;
37 static int hf_cbor_item_items;
38 static int hf_cbor_item_pairs5;
39 static int hf_cbor_item_pairs;
40 static int hf_cbor_item_float_simple_type;
41 static int hf_cbor_item_unsigned_integer;
42 static int hf_cbor_item_negative_integer;
43 static int hf_cbor_item_text_string;
44 static int hf_cbor_item_byte_string;
45 static int hf_cbor_item_array;
46 static int hf_cbor_item_map;
47 static int hf_cbor_item_tag;
48 static int hf_cbor_item_float_simple;
49 static int hf_cbor_type_uint5;
50 static int hf_cbor_type_uint;
51 static int hf_cbor_type_nint;
52 static int hf_cbor_type_byte_string;
53 static int hf_cbor_type_byte_string_indef;
54 static int hf_cbor_type_text_string;
55 static int hf_cbor_type_text_string_indef;
56 static int hf_cbor_type_tag5;
57 static int hf_cbor_type_tag;
58 static int hf_cbor_type_simple_data5;
59 static int hf_cbor_type_simple_data8;
60 static int hf_cbor_type_float16;
61 static int hf_cbor_type_float32;
62 static int hf_cbor_type_float64;
64 static int ett_cbor;
65 static int ett_cbor_type;
66 static int ett_cbor_unsigned_integer;
67 static int ett_cbor_negative_integer;
68 static int ett_cbor_byte_string;
69 static int ett_cbor_byte_string_indef;
70 static int ett_cbor_text_string;
71 static int ett_cbor_text_string_indef;
72 static int ett_cbor_array;
73 static int ett_cbor_map;
74 static int ett_cbor_tag;
75 static int ett_cbor_float_simple;
77 static expert_field ei_cbor_invalid_minor_type;
78 static expert_field ei_cbor_invalid_element;
79 static expert_field ei_cbor_too_long_length;
80 static expert_field ei_cbor_max_recursion_depth_reached;
82 static dissector_handle_t cbor_handle;
83 static dissector_handle_t cborseq_handle;
85 #define CBOR_TYPE_USIGNED_INT 0
86 #define CBOR_TYPE_NEGATIVE_INT 1
87 #define CBOR_TYPE_BYTE_STRING 2
88 #define CBOR_TYPE_TEXT_STRING 3
89 #define CBOR_TYPE_ARRAY 4
90 #define CBOR_TYPE_MAP 5
91 #define CBOR_TYPE_TAGGED 6
92 #define CBOR_TYPE_FLOAT 7
94 static const value_string major_type_vals[] = {
95 { 0, "Unsigned Integer" },
96 { 1, "Negative Integer" },
97 { 2, "Byte String" },
98 { 3, "Text String" },
99 { 4, "Array" },
100 { 5, "Map" },
101 { 6, "Tagged" },
102 { 7, "Floating-Point or Simple" },
103 { 0, NULL }
106 static const value_string integer_size_vals[] = {
107 { 24, "1 byte" },
108 { 25, "2 bytes" },
109 { 26, "4 bytes" },
110 { 27, "8 bytes" },
111 { 28, "Reserved for future additions" },
112 { 29, "Reserved for future additions" },
113 { 30, "Reserved for future additions" },
114 { 31, "No argument value is derived" },
115 { 0, NULL }
118 static const value_string length_size_vals[] = {
119 { 24, "1 byte" },
120 { 25, "2 bytes" },
121 { 26, "4 bytes" },
122 { 27, "8 bytes" },
123 { 28, "Reserved for future additions" },
124 { 29, "Reserved for future additions" },
125 { 30, "Reserved for future additions" },
126 { 31, "Indefinite Length" },
127 { 0, NULL }
130 static const value_string float_simple_type_vals[] = {
131 { 24, "Simple value" },
132 { 25, "IEEE 754 Half-Precision Float" },
133 { 26, "IEEE 754 Single-Precision Float" },
134 { 27, "IEEE 754 Double-Precision Float" },
135 { 28, "Reserved for future additions" },
136 { 29, "Reserved for future additions" },
137 { 30, "Reserved for future additions" },
138 { 31, "Break indefinite length" },
139 { 0, NULL }
142 /* see https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml#tags */
143 static const value_string tag32_vals[] = {
144 { 0, "Standard date/time string" },
145 { 1, "Epoch-based date/time" },
146 { 2, "Positive bignum" },
147 { 3, "Negative bignum" },
148 { 4, "Decimal fraction" },
149 { 5, "Bigfloat" },
150 { 21, "Expected conversion to base64url encoding" },
151 { 22, "Expected conversion to base64 encoding" },
152 { 23, "Expected conversion to base16 encoding" },
153 { 24, "Encoded CBOR data item" },
154 { 25, "reference the nth previously seen string" },
155 { 26, "Serialised Perl object with classname and constructor arguments" },
156 { 27, "Serialised language-independent object with type name and constructor arguments" },
157 { 28, "mark value as (potentially) shared" },
158 { 29, "reference nth marked value" },
159 { 30, "Rational number" },
160 { 32, "URI" },
161 { 33, "base64url" },
162 { 34, "base64" },
163 { 35, "Regular expression" },
164 { 36, "MIME message" },
165 { 37, "Binary UUID" },
166 { 38, "Language-tagged string" },
167 { 39, "Identifier" },
168 { 256, "mark value as having string references" },
169 { 257, "Binary MIME message" },
170 { 264, "Decimal fraction with arbitrary exponent" },
171 { 265, "Bigfloat with arbitrary exponent" },
172 { 22098, "hint that indicates an additional level of indirection" },
173 { 55799, "Self-describe CBOR" },
174 { 0, NULL },
177 static const val64_string tag64_vals[] = {
178 { 0, "Standard date/time string" },
179 { 1, "Epoch-based date/time" },
180 { 2, "Positive bignum" },
181 { 3, "Negative bignum" },
182 { 4, "Decimal fraction" },
183 { 5, "Bigfloat" },
184 { 21, "Expected conversion to base64url encoding" },
185 { 22, "Expected conversion to base64 encoding" },
186 { 23, "Expected conversion to base16 encoding" },
187 { 24, "Encoded CBOR data item" },
188 { 25, "reference the nth previously seen string" },
189 { 26, "Serialised Perl object with classname and constructor arguments" },
190 { 27, "Serialised language-independent object with type name and constructor arguments" },
191 { 28, "mark value as (potentially) shared" },
192 { 29, "reference nth marked value" },
193 { 30, "Rational number" },
194 { 32, "URI" },
195 { 33, "base64url" },
196 { 34, "base64" },
197 { 35, "Regular expression" },
198 { 36, "MIME message" },
199 { 37, "Binary UUID" },
200 { 38, "Language-tagged string" },
201 { 39, "Identifier" },
202 { 256, "mark value as having string references" },
203 { 257, "Binary MIME message" },
204 { 264, "Decimal fraction with arbitrary exponent" },
205 { 265, "Bigfloat with arbitrary exponent" },
206 { 22098, "hint that indicates an additional level of indirection" },
207 { 55799, "Self-describe CBOR" },
208 { 0, NULL },
211 static const value_string vals_simple_data[] = {
212 { 20, "False" },
213 { 21, "True" },
214 { 22, "Null" },
215 { 23, "Undefined" },
216 { 0, NULL },
219 static bool
220 dissect_cbor_main_type(tvbuff_t *tvb, packet_info *pinfo, proto_tree *cbor_tree, int *offset);
222 static bool
223 dissect_cbor_float_simple_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *cbor_tree, int *offset, uint8_t type_minor);
225 static bool
226 dissect_cbor_unsigned_integer(tvbuff_t *tvb, packet_info *pinfo, proto_tree *cbor_tree, int *offset, uint8_t type_minor)
228 uint64_t value = 0;
229 proto_item *item;
230 proto_tree *subtree;
232 item = proto_tree_add_item(cbor_tree, hf_cbor_item_unsigned_integer, tvb, *offset, -1, ENC_NA);
233 subtree = proto_item_add_subtree(item, ett_cbor_unsigned_integer);
235 proto_tree_add_item(subtree, hf_cbor_item_major_type, tvb, *offset, 1, ENC_BIG_ENDIAN);
236 if (type_minor <= 0x17) {
237 proto_tree_add_item(subtree, hf_cbor_type_uint5, tvb, *offset, 1, ENC_BIG_ENDIAN);
238 value = type_minor;
239 } else {
240 proto_tree_add_item(subtree, hf_cbor_item_integer_size, tvb, *offset, 1, ENC_BIG_ENDIAN);
242 *offset += 1;
244 switch (type_minor) {
245 case 0x18:
246 proto_tree_add_item_ret_uint64(subtree, hf_cbor_type_uint, tvb, *offset, 1, ENC_BIG_ENDIAN, &value);
247 *offset += 1;
248 break;
249 case 0x19:
250 proto_tree_add_item_ret_uint64(subtree, hf_cbor_type_uint, tvb, *offset, 2, ENC_BIG_ENDIAN, &value);
251 *offset += 2;
252 break;
253 case 0x1a:
254 proto_tree_add_item_ret_uint64(subtree, hf_cbor_type_uint, tvb, *offset, 4, ENC_BIG_ENDIAN, &value);
255 *offset += 4;
256 break;
257 case 0x1b:
258 proto_tree_add_item_ret_uint64(subtree, hf_cbor_type_uint, tvb, *offset, 8, ENC_BIG_ENDIAN, &value);
259 *offset += 8;
260 break;
261 default:
262 if (type_minor > 0x17) {
263 expert_add_info_format(pinfo, subtree, &ei_cbor_invalid_minor_type,
264 "invalid minor type %i in unsigned integer", type_minor);
265 return false;
267 break;
270 proto_item_append_text(item, ": %" PRIu64, value);
271 proto_item_set_end(item, tvb, *offset);
273 return true;
276 static bool
277 dissect_cbor_negative_integer(tvbuff_t *tvb, packet_info *pinfo, proto_tree *cbor_tree, int *offset, uint8_t type_minor)
279 int64_t value = 0;
280 proto_item *item;
281 proto_tree *subtree;
283 item = proto_tree_add_item(cbor_tree, hf_cbor_item_negative_integer, tvb, *offset, -1, ENC_NA);
284 subtree = proto_item_add_subtree(item, ett_cbor_negative_integer);
286 proto_tree_add_item(subtree, hf_cbor_item_major_type, tvb, *offset, 1, ENC_BIG_ENDIAN);
287 if (type_minor <= 0x17) {
288 value = (int64_t)-1 - type_minor;
289 /* Keep correct bit representation with a modified value. */
290 proto_tree_add_int64_bits_format_value(subtree, hf_cbor_type_nint, tvb, 3, 5, type_minor, ENC_BIG_ENDIAN, "%" PRId64, value);
291 } else {
292 proto_tree_add_item(subtree, hf_cbor_item_integer_size, tvb, *offset, 1, ENC_BIG_ENDIAN);
294 *offset += 1;
296 switch (type_minor) {
297 case 0x18:
298 value = (int64_t)-1 - tvb_get_uint8(tvb, *offset);
299 proto_tree_add_int64(subtree, hf_cbor_type_nint, tvb, *offset, 1, value);
300 *offset += 1;
301 break;
302 case 0x19:
303 value = (int64_t)-1 - tvb_get_ntohs(tvb, *offset);
304 proto_tree_add_int64(subtree, hf_cbor_type_nint, tvb, *offset, 2, value);
305 *offset += 2;
306 break;
307 case 0x1a:
308 value = (int64_t)-1 - tvb_get_ntohl(tvb, *offset);
309 proto_tree_add_int64(subtree, hf_cbor_type_nint, tvb, *offset, 4, value);
310 *offset += 4;
311 break;
312 case 0x1b:
313 /* TODO: an overflow could happen here, for negative int < INT64_MIN */
314 value = (int64_t)-1 - tvb_get_ntoh64(tvb, *offset);
315 if (value > -1) {
316 expert_add_info_format(pinfo, subtree, &ei_cbor_too_long_length,
317 "The value is too small, Wireshark can not display it correctly");
319 proto_tree_add_int64(subtree, hf_cbor_type_nint, tvb, *offset, 8, value);
320 *offset += 8;
321 break;
322 default:
323 if (type_minor > 0x17) {
324 expert_add_info_format(pinfo, subtree, &ei_cbor_invalid_minor_type,
325 "invalid minor type %i in negative integer", type_minor);
326 return false;
328 break;
331 proto_item_append_text(item, ": %" PRId64, value);
332 proto_item_set_end(item, tvb, *offset);
334 return true;
337 static bool
338 // NOLINTNEXTLINE(misc-no-recursion)
339 dissect_cbor_byte_string(tvbuff_t *tvb, packet_info *pinfo, proto_tree *cbor_tree, int *offset, uint8_t type_minor)
341 uint64_t length;
342 int eof_type;
343 proto_tree *subtree;
344 proto_item *item;
346 item = proto_tree_add_item(cbor_tree, hf_cbor_item_byte_string, tvb, *offset, -1, ENC_NA);
347 subtree = proto_item_add_subtree(item, ett_cbor_byte_string);
349 proto_tree_add_item(subtree, hf_cbor_item_major_type, tvb, *offset, 1, ENC_BIG_ENDIAN);
350 if (type_minor <= 0x17) {
351 proto_tree_add_item(subtree, hf_cbor_item_length5, tvb, *offset, 1, ENC_BIG_ENDIAN);
352 length = type_minor;
353 } else {
354 proto_tree_add_item(subtree, hf_cbor_item_length_size, tvb, *offset, 1, ENC_BIG_ENDIAN);
356 *offset += 1;
358 switch (type_minor) {
359 case 0x18:
360 proto_tree_add_item_ret_uint64(subtree, hf_cbor_item_length, tvb, *offset, 1, ENC_BIG_ENDIAN, &length);
361 *offset += 1;
362 break;
363 case 0x19:
364 proto_tree_add_item_ret_uint64(subtree, hf_cbor_item_length, tvb, *offset, 2, ENC_BIG_ENDIAN, &length);
365 *offset += 2;
366 break;
367 case 0x1a:
368 proto_tree_add_item_ret_uint64(subtree, hf_cbor_item_length, tvb, *offset, 4, ENC_BIG_ENDIAN, &length);
369 *offset += 4;
370 break;
371 case 0x1b:
372 proto_tree_add_item_ret_uint64(subtree, hf_cbor_item_length, tvb, *offset, 8, ENC_BIG_ENDIAN, &length);
373 *offset += 8;
374 break;
375 case 0x1f:
376 proto_item_append_text(item, ": (indefinite length)");
377 item = proto_tree_add_item(subtree, hf_cbor_type_byte_string_indef, tvb, *offset, 1, ENC_NA);
378 subtree = proto_item_add_subtree(item, ett_cbor_byte_string_indef);
379 while (1) {
380 eof_type = tvb_get_uint8(tvb, *offset);
381 if (eof_type == 0xff) {
382 dissect_cbor_float_simple_data(tvb, pinfo, subtree, offset, 0x1f);
383 proto_item_set_end(item, tvb, *offset);
384 return true;
387 if (((eof_type & 0xe0) >> 5) != CBOR_TYPE_BYTE_STRING) {
388 expert_add_info_format(pinfo, subtree, &ei_cbor_invalid_element,
389 "invalid element %i, expected byte string", (eof_type & 0xe0) >> 5);
390 return false;
393 unsigned recursion_depth = p_get_proto_depth(pinfo, proto_cbor);
394 if (recursion_depth > prefs.gui_max_tree_depth) {
395 proto_tree_add_expert(subtree, pinfo, &ei_cbor_max_recursion_depth_reached, tvb, 0, 0);
396 return false;
398 p_set_proto_depth(pinfo, proto_cbor, recursion_depth + 1);
400 bool recursed = dissect_cbor_byte_string(tvb, pinfo, subtree, offset, eof_type & 0x1f);
401 p_set_proto_depth(pinfo, proto_cbor, recursion_depth);
403 if (!recursed) {
404 return false;
407 DISSECTOR_ASSERT_NOT_REACHED();
408 return false;
409 default:
410 if (type_minor > 0x17) {
411 expert_add_info_format(pinfo, subtree, &ei_cbor_invalid_minor_type,
412 "invalid minor type %i in byte string", type_minor);
413 return false;
415 break;
418 if (length > INT32_MAX || *offset + (int)length < *offset) {
419 expert_add_info_format(pinfo, subtree, &ei_cbor_too_long_length,
420 "the length (%" PRIu64 ") of the byte string too long", length);
421 return false;
424 proto_tree_add_item(subtree, hf_cbor_type_byte_string, tvb, *offset, (int)length, ENC_BIG_ENDIAN|ENC_NA);
425 *offset += (int)length;
427 proto_item_append_text(item, ": (%" PRIu64 " byte%s)", length, plurality(length, "", "s"));
428 proto_item_set_end(item, tvb, *offset);
430 return true;
433 static bool
434 // NOLINTNEXTLINE(misc-no-recursion)
435 dissect_cbor_text_string(tvbuff_t *tvb, packet_info *pinfo, proto_tree *cbor_tree, int *offset, uint8_t type_minor)
437 const uint8_t *value = NULL;
438 uint64_t length = 0;
439 int eof_type;
440 proto_tree *subtree;
441 proto_item *item;
443 item = proto_tree_add_item(cbor_tree, hf_cbor_item_text_string, tvb, *offset, -1, ENC_NA);
444 subtree = proto_item_add_subtree(item, ett_cbor_text_string);
446 proto_tree_add_item(subtree, hf_cbor_item_major_type, tvb, *offset, 1, ENC_BIG_ENDIAN);
447 if (type_minor <= 0x17) {
448 proto_tree_add_item(subtree, hf_cbor_item_length5, tvb, *offset, 1, ENC_BIG_ENDIAN);
449 length = type_minor;
450 } else {
451 proto_tree_add_item(subtree, hf_cbor_item_length_size, tvb, *offset, 1, ENC_BIG_ENDIAN);
453 *offset += 1;
455 switch (type_minor) {
456 case 0x18:
457 proto_tree_add_item_ret_uint64(subtree, hf_cbor_item_length, tvb, *offset, 1, ENC_BIG_ENDIAN, &length);
458 *offset += 1;
459 break;
460 case 0x19:
461 proto_tree_add_item_ret_uint64(subtree, hf_cbor_item_length, tvb, *offset, 2, ENC_BIG_ENDIAN, &length);
462 *offset += 2;
463 break;
464 case 0x1a:
465 proto_tree_add_item_ret_uint64(subtree, hf_cbor_item_length, tvb, *offset, 4, ENC_BIG_ENDIAN, &length);
466 *offset += 4;
467 break;
468 case 0x1b:
469 proto_tree_add_item_ret_uint64(subtree, hf_cbor_item_length, tvb, *offset, 8, ENC_BIG_ENDIAN, &length);
470 *offset += 8;
471 break;
472 case 0x1f:
473 proto_item_append_text(item, ": (indefinite length)");
474 item = proto_tree_add_item(subtree, hf_cbor_type_text_string_indef, tvb, *offset, 1, ENC_NA);
475 subtree = proto_item_add_subtree(item, ett_cbor_text_string_indef);
476 while (1) {
477 eof_type = tvb_get_uint8(tvb, *offset);
478 if (eof_type == 0xff) {
479 dissect_cbor_float_simple_data(tvb, pinfo, subtree, offset, 0x1f);
480 proto_item_set_end(item, tvb, *offset);
481 return true;
484 if (((eof_type & 0xe0) >> 5) != CBOR_TYPE_TEXT_STRING) {
485 expert_add_info_format(pinfo, subtree, &ei_cbor_invalid_element,
486 "invalid element %i, expected text string", (eof_type & 0xe0) >> 5);
487 return false;
490 unsigned recursion_depth = p_get_proto_depth(pinfo, proto_cbor);
491 if (recursion_depth > prefs.gui_max_tree_depth) {
492 proto_tree_add_expert(subtree, pinfo, &ei_cbor_max_recursion_depth_reached, tvb, 0, 0);
493 return false;
495 p_set_proto_depth(pinfo, proto_cbor, recursion_depth + 1);
497 bool recursed = dissect_cbor_text_string(tvb, pinfo, subtree, offset, eof_type & 0x1f);
498 p_set_proto_depth(pinfo, proto_cbor, recursion_depth);
500 if (!recursed) {
501 return false;
504 DISSECTOR_ASSERT_NOT_REACHED();
505 return false;
506 default:
507 if (type_minor > 0x17) {
508 expert_add_info_format(pinfo, subtree, &ei_cbor_invalid_minor_type,
509 "invalid minor type %i in text string", type_minor);
510 return false;
512 break;
515 if (length > INT32_MAX || *offset + (int)length < *offset) {
516 expert_add_info_format(pinfo, subtree, &ei_cbor_too_long_length,
517 "the length (%" PRIu64 ") of the text string too long", length);
518 return false;
521 proto_tree_add_item_ret_string(subtree, hf_cbor_type_text_string, tvb, *offset, (int)length, ENC_BIG_ENDIAN|ENC_UTF_8, pinfo->pool, &value);
522 *offset += (int)length;
524 proto_item_append_text(item, ": %s", value);
525 proto_item_set_end(item, tvb, *offset);
527 return true;
530 static bool
531 // NOLINTNEXTLINE(misc-no-recursion)
532 dissect_cbor_array(tvbuff_t *tvb, packet_info *pinfo, proto_tree *cbor_tree, int *offset, uint8_t type_minor)
534 uint64_t length = 0;
535 proto_tree *subtree;
536 proto_item *item;
537 bool indefinite = false;
539 item = proto_tree_add_item(cbor_tree, hf_cbor_item_array, tvb, *offset, -1, ENC_NA);
540 subtree = proto_item_add_subtree(item, ett_cbor_array);
542 proto_tree_add_item(subtree, hf_cbor_item_major_type, tvb, *offset, 1, ENC_BIG_ENDIAN);
544 if (type_minor <= 0x17) {
545 proto_tree_add_item(subtree, hf_cbor_item_items5, tvb, *offset, 1, ENC_BIG_ENDIAN);
546 length = type_minor;
547 } else {
548 proto_tree_add_item(subtree, hf_cbor_item_length_size, tvb, *offset, 1, ENC_BIG_ENDIAN);
550 *offset += 1;
552 switch (type_minor) {
553 case 0x18:
554 proto_tree_add_item_ret_uint64(subtree, hf_cbor_item_items, tvb, *offset, 1, ENC_BIG_ENDIAN, &length);
555 *offset += 1;
556 break;
557 case 0x19:
558 proto_tree_add_item_ret_uint64(subtree, hf_cbor_item_items, tvb, *offset, 2, ENC_BIG_ENDIAN, &length);
559 *offset += 2;
560 break;
561 case 0x1a:
562 proto_tree_add_item_ret_uint64(subtree, hf_cbor_item_items, tvb, *offset, 4, ENC_BIG_ENDIAN, &length);
563 *offset += 4;
564 break;
565 case 0x1b:
566 proto_tree_add_item_ret_uint64(subtree, hf_cbor_item_items, tvb, *offset, 8, ENC_BIG_ENDIAN, &length);
567 *offset += 8;
568 break;
569 case 0x1f:
570 length = INT_MAX;
571 indefinite = true;
572 break;
573 default:
574 if (type_minor > 0x17) {
575 expert_add_info_format(pinfo, subtree, &ei_cbor_invalid_minor_type,
576 "invalid minor type %i in array", type_minor);
577 return false;
579 break;
582 for (uint64_t i = 0; i < length; i++) {
583 if (indefinite) {
584 int value = tvb_get_uint8(tvb, *offset);
585 if (value == 0xff) {
586 dissect_cbor_float_simple_data(tvb, pinfo, subtree, offset, 0x1f);
587 break;
591 if (!dissect_cbor_main_type(tvb, pinfo, subtree, offset)) {
592 return false;
596 if (indefinite) {
597 proto_item_append_text(item, ": (indefinite length)");
598 } else {
599 proto_item_append_text(item, ": (%" PRIu64 " item%s)", length, plurality(length, "", "s"));
601 proto_item_set_end(item, tvb, *offset);
603 return true;
606 static bool
607 // NOLINTNEXTLINE(misc-no-recursion)
608 dissect_cbor_map(tvbuff_t *tvb, packet_info *pinfo, proto_tree *cbor_tree, int *offset, uint8_t type_minor)
610 uint64_t length = 0;
611 proto_tree *subtree;
612 proto_item *item;
613 bool indefinite = false;
615 item = proto_tree_add_item(cbor_tree, hf_cbor_item_map, tvb, *offset, -1, ENC_NA);
616 subtree = proto_item_add_subtree(item, ett_cbor_map);
618 proto_tree_add_item(subtree, hf_cbor_item_major_type, tvb, *offset, 1, ENC_BIG_ENDIAN);
620 if (type_minor <= 0x17) {
621 proto_tree_add_item(subtree, hf_cbor_item_pairs5, tvb, *offset, 1, ENC_BIG_ENDIAN);
622 length = type_minor;
623 } else {
624 proto_tree_add_item(subtree, hf_cbor_item_length_size, tvb, *offset, 1, ENC_BIG_ENDIAN);
626 *offset += 1;
628 switch (type_minor) {
629 case 0x18:
630 proto_tree_add_item_ret_uint64(subtree, hf_cbor_item_pairs, tvb, *offset, 1, ENC_BIG_ENDIAN, &length);
631 *offset += 1;
632 break;
633 case 0x19:
634 proto_tree_add_item_ret_uint64(subtree, hf_cbor_item_pairs, tvb, *offset, 2, ENC_BIG_ENDIAN, &length);
635 *offset += 2;
636 break;
637 case 0x1a:
638 proto_tree_add_item_ret_uint64(subtree, hf_cbor_item_pairs, tvb, *offset, 4, ENC_BIG_ENDIAN, &length);
639 *offset += 4;
640 break;
641 case 0x1b:
642 proto_tree_add_item_ret_uint64(subtree, hf_cbor_item_pairs, tvb, *offset, 8, ENC_BIG_ENDIAN, &length);
643 *offset += 8;
644 break;
645 case 0x1f:
646 length = INT_MAX;
647 indefinite = true;
648 break;
649 default:
650 if (type_minor > 0x17) {
651 expert_add_info_format(pinfo, subtree, &ei_cbor_invalid_minor_type,
652 "invalid minor type %i in map", type_minor);
653 return false;
655 break;
658 for (uint64_t i = 0; i < length; i++) {
659 if (indefinite) {
660 int value = tvb_get_uint8(tvb, *offset);
661 if (value == 0xff) {
662 dissect_cbor_float_simple_data(tvb, pinfo, subtree, offset, 0x1f);
663 break;
667 if (!dissect_cbor_main_type(tvb, pinfo, subtree, offset)) {
668 return false;
671 if (!dissect_cbor_main_type(tvb, pinfo, subtree, offset)) {
672 return false;
676 if (indefinite) {
677 proto_item_append_text(item, ": (indefinite length)");
678 } else {
679 proto_item_append_text(item, ": (%" PRIu64 " pair%s)", length, plurality(length, "", "s"));
681 proto_item_set_end(item, tvb, *offset);
683 return true;
686 static bool
687 // NOLINTNEXTLINE(misc-no-recursion)
688 dissect_cbor_tag(tvbuff_t *tvb, packet_info *pinfo, proto_tree *cbor_tree, int *offset, uint8_t type_minor)
690 uint64_t tag = 0;
691 proto_item *item;
692 proto_tree *subtree;
694 item = proto_tree_add_item(cbor_tree, hf_cbor_item_tag, tvb, *offset, -1, ENC_NA);
695 subtree = proto_item_add_subtree(item, ett_cbor_tag);
697 proto_tree_add_item(subtree, hf_cbor_item_major_type, tvb, *offset, 1, ENC_BIG_ENDIAN);
699 if (type_minor <= 0x17) {
700 proto_tree_add_item(subtree, hf_cbor_type_tag5, tvb, *offset, 1, ENC_BIG_ENDIAN);
701 tag = type_minor;
702 } else {
703 proto_tree_add_item(subtree, hf_cbor_item_integer_size, tvb, *offset, 1, ENC_BIG_ENDIAN);
705 *offset += 1;
707 switch (type_minor) {
708 case 0x18:
709 proto_tree_add_item_ret_uint64(subtree, hf_cbor_type_tag, tvb, *offset, 1, ENC_BIG_ENDIAN, &tag);
710 *offset += 1;
711 break;
712 case 0x19:
713 proto_tree_add_item_ret_uint64(subtree, hf_cbor_type_tag, tvb, *offset, 2, ENC_BIG_ENDIAN, &tag);
714 *offset += 2;
715 break;
716 case 0x1a:
717 proto_tree_add_item_ret_uint64(subtree, hf_cbor_type_tag, tvb, *offset, 4, ENC_BIG_ENDIAN, &tag);
718 *offset += 4;
719 break;
720 case 0x1b:
721 proto_tree_add_item_ret_uint64(subtree, hf_cbor_type_tag, tvb, *offset, 8, ENC_BIG_ENDIAN, &tag);
722 *offset += 8;
723 break;
724 default:
725 if (type_minor > 0x17) {
726 expert_add_info_format(pinfo, subtree, &ei_cbor_invalid_minor_type,
727 "invalid minor type %i in tag", type_minor);
728 return false;
730 break;
733 if (!dissect_cbor_main_type(tvb, pinfo, subtree, offset)) {
734 return false;
737 proto_item_append_text(item, ": %s (%" PRIu64 ")", val64_to_str(tag, tag64_vals, "Unknown"), tag);
738 proto_item_set_end(item, tvb, *offset);
740 return true;
743 /* based on code from rfc7049 appendix-D */
744 static void
745 decode_half(tvbuff_t *tvb, proto_tree *tree, proto_item *item, int *offset, int hfindex)
747 char value[6];
748 int half, exponent, mantissa;
749 float val = 0;
751 half = tvb_get_ntohs(tvb, *offset);
752 exponent = (half >> 10) & 0x1f;
753 mantissa = half & 0x3ff;
755 if (exponent == 0) {
756 val = ldexpf((float)mantissa, -24);
757 if (half & 0x8000) {
758 val = -val;
760 proto_tree_add_float(tree, hfindex, tvb, *offset, 2, val);
761 proto_item_set_text(item, "Float: %." G_STRINGIFY(FLT_DIG) "g", val);
762 } else if (exponent != 31) {
763 val = ldexpf((float)(mantissa + 1024), exponent - 25);
764 if (half & 0x8000) {
765 val = -val;
767 proto_tree_add_float(tree, hfindex, tvb, *offset, 2, val);
768 proto_item_set_text(item, "Float: %." G_STRINGIFY(FLT_DIG) "g", val);
769 } else {
770 snprintf(value, sizeof(value), "%s%s", half & 0x8000 ? "-" : "", mantissa == 0 ? "inf" : "nan");
771 proto_tree_add_float_format_value(tree, hfindex, tvb, *offset, 2, 0, "%s", value);
772 proto_item_set_text(item, "Float: %s", value);
776 static bool
777 dissect_cbor_float_simple_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *cbor_tree, int *offset, uint8_t type_minor)
779 uint32_t simple;
780 float f_value;
781 double d_value;
782 proto_item *item;
783 proto_tree *subtree;
785 item = proto_tree_add_item(cbor_tree, hf_cbor_item_float_simple, tvb, *offset, -1, ENC_NA);
786 subtree = proto_item_add_subtree(item, ett_cbor_float_simple);
788 proto_tree_add_item(subtree, hf_cbor_item_major_type, tvb, *offset, 1, ENC_BIG_ENDIAN);
790 if (type_minor <= 0x17) {
791 proto_tree_add_item_ret_uint(subtree, hf_cbor_type_simple_data5, tvb, *offset, 1, ENC_BIG_ENDIAN, &simple);
792 proto_item_set_text(item, "Simple: %s (%u)", val_to_str_const(simple, vals_simple_data, "Unknown"), simple);
793 } else {
794 proto_tree_add_item(subtree, hf_cbor_item_float_simple_type, tvb, *offset, 1, ENC_BIG_ENDIAN);
796 *offset += 1;
798 switch (type_minor) {
799 case 0x18:
800 proto_tree_add_item_ret_uint(subtree, hf_cbor_type_simple_data8, tvb, *offset, 1, ENC_BIG_ENDIAN, &simple);
801 proto_item_set_text(item, "Simple: %s (%u)", val_to_str_const(simple, vals_simple_data, "Unknown"), simple);
802 *offset += 1;
803 break;
804 case 0x19:
805 decode_half(tvb, subtree, item, offset, hf_cbor_type_float16);
806 *offset += 2;
807 break;
808 case 0x1a:
809 f_value = tvb_get_ntohieee_float(tvb, *offset);
810 proto_tree_add_item(subtree, hf_cbor_type_float32, tvb, *offset, 4, ENC_BIG_ENDIAN);
811 proto_item_set_text(item, "Float: %." G_STRINGIFY(FLT_DIG) "g", f_value);
812 *offset += 4;
813 break;
814 case 0x1b:
815 d_value = tvb_get_ntohieee_double(tvb, *offset);
816 proto_tree_add_item(subtree, hf_cbor_type_float64, tvb, *offset, 8, ENC_BIG_ENDIAN);
817 proto_item_set_text(item, "Float: %." G_STRINGIFY(DBL_DIG) "g", d_value);
818 *offset += 8;
819 break;
820 case 0x1f:
821 proto_item_set_text(item, "Break indefinite length (%u)", type_minor);
822 break;
823 default:
824 if (type_minor > 0x17) {
825 expert_add_info_format(pinfo, subtree, &ei_cbor_invalid_minor_type,
826 "invalid minor type %i in simple data and float", type_minor);
827 return false;
829 break;
832 proto_item_set_end(item, tvb, *offset);
834 return true;
838 static bool
839 // NOLINTNEXTLINE(misc-no-recursion)
840 dissect_cbor_main_type(tvbuff_t *tvb, packet_info *pinfo, proto_tree *cbor_tree, int *offset)
842 uint8_t type;
843 uint8_t type_major;
844 uint8_t type_minor;
846 type = tvb_get_uint8(tvb, *offset);
848 type_major = (type & 0xe0) >> 5;
849 type_minor = (type & 0x1f);
851 unsigned recursion_depth = p_get_proto_depth(pinfo, proto_cbor);
853 /* dissect_cbor_main_type and dissect_cbor_tag/dissect_cbor_map can exhaust
854 * the stack calling each other recursively on malformed packets otherwise */
855 if (recursion_depth > prefs.gui_max_tree_depth) {
856 proto_tree_add_expert(cbor_tree, pinfo, &ei_cbor_max_recursion_depth_reached, tvb, 0, 0);
857 return false;
859 p_set_proto_depth(pinfo, proto_cbor, recursion_depth + 1);
861 bool valid = false;
862 switch (type_major) {
863 case CBOR_TYPE_USIGNED_INT:
864 valid = dissect_cbor_unsigned_integer(tvb, pinfo, cbor_tree, offset, type_minor);
865 break;
866 case CBOR_TYPE_NEGATIVE_INT:
867 valid = dissect_cbor_negative_integer(tvb, pinfo, cbor_tree, offset, type_minor);
868 break;
869 case CBOR_TYPE_BYTE_STRING:
870 valid = dissect_cbor_byte_string(tvb, pinfo, cbor_tree, offset, type_minor);
871 break;
872 case CBOR_TYPE_TEXT_STRING:
873 valid = dissect_cbor_text_string(tvb, pinfo, cbor_tree, offset, type_minor);
874 break;
875 case CBOR_TYPE_ARRAY:
876 valid = dissect_cbor_array(tvb, pinfo, cbor_tree, offset, type_minor);
877 break;
878 case CBOR_TYPE_MAP:
879 valid = dissect_cbor_map(tvb, pinfo, cbor_tree, offset, type_minor);
880 break;
881 case CBOR_TYPE_TAGGED:
882 valid = dissect_cbor_tag(tvb, pinfo, cbor_tree, offset, type_minor);
883 break;
884 case CBOR_TYPE_FLOAT:
885 valid = dissect_cbor_float_simple_data(tvb, pinfo, cbor_tree, offset, type_minor);
886 break;
887 default:
888 DISSECTOR_ASSERT_NOT_REACHED();
891 p_set_proto_depth(pinfo, proto_cbor, recursion_depth);
892 return valid;
895 static int
896 dissect_cbor(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data _U_)
898 int offset = 0;
899 proto_item *cbor_root;
900 proto_tree *cbor_tree;
902 cbor_root = proto_tree_add_item(parent_tree, proto_cbor, tvb, offset, -1, ENC_NA);
903 cbor_tree = proto_item_add_subtree(cbor_root, ett_cbor);
904 dissect_cbor_main_type(tvb, pinfo, cbor_tree, &offset);
906 proto_item_set_len(cbor_root, offset);
907 return offset;
910 static int
911 dissect_cborseq(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data _U_)
913 int offset = 0;
914 proto_item *cbor_root;
915 proto_tree *cbor_tree;
917 cbor_root = proto_tree_add_item(parent_tree, proto_cbor, tvb, offset, -1, ENC_NA);
918 proto_item_append_text(cbor_root, " Sequence");
919 cbor_tree = proto_item_add_subtree(cbor_root, ett_cbor);
920 while ((unsigned)offset < tvb_reported_length(tvb)) {
921 if (!dissect_cbor_main_type(tvb, pinfo, cbor_tree, &offset)) {
922 break;
926 return offset;
929 void
930 proto_register_cbor(void)
932 static hf_register_info hf[] = {
933 { &hf_cbor_item_major_type,
934 { "Major Type", "cbor.item.major_type",
935 FT_UINT8, BASE_DEC, VALS(major_type_vals), 0xe0,
936 NULL, HFILL }
938 { &hf_cbor_item_integer_size,
939 { "Size", "cbor.item.size",
940 FT_UINT8, BASE_DEC, VALS(integer_size_vals), 0x1f,
941 NULL, HFILL }
943 { &hf_cbor_item_length_size,
944 { "Size", "cbor.item.size",
945 FT_UINT8, BASE_DEC, VALS(length_size_vals), 0x1f,
946 NULL, HFILL }
948 { &hf_cbor_item_length5,
949 { "Length", "cbor.item.length5",
950 FT_UINT8, BASE_DEC, NULL, 0x1f,
951 NULL, HFILL }
953 { &hf_cbor_item_length,
954 { "Length", "cbor.item.length",
955 FT_UINT64, BASE_DEC, NULL, 0x00,
956 NULL, HFILL }
958 { &hf_cbor_item_items5,
959 { "Items", "cbor.item.items5",
960 FT_UINT8, BASE_DEC, NULL, 0x1f,
961 NULL, HFILL }
963 { &hf_cbor_item_items,
964 { "Items", "cbor.item.items",
965 FT_UINT64, BASE_DEC, NULL, 0x00,
966 NULL, HFILL }
968 { &hf_cbor_item_pairs5,
969 { "Pairs", "cbor.item.pairs",
970 FT_UINT8, BASE_DEC, NULL, 0x1f,
971 NULL, HFILL }
973 { &hf_cbor_item_pairs,
974 { "Pairs", "cbor.item.pairs",
975 FT_UINT64, BASE_DEC, NULL, 0x00,
976 NULL, HFILL }
978 { &hf_cbor_item_float_simple_type,
979 { "Type", "cbor.item.float_simple_type",
980 FT_UINT8, BASE_DEC, VALS(float_simple_type_vals), 0x1f,
981 NULL, HFILL }
983 { &hf_cbor_item_unsigned_integer,
984 { "Unsigned Integer", "cbor.item.unsigned_integer",
985 FT_NONE, BASE_NONE, NULL, 0x00,
986 NULL, HFILL }
988 { &hf_cbor_item_negative_integer,
989 { "Negative Integer", "cbor.item.negative_integer",
990 FT_NONE, BASE_NONE, NULL, 0x00,
991 NULL, HFILL }
993 { &hf_cbor_item_text_string,
994 { "Text String", "cbor.item.textstring",
995 FT_NONE, BASE_NONE, NULL, 0x00,
996 NULL, HFILL }
998 { &hf_cbor_item_byte_string,
999 { "Byte String", "cbor.item.bytestring",
1000 FT_NONE, BASE_NONE, NULL, 0x00,
1001 NULL, HFILL }
1003 { &hf_cbor_item_array,
1004 { "Array", "cbor.item.array",
1005 FT_NONE, BASE_NONE, NULL, 0x00,
1006 NULL, HFILL }
1008 { &hf_cbor_item_map,
1009 { "Map", "cbor.item.map",
1010 FT_NONE, BASE_NONE, NULL, 0x00,
1011 NULL, HFILL }
1013 { &hf_cbor_item_tag,
1014 { "Tag", "cbor.item.tag",
1015 FT_NONE, BASE_NONE, NULL, 0x00,
1016 NULL, HFILL }
1018 { &hf_cbor_item_float_simple,
1019 { "Floating-point or Simple", "cbor.item.float_or_simple",
1020 FT_NONE, BASE_NONE, NULL, 0x00,
1021 NULL, HFILL }
1023 { &hf_cbor_type_uint5,
1024 { "Unsigned Integer", "cbor.type.uint",
1025 FT_UINT8, BASE_DEC, NULL, 0x1f,
1026 NULL, HFILL }
1028 { &hf_cbor_type_uint,
1029 { "Unsigned Integer", "cbor.type.uint",
1030 FT_UINT64, BASE_DEC, NULL, 0x00,
1031 NULL, HFILL }
1033 { &hf_cbor_type_nint,
1034 { "Negative Integer", "cbor.type.nint",
1035 FT_INT64, BASE_DEC, NULL, 0x00,
1036 NULL, HFILL }
1038 { &hf_cbor_type_byte_string,
1039 { "Byte String", "cbor.type.bytestring",
1040 FT_BYTES, BASE_NONE, NULL, 0x00,
1041 NULL, HFILL }
1043 { &hf_cbor_type_byte_string_indef,
1044 { "Byte String (indefinite length)", "cbor.type.bytestring.indef",
1045 FT_NONE, BASE_NONE, NULL, 0x0,
1046 NULL, HFILL }
1048 { &hf_cbor_type_text_string,
1049 { "Text String", "cbor.type.textstring",
1050 FT_STRING, BASE_NONE, NULL, 0x00,
1051 NULL, HFILL }
1053 { &hf_cbor_type_text_string_indef,
1054 { "Text String (indefinite length)", "cbor.type.textstring.indef",
1055 FT_NONE, BASE_NONE, NULL, 0x0,
1056 NULL, HFILL }
1058 { &hf_cbor_type_tag5,
1059 { "Tag", "cbor.type.tag",
1060 FT_UINT8, BASE_DEC, VALS(tag32_vals), 0x1f,
1061 NULL, HFILL }
1063 { &hf_cbor_type_tag,
1064 { "Tag", "cbor.type.tag",
1065 FT_UINT64, BASE_DEC|BASE_VAL64_STRING, VALS64(tag64_vals), 0x00,
1066 NULL, HFILL }
1068 { &hf_cbor_type_simple_data5,
1069 { "Simple data", "cbor.type.simple_data",
1070 FT_UINT8, BASE_DEC, VALS(vals_simple_data), 0x1f,
1071 NULL, HFILL }
1073 { &hf_cbor_type_simple_data8,
1074 { "Simple data", "cbor.type.simple_data",
1075 FT_UINT8, BASE_DEC, VALS(vals_simple_data), 0x00,
1076 NULL, HFILL }
1078 { &hf_cbor_type_float16,
1079 { "Float 16 Bit", "cbor.type.float16",
1080 FT_FLOAT, BASE_NONE, NULL, 0x00,
1081 NULL, HFILL }
1083 { &hf_cbor_type_float32,
1084 { "Float 32 Bit", "cbor.type.float32",
1085 FT_FLOAT, BASE_NONE, NULL, 0x00,
1086 NULL, HFILL }
1088 { &hf_cbor_type_float64,
1089 { "Float 64 Bit", "cbor.type.float64",
1090 FT_DOUBLE, BASE_NONE, NULL, 0x00,
1091 NULL, HFILL }
1095 static int *ett[] = {
1096 &ett_cbor,
1097 &ett_cbor_type,
1098 &ett_cbor_unsigned_integer,
1099 &ett_cbor_negative_integer,
1100 &ett_cbor_byte_string,
1101 &ett_cbor_byte_string_indef,
1102 &ett_cbor_text_string,
1103 &ett_cbor_text_string_indef,
1104 &ett_cbor_array,
1105 &ett_cbor_map,
1106 &ett_cbor_tag,
1107 &ett_cbor_float_simple
1110 static ei_register_info ei[] = {
1111 { &ei_cbor_invalid_minor_type,
1112 { "cbor.invalid_minor_type", PI_MALFORMED, PI_WARN, "Invalid minor type", EXPFILL }},
1113 { &ei_cbor_invalid_element,
1114 { "cbor.invalid_element", PI_MALFORMED, PI_WARN, "Invalid element", EXPFILL }},
1115 { &ei_cbor_too_long_length,
1116 { "cbor.too_long_length", PI_MALFORMED, PI_WARN, "Too long length", EXPFILL }},
1117 { &ei_cbor_max_recursion_depth_reached,
1118 { "cbor.max_recursion_depth_reached", PI_PROTOCOL, PI_WARN, "Maximum allowed recursion depth reached. Dissection stopped.", EXPFILL }},
1121 expert_module_t *expert_cbor;
1123 proto_cbor = proto_register_protocol("Concise Binary Object Representation", "CBOR", "cbor");
1124 proto_register_field_array(proto_cbor, hf, array_length(hf));
1125 proto_register_subtree_array(ett, array_length(ett));
1126 expert_cbor = expert_register_protocol(proto_cbor);
1127 expert_register_field_array(expert_cbor, ei, array_length(ei));
1129 cbor_handle = register_dissector("cbor", dissect_cbor, proto_cbor);
1130 cborseq_handle = register_dissector_with_description("cborseq", "CBOR Sequence", dissect_cborseq, proto_cbor);
1133 void
1134 proto_reg_handoff_cbor(void)
1136 dissector_add_string("media_type", "application/cbor", cbor_handle); /* RFC 7049 */
1137 dissector_add_string("media_type", "application/senml+cbor", cbor_handle); /* RFC 8428 */
1138 dissector_add_string("media_type", "application/sensml+cbor", cbor_handle); /* RFC 8428 */
1139 dissector_add_string("media_type", "application/cbor-seq", cborseq_handle); /* RFC 8742 */
1141 dissector_add_string("media_type.suffix", "cbor", cbor_handle);
1142 dissector_add_string("media_type.suffix", "cbor-seq", cborseq_handle);
1146 * Editor modelines - https://www.wireshark.org/tools/modelines.html
1148 * Local variables:
1149 * c-basic-offset: 8
1150 * tab-width: 8
1151 * indent-tabs-mode: t
1152 * End:
1154 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
1155 * :indentSize=8:tabSize=8:noTabs=false: