2 * Routines for CBOR Object Signing and Encryption (COSE) dissection
4 * RFC 8949: https://tools.ietf.org/html/rfc8949
5 * RFC 9052: https://tools.ietf.org/html/rfc9052
6 * RFC 9053: https://tools.ietf.org/html/rfc9053
7 * RFC 9360: https://tools.ietf.org/html/rfc9360
9 * Copyright 2019-2021, Brian Sipos <brian.sipos@gmail.com>
11 * Wireshark - Network traffic analyzer
12 * By Gerald Combs <gerald@wireshark.org>
13 * Copyright 1998 Gerald Combs
15 * SPDX-License-Identifier: LGPL-2.1-or-later
19 #define WS_LOG_DOMAIN "packet-cose"
21 #include "packet-cose.h"
22 #include "packet-media-type.h"
23 #include <epan/wscbor.h>
24 #include <epan/packet.h>
25 #include <epan/prefs.h>
26 #include <epan/proto.h>
27 #include <epan/media_params.h>
28 #include <epan/expert.h>
29 #include <epan/exceptions.h>
30 #include <wsutil/array.h>
33 void proto_register_cose(void);
34 void proto_reg_handoff_cose(void);
36 /// Protocol column name
37 static const char *const proto_name_cose
= "COSE";
39 /// Protocol preferences and defaults
42 static int proto_cose
;
43 static int proto_cose_params
;
45 /// Dissect opaque CBOR data
46 static dissector_handle_t handle_cbor
;
48 static dissector_handle_t handle_cose_msg_hdr
;
49 static dissector_handle_t handle_cose_msg_tagged
;
50 static dissector_handle_t handle_cose_media_type
;
51 static dissector_handle_t handle_cose_sign
;
52 static dissector_handle_t handle_cose_sign1
;
53 static dissector_handle_t handle_cose_encrypt
;
54 static dissector_handle_t handle_cose_encrypt0
;
55 static dissector_handle_t handle_cose_mac
;
56 static dissector_handle_t handle_cose_mac0
;
57 static dissector_handle_t handle_cose_key
;
58 static dissector_handle_t handle_cose_key_set
;
60 /// Dissect opaque data
61 static dissector_table_t table_media
;
62 /// Dissect extension items
63 static dissector_table_t table_cose_msg_tag
;
64 static dissector_table_t table_cose_media_subtype
;
65 static dissector_table_t table_header
;
66 static dissector_table_t table_keyparam
;
68 static const val64_string alg_vals
[] = {
82 {-34, "ECDH-SS + A256KW"},
83 {-33, "ECDH-SS + A192KW"},
84 {-32, "ECDH-SS + A128KW"},
85 {-31, "ECDH-ES + A256KW"},
86 {-30, "ECDH-ES + A192KW"},
87 {-29, "ECDH-ES + A128KW"},
88 {-28, "ECDH-SS + HKDF-512"},
89 {-27, "ECDH-SS + HKDF-256"},
90 {-26, "ECDH-ES + HKDF-512"},
91 {-25, "ECDH-ES + HKDF-256"},
97 {-13, "direct+HKDF-AES-256"},
98 {-12, "direct+HKDF-AES-128"},
99 {-11, "direct+HKDF-SHA-512"},
100 {-10, "direct+HKDF-SHA-256"},
115 {10, "AES-CCM-16-64-128"},
116 {11, "AES-CCM-16-64-256"},
117 {12, "AES-CCM-64-64-128"},
118 {13, "AES-CCM-64-64-256"},
119 {14, "AES-MAC 128/64"},
120 {15, "AES-MAC 256/64"},
121 {24, "ChaCha20/Poly1305"},
122 {25, "AES-MAC 128/128"},
123 {26, "AES-MAC 256/128"},
124 {30, "AES-CCM-16-128-128"},
125 {31, "AES-CCM-16-128-256"},
126 {32, "AES-CCM-64-128-128"},
127 {33, "AES-CCM-64-128-256"},
128 {34, "IV-GENERATION"},
132 static const val64_string kty_vals
[] = {
142 static const val64_string keyops_vals
[] = {
156 static const val64_string crv_vals
[] = {
168 static int hf_msg_tag
;
169 static int hf_hdr_prot_bstr
;
170 static int hf_hdr_unprot
;
171 static int hf_payload_null
;
172 static int hf_payload_bstr
;
173 static int hf_cose_signature_list
;
174 static int hf_cose_signature
;
175 static int hf_signature
;
176 static int hf_ciphertext_null
;
177 static int hf_ciphertext_bstr
;
178 static int hf_cose_recipient_list
;
179 static int hf_cose_recipient
;
182 static int hf_hdr_label_int
;
183 static int hf_hdr_label_tstr
;
185 static int hf_hdr_salt
;
186 static int hf_hdr_static_key
;
187 static int hf_hdr_ephem_key
;
188 static int hf_hdr_alg_int
;
189 static int hf_hdr_alg_tstr
;
190 static int hf_hdr_crit_list
;
191 static int hf_hdr_ctype_uint
;
192 static int hf_hdr_ctype_tstr
;
193 static int hf_hdr_kid
;
194 static int hf_hdr_kid_text
;
195 static int hf_hdr_iv
;
196 static int hf_hdr_piv
;
197 static int hf_hdr_x5bag
;
198 static int hf_hdr_x5chain
;
199 static int hf_hdr_x5t
;
200 static int hf_hdr_x5t_hash
;
201 static int hf_hdr_x5u
;
205 static int hf_keyparam_kty_int
;
206 static int hf_keyparam_kty_tstr
;
207 static int hf_keyparam_keyops_list
;
208 static int hf_keyparam_keyops_int
;
209 static int hf_keyparam_keyops_tstr
;
210 static int hf_keyparam_baseiv
;
211 static int hf_keyparam_crv_int
;
212 static int hf_keyparam_crv_tstr
;
213 static int hf_keyparam_xcoord
;
214 static int hf_keyparam_ycoord
;
215 static int hf_keyparam_dcoord
;
216 static int hf_keyparam_k
;
218 /// Field definitions
219 static hf_register_info fields
[] = {
220 {&hf_msg_tag
, {"Message type tag", "cose.msgtag", FT_UINT64
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
221 {&hf_hdr_prot_bstr
, {"Protected Headers (bstr)", "cose.msg.prot_bstr", FT_BYTES
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
222 {&hf_hdr_unprot
, {"Unprotected Headers", "cose.msg.unprot", FT_NONE
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
224 {&hf_payload_null
, {"Payload Detached", "cose.msg.detached_payload", FT_NONE
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
225 {&hf_payload_bstr
, {"Payload", "cose.msg.payload", FT_BYTES
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
226 {&hf_signature
, {"Signature", "cose.msg.signature", FT_BYTES
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
227 {&hf_cose_signature_list
, {"Signature List, Count", "cose.msg.signature_list", FT_UINT64
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
228 {&hf_cose_signature
, {"COSE_Signature", "cose.msg.cose_signature", FT_NONE
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
229 {&hf_ciphertext_null
, {"Ciphertext Detached", "cose.msg.detached_ciphertext", FT_NONE
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
230 {&hf_ciphertext_bstr
, {"Ciphertext", "cose.msg.ciphertext", FT_BYTES
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
231 {&hf_cose_recipient_list
, {"Recipient List, Count", "cose.msg.recipient_list", FT_UINT64
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
232 {&hf_cose_recipient
, {"COSE_Recipient", "cose.msg.cose_recipient", FT_NONE
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
233 {&hf_tag
, {"Tag", "cose.msg.mac_tag", FT_BYTES
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
235 {&hf_hdr_label_int
, {"Label", "cose.header_label.int", FT_INT64
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
236 {&hf_hdr_label_tstr
, {"Label", "cose.header_label.tstr", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
238 {&hf_hdr_salt
, {"Salt", "cose.salt", FT_BYTES
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
239 {&hf_hdr_static_key
, {"Static Key", "cose.static_key", FT_NONE
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
240 {&hf_hdr_ephem_key
, {"Ephemeral Key", "cose.ephem_key", FT_NONE
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
241 {&hf_hdr_alg_int
, {"Algorithm", "cose.alg.int", FT_INT64
, BASE_DEC
| BASE_VAL64_STRING
, VALS64(alg_vals
), 0x0, NULL
, HFILL
}},
242 {&hf_hdr_alg_tstr
, {"Algorithm", "cose.alg.tstr", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
243 {&hf_hdr_crit_list
, {"Critical Headers, Count", "cose.crit", FT_INT64
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
244 {&hf_hdr_ctype_uint
, {"Content-Format", "cose.content-type.uint", FT_INT64
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
245 {&hf_hdr_ctype_tstr
, {"Content-Type", "cose.content-type.tstr", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
246 {&hf_hdr_kid
, {"Key identifier", "cose.kid", FT_BYTES
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
247 {&hf_hdr_kid_text
, {"As Text", "cose.kid.as_text", FT_STRING
, BASE_NONE
, NULL
, 0x0, "The kid byte string interpreted as UTF-8 text", HFILL
}},
248 {&hf_hdr_iv
, {"IV", "cose.iv", FT_BYTES
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
249 {&hf_hdr_piv
, {"Partial IV", "cose.piv", FT_BYTES
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
251 {&hf_hdr_x5bag
, {"X509 Bag (x5bag)", "cose.x5bag", FT_NONE
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
252 {&hf_hdr_x5chain
, {"X509 Chain (x5chain)", "cose.x5chain", FT_NONE
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
253 {&hf_hdr_x5t
, {"X509 Thumbprint (x5t)", "cose.x5t", FT_NONE
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
254 {&hf_hdr_x5t_hash
, {"Hash Value", "cose.x5t.hash", FT_BYTES
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
255 {&hf_hdr_x5u
, {"X509 URI (x5u)", "cose.x5u", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
257 {&hf_key
, {"COSE_Key", "cose.key", FT_NONE
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
259 {&hf_keyparam_kty_int
, {"Key Type", "cose.kty.int", FT_INT64
, BASE_DEC
| BASE_VAL64_STRING
, VALS64(kty_vals
), 0x0, NULL
, HFILL
}},
260 {&hf_keyparam_kty_tstr
, {"Key Type", "cose.kty.tstr", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
261 {&hf_keyparam_keyops_list
, {"Key Operations", "cose.keyops", FT_NONE
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
262 {&hf_keyparam_keyops_int
, {"Operation", "cose.keyops.int", FT_INT64
, BASE_DEC
| BASE_VAL64_STRING
, VALS64(keyops_vals
), 0x0, NULL
, HFILL
}},
263 {&hf_keyparam_keyops_tstr
, {"Operation", "cose.keyops.tstr", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
264 {&hf_keyparam_baseiv
, {"Base IV", "cose.baseiv", FT_BYTES
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
265 {&hf_keyparam_crv_int
, {"Curve Type", "cose.crv.int", FT_INT64
, BASE_DEC
| BASE_VAL64_STRING
, VALS64(crv_vals
), 0x0, NULL
, HFILL
}},
266 {&hf_keyparam_crv_tstr
, {"Curve Type", "cose.crv.tstr", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
267 {&hf_keyparam_xcoord
, {"X-coordinate", "cose.key.xcoord", FT_BYTES
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
268 {&hf_keyparam_ycoord
, {"Y-coordinate", "cose.key.ycoord", FT_BYTES
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
269 {&hf_keyparam_dcoord
, {"Private Key", "cose.key.dcoord", FT_BYTES
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
270 {&hf_keyparam_k
, {"Key", "cose.key.k", FT_BYTES
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
274 static int ett_sig_list
;
276 static int ett_recip_list
;
277 static int ett_recip
;
278 static int ett_prot_bstr
;
279 static int ett_unprot
;
280 static int ett_hdr_map
;
281 static int ett_hdr_label
;
282 static int ett_hdr_kid
;
283 static int ett_hdr_static_key
;
284 static int ett_hdr_ephem_key
;
285 static int ett_hdr_crit_list
;
286 static int ett_hdr_x5cert_list
;
287 static int ett_hdr_x5t_list
;
289 static int ett_key_set
;
290 static int ett_keyops_list
;
292 static int *ett
[] = {
306 &ett_hdr_x5cert_list
,
313 static expert_field ei_invalid_tag
;
314 static expert_field ei_value_partial_decode
;
315 static ei_register_info expertitems
[] = {
316 {&ei_invalid_tag
, { "cose.invalid_tag", PI_UNDECODED
, PI_WARN
, "COSE dissector did not match any known tag", EXPFILL
}},
317 {&ei_value_partial_decode
, { "cose.partial_decode", PI_MALFORMED
, PI_WARN
, "Value is only partially decoded", EXPFILL
}},
320 unsigned cose_param_key_hash(const void *ptr
) {
321 const cose_param_key_t
*obj
= (const cose_param_key_t
*)ptr
;
323 if (obj
->principal
) {
324 val
^= g_int64_hash(obj
->principal
);
327 val
^= g_variant_hash(obj
->label
);
332 gboolean
cose_param_key_equal(const void *a
, const void *b
) {
333 const cose_param_key_t
*aobj
= (const cose_param_key_t
*)a
;
334 const cose_param_key_t
*bobj
= (const cose_param_key_t
*)b
;
336 if (aobj
->principal
&& bobj
->principal
) {
337 if (!g_variant_equal(aobj
->principal
, bobj
->principal
)) {
341 else if ((aobj
->principal
== NULL
) != (bobj
->principal
== NULL
)) {
346 if (aobj
->label
&& bobj
->label
) {
347 match
= g_variant_equal(aobj
->label
, bobj
->label
);
356 void cose_param_key_free(void *ptr
) {
357 cose_param_key_t
*obj
= (cose_param_key_t
*)ptr
;
358 if (obj
->principal
) {
359 g_variant_unref(obj
->principal
);
362 g_variant_unref(obj
->label
);
367 /** Get a specific item value (map key or value) from a header map.
368 * @param alloc The allocator for temporary data.
369 * @param tvb The buffer to read from.
370 * @param[in,out] offset The starting offset to read and advance.
371 * @return A pointer to the simple value or NULL.
373 static GVariant
* get_header_value(wmem_allocator_t
*alloc
, tvbuff_t
*tvb
, int *offset
) {
374 GVariant
*result
= NULL
;
376 wscbor_chunk_t
*chunk
= wscbor_chunk_read(alloc
, tvb
, offset
);
377 switch (chunk
->type_major
) {
379 case CBOR_TYPE_NEGINT
: {
380 int64_t *label
= wscbor_require_int64(alloc
, chunk
);
382 result
= g_variant_new_int64(*label
);
386 case CBOR_TYPE_STRING
: {
387 const char *label
= wscbor_require_tstr(alloc
, chunk
);
389 result
= g_variant_new_string(label
);
396 wscbor_chunk_free(chunk
);
400 /** Dissect an ID-value pair within a context.
402 * @param dis_table The cose_param_key_t dissector table.
403 * @param[in,out] ctx The context from other pairs.
404 * @return True if the pair was dissected (even as opaque CBOR data).
406 static bool dissect_header_pair(dissector_table_t dis_table
, cose_header_context_t
*ctx
, tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, int *offset
) {
407 wscbor_chunk_t
*chunk_label
= wscbor_chunk_read(pinfo
->pool
, tvb
, offset
);
409 proto_item
*item_label
= NULL
;
410 proto_tree
*volatile tree_label
= NULL
;
411 tvbuff_t
*volatile tvb_value
= NULL
;
413 cose_param_key_t key
= { 0 };
415 const char *label_str
= NULL
;
416 switch (chunk_label
->type_major
) {
418 case CBOR_TYPE_NEGINT
: {
419 int64_t *label
= wscbor_require_int64(pinfo
->pool
, chunk_label
);
420 item_label
= proto_tree_add_cbor_int64(tree
, hf_hdr_label_int
, pinfo
, tvb
, chunk_label
, label
);
422 key
.label
= ctx
->label
=
423 g_variant_new_int64(*label
);
424 label_str
= wmem_strdup_printf(pinfo
->pool
, "%" PRId64
, *label
);
428 case CBOR_TYPE_STRING
: {
429 const char *label
= wscbor_require_tstr(pinfo
->pool
, chunk_label
);
430 item_label
= proto_tree_add_cbor_tstr(tree
, hf_hdr_label_tstr
, pinfo
, tvb
, chunk_label
);
432 key
.label
= ctx
->label
=
433 g_variant_new_string(label
);
442 // First attempt with context then without
443 key
.principal
= ctx
->principal
;
444 dissector_handle_t dissector
= dissector_get_custom_table_handle(dis_table
, &key
);
446 key
.principal
= NULL
;
447 dissector
= dissector_get_custom_table_handle(dis_table
, &key
);
449 const char *dis_name
= dissector_handle_get_description(dissector
);
451 proto_item_set_text(item_label
, "Label: %s (%s)", dis_name
, label_str
);
454 tree_label
= proto_item_add_subtree(item_label
, ett_hdr_label
);
456 // Peek into the value as tvb
457 const int offset_value
= *offset
;
458 if (!wscbor_skip_next_item(pinfo
->pool
, tvb
, offset
)) {
461 tvb_value
= tvb_new_subset_length(tvb
, offset_value
, *offset
- offset_value
);
465 sublen
= call_dissector_only(dissector
, tvb_value
, pinfo
, tree_label
, ctx
);
467 ((sublen
> 0) && ((unsigned)sublen
< tvb_reported_length(tvb_value
)))) {
468 expert_add_info(pinfo
, proto_tree_get_parent(tree
), &ei_value_partial_decode
);
472 g_variant_unref(ctx
->label
);
477 call_dissector(handle_cbor
, tvb_value
, pinfo
, tree_label
);
486 cose_header_context_cleanup(void *user_data
) {
487 cose_header_context_t
*ctx
= (cose_header_context_t
*)user_data
;
489 if (ctx
->principal
) {
490 g_variant_unref(ctx
->principal
);
493 g_variant_unref(ctx
->label
);
499 g_variant_cleanup(void *user_data
) {
500 GVariant
*var
= (GVariant
*)user_data
;
501 g_variant_unref(var
);
504 /** Dissect an entire header map, either for messages, recipients, or keys.
506 * @param dis_table The cose_param_key_t dissector table.
507 * @param tvb The source data.
508 * @param tree The parent of the header map.
509 * @param[in,out] offset The data offset.
510 * @param principal_key The map key associated with a principal value to read first.
512 static void dissect_header_map(dissector_table_t dis_table
, tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, int *offset
, GVariant
*principal_key
) {
513 wscbor_chunk_t
*chunk_hdr_map
= wscbor_chunk_read(pinfo
->pool
, tvb
, offset
);
514 wscbor_require_map(chunk_hdr_map
);
515 proto_item
*item_hdr_map
= proto_tree_get_parent(tree
);
516 wscbor_chunk_mark_errors(pinfo
, item_hdr_map
, chunk_hdr_map
);
517 if (!wscbor_skip_if_errors(pinfo
->pool
, tvb
, offset
, chunk_hdr_map
)) {
518 proto_tree
*tree_hdr_map
= proto_item_add_subtree(item_hdr_map
, ett_hdr_map
);
520 cose_header_context_t
*ctx
= wmem_new0(pinfo
->pool
, cose_header_context_t
);
521 CLEANUP_PUSH(cose_header_context_cleanup
, ctx
);
523 // Peek ahead to principal key (and value) first
525 int peek_offset
= *offset
;
526 for (uint64_t ix
= 0; ix
< chunk_hdr_map
->head_value
; ++ix
) {
527 GVariant
*key
= get_header_value(pinfo
->pool
, tvb
, &peek_offset
);
529 if (g_variant_equal(key
, principal_key
)) {
530 ctx
->principal
= get_header_value(pinfo
->pool
, tvb
, &peek_offset
);
532 g_variant_unref(key
);
533 if (ctx
->principal
) {
537 // ignore non-principal value entirely
538 wscbor_skip_next_item(pinfo
->pool
, tvb
, &peek_offset
);
542 for (uint64_t ix
= 0; ix
< chunk_hdr_map
->head_value
; ++ix
) {
543 if (!dissect_header_pair(dis_table
, ctx
, tvb
, pinfo
, tree_hdr_map
, offset
)) {
548 CLEANUP_CALL_AND_POP
;
549 wmem_free(pinfo
->pool
, ctx
);
552 proto_item_set_len(item_hdr_map
, *offset
- chunk_hdr_map
->start
);
555 static int dissect_cose_msg_header_map(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
) {
557 GVariant
*alg_key
= g_variant_new_int64(1);
558 CLEANUP_PUSH(g_variant_cleanup
, alg_key
);
560 dissect_header_map(table_header
, tvb
, pinfo
, tree
, &offset
, alg_key
);
562 CLEANUP_CALL_AND_POP
;
566 /** Indicate the tag which informed the message type.
568 static void dissect_msg_tag(tvbuff_t
*tvb
, packet_info
*pinfo _U_
, proto_tree
*tree_msg
, const wscbor_chunk_t
*chunk_msg _U_
, const void *data
) {
572 const wscbor_tag_t
*tag
= (const wscbor_tag_t
*)data
;
574 proto_tree_add_uint64(tree_msg
, hf_msg_tag
, tvb
, tag
->start
, tag
->length
, tag
->value
);
577 /** Common behavior for pair of header maps.
579 static void dissect_headers(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, int *offset
) {
581 wscbor_chunk_t
*chunk_prot_bstr
= wscbor_chunk_read(pinfo
->pool
, tvb
, offset
);
582 tvbuff_t
*tvb_prot
= wscbor_require_bstr(pinfo
->pool
, chunk_prot_bstr
);
583 proto_item
*item_prot_bstr
= proto_tree_add_cbor_bstr(tree
, hf_hdr_prot_bstr
, pinfo
, tvb
, chunk_prot_bstr
);
585 proto_tree
*tree_prot
= proto_item_add_subtree(item_prot_bstr
, ett_prot_bstr
);
587 if (tvb_reported_length(tvb_prot
) > 0) {
588 dissect_cose_msg_header_map(tvb_prot
, pinfo
, tree_prot
, NULL
);
593 tvbuff_t
*tvb_unprot
= tvb_new_subset_remaining(tvb
, *offset
);
594 proto_item
*item_unprot
= proto_tree_add_item(tree
, hf_hdr_unprot
, tvb_unprot
, 0, -1, ENC_NA
);
595 proto_tree
*tree_unprot
= proto_item_add_subtree(item_unprot
, ett_unprot
);
596 const int sublen
= dissect_cose_msg_header_map(tvb_unprot
, pinfo
, tree_unprot
, NULL
);
598 proto_item_set_len(item_unprot
, sublen
);
601 /** Common behavior for payload.
603 static void dissect_payload(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, int *offset
) {
604 wscbor_chunk_t
*chunk
= wscbor_chunk_read(pinfo
->pool
, tvb
, offset
);
605 if (chunk
->type_major
== CBOR_TYPE_FLOAT_CTRL
) {
606 proto_tree_add_cbor_ctrl(tree
, hf_payload_null
, pinfo
, tvb
, chunk
);
609 wscbor_require_bstr(pinfo
->pool
, chunk
);
610 proto_tree_add_cbor_bstr(tree
, hf_payload_bstr
, pinfo
, tvb
, chunk
);
613 static void dissect_signature(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, int *offset
) {
614 wscbor_chunk_t
*chunk
= wscbor_chunk_read(pinfo
->pool
, tvb
, offset
);
615 wscbor_require_bstr(pinfo
->pool
, chunk
);
616 proto_tree_add_cbor_bstr(tree
, hf_signature
, pinfo
, tvb
, chunk
);
618 static void dissect_cose_signature(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, int *offset
) {
619 wscbor_chunk_t
*chunk_sig
= wscbor_chunk_read(pinfo
->pool
, tvb
, offset
);
620 wscbor_require_array_size(chunk_sig
, 3, 3);
621 proto_item
*item_sig
= proto_tree_add_cbor_container(tree
, hf_cose_signature
, pinfo
, tvb
, chunk_sig
);
622 if (!wscbor_skip_if_errors(pinfo
->pool
, tvb
, offset
, chunk_sig
)) {
623 proto_tree
*tree_sig
= proto_item_add_subtree(item_sig
, ett_sig
);
625 dissect_headers(tvb
, pinfo
, tree_sig
, offset
);
626 dissect_signature(tvb
, pinfo
, tree_sig
, offset
);
628 proto_item_set_len(item_sig
, *offset
- chunk_sig
->start
);
630 static void dissect_ciphertext(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, int *offset
) {
631 wscbor_chunk_t
*chunk
= wscbor_chunk_read(pinfo
->pool
, tvb
, offset
);
632 if (chunk
->type_major
== CBOR_TYPE_FLOAT_CTRL
) {
633 proto_tree_add_cbor_ctrl(tree
, hf_ciphertext_null
, pinfo
, tvb
, chunk
);
636 wscbor_require_bstr(pinfo
->pool
, chunk
);
637 proto_tree_add_cbor_bstr(tree
, hf_ciphertext_bstr
, pinfo
, tvb
, chunk
);
640 static void dissect_cose_recipient(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, int *offset
);
642 // NOLINTNEXTLINE(misc-no-recursion)
643 static void dissect_cose_recipient_list(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, int *offset
) {
644 wscbor_chunk_t
*chunk_list
= wscbor_chunk_read(pinfo
->pool
, tvb
, offset
);
645 wscbor_require_array(chunk_list
);
646 proto_item
*item_list
= proto_tree_add_cbor_container(tree
, hf_cose_recipient_list
, pinfo
, tvb
, chunk_list
);
647 if (!wscbor_skip_if_errors(pinfo
->pool
, tvb
, offset
, chunk_list
)) {
648 proto_tree
*tree_recip_list
= proto_item_add_subtree(item_list
, ett_recip_list
);
650 for (uint64_t ix
= 0; ix
< chunk_list
->head_value
; ++ix
) {
651 dissect_cose_recipient(tvb
, pinfo
, tree_recip_list
, offset
);
654 proto_item_set_len(item_list
, *offset
- chunk_list
->start
);
657 // NOLINTNEXTLINE(misc-no-recursion)
658 static void dissect_cose_recipient(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, int *offset
) {
659 wscbor_chunk_t
*chunk_recip
= wscbor_chunk_read(pinfo
->pool
, tvb
, offset
);
660 wscbor_require_array_size(chunk_recip
, 3, 4);
661 proto_item
*item_recip
= proto_tree_add_cbor_container(tree
, hf_cose_recipient
, pinfo
, tvb
, chunk_recip
);
662 if (!wscbor_skip_if_errors(pinfo
->pool
, tvb
, offset
, chunk_recip
)) {
663 proto_tree
*tree_recip
= proto_item_add_subtree(item_recip
, ett_recip
);
665 dissect_headers(tvb
, pinfo
, tree_recip
, offset
);
666 dissect_ciphertext(tvb
, pinfo
, tree_recip
, offset
);
667 if (chunk_recip
->head_value
> 3) {
668 increment_dissection_depth(pinfo
);
669 dissect_cose_recipient_list(tvb
, pinfo
, tree_recip
, offset
);
670 decrement_dissection_depth(pinfo
);
673 proto_item_set_len(item_recip
, *offset
- chunk_recip
->start
);
676 static void dissect_tag(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, int *offset
) {
677 wscbor_chunk_t
*chunk
= wscbor_chunk_read(pinfo
->pool
, tvb
, offset
);
678 wscbor_require_bstr(pinfo
->pool
, chunk
);
679 proto_tree_add_cbor_bstr(tree
, hf_tag
, pinfo
, tvb
, chunk
);
682 // Top-level protocol dissectors
683 static int dissect_cose_sign(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
) {
686 wscbor_chunk_t
*chunk_msg
= wscbor_chunk_read(pinfo
->pool
, tvb
, &offset
);
687 wscbor_require_array_size(chunk_msg
, 4, 4);
688 proto_item
*item_msg
= proto_tree_add_cbor_container(tree
, proto_cose
, pinfo
, tvb
, chunk_msg
);
689 proto_item_append_text(item_msg
, ": COSE_Sign");
690 if (!wscbor_skip_if_errors(pinfo
->pool
, tvb
, &offset
, chunk_msg
)) {
691 proto_tree
*tree_msg
= proto_item_add_subtree(item_msg
, ett_msg
);
692 dissect_msg_tag(tvb
, pinfo
, tree_msg
, chunk_msg
, data
);
694 dissect_headers(tvb
, pinfo
, tree_msg
, &offset
);
695 dissect_payload(tvb
, pinfo
, tree_msg
, &offset
);
697 wscbor_chunk_t
*chunk_sig_list
= wscbor_chunk_read(pinfo
->pool
, tvb
, &offset
);
698 wscbor_require_array(chunk_sig_list
);
699 proto_item
*item_sig_list
= proto_tree_add_cbor_container(tree_msg
, hf_cose_signature_list
, pinfo
, tvb
, chunk_sig_list
);
700 if (!wscbor_skip_if_errors(pinfo
->pool
, tvb
, &offset
, chunk_sig_list
)) {
701 proto_tree
*tree_sig_list
= proto_item_add_subtree(item_sig_list
, ett_sig_list
);
703 for (uint64_t ix
= 0; ix
< chunk_sig_list
->head_value
; ++ix
) {
704 dissect_cose_signature(tvb
, pinfo
, tree_sig_list
, &offset
);
707 proto_item_set_len(item_sig_list
, offset
- chunk_sig_list
->start
);
713 static int dissect_cose_sign1(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
) {
716 wscbor_chunk_t
*chunk_msg
= wscbor_chunk_read(pinfo
->pool
, tvb
, &offset
);
717 wscbor_require_array_size(chunk_msg
, 4, 4);
718 proto_item
*item_msg
= proto_tree_add_cbor_container(tree
, proto_cose
, pinfo
, tvb
, chunk_msg
);
719 proto_item_append_text(item_msg
, ": COSE_Sign1");
720 if (!wscbor_skip_if_errors(pinfo
->pool
, tvb
, &offset
, chunk_msg
)) {
721 proto_tree
*tree_msg
= proto_item_add_subtree(item_msg
, ett_msg
);
722 dissect_msg_tag(tvb
, pinfo
, tree_msg
, chunk_msg
, data
);
724 dissect_headers(tvb
, pinfo
, tree_msg
, &offset
);
725 dissect_payload(tvb
, pinfo
, tree_msg
, &offset
);
726 dissect_signature(tvb
, pinfo
, tree_msg
, &offset
);
732 static int dissect_cose_encrypt(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
) {
735 wscbor_chunk_t
*chunk_msg
= wscbor_chunk_read(pinfo
->pool
, tvb
, &offset
);
736 wscbor_require_array_size(chunk_msg
, 4, 4);
737 proto_item
*item_msg
= proto_tree_add_cbor_container(tree
, proto_cose
, pinfo
, tvb
, chunk_msg
);
738 proto_item_append_text(item_msg
, ": COSE_Encrypt");
739 if (!wscbor_skip_if_errors(pinfo
->pool
, tvb
, &offset
, chunk_msg
)) {
740 proto_tree
*tree_msg
= proto_item_add_subtree(item_msg
, ett_msg
);
741 dissect_msg_tag(tvb
, pinfo
, tree_msg
, chunk_msg
, data
);
743 dissect_headers(tvb
, pinfo
, tree_msg
, &offset
);
744 dissect_ciphertext(tvb
, pinfo
, tree_msg
, &offset
);
745 dissect_cose_recipient_list(tvb
, pinfo
, tree_msg
, &offset
);
751 static int dissect_cose_encrypt0(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
) {
754 wscbor_chunk_t
*chunk_msg
= wscbor_chunk_read(pinfo
->pool
, tvb
, &offset
);
755 wscbor_require_array_size(chunk_msg
, 3, 3);
756 proto_item
*item_msg
= proto_tree_add_cbor_container(tree
, proto_cose
, pinfo
, tvb
, chunk_msg
);
757 proto_item_append_text(item_msg
, ": COSE_Encrypt0");
758 if (!wscbor_skip_if_errors(pinfo
->pool
, tvb
, &offset
, chunk_msg
)) {
759 proto_tree
*tree_msg
= proto_item_add_subtree(item_msg
, ett_msg
);
760 dissect_msg_tag(tvb
, pinfo
, tree_msg
, chunk_msg
, data
);
762 dissect_headers(tvb
, pinfo
, tree_msg
, &offset
);
763 dissect_ciphertext(tvb
, pinfo
, tree_msg
, &offset
);
769 static int dissect_cose_mac(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
) {
772 wscbor_chunk_t
*chunk_msg
= wscbor_chunk_read(pinfo
->pool
, tvb
, &offset
);
773 wscbor_require_array_size(chunk_msg
, 5, 5);
774 proto_item
*item_msg
= proto_tree_add_cbor_container(tree
, proto_cose
, pinfo
, tvb
, chunk_msg
);
775 proto_item_append_text(item_msg
, ": COSE_Mac");
776 if (!wscbor_skip_if_errors(pinfo
->pool
, tvb
, &offset
, chunk_msg
)) {
777 proto_tree
*tree_msg
= proto_item_add_subtree(item_msg
, ett_msg
);
778 dissect_msg_tag(tvb
, pinfo
, tree_msg
, chunk_msg
, data
);
780 dissect_headers(tvb
, pinfo
, tree_msg
, &offset
);
781 dissect_payload(tvb
, pinfo
, tree_msg
, &offset
);
782 dissect_tag(tvb
, pinfo
, tree_msg
, &offset
);
783 dissect_cose_recipient_list(tvb
, pinfo
, tree_msg
, &offset
);
789 static int dissect_cose_mac0(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
) {
792 wscbor_chunk_t
*chunk_msg
= wscbor_chunk_read(pinfo
->pool
, tvb
, &offset
);
793 wscbor_require_array_size(chunk_msg
, 4, 4);
794 proto_item
*item_msg
= proto_tree_add_cbor_container(tree
, proto_cose
, pinfo
, tvb
, chunk_msg
);
795 proto_item_append_text(item_msg
, ": COSE_Mac0");
796 if (!wscbor_skip_if_errors(pinfo
->pool
, tvb
, &offset
, chunk_msg
)) {
797 proto_tree
*tree_msg
= proto_item_add_subtree(item_msg
, ett_msg
);
798 dissect_msg_tag(tvb
, pinfo
, tree_msg
, chunk_msg
, data
);
800 dissect_headers(tvb
, pinfo
, tree_msg
, &offset
);
801 dissect_payload(tvb
, pinfo
, tree_msg
, &offset
);
802 dissect_tag(tvb
, pinfo
, tree_msg
, &offset
);
808 /** Dissect a tagged COSE message.
810 static int dissect_cose_msg_tagged(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
) {
813 // All messages have the same base structure, attempt all tags present
814 wscbor_chunk_t
*chunk
= wscbor_chunk_read(pinfo
->pool
, tvb
, &offset
);
815 for (wmem_list_frame_t
*it
= wmem_list_head(chunk
->tags
); it
;
816 it
= wmem_list_frame_next(it
)) {
817 wscbor_tag_t
*tag
= (wscbor_tag_t
*) wmem_list_frame_data(it
);
818 // first usable tag wins
819 dissector_handle_t dissector
= dissector_get_custom_table_handle(table_cose_msg_tag
, &(tag
->value
));
823 int sublen
= call_dissector_only(dissector
, tvb
, pinfo
, tree
, tag
);
829 proto_item
*item_msg
= proto_tree_add_item(tree
, proto_cose
, tvb
, 0, -1, ENC_NA
);
830 expert_add_info(pinfo
, item_msg
, &ei_invalid_tag
);
834 /** Dissect the application/cose media type with optional parameters.
836 static int dissect_cose_media_type(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
) {
837 const media_content_info_t
*content_info
= (media_content_info_t
*)data
;
839 const char *subtype
= NULL
;
840 if (content_info
->media_str
) {
841 subtype
= ws_find_media_type_parameter(pinfo
->pool
, content_info
->media_str
, "cose-type");
844 dissector_handle_t dissector
;
846 dissector
= dissector_get_string_handle(table_cose_media_subtype
, subtype
);
849 // no media type parameter, require tagged message
850 dissector
= handle_cose_msg_tagged
;
853 int sublen
= call_dissector_only(dissector
, tvb
, pinfo
, tree
, NULL
);
858 proto_item
*item_msg
= proto_tree_add_item(tree
, proto_cose
, tvb
, 0, -1, ENC_NA
);
859 expert_add_info(pinfo
, item_msg
, &ei_invalid_tag
);
863 static void dissect_value_alg(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, int *offset
) {
864 wscbor_chunk_t
*chunk
= wscbor_chunk_read(pinfo
->pool
, tvb
, offset
);
865 switch (chunk
->type_major
) {
867 case CBOR_TYPE_NEGINT
: {
868 int64_t *val
= wscbor_require_int64(pinfo
->pool
, chunk
);
869 proto_tree_add_cbor_int64(tree
, hf_hdr_alg_int
, pinfo
, tvb
, chunk
, val
);
872 case CBOR_TYPE_STRING
: {
873 proto_tree_add_cbor_tstr(tree
, hf_hdr_alg_tstr
, pinfo
, tvb
, chunk
);
881 static int dissect_header_salt(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
) {
884 wscbor_chunk_t
*chunk
= wscbor_chunk_read(pinfo
->pool
, tvb
, &offset
);
885 wscbor_require_bstr(pinfo
->pool
, chunk
);
886 proto_tree_add_cbor_bstr(tree
, hf_hdr_salt
, pinfo
, tvb
, chunk
);
891 static void dissect_value_cose_key(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, int *offset
) {
892 GVariant
*kty_key
= g_variant_new_int64(1);
893 CLEANUP_PUSH(g_variant_cleanup
, kty_key
);
895 dissect_header_map(table_keyparam
, tvb
, pinfo
, tree
, offset
, kty_key
);
897 CLEANUP_CALL_AND_POP
;
900 static int dissect_header_static_key(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
) {
902 proto_item
*item_ctr
= proto_tree_add_item(tree
, hf_hdr_static_key
, tvb
, 0, -1, ENC_NA
);
903 proto_tree
*tree_ctr
= proto_item_add_subtree(item_ctr
, ett_hdr_static_key
);
904 dissect_value_cose_key(tvb
, pinfo
, tree_ctr
, &offset
);
908 static int dissect_header_ephem_key(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
) {
910 proto_item
*item_ctr
= proto_tree_add_item(tree
, hf_hdr_ephem_key
, tvb
, 0, -1, ENC_NA
);
911 proto_tree
*tree_ctr
= proto_item_add_subtree(item_ctr
, ett_hdr_ephem_key
);
912 dissect_value_cose_key(tvb
, pinfo
, tree_ctr
, &offset
);
916 static int dissect_header_alg(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
) {
918 dissect_value_alg(tvb
, pinfo
, tree
, &offset
);
922 static int dissect_header_crit(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
) {
925 wscbor_chunk_t
*chunk_list
= wscbor_chunk_read(pinfo
->pool
, tvb
, &offset
);
926 wscbor_require_array(chunk_list
);
927 proto_item
*item_list
= proto_tree_add_cbor_container(tree
, hf_hdr_crit_list
, pinfo
, tvb
, chunk_list
);
928 if (!wscbor_skip_if_errors(pinfo
->pool
, tvb
, &offset
, chunk_list
)) {
929 proto_tree
*tree_list
= proto_item_add_subtree(item_list
, ett_hdr_crit_list
);
931 for (uint64_t ix
= 0; ix
< chunk_list
->head_value
; ++ix
) {
932 wscbor_chunk_t
*chunk_label
= wscbor_chunk_read(pinfo
->pool
, tvb
, &offset
);
933 switch (chunk_label
->type_major
) {
935 case CBOR_TYPE_NEGINT
: {
936 int64_t *label
= wscbor_require_int64(pinfo
->pool
, chunk_label
);
937 proto_tree_add_cbor_int64(tree_list
, hf_hdr_label_int
, pinfo
, tvb
, chunk_label
, label
);
940 case CBOR_TYPE_STRING
: {
941 proto_tree_add_cbor_tstr(tree_list
, hf_hdr_label_tstr
, pinfo
, tvb
, chunk_label
);
950 proto_item_set_len(item_list
, offset
);
954 static int dissect_header_ctype(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
) {
957 wscbor_chunk_t
*chunk
= wscbor_chunk_read(pinfo
->pool
, tvb
, &offset
);
958 switch (chunk
->type_major
) {
959 case CBOR_TYPE_UINT
: {
960 uint64_t *val
= wscbor_require_uint64(pinfo
->pool
, chunk
);
961 proto_tree_add_cbor_uint64(tree
, hf_hdr_ctype_uint
, pinfo
, tvb
, chunk
, val
);
964 case CBOR_TYPE_STRING
: {
965 proto_tree_add_cbor_tstr(tree
, hf_hdr_ctype_tstr
, pinfo
, tvb
, chunk
);
975 static int dissect_header_kid(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
) {
978 wscbor_chunk_t
*chunk
= wscbor_chunk_read(pinfo
->pool
, tvb
, &offset
);
979 tvbuff_t
*val
= wscbor_require_bstr(pinfo
->pool
, chunk
);
980 proto_item
*item_kid
= proto_tree_add_cbor_bstr(tree
, hf_hdr_kid
, pinfo
, tvb
, chunk
);
982 if (val
&& tvb_utf_8_isprint(val
, 0, -1)) {
983 proto_tree
*tree_kid
= proto_item_add_subtree(item_kid
, ett_hdr_kid
);
984 proto_item
*kid_text
= proto_tree_add_item(tree_kid
, hf_hdr_kid_text
, val
, 0, tvb_reported_length(val
), ENC_UTF_8
);
985 proto_item_set_generated(kid_text
);
991 static int dissect_header_iv(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
) {
994 wscbor_chunk_t
*chunk
= wscbor_chunk_read(pinfo
->pool
, tvb
, &offset
);
995 wscbor_require_bstr(pinfo
->pool
, chunk
);
996 proto_tree_add_cbor_bstr(tree
, hf_hdr_iv
, pinfo
, tvb
, chunk
);
1001 static int dissect_header_piv(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
) {
1004 wscbor_chunk_t
*chunk
= wscbor_chunk_read(pinfo
->pool
, tvb
, &offset
);
1005 wscbor_require_bstr(pinfo
->pool
, chunk
);
1006 proto_tree_add_cbor_bstr(tree
, hf_hdr_piv
, pinfo
, tvb
, chunk
);
1011 static void dissect_value_x5cert(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, int *offset
) {
1012 wscbor_chunk_t
*chunk_item
= wscbor_chunk_read(pinfo
->pool
, tvb
, offset
);
1013 tvbuff_t
*tvb_item
= wscbor_require_bstr(pinfo
->pool
, chunk_item
);
1016 // disallow column text rewrite
1017 char *info_text
= wmem_strdup(pinfo
->pool
, col_get_text(pinfo
->cinfo
, COL_INFO
));
1020 dissector_try_string_with_data(
1022 "application/pkix-cert",
1033 col_add_str(pinfo
->cinfo
, COL_INFO
, info_text
);
1037 static void dissect_value_cosex509(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, int hfindex
, int *offset
) {
1038 proto_item
*item_ctr
= proto_tree_add_item(tree
, hfindex
, tvb
, 0, -1, ENC_NA
);
1039 proto_tree
*tree_ctr
= proto_item_add_subtree(item_ctr
, ett_hdr_x5cert_list
);
1041 wscbor_chunk_t
*chunk_ctr
= wscbor_chunk_read(pinfo
->pool
, tvb
, offset
);
1042 switch (chunk_ctr
->type_major
) {
1043 case CBOR_TYPE_ARRAY
: {
1044 wscbor_require_array(chunk_ctr
);
1045 if (!wscbor_skip_if_errors(pinfo
->pool
, tvb
, offset
, chunk_ctr
)) {
1046 for (uint64_t ix
= 0; ix
< chunk_ctr
->head_value
; ++ix
) {
1047 dissect_value_x5cert(tvb
, pinfo
, tree_ctr
, offset
);
1052 case CBOR_TYPE_BYTESTRING
: {
1053 // re-read this chunk as cert
1054 *offset
= chunk_ctr
->start
;
1055 dissect_value_x5cert(tvb
, pinfo
, tree_ctr
, offset
);
1063 static int dissect_header_x5bag(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
) {
1065 dissect_value_cosex509(tvb
, pinfo
, tree
, hf_hdr_x5bag
, &offset
);
1068 static int dissect_header_x5chain(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
) {
1070 dissect_value_cosex509(tvb
, pinfo
, tree
, hf_hdr_x5chain
, &offset
);
1074 static int dissect_header_x5t(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
) {
1077 wscbor_chunk_t
*chunk_list
= wscbor_chunk_read(pinfo
->pool
, tvb
, &offset
);
1078 wscbor_require_array_size(chunk_list
, 2, 2);
1079 proto_item
*item_list
= proto_tree_add_cbor_container(tree
, hf_hdr_x5t
, pinfo
, tvb
, chunk_list
);
1080 if (!wscbor_skip_if_errors(pinfo
->pool
, tvb
, &offset
, chunk_list
)) {
1081 proto_tree
*tree_list
= proto_item_add_subtree(item_list
, ett_hdr_x5t_list
);
1083 dissect_value_alg(tvb
, pinfo
, tree_list
, &offset
);
1085 wscbor_chunk_t
*chunk_hash
= wscbor_chunk_read(pinfo
->pool
, tvb
, &offset
);
1086 wscbor_require_bstr(pinfo
->pool
, chunk_hash
);
1087 proto_tree_add_cbor_bstr(tree_list
, hf_hdr_x5t_hash
, pinfo
, tvb
, chunk_hash
);
1094 static int dissect_header_x5u(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
) {
1097 wscbor_chunk_t
*chunk
= wscbor_chunk_read(pinfo
->pool
, tvb
, &offset
);
1098 wscbor_require_major_type(chunk
, CBOR_TYPE_STRING
);
1099 proto_tree_add_cbor_tstr(tree
, hf_hdr_x5u
, pinfo
, tvb
, chunk
);
1104 static int dissect_cose_key(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
) {
1107 proto_item
*item_msg
= proto_tree_add_item(tree
, proto_cose
, tvb
, 0, -1, ENC_NA
);
1108 proto_item_append_text(item_msg
, ": COSE_Key");
1110 dissect_value_cose_key(tvb
, pinfo
, tree
, &offset
);
1115 static int dissect_cose_key_set(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
) {
1118 wscbor_chunk_t
*chunk_set
= wscbor_chunk_read(pinfo
->pool
, tvb
, &offset
);
1119 wscbor_require_array(chunk_set
);
1120 proto_item
*item_set
= proto_tree_add_cbor_container(tree
, proto_cose
, pinfo
, tvb
, chunk_set
);
1121 proto_item_append_text(item_set
, ": COSE_KeySet");
1122 if (!wscbor_skip_if_errors(pinfo
->pool
, tvb
, &offset
, chunk_set
)) {
1123 proto_tree
*tree_set
= proto_item_add_subtree(item_set
, ett_key_set
);
1125 for (uint64_t ix
= 0; ix
< chunk_set
->head_value
; ++ix
) {
1126 proto_item
*item_key
= proto_tree_add_item(tree_set
, hf_key
, tvb
, offset
, -1, ENC_NA
);
1127 proto_tree
*tree_key
= proto_item_add_subtree(item_key
, ett_key
);
1129 const int offset_key
= offset
;
1130 dissect_value_cose_key(tvb
, pinfo
, tree_key
, &offset
);
1131 proto_item_set_len(item_key
, offset
- offset_key
);
1135 proto_item_set_len(item_set
, offset
);
1139 static int dissect_keyparam_kty(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
) {
1142 wscbor_chunk_t
*chunk
= wscbor_chunk_read(pinfo
->pool
, tvb
, &offset
);
1143 switch (chunk
->type_major
) {
1144 case CBOR_TYPE_UINT
:
1145 case CBOR_TYPE_NEGINT
: {
1146 int64_t *val
= wscbor_require_int64(pinfo
->pool
, chunk
);
1147 proto_tree_add_cbor_int64(tree
, hf_keyparam_kty_int
, pinfo
, tvb
, chunk
, val
);
1150 case CBOR_TYPE_STRING
: {
1151 proto_tree_add_cbor_tstr(tree
, hf_keyparam_kty_tstr
, pinfo
, tvb
, chunk
);
1161 static int dissect_keyparam_keyops(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
) {
1164 wscbor_chunk_t
*chunk_list
= wscbor_chunk_read(pinfo
->pool
, tvb
, &offset
);
1165 wscbor_require_array(chunk_list
);
1166 proto_item
*item_list
= proto_tree_add_cbor_container(tree
, hf_keyparam_keyops_list
, pinfo
, tvb
, chunk_list
);
1167 if (!wscbor_skip_if_errors(pinfo
->pool
, tvb
, &offset
, chunk_list
)) {
1168 proto_tree
*tree_list
= proto_item_add_subtree(item_list
, ett_keyops_list
);
1170 for (uint64_t ix
= 0; ix
< chunk_list
->head_value
; ++ix
) {
1171 wscbor_chunk_t
*chunk
= wscbor_chunk_read(pinfo
->pool
, tvb
, &offset
);
1172 switch (chunk
->type_major
) {
1173 case CBOR_TYPE_UINT
:
1174 case CBOR_TYPE_NEGINT
: {
1175 int64_t *val
= wscbor_require_int64(pinfo
->pool
, chunk
);
1176 proto_tree_add_cbor_int64(tree_list
, hf_keyparam_keyops_int
, pinfo
, tvb
, chunk
, val
);
1179 case CBOR_TYPE_STRING
: {
1180 proto_tree_add_cbor_tstr(tree_list
, hf_keyparam_keyops_tstr
, pinfo
, tvb
, chunk
);
1188 proto_item_set_len(item_list
, offset
- chunk_list
->start
);
1193 static int dissect_keyparam_baseiv(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
) {
1196 wscbor_chunk_t
*chunk
= wscbor_chunk_read(pinfo
->pool
, tvb
, &offset
);
1197 wscbor_require_bstr(pinfo
->pool
, chunk
);
1198 proto_tree_add_cbor_bstr(tree
, hf_keyparam_baseiv
, pinfo
, tvb
, chunk
);
1203 static int dissect_keyparam_crv(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
) {
1206 wscbor_chunk_t
*chunk
= wscbor_chunk_read(pinfo
->pool
, tvb
, &offset
);
1207 switch (chunk
->type_major
) {
1208 case CBOR_TYPE_UINT
:
1209 case CBOR_TYPE_NEGINT
: {
1210 int64_t *val
= wscbor_require_int64(pinfo
->pool
, chunk
);
1211 proto_tree_add_cbor_int64(tree
, hf_keyparam_crv_int
, pinfo
, tvb
, chunk
, val
);
1214 case CBOR_TYPE_STRING
: {
1215 proto_tree_add_cbor_tstr(tree
, hf_keyparam_crv_tstr
, pinfo
, tvb
, chunk
);
1225 static int dissect_keyparam_xcoord(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
) {
1228 wscbor_chunk_t
*chunk
= wscbor_chunk_read(pinfo
->pool
, tvb
, &offset
);
1229 wscbor_require_bstr(pinfo
->pool
, chunk
);
1230 proto_tree_add_cbor_bstr(tree
, hf_keyparam_xcoord
, pinfo
, tvb
, chunk
);
1235 static int dissect_keyparam_ycoord(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
) {
1238 wscbor_chunk_t
*chunk
= wscbor_chunk_read(pinfo
->pool
, tvb
, &offset
);
1239 switch (chunk
->type_major
) {
1240 case CBOR_TYPE_FLOAT_CTRL
: {
1241 proto_tree_add_item(tree
, hf_keyparam_ycoord
, tvb
, 0, 0, ENC_NA
);
1244 case CBOR_TYPE_BYTESTRING
: {
1245 proto_tree_add_cbor_bstr(tree
, hf_keyparam_ycoord
, pinfo
, tvb
, chunk
);
1255 static int dissect_keyparam_dcoord(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
) {
1258 wscbor_chunk_t
*chunk
= wscbor_chunk_read(pinfo
->pool
, tvb
, &offset
);
1259 wscbor_require_bstr(pinfo
->pool
, chunk
);
1260 proto_tree_add_cbor_bstr(tree
, hf_keyparam_dcoord
, pinfo
, tvb
, chunk
);
1265 static int dissect_keyparam_k(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
) {
1268 wscbor_chunk_t
*chunk
= wscbor_chunk_read(pinfo
->pool
, tvb
, &offset
);
1269 wscbor_require_bstr(pinfo
->pool
, chunk
);
1270 proto_tree_add_cbor_bstr(tree
, hf_keyparam_k
, pinfo
, tvb
, chunk
);
1275 /** Register a message dissector.
1277 static void register_msg_dissector(dissector_handle_t dis_h
, uint64_t tag_int
, const char *media_subtype
) {
1278 uint64_t *key_int
= g_new(uint64_t, 1);
1280 dissector_add_custom_table_handle("cose.msgtag", key_int
, dis_h
);
1282 dissector_add_string("cose.mediasub", media_subtype
, dis_h
);
1285 /** Register a header dissector.
1286 * @param dissector The dissector function.
1287 * @param label The associated map label.
1288 * @param name The header name.
1290 static void register_header_dissector(dissector_t dissector
, GVariant
*label
, const char *name
) {
1291 dissector_handle_t dis_h
= create_dissector_handle_with_name_and_description(dissector
, proto_cose_params
, NULL
, name
);
1293 cose_param_key_t
*key
= g_new0(cose_param_key_t
, 1);
1296 dissector_add_custom_table_handle("cose.header", key
, dis_h
);
1299 /** Register a key parameter dissector.
1300 * @param dissector The dissector function.
1301 * @param kty The associated key type "kty" or NULL.
1302 * @param label The associated map label.
1303 * @param name The header name.
1305 static void register_keyparam_dissector(dissector_t dissector
, GVariant
*kty
, GVariant
*label
, const char *name
) {
1306 dissector_handle_t dis_h
= create_dissector_handle_with_name_and_description(dissector
, proto_cose_params
, NULL
, name
);
1308 cose_param_key_t
*key
= g_new0(cose_param_key_t
, 1);
1311 key
->principal
= kty
;
1315 dissector_add_custom_table_handle("cose.keyparam", key
, dis_h
);
1318 /// Initialize for a new file load
1319 static void cose_init(void) {
1322 /// Cleanup after a file
1323 static void cose_cleanup(void) {
1326 /// Re-initialize after a configuration change
1327 static void cose_reinit(void) {
1330 /// Overall registration of the protocol
1331 void proto_register_cose(void) {
1332 proto_cose
= proto_register_protocol("CBOR Object Signing and Encryption", proto_name_cose
, "cose");
1333 register_init_routine(&cose_init
);
1334 register_cleanup_routine(&cose_cleanup
);
1336 proto_cose_params
= proto_register_protocol_in_name_only(
1337 "COSE Parameter Subdissectors",
1338 "COSE Parameter Subdissectors",
1344 proto_register_field_array(proto_cose
, fields
, array_length(fields
));
1345 proto_register_subtree_array(ett
, array_length(ett
));
1346 expert_module_t
*expert
= expert_register_protocol(proto_cose
);
1347 expert_register_field_array(expert
, expertitems
, array_length(expertitems
));
1349 handle_cose_msg_hdr
= register_dissector("cose.msg.headers", dissect_cose_msg_header_map
, proto_cose
);
1351 table_cose_msg_tag
= register_custom_dissector_table("cose.msgtag", "COSE Message Tag", proto_cose
, g_int64_hash
, g_int64_equal
, g_free
);
1352 table_cose_media_subtype
= register_dissector_table("cose.mediasub", "COSE Media Subtype", proto_cose
, FT_STRINGZ
, STRING_CASE_INSENSITIVE
);
1354 handle_cose_msg_tagged
= register_dissector("cose", dissect_cose_msg_tagged
, proto_cose_params
);
1355 handle_cose_media_type
= create_dissector_handle_with_name(dissect_cose_media_type
, proto_cose
, "cose");
1357 // RFC 9052 data item names (Table 1)
1358 handle_cose_sign
= register_dissector_with_description("cose_sign", "COSE_Sign", dissect_cose_sign
, proto_cose
);
1359 handle_cose_sign1
= register_dissector_with_description("cose_sign1", "COSE_Sign1", dissect_cose_sign1
, proto_cose
);
1360 handle_cose_encrypt
= register_dissector_with_description("cose_encrypt", "COSE_Encrypt", dissect_cose_encrypt
, proto_cose
);
1361 handle_cose_encrypt0
= register_dissector_with_description("cose_encrypt0", "COSE_Encrypt0", dissect_cose_encrypt0
, proto_cose
);
1362 handle_cose_mac
= register_dissector_with_description("cose_mac", "COSE_Mac", dissect_cose_mac
, proto_cose
);
1363 handle_cose_mac0
= register_dissector_with_description("cose_mac0", "COSE_Mac0", dissect_cose_mac0
, proto_cose
);
1365 table_header
= register_custom_dissector_table("cose.header", "COSE Header Parameter", proto_cose
, cose_param_key_hash
, cose_param_key_equal
, cose_param_key_free
);
1367 // RFC 9052 data item names (Section 11.3.2)
1368 handle_cose_key
= register_dissector_with_description("cose_key", "COSE_Key", dissect_cose_key
, proto_cose
);
1369 handle_cose_key_set
= register_dissector_with_description("cose_key_set", "COSE_KeySet", dissect_cose_key_set
, proto_cose
);
1371 table_keyparam
= register_custom_dissector_table("cose.keyparam", "COSE Key Parameter", proto_cose
, cose_param_key_hash
, cose_param_key_equal
, cose_param_key_free
);
1373 module_t
*module_cose
= prefs_register_protocol(proto_cose
, cose_reinit
);
1377 void proto_reg_handoff_cose(void) {
1378 table_media
= find_dissector_table("media_type");
1379 handle_cbor
= find_dissector("cbor");
1381 dissector_add_string("media_type", "application/cose", handle_cose_media_type
);
1382 dissector_add_string("media_type.suffix", "cose", handle_cose_media_type
);
1384 // RFC 9052 tags and media type "cose-type" names (Table 1)
1385 register_msg_dissector(handle_cose_sign
, 98, "cose-sign");
1386 register_msg_dissector(handle_cose_sign1
, 18, "cose-sign1");
1387 register_msg_dissector(handle_cose_encrypt
, 96, "cose-encrypt");
1388 register_msg_dissector(handle_cose_encrypt0
, 16, "cose-encrypt0");
1389 register_msg_dissector(handle_cose_mac
, 97, "cose-mac");
1390 register_msg_dissector(handle_cose_mac0
, 17, "cose-mac0");
1392 // RFC 9053 header labels
1393 register_header_dissector(dissect_header_salt
, g_variant_new_int64(-20), "salt");
1394 register_header_dissector(dissect_header_static_key
, g_variant_new_int64(-2), "static key");
1395 register_header_dissector(dissect_header_ephem_key
, g_variant_new_int64(-1), "ephemeral key");
1396 register_header_dissector(dissect_header_alg
, g_variant_new_int64(1), "alg");
1397 register_header_dissector(dissect_header_crit
, g_variant_new_int64(2), "crit");
1398 register_header_dissector(dissect_header_ctype
, g_variant_new_int64(3), "content type");
1399 register_header_dissector(dissect_header_kid
, g_variant_new_int64(4), "kid");
1400 register_header_dissector(dissect_header_iv
, g_variant_new_int64(5), "IV");
1401 register_header_dissector(dissect_header_piv
, g_variant_new_int64(6), "Partial IV");
1402 // RFC 9360 header labels
1403 register_header_dissector(dissect_header_x5bag
, g_variant_new_int64(32), "x5bag");
1404 register_header_dissector(dissect_header_x5chain
, g_variant_new_int64(33), "x5chain");
1405 register_header_dissector(dissect_header_x5t
, g_variant_new_int64(34), "x5t");
1406 register_header_dissector(dissect_header_x5u
, g_variant_new_int64(35), "x5u");
1408 dissector_add_string("media_type", "application/cose-key", handle_cose_key
);
1409 dissector_add_string("media_type", "application/cose-key-set", handle_cose_key_set
);
1410 // RFC 9052 key parameter labels
1411 register_keyparam_dissector(dissect_keyparam_kty
, NULL
, g_variant_new_int64(1), "kty");
1412 register_keyparam_dissector(dissect_header_kid
, NULL
, g_variant_new_int64(2), "kid");
1413 register_keyparam_dissector(dissect_header_alg
, NULL
, g_variant_new_int64(3), "alg");
1414 register_keyparam_dissector(dissect_keyparam_keyops
, NULL
, g_variant_new_int64(4), "key_ops");
1415 register_keyparam_dissector(dissect_keyparam_baseiv
, NULL
, g_variant_new_int64(5), "Base IV");
1416 // kty-specific parameters
1418 GVariant
*kty
= g_variant_new_int64(1);
1419 register_keyparam_dissector(dissect_keyparam_crv
, kty
, g_variant_new_int64(-1), "crv");
1420 register_keyparam_dissector(dissect_keyparam_xcoord
, kty
, g_variant_new_int64(-2), "x");
1421 register_keyparam_dissector(dissect_keyparam_dcoord
, kty
, g_variant_new_int64(-3), "d");
1422 g_variant_unref(kty
);
1425 GVariant
*kty
= g_variant_new_int64(2);
1426 register_keyparam_dissector(dissect_keyparam_crv
, kty
, g_variant_new_int64(-1), "crv");
1427 register_keyparam_dissector(dissect_keyparam_xcoord
, kty
, g_variant_new_int64(-2), "x");
1428 register_keyparam_dissector(dissect_keyparam_ycoord
, kty
, g_variant_new_int64(-3), "y");
1429 register_keyparam_dissector(dissect_keyparam_dcoord
, kty
, g_variant_new_int64(-4), "d");
1430 g_variant_unref(kty
);
1433 GVariant
*kty
= g_variant_new_int64(4);
1434 register_keyparam_dissector(dissect_keyparam_k
, kty
, g_variant_new_int64(-1), "k");
1435 g_variant_unref(kty
);