Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / epan / dissectors / packet-cose.c
blobd1d71fbec46cbd1ea2e12ed5955d2732e0cbaed0
1 /* packet-cose.c
2 * Routines for CBOR Object Signing and Encryption (COSE) dissection
3 * References:
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
18 #include <config.h>
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>
31 #include <inttypes.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
41 /// Protocol handles
42 static int proto_cose;
43 static int proto_cose_params;
45 /// Dissect opaque CBOR data
46 static dissector_handle_t handle_cbor;
47 /// Dissector handles
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[] = {
69 {-65535, "RS1"},
70 {-259, "RS512"},
71 {-258, "RS384"},
72 {-257, "RS256"},
73 {-47, "ES256K"},
74 {-45, "SHAKE256"},
75 {-44, "SHA-512"},
76 {-43, "SHA-384"},
77 {-39, "PS512"},
78 {-38, "PS384"},
79 {-37, "PS256"},
80 {-36, "ES512"},
81 {-35, "ES384"},
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"},
92 {-18, "SHAKE128"},
93 {-17, "SHA-512/256"},
94 {-16, "SHA-256"},
95 {-15, "SHA-256/64"},
96 {-14, "SHA-1"},
97 {-13, "direct+HKDF-AES-256"},
98 {-12, "direct+HKDF-AES-128"},
99 {-11, "direct+HKDF-SHA-512"},
100 {-10, "direct+HKDF-SHA-256"},
101 {-8, "EdDSA"},
102 {-7, "ES256"},
103 {-6, "direct"},
104 {-5, "A256KW"},
105 {-4, "A192KW"},
106 {-3, "A128KW"},
107 {0, "Reserved"},
108 {1, "A128GCM"},
109 {2, "A192GCM"},
110 {3, "A256GCM"},
111 {4, "HMAC 256/64"},
112 {5, "HMAC 256/256"},
113 {6, "HMAC 384/384"},
114 {7, "HMAC 512/512"},
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"},
129 {0, NULL},
132 static const val64_string kty_vals[] = {
133 {0, "Reserved"},
134 {1, "OKP"},
135 {2, "EC2"},
136 {3, "RSA"},
137 {4, "Symmetric"},
138 {5, "HSS-LMS"},
139 {0, NULL},
142 static const val64_string keyops_vals[] = {
143 {1, "sign"},
144 {2, "verify"},
145 {3, "encrypt"},
146 {4, "decrypt"},
147 {5, "key wrap"},
148 {6, "key unwrap"},
149 {7, "derive key"},
150 {8, "derive bits"},
151 {9, "MAC create"},
152 {10, "MAC verify"},
153 {0, NULL},
156 static const val64_string crv_vals[] = {
157 {0, "Reserved"},
158 {1, "P-256"},
159 {2, "P-384"},
160 {3, "P-521"},
161 {4, "X25519"},
162 {5, "X448"},
163 {6, "Ed25519"},
164 {7, "Ed448"},
165 {0, NULL},
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;
180 static int hf_tag;
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;
203 static int hf_key;
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}},
273 static int ett_msg;
274 static int ett_sig_list;
275 static int ett_sig;
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;
288 static int ett_key;
289 static int ett_key_set;
290 static int ett_keyops_list;
291 /// Tree structures
292 static int *ett[] = {
293 &ett_msg,
294 &ett_sig_list,
295 &ett_sig,
296 &ett_recip_list,
297 &ett_recip,
298 &ett_prot_bstr,
299 &ett_unprot,
300 &ett_hdr_map,
301 &ett_hdr_label,
302 &ett_hdr_kid,
303 &ett_hdr_static_key,
304 &ett_hdr_ephem_key,
305 &ett_hdr_crit_list,
306 &ett_hdr_x5cert_list,
307 &ett_hdr_x5t_list,
308 &ett_key,
309 &ett_key_set,
310 &ett_keyops_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;
322 unsigned val = 0;
323 if (obj->principal) {
324 val ^= g_int64_hash(obj->principal);
326 if (obj->label) {
327 val ^= g_variant_hash(obj->label);
329 return val;
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)) {
338 return FALSE;
341 else if ((aobj->principal == NULL) != (bobj->principal == NULL)) {
342 return FALSE;
345 gboolean match;
346 if (aobj->label && bobj->label) {
347 match = g_variant_equal(aobj->label, bobj->label);
349 else {
350 // don't care
351 match = FALSE;
353 return match;
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);
361 if (obj->label) {
362 g_variant_unref(obj->label);
364 g_free(obj);
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) {
378 case CBOR_TYPE_UINT:
379 case CBOR_TYPE_NEGINT: {
380 int64_t *label = wscbor_require_int64(alloc, chunk);
381 if (label) {
382 result = g_variant_new_int64(*label);
384 break;
386 case CBOR_TYPE_STRING: {
387 const char *label = wscbor_require_tstr(alloc, chunk);
388 if (label) {
389 result = g_variant_new_string(label);
391 break;
393 default:
394 break;
396 wscbor_chunk_free(chunk);
397 return result;
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) {
417 case CBOR_TYPE_UINT:
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);
421 if (label) {
422 key.label = ctx->label =
423 g_variant_new_int64(*label);
424 label_str = wmem_strdup_printf(pinfo->pool, "%" PRId64, *label);
426 break;
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);
431 if (label) {
432 key.label = ctx->label =
433 g_variant_new_string(label);
434 label_str = label;
436 break;
438 default:
439 break;
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);
445 if (!dissector) {
446 key.principal = NULL;
447 dissector = dissector_get_custom_table_handle(dis_table, &key);
449 const char *dis_name = dissector_handle_get_description(dissector);
450 if (dis_name) {
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)) {
459 return false;
461 tvb_value = tvb_new_subset_length(tvb, offset_value, *offset - offset_value);
463 int sublen = 0;
464 if (dissector) {
465 sublen = call_dissector_only(dissector, tvb_value, pinfo, tree_label, ctx);
466 if ((sublen < 0) ||
467 ((sublen > 0) && ((unsigned)sublen < tvb_reported_length(tvb_value)))) {
468 expert_add_info(pinfo, proto_tree_get_parent(tree), &ei_value_partial_decode);
471 if (ctx->label) {
472 g_variant_unref(ctx->label);
473 ctx->label = NULL;
475 if (sublen == 0) {
476 TRY {
477 call_dissector(handle_cbor, tvb_value, pinfo, tree_label);
479 CATCH_ALL {}
480 ENDTRY;
482 return true;
485 static void
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);
492 if (ctx->label) {
493 g_variant_unref(ctx->label);
494 ctx->label = NULL;
498 static void
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
524 if (principal_key) {
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);
528 if (key) {
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) {
534 break;
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)) {
544 break;
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_) {
556 int offset = 0;
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;
563 return offset;
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) {
569 if (!data) {
570 return;
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) {
580 // Protected in bstr
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);
584 if (tvb_prot) {
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);
592 // Unprotected
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);
597 *offset += sublen;
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);
608 else {
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);
635 else {
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) {
684 int offset = 0;
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);
710 return offset;
713 static int dissect_cose_sign1(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) {
714 int offset = 0;
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);
729 return offset;
732 static int dissect_cose_encrypt(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) {
733 int offset = 0;
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);
748 return offset;
751 static int dissect_cose_encrypt0(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) {
752 int offset = 0;
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);
766 return offset;
769 static int dissect_cose_mac(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) {
770 int offset = 0;
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);
786 return offset;
789 static int dissect_cose_mac0(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) {
790 int offset = 0;
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);
805 return 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_) {
811 int offset = 0;
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));
820 if (!dissector) {
821 continue;
823 int sublen = call_dissector_only(dissector, tvb, pinfo, tree, tag);
824 if (sublen > 0) {
825 return sublen;
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);
831 return -1;
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;
845 if (subtype) {
846 dissector = dissector_get_string_handle(table_cose_media_subtype, subtype);
848 else {
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);
854 if (sublen > 0) {
855 return sublen;
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);
860 return -1;
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) {
866 case CBOR_TYPE_UINT:
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);
870 break;
872 case CBOR_TYPE_STRING: {
873 proto_tree_add_cbor_tstr(tree, hf_hdr_alg_tstr, pinfo, tvb, chunk);
874 break;
876 default:
877 break;
881 static int dissect_header_salt(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
882 int offset = 0;
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);
888 return offset;
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_) {
901 int offset = 0;
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);
905 return offset;
908 static int dissect_header_ephem_key(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
909 int offset = 0;
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);
913 return offset;
916 static int dissect_header_alg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
917 int offset = 0;
918 dissect_value_alg(tvb, pinfo, tree, &offset);
919 return offset;
922 static int dissect_header_crit(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
923 int offset = 0;
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) {
934 case CBOR_TYPE_UINT:
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);
938 break;
940 case CBOR_TYPE_STRING: {
941 proto_tree_add_cbor_tstr(tree_list, hf_hdr_label_tstr, pinfo, tvb, chunk_label);
942 break;
944 default:
945 break;
950 proto_item_set_len(item_list, offset);
951 return offset;
954 static int dissect_header_ctype(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
955 int offset = 0;
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);
962 break;
964 case CBOR_TYPE_STRING: {
965 proto_tree_add_cbor_tstr(tree, hf_hdr_ctype_tstr, pinfo, tvb, chunk);
966 break;
968 default:
969 break;
972 return offset;
975 static int dissect_header_kid(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
976 int offset = 0;
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);
988 return offset;
991 static int dissect_header_iv(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
992 int offset = 0;
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);
998 return offset;
1001 static int dissect_header_piv(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
1002 int offset = 0;
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);
1008 return offset;
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);
1015 if (tvb_item) {
1016 // disallow column text rewrite
1017 char *info_text = wmem_strdup(pinfo->pool, col_get_text(pinfo->cinfo, COL_INFO));
1019 TRY {
1020 dissector_try_string_with_data(
1021 table_media,
1022 "application/pkix-cert",
1023 tvb_item,
1024 pinfo,
1025 tree,
1026 true,
1027 NULL
1030 CATCH_ALL {}
1031 ENDTRY;
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);
1050 break;
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);
1056 break;
1058 default:
1059 break;
1063 static int dissect_header_x5bag(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
1064 int offset = 0;
1065 dissect_value_cosex509(tvb, pinfo, tree, hf_hdr_x5bag, &offset);
1066 return offset;
1068 static int dissect_header_x5chain(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
1069 int offset = 0;
1070 dissect_value_cosex509(tvb, pinfo, tree, hf_hdr_x5chain, &offset);
1071 return offset;
1074 static int dissect_header_x5t(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
1075 int offset = 0;
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);
1091 return offset;
1094 static int dissect_header_x5u(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
1095 int offset = 0;
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);
1101 return offset;
1104 static int dissect_cose_key(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
1105 int offset = 0;
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);
1112 return offset;
1115 static int dissect_cose_key_set(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
1116 int offset = 0;
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);
1136 return offset;
1139 static int dissect_keyparam_kty(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
1140 int offset = 0;
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);
1148 break;
1150 case CBOR_TYPE_STRING: {
1151 proto_tree_add_cbor_tstr(tree, hf_keyparam_kty_tstr, pinfo, tvb, chunk);
1152 break;
1154 default:
1155 break;
1158 return offset;
1161 static int dissect_keyparam_keyops(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
1162 int offset = 0;
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);
1177 break;
1179 case CBOR_TYPE_STRING: {
1180 proto_tree_add_cbor_tstr(tree_list, hf_keyparam_keyops_tstr, pinfo, tvb, chunk);
1181 break;
1183 default:
1184 break;
1188 proto_item_set_len(item_list, offset - chunk_list->start);
1190 return offset;
1193 static int dissect_keyparam_baseiv(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
1194 int offset = 0;
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);
1200 return offset;
1203 static int dissect_keyparam_crv(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
1204 int offset = 0;
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);
1212 break;
1214 case CBOR_TYPE_STRING: {
1215 proto_tree_add_cbor_tstr(tree, hf_keyparam_crv_tstr, pinfo, tvb, chunk);
1216 break;
1218 default:
1219 break;
1222 return offset;
1225 static int dissect_keyparam_xcoord(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
1226 int offset = 0;
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);
1232 return offset;
1235 static int dissect_keyparam_ycoord(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
1236 int offset = 0;
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);
1242 break;
1244 case CBOR_TYPE_BYTESTRING: {
1245 proto_tree_add_cbor_bstr(tree, hf_keyparam_ycoord, pinfo, tvb, chunk);
1246 break;
1248 default:
1249 break;
1252 return offset;
1255 static int dissect_keyparam_dcoord(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
1256 int offset = 0;
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);
1262 return offset;
1265 static int dissect_keyparam_k(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
1266 int offset = 0;
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);
1272 return offset;
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);
1279 *key_int = tag_int;
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);
1294 key->label = label;
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);
1309 if (kty) {
1310 g_variant_ref(kty);
1311 key->principal = kty;
1313 key->label = label;
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",
1339 "cose_params",
1340 proto_cose,
1341 FT_PROTOCOL
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);
1374 (void)module_cose;
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);
1438 cose_reinit();