Coverage Report

Created: 2026-11-12 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-matter.c
Line
Count
Source
1
/* packet-matter.c
2
 * Routines for Matter IoT protocol dissection
3
 * Copyright 2023, Nicolás Alvarez <nicolas.alvarez@gmail.com>
4
 * Copyright 2024, Arkadiusz Bokowy <a.bokowy@samsung.com>
5
 *
6
 * Wireshark - Network traffic analyzer
7
 * By Gerald Combs <gerald@wireshark.org>
8
 * Copyright 1998 Gerald Combs
9
 *
10
 * SPDX-License-Identifier: GPL-2.0-or-later
11
 */
12
13
/*
14
 * The Matter protocol provides an interoperable application
15
 * layer solution for smart home devices over IPv6.
16
 *
17
 * The specification can be freely requested at:
18
 * https://csa-iot.org/developer-resource/specifications-download-request/
19
 *
20
 * Comments below reference section numbers of the Matter Core Specification R1.0 (22-27349-001).
21
 *
22
 * Matter-TLV dissector is based on Matter Specification Version 1.3.
23
 */
24
25
#include <config.h>
26
27
#include <epan/expert.h>
28
#include <epan/packet.h>
29
#include <wsutil/array.h>
30
31
/* Prototypes */
32
/* (Required to prevent [-Wmissing-prototypes] warnings */
33
void proto_reg_handoff_matter(void);
34
void proto_register_matter(void);
35
36
/* Initialize the protocol and registered fields */
37
static dissector_handle_t matter_handle;
38
39
static int proto_matter;
40
static int hf_message_flags;
41
static int hf_message_version;
42
static int hf_message_has_source;
43
static int hf_message_dsiz;
44
static int hf_message_session_id;
45
static int hf_message_security_flags;
46
static int hf_message_flag_privacy;
47
static int hf_message_flag_control;
48
static int hf_message_flag_extensions;
49
static int hf_message_session_type;
50
static int hf_message_counter;
51
static int hf_message_src_id;
52
static int hf_message_dest_node_id;
53
static int hf_message_dest_group_id;
54
static int hf_message_privacy_header;
55
56
static int hf_payload;
57
static int hf_payload_mic;
58
static int hf_payload_exchange_flags;
59
static int hf_payload_flag_initiator;
60
static int hf_payload_flag_ack;
61
static int hf_payload_flag_reliability;
62
static int hf_payload_flag_secured_extensions;
63
static int hf_payload_flag_vendor;
64
static int hf_payload_protocol_opcode;
65
static int hf_payload_exchange_id;
66
static int hf_payload_protocol_vendor_id;
67
static int hf_payload_protocol_id;
68
static int hf_payload_ack_counter;
69
static int hf_payload_secured_ext_length;
70
static int hf_payload_secured_ext;
71
static int hf_payload_application;
72
73
static int hf_matter_tlv_elem;
74
static int hf_matter_tlv_elem_control;
75
static int hf_matter_tlv_elem_control_tag_format;
76
static int hf_matter_tlv_elem_control_element_type;
77
static int hf_matter_tlv_elem_tag;
78
static int hf_matter_tlv_elem_length;
79
static int hf_matter_tlv_elem_value_int;
80
static int hf_matter_tlv_elem_value_uint;
81
static int hf_matter_tlv_elem_value_bytes;
82
83
static int ett_matter;
84
static int ett_message_flags;
85
static int ett_security_flags;
86
static int ett_payload;
87
static int ett_exchange_flags;
88
89
static int ett_matter_tlv;
90
static int ett_matter_tlv_control;
91
92
static expert_field ei_matter_tlv_unsupported_control;
93
static expert_field ei_matter_short_secured_payload;
94
95
/* message flags + session ID + security flags + counter */
96
0
#define MATTER_MIN_LENGTH 8
97
98
// Section 3.6
99
0
#define CRYPTO_AEAD_MIC_LENGTH 16
100
101
// Section 4.4.1.2
102
16
#define MESSAGE_FLAG_VERSION_MASK       0xF0
103
16
#define MESSAGE_FLAG_HAS_SOURCE         0x04
104
0
#define MESSAGE_FLAG_HAS_DEST_NODE      0x01
105
0
#define MESSAGE_FLAG_HAS_DEST_GROUP     0x02
106
16
#define MESSAGE_FLAG_DSIZ_MASK          0x03
107
108
// Section 4.4.1.4
109
16
#define SECURITY_FLAG_HAS_PRIVACY       0x80
110
16
#define SECURITY_FLAG_IS_CONTROL        0x40
111
16
#define SECURITY_FLAG_HAS_EXTENSIONS    0x20
112
16
#define SECURITY_FLAG_SESSION_TYPE_MASK 0x03
113
0
#define SECURITY_FLAG_SESSION_TYPE_UNICAST 0
114
0
#define SECURITY_FLAG_SESSION_TYPE_GROUP   1
115
116
// Section 4.4.3.1
117
16
#define EXCHANGE_FLAG_IS_INITIATOR      0x01
118
16
#define EXCHANGE_FLAG_ACK_MSG           0x02
119
16
#define EXCHANGE_FLAG_RELIABILITY       0x04
120
16
#define EXCHANGE_FLAG_HAS_SECURED_EXT   0x08
121
16
#define EXCHANGE_FLAG_HAS_VENDOR_PROTO  0x10
122
123
static const value_string dsiz_vals[] = {
124
    { 0, "Not present" },
125
    { MESSAGE_FLAG_HAS_DEST_NODE,  "64-bit Node ID" },
126
    { MESSAGE_FLAG_HAS_DEST_GROUP, "16-bit Group ID" },
127
    { 0, NULL }
128
};
129
130
static const value_string session_type_vals[] = {
131
    { 0, "Unicast Session" },
132
    { 1, "Group Session" },
133
    { 0, NULL }
134
};
135
136
// Appendix 7.2. Tag Control Field
137
static const value_string matter_tlv_tag_format_vals[] = {
138
    { 0, "Anonymous Tag Form, 0 octets" },
139
    { 1, "Context-specific Tag Form, 1 octet" },
140
    { 2, "Common Profile Tag Form, 2 octets" },
141
    { 3, "Common Profile Tag Form, 4 octets" },
142
    { 4, "Implicit Profile Tag Form, 2 octets" },
143
    { 5, "Implicit Profile Tag Form, 4 octets" },
144
    { 6, "Fully-qualified Tag Form, 6 octets" },
145
    { 7, "Fully-qualified Tag Form, 8 octets" },
146
    { 0, NULL }
147
};
148
149
// Appendix 7.1. Element Type Field
150
static const value_string matter_tlv_elem_type_vals[] = {
151
    { 0x00, "Signed Integer, 1-octet value" },
152
    { 0x01, "Signed Integer, 2-octet value" },
153
    { 0x02, "Signed Integer, 4-octet value" },
154
    { 0x03, "Signed Integer, 8-octet value" },
155
    { 0x04, "Unsigned Integer, 1-octet value" },
156
    { 0x05, "Unsigned Integer, 2-octet value" },
157
    { 0x06, "Unsigned Integer, 4-octet value" },
158
    { 0x07, "Unsigned Integer, 8-octet value" },
159
    { 0x08, "Boolean False" },
160
    { 0x09, "Boolean True" },
161
    { 0x0A, "Floating Point Number, 4-octet value" },
162
    { 0x0B, "Floating Point Number, 8-octet value" },
163
    { 0x0C, "UTF-8 String, 1-octet length" },
164
    { 0x0D, "UTF-8 String, 2-octet length" },
165
    { 0x0E, "UTF-8 String, 4-octet length" },
166
    { 0x0F, "UTF-8 String, 8-octet length" },
167
    { 0x10, "Octet String, 1-octet length" },
168
    { 0x11, "Octet String, 2-octet length" },
169
    { 0x12, "Octet String, 4-octet length" },
170
    { 0x13, "Octet String, 8-octet length" },
171
    { 0x14, "Null" },
172
    { 0x15, "Structure" },
173
    { 0x16, "Array" },
174
    { 0x17, "List" },
175
    // XXX: If the Tag Control Field is set to 0x00 (Anonymous Tag), the
176
    //      value of 0x18 means "End of Container". For other Tag Control
177
    //      Field values, the value of 0x18 is reserved.
178
    // TODO: This should be handled in the dissector.
179
    { 0x18, "End of Container" },
180
    { 0x19, "Reserved" },
181
    { 0x1A, "Reserved" },
182
    { 0x1B, "Reserved" },
183
    { 0x1C, "Reserved" },
184
    { 0x1D, "Reserved" },
185
    { 0x1E, "Reserved" },
186
    { 0x1F, "Reserved" },
187
    { 0, NULL }
188
};
189
190
static int
191
dissect_matter_payload(tvbuff_t *tvb, packet_info *pinfo, proto_tree *pl_tree);
192
193
static int
194
dissect_matter(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
195
0
{
196
0
    proto_item *ti;
197
0
    proto_tree *matter_tree;
198
0
    uint32_t    offset = 0;
199
200
    /* info extracted from the packet */
201
0
    bool is_unsecured_session;
202
0
    uint8_t message_flags = 0;
203
0
    uint8_t security_flags = 0;
204
0
    uint8_t message_dsiz = 0;
205
0
    uint8_t message_session_type = 0;
206
0
    uint32_t session_id = 0;
207
208
    /* Check that the packet is long enough for it to belong to us. */
209
0
    if (tvb_reported_length(tvb) < MATTER_MIN_LENGTH)
210
0
        return 0;
211
212
0
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "Matter");
213
214
    /* create display subtree for the protocol */
215
0
    ti = proto_tree_add_item(tree, proto_matter, tvb, 0, -1, ENC_NA);
216
217
0
    matter_tree = proto_item_add_subtree(ti, ett_matter);
218
219
0
    static int* const message_flag_fields[] = {
220
0
        &hf_message_version,
221
0
        &hf_message_has_source,
222
0
        &hf_message_dsiz,
223
0
        NULL
224
0
    };
225
0
    static int* const message_secflag_fields[] = {
226
0
        &hf_message_flag_privacy,
227
0
        &hf_message_flag_control,
228
0
        &hf_message_flag_extensions,
229
0
        &hf_message_session_type,
230
0
        NULL
231
0
    };
232
233
    // Section 4.4.1.2
234
0
    proto_tree_add_bitmask(matter_tree, tvb, offset, hf_message_flags, ett_message_flags, message_flag_fields, ENC_LITTLE_ENDIAN);
235
0
    message_flags = tvb_get_uint8(tvb, offset);
236
0
    message_dsiz = (message_flags & MESSAGE_FLAG_DSIZ_MASK);
237
0
    offset += 1;
238
239
    // Section 4.4.1.3
240
0
    proto_tree_add_item_ret_uint(matter_tree, hf_message_session_id, tvb, offset, 2, ENC_LITTLE_ENDIAN, &session_id);
241
0
    offset += 2;
242
243
    // Section 4.4.1.4
244
0
    proto_tree_add_bitmask(matter_tree, tvb, offset, hf_message_security_flags, ett_security_flags, message_secflag_fields, ENC_LITTLE_ENDIAN);
245
0
    security_flags = tvb_get_uint8(tvb, offset);
246
0
    message_session_type = (security_flags & SECURITY_FLAG_SESSION_TYPE_MASK);
247
0
    offset += 1;
248
249
    // Section 4.4.1.4: "The Unsecured Session SHALL be indicated
250
    // when both Session Type and Session ID are set to 0."
251
0
    is_unsecured_session = (message_session_type == SECURITY_FLAG_SESSION_TYPE_UNICAST && session_id == 0);
252
253
0
    if (is_unsecured_session)
254
0
        col_set_str(pinfo->cinfo, COL_INFO, "Unsecured Session");
255
0
    else if (message_session_type == SECURITY_FLAG_SESSION_TYPE_UNICAST)
256
0
        col_add_fstr(pinfo->cinfo, COL_INFO, "Unicast Session [0x%04x]", session_id);
257
0
    else if (message_session_type == SECURITY_FLAG_SESSION_TYPE_GROUP)
258
0
        col_add_fstr(pinfo->cinfo, COL_INFO, "Group Session [0x%04x]", session_id);
259
260
    // decryption of message privacy is not yet supported,
261
    // but add an opaque field with the encrypted blob
262
    // Section 4.8.3
263
0
    if (security_flags & SECURITY_FLAG_HAS_PRIVACY) {
264
265
0
        uint32_t privacy_header_length = 4;
266
0
        if (message_flags & MESSAGE_FLAG_HAS_SOURCE) {
267
0
            privacy_header_length += 8;
268
0
        }
269
0
        if (message_dsiz == MESSAGE_FLAG_HAS_DEST_NODE) {
270
0
            privacy_header_length += 8;
271
0
        } else if (message_dsiz == MESSAGE_FLAG_HAS_DEST_GROUP) {
272
0
            privacy_header_length += 2;
273
0
        }
274
0
        proto_tree_add_bytes_format(matter_tree, hf_message_privacy_header, tvb, offset, privacy_header_length, NULL, "Encrypted Headers");
275
0
        offset += privacy_header_length;
276
277
0
    } else {
278
279
        // Section 4.4.1.5
280
0
        unsigned int message_counter;
281
0
        proto_tree_add_item_ret_uint(matter_tree, hf_message_counter, tvb, offset, 4, ENC_LITTLE_ENDIAN, &message_counter);
282
0
        col_append_fstr(pinfo->cinfo, COL_INFO, ": Counter=%u", message_counter);
283
0
        offset += 4;
284
285
        // Section 4.4.1.6
286
0
        if (message_flags & MESSAGE_FLAG_HAS_SOURCE) {
287
0
            uint64_t node_id;
288
0
            proto_tree_add_item_ret_uint64(matter_tree, hf_message_src_id, tvb, offset, 8, ENC_LITTLE_ENDIAN, &node_id);
289
0
            col_append_fstr(pinfo->cinfo, COL_INFO, " Src=0x%016" PRIx64, node_id);
290
0
            offset += 8;
291
0
        }
292
293
        // Section 4.4.1.7
294
0
        if (message_dsiz == MESSAGE_FLAG_HAS_DEST_NODE) {
295
0
            uint64_t node_id;
296
0
            proto_tree_add_item_ret_uint64(matter_tree, hf_message_dest_node_id, tvb, offset, 8, ENC_LITTLE_ENDIAN, &node_id);
297
0
            col_append_fstr(pinfo->cinfo, COL_INFO, " Dest=0x%016" PRIx64, node_id);
298
0
            offset += 8;
299
0
        } else if (message_dsiz == MESSAGE_FLAG_HAS_DEST_GROUP) {
300
0
            unsigned int group_id;
301
0
            proto_tree_add_item_ret_uint(matter_tree, hf_message_dest_group_id, tvb, offset, 2, ENC_LITTLE_ENDIAN, &group_id);
302
0
            col_append_fstr(pinfo->cinfo, COL_INFO, " Group=0x%04x", group_id);
303
0
            offset += 2;
304
0
        }
305
306
0
    }
307
308
0
    if (is_unsecured_session) {
309
0
        proto_item *payload_item = proto_tree_add_none_format(matter_tree, hf_payload, tvb, offset, -1, "Protocol Payload");
310
0
        proto_tree *payload_tree = proto_item_add_subtree(payload_item, ett_payload);
311
0
        tvbuff_t *next_tvb = tvb_new_subset_remaining(tvb, offset);
312
313
0
        offset += dissect_matter_payload(next_tvb, pinfo, payload_tree);
314
0
    } else {
315
        // Secured sessions not yet supported in the dissector.
316
0
        unsigned remaining = tvb_reported_length_remaining(tvb, offset);
317
0
        uint32_t payload_length;
318
319
0
        if (remaining < CRYPTO_AEAD_MIC_LENGTH) {
320
0
            proto_tree_add_expert_remaining(matter_tree, pinfo, &ei_matter_short_secured_payload, tvb, offset);
321
0
            return tvb_captured_length(tvb);
322
0
        }
323
0
        payload_length = remaining - CRYPTO_AEAD_MIC_LENGTH;
324
0
        proto_tree_add_none_format(matter_tree, hf_payload, tvb, offset, payload_length, "Encrypted Payload (%u bytes)", payload_length);
325
0
        proto_tree_add_item(matter_tree, hf_payload_mic, tvb, offset + payload_length, CRYPTO_AEAD_MIC_LENGTH, ENC_NA);
326
0
    }
327
328
0
    return offset;
329
0
}
330
331
static int
332
dissect_matter_payload(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *pl_tree)
333
0
{
334
0
    uint32_t offset = 0;
335
336
0
    uint8_t exchange_flags = 0;
337
338
0
    static int* const exchange_flag_fields[] = {
339
0
        &hf_payload_flag_initiator,
340
0
        &hf_payload_flag_ack,
341
0
        &hf_payload_flag_reliability,
342
0
        &hf_payload_flag_secured_extensions,
343
0
        &hf_payload_flag_vendor,
344
0
        NULL
345
0
    };
346
    // Section 4.4.3.1
347
0
    proto_tree_add_bitmask(pl_tree, tvb, offset, hf_payload_exchange_flags, ett_exchange_flags, exchange_flag_fields, ENC_LITTLE_ENDIAN);
348
0
    exchange_flags = tvb_get_uint8(tvb, offset);
349
0
    offset += 1;
350
351
    // Section 4.4.3.2
352
0
    proto_tree_add_item(pl_tree, hf_payload_protocol_opcode, tvb, offset, 1, ENC_LITTLE_ENDIAN);
353
0
    offset += 1;
354
355
    // Section 4.4.3.3
356
0
    proto_tree_add_item(pl_tree, hf_payload_exchange_id, tvb, offset, 2, ENC_LITTLE_ENDIAN);
357
0
    offset += 2;
358
359
0
    if (exchange_flags & EXCHANGE_FLAG_HAS_VENDOR_PROTO) {
360
        // NOTE: The Matter specification R1.0 (22-27349) section 4.4 says
361
        // the Vendor ID comes after the Protocol ID. However, the SDK
362
        // implementation expects and produces the vendor ID first and the
363
        // protocol ID afterwards. This was reported, and the maintainers
364
        // declared it a bug in the *specification*, which will be resolved
365
        // in a future version:
366
        // https://github.com/project-chip/connectedhomeip/issues/25003
367
        // So we parse Vendor ID first, contrary to the current spec.
368
0
        proto_tree_add_item(pl_tree, hf_payload_protocol_vendor_id, tvb, offset, 2, ENC_LITTLE_ENDIAN);
369
0
        offset += 2;
370
0
    }
371
372
    // Section 4.4.3.4
373
0
    proto_tree_add_item(pl_tree, hf_payload_protocol_id, tvb, offset, 2, ENC_LITTLE_ENDIAN);
374
0
    offset += 2;
375
376
    // Section 4.4.3.6
377
0
    if (exchange_flags & EXCHANGE_FLAG_ACK_MSG) {
378
0
        unsigned int ack_counter;
379
0
        proto_tree_add_item_ret_uint(pl_tree, hf_payload_ack_counter, tvb, offset, 4, ENC_LITTLE_ENDIAN, &ack_counter);
380
0
        col_append_fstr(pinfo->cinfo, COL_INFO, " AckCounter=%u", ack_counter);
381
0
        offset += 4;
382
0
    }
383
384
    // Section 4.4.3.7
385
0
    if (exchange_flags & EXCHANGE_FLAG_HAS_SECURED_EXT) {
386
0
        uint32_t secured_ext_len = 0;
387
0
        proto_tree_add_item_ret_uint(pl_tree, hf_payload_secured_ext_length, tvb, offset, 2, ENC_LITTLE_ENDIAN, &secured_ext_len);
388
0
        offset += 2;
389
0
        proto_tree_add_item(pl_tree, hf_payload_secured_ext, tvb, offset, secured_ext_len, ENC_NA);
390
0
        offset += secured_ext_len;
391
0
    }
392
0
    uint32_t application_length = tvb_reported_length_remaining(tvb, offset);
393
0
    proto_tree_add_bytes_format(pl_tree, hf_payload_application, tvb, offset, application_length, NULL, "Application payload (%u bytes)", application_length);
394
0
    offset += application_length;
395
0
    return offset;
396
0
}
397
398
// Dissect the Matter-defined TLV encoding.
399
// Appendix A: Tag-length-value (TLV) Encoding Format
400
static int
401
// NOLINTNEXTLINE(misc-no-recursion)
402
dissect_matter_tlv(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
403
0
{
404
    // For signed and unsigned integer types and for UTF-8 and octet strings,
405
    // the length is encoded in the lowest 2 bits of the control byte.
406
0
    static const int elem_sizes[] = { 1, 2, 4, 8 };
407
408
0
    int matter_tlv_elem_tag = hf_matter_tlv_elem_tag;
409
0
    unsigned length = tvb_reported_length_remaining(tvb, 0);
410
0
    unsigned offset = 0;
411
412
0
    if (data != NULL)
413
        // Use caller-provided tag field.
414
0
        matter_tlv_elem_tag = *((int *)data);
415
416
0
    while (offset < length) {
417
418
        // The new element is created with initial length set to 1 which accounts
419
        // for the control byte (tag format and element type). The length will be
420
        // updated once the element is fully dissected.
421
0
        proto_item *ti_element = proto_tree_add_item(tree, hf_matter_tlv_elem, tvb, offset, 1, ENC_NA);
422
0
        proto_tree *tree_element = proto_item_add_subtree(ti_element, ett_matter_tlv);
423
0
        int base_offset = offset;
424
425
0
        uint32_t control_tag_format = 0;
426
0
        uint32_t control_element = 0;
427
428
0
        proto_item *ti_control = proto_tree_add_item(tree_element, hf_matter_tlv_elem_control, tvb, offset, 1, ENC_NA);
429
0
        proto_tree *tree_control = proto_item_add_subtree(ti_control, ett_matter_tlv_control);
430
        // The tag format is determined by the upper 3 bits of the control byte.
431
0
        proto_tree_add_item_ret_uint(tree_control, hf_matter_tlv_elem_control_tag_format, tvb, offset, 1, ENC_NA, &control_tag_format);
432
        // The element type is determined by the lower 5 bits of the control byte.
433
0
        proto_tree_add_item_ret_uint(tree_control, hf_matter_tlv_elem_control_element_type, tvb, offset, 1, ENC_NA, &control_element);
434
435
0
        offset += 1;
436
437
0
        proto_item_append_text(ti_element, ": %s", val_to_str_const(control_element, matter_tlv_elem_type_vals, "Unknown"));
438
439
        // The control byte 0x18 means "End of Container".
440
0
        if (control_tag_format == 0 && control_element == 0x18)
441
0
            return offset;
442
443
0
        switch (control_tag_format)
444
0
        {
445
0
        case 0: // Anonymous Tag Form (0 octets)
446
0
            break;
447
0
        case 1: // Context-specific Tag Form (1 octet)
448
0
            proto_tree_add_item(tree_element, matter_tlv_elem_tag, tvb, offset, 1, ENC_NA);
449
0
            offset += 1;
450
0
            break;
451
0
        default:
452
0
            goto unsupported_control;
453
0
        }
454
455
        // The string length might be encoded on 1, 2, 4 or 8 octets. In theory,
456
        // the length can be up to 2^64 - 1 bytes, but in practice, it should be
457
        // limited to a reasonable value (it should be safe to assume that the
458
        // length will not exceed 2^16 - 1 bytes).
459
0
        uint64_t str_length;
460
461
0
        switch (control_element)
462
0
        {
463
0
        case 0x00: // Signed Integer, 1-octet value
464
0
        case 0x01: // Signed Integer, 2-octet value
465
0
        case 0x02: // Signed Integer, 4-octet value
466
0
        case 0x03: // Signed Integer, 8-octet value
467
0
        case 0x04: // Unsigned Integer, 1-octet value
468
0
        case 0x05: // Unsigned Integer, 2-octet value
469
0
        case 0x06: // Unsigned Integer, 4-octet value
470
0
        case 0x07: // Unsigned Integer, 8-octet value
471
0
        {
472
            // Integer type (signed or unsigned) is encoded in the 3rd bit of the control element.
473
0
            int hf = (control_element & 0x04) ? hf_matter_tlv_elem_value_uint : hf_matter_tlv_elem_value_int;
474
0
            int size = elem_sizes[control_element & 0x03];
475
0
            proto_tree_add_item(tree_element, hf, tvb, offset, size, ENC_LITTLE_ENDIAN);
476
0
            offset += size;
477
0
            break;
478
0
        }
479
0
        case 0x08: // Boolean False
480
0
        case 0x09: // Boolean True
481
0
            break;
482
0
        case 0x10: // Octet String (1-octet length)
483
0
        case 0x11: // Octet String (2-octet length)
484
0
        case 0x12: // Octet String (4-octet length)
485
0
        case 0x13: // Octet String (8-octet length)
486
0
        {
487
0
            int size = elem_sizes[control_element & 0x03];
488
0
            proto_tree_add_item_ret_uint64(tree_element, hf_matter_tlv_elem_length, tvb, offset, size, ENC_LITTLE_ENDIAN, &str_length);
489
0
            offset += size;
490
0
            proto_tree_add_item(tree_element, hf_matter_tlv_elem_value_bytes, tvb, offset, (int)str_length, ENC_NA);
491
0
            offset += (int)str_length;
492
0
            break;
493
0
        }
494
0
        case 0x14: // Null
495
0
            break;
496
0
        case 0x15: // Structure
497
0
        case 0x16: // Array
498
0
        case 0x17: // List
499
0
            increment_dissection_depth(pinfo);
500
0
            offset += dissect_matter_tlv(tvb_new_subset_remaining(tvb, offset), pinfo, tree_element, data);
501
0
            decrement_dissection_depth(pinfo);
502
0
            break;
503
0
        default:
504
0
            goto unsupported_control;
505
0
        }
506
507
0
        proto_item_set_len(ti_element, offset - base_offset);
508
0
        continue;
509
510
0
unsupported_control:
511
0
        expert_add_info(pinfo, tree_control, &ei_matter_tlv_unsupported_control);
512
0
        proto_item_set_len(ti_element, offset - base_offset);
513
0
        return length;
514
0
    }
515
516
0
    return length;
517
0
}
518
519
void
520
proto_register_matter(void)
521
16
{
522
16
    static hf_register_info hf[] = {
523
16
        { &hf_message_flags,
524
16
          { "Message Flags", "matter.message.flags",
525
16
            FT_UINT8, BASE_HEX, NULL, 0,
526
16
            NULL, HFILL }
527
16
        },
528
16
        { &hf_message_version,
529
16
          { "Version", "matter.message.version",
530
16
            FT_UINT8, BASE_DEC, NULL, MESSAGE_FLAG_VERSION_MASK,
531
16
            "Message format version", HFILL }
532
16
        },
533
16
        { &hf_message_has_source,
534
16
          { "Has Source ID", "matter.message.has_source_id",
535
16
            FT_BOOLEAN, 8, NULL, MESSAGE_FLAG_HAS_SOURCE,
536
16
            "Source ID field is present", HFILL }
537
16
        },
538
16
        { &hf_message_dsiz,
539
16
          { "Destination ID Type", "matter.message.dsiz",
540
16
            FT_UINT8, BASE_DEC, VALS(dsiz_vals), MESSAGE_FLAG_DSIZ_MASK,
541
16
            "Size and meaning of the Destination Node ID field", HFILL }
542
16
        },
543
16
        { &hf_message_session_id,
544
16
          { "Session ID", "matter.message.session_id",
545
16
            FT_UINT16, BASE_HEX, NULL, 0,
546
16
            "The session associated with this message", HFILL }
547
16
        },
548
16
        { &hf_message_security_flags,
549
16
          { "Security Flags", "matter.message.security_flags",
550
16
            FT_UINT8, BASE_HEX, NULL, 0,
551
16
            "Message security flags", HFILL }
552
16
        },
553
16
        { &hf_message_flag_privacy,
554
16
          { "Privacy", "matter.message.has_privacy",
555
16
            FT_BOOLEAN, 8, NULL, SECURITY_FLAG_HAS_PRIVACY,
556
16
            "Whether the message is encoded with privacy enhancements", HFILL }
557
16
        },
558
16
        { &hf_message_flag_control,
559
16
          { "Control", "matter.message.is_control",
560
16
            FT_BOOLEAN, 8, NULL, SECURITY_FLAG_IS_CONTROL,
561
16
            "Whether this is a control message", HFILL }
562
16
        },
563
16
        { &hf_message_flag_extensions,
564
16
          { "Message Extensions", "matter.message.has_extensions",
565
16
            FT_BOOLEAN, 8, NULL, SECURITY_FLAG_HAS_EXTENSIONS,
566
16
            "Whether message extensions are present", HFILL }
567
16
        },
568
16
        { &hf_message_session_type,
569
16
          { "Session Type", "matter.message.session_type",
570
16
            FT_UINT8, BASE_HEX, VALS(session_type_vals), SECURITY_FLAG_SESSION_TYPE_MASK,
571
16
            "The type of session associated with the message", HFILL }
572
16
        },
573
16
        { &hf_message_counter,
574
16
          { "Message Counter", "matter.message.counter",
575
16
            FT_UINT32, BASE_DEC, NULL, 0,
576
16
            NULL, HFILL }
577
16
        },
578
16
        { &hf_message_src_id,
579
16
          { "Source Node ID", "matter.message.src_id",
580
16
            FT_UINT64, BASE_HEX, NULL, 0,
581
16
            "Unique identifier of the source node", HFILL }
582
16
        },
583
16
        { &hf_message_dest_node_id,
584
16
          { "Destination Node ID", "matter.message.dest_node_id",
585
16
            FT_UINT64, BASE_HEX, NULL, 0,
586
16
            "Unique identifier of the destination node", HFILL }
587
16
        },
588
16
        { &hf_message_dest_group_id,
589
16
          { "Destination Group ID", "matter.message.dest_group_id",
590
16
            FT_UINT16, BASE_HEX, NULL, 0,
591
16
            "Unique identifier of the destination group", HFILL }
592
16
        },
593
16
        { &hf_message_privacy_header,
594
16
          { "Encrypted header fields", "matter.message.privacy_header",
595
16
            FT_BYTES, BASE_NONE, NULL, 0,
596
16
            "Headers encrypted with message privacy", HFILL }
597
16
        },
598
16
        { &hf_payload,
599
16
          { "Payload", "matter.payload",
600
16
            FT_NONE, BASE_NONE, NULL, 0,
601
16
            "Message Payload", HFILL }
602
16
        },
603
16
        { &hf_payload_mic,
604
16
          { "Integrity Check", "matter.payload.mic",
605
16
            FT_BYTES, BASE_NONE, NULL, 0,
606
16
            "Message Integrity Check (MIC) for the encrypted payload", HFILL }
607
16
        },
608
16
        { &hf_payload_exchange_flags,
609
16
          { "Exchange Flags", "matter.payload.exchange_flags",
610
16
            FT_UINT8, BASE_HEX, NULL, 0,
611
16
            "Flags related to the exchange", HFILL }
612
16
        },
613
16
        { &hf_payload_flag_initiator,
614
16
          { "Initiator", "matter.payload.initiator",
615
16
            FT_BOOLEAN, 8, NULL, EXCHANGE_FLAG_IS_INITIATOR,
616
16
            "Whether the message was sent by the initiator of the exchange", HFILL }
617
16
        },
618
16
        { &hf_payload_flag_ack,
619
16
          { "Acknowledgement", "matter.payload.ack_msg",
620
16
            FT_BOOLEAN, 8, NULL, EXCHANGE_FLAG_ACK_MSG,
621
16
            "Whether the message is an acknowledgement of a previously-received message", HFILL }
622
16
        },
623
16
        { &hf_payload_flag_reliability,
624
16
          { "Reliability", "matter.payload.reliability",
625
16
            FT_BOOLEAN, 8, NULL, EXCHANGE_FLAG_RELIABILITY,
626
16
            "Whether the sender wishes to receive an acknowledgement for this message", HFILL }
627
16
        },
628
16
        { &hf_payload_flag_secured_extensions,
629
16
          { "Secure extensions", "matter.payload.has_secured_ext",
630
16
            FT_BOOLEAN, 8, NULL, EXCHANGE_FLAG_HAS_SECURED_EXT,
631
16
            "Whether this message contains Secured Extensions", HFILL }
632
16
        },
633
16
        { &hf_payload_flag_vendor,
634
16
          { "Has Vendor ID", "matter.payload.has_vendor_protocol",
635
16
            FT_BOOLEAN, 8, NULL, EXCHANGE_FLAG_HAS_VENDOR_PROTO,
636
16
            "Whether this message contains a protocol vendor ID", HFILL }
637
16
        },
638
16
        { &hf_payload_protocol_opcode,
639
16
          { "Protocol Opcode", "matter.payload.protocol_opcode",
640
16
            FT_UINT8, BASE_HEX, NULL, 0,
641
16
            "Opcode of the message (depends on Protocol ID)", HFILL }
642
16
        },
643
16
        { &hf_payload_exchange_id,
644
16
          { "Exchange ID", "matter.payload.exchange_id",
645
16
            FT_UINT16, BASE_HEX, NULL, 0,
646
16
            "The exchange to which the message belongs", HFILL }
647
16
        },
648
16
        { &hf_payload_protocol_vendor_id,
649
16
          { "Protocol Vendor ID", "matter.payload.protocol_vendor_id",
650
16
            FT_UINT16, BASE_HEX, NULL, 0,
651
16
            "Vendor ID namespace for the protocol ID", HFILL }
652
16
        },
653
16
        { &hf_payload_protocol_id,
654
16
          { "Protocol ID", "matter.payload.protocol_id",
655
16
            FT_UINT16, BASE_HEX, NULL, 0,
656
16
            "The protocol in which the Protocol Opcode of the message is defined", HFILL }
657
16
        },
658
16
        { &hf_payload_ack_counter,
659
16
          { "Acknowledged message counter", "matter.payload.ack_counter",
660
16
            FT_UINT32, BASE_DEC, NULL, 0,
661
16
            "The message counter of a previous message that is being acknowledged by this message", HFILL }
662
16
        },
663
16
        { &hf_payload_secured_ext_length,
664
16
          { "Secured extensions length", "matter.payload.secured_ext.length",
665
16
            FT_UINT16, BASE_DEC, NULL, 0,
666
16
            "Secured extensions payload length, in bytes", HFILL }
667
16
        },
668
16
        { &hf_payload_secured_ext,
669
16
          { "Secured extensions payload", "matter.payload.secured_ext",
670
16
            FT_BYTES, BASE_NONE, NULL, 0,
671
16
            NULL, HFILL }
672
16
        },
673
16
        { &hf_payload_application,
674
16
          { "Application payload", "matter.payload.application",
675
16
            FT_BYTES, BASE_NONE, NULL, 0,
676
16
            NULL, HFILL }
677
16
        },
678
16
        { &hf_matter_tlv_elem,
679
16
          { "TLV Element", "matter.tlv",
680
16
            FT_NONE, BASE_NONE, NULL, 0x0,
681
16
            "Matter-TLV Element", HFILL }
682
16
        },
683
16
        { &hf_matter_tlv_elem_control,
684
16
          { "Control Byte", "matter.tlv.control",
685
16
            FT_UINT8, BASE_HEX, NULL, 0x0,
686
16
            "Matter-TLV Control Byte", HFILL }
687
16
        },
688
16
        { &hf_matter_tlv_elem_control_tag_format,
689
16
          { "Tag Format", "matter.tlv.control.tag",
690
16
            FT_UINT8, BASE_HEX, VALS(matter_tlv_tag_format_vals), 0xE0,
691
16
            NULL, HFILL }
692
16
        },
693
16
        { &hf_matter_tlv_elem_control_element_type,
694
16
          { "Element Type", "matter.tlv.control.element",
695
16
            FT_UINT8, BASE_HEX, VALS(matter_tlv_elem_type_vals), 0x1F,
696
16
            NULL, HFILL }
697
16
        },
698
16
        { &hf_matter_tlv_elem_tag,
699
16
          { "Tag", "matter.tlv.tag",
700
16
            FT_UINT32, BASE_HEX, NULL, 0x0,
701
16
            NULL, HFILL }
702
16
        },
703
16
        { &hf_matter_tlv_elem_length,
704
16
          { "Length", "matter.tlv.length",
705
16
            FT_UINT64, BASE_DEC, NULL, 0x0,
706
16
            NULL, HFILL }
707
16
        },
708
16
        { &hf_matter_tlv_elem_value_int,
709
16
          { "Value", "matter.tlv.value_int",
710
16
            FT_INT64, BASE_DEC, NULL, 0x0,
711
16
            NULL, HFILL }
712
16
        },
713
16
        { &hf_matter_tlv_elem_value_uint,
714
16
          { "Value", "matter.tlv.value_uint",
715
16
            FT_UINT64, BASE_DEC, NULL, 0x0,
716
16
            NULL, HFILL }
717
16
        },
718
16
        { &hf_matter_tlv_elem_value_bytes,
719
16
          { "Value", "matter.tlv.value_bytes",
720
16
            FT_BYTES, BASE_NONE, NULL, 0x0,
721
16
            NULL, HFILL }
722
16
        },
723
16
    };
724
725
    /* Setup protocol subtree array */
726
16
    static int *ett[] = {
727
16
        &ett_matter,
728
16
        &ett_message_flags,
729
16
        &ett_security_flags,
730
16
        &ett_payload,
731
16
        &ett_exchange_flags,
732
16
        &ett_matter_tlv,
733
16
        &ett_matter_tlv_control,
734
16
    };
735
736
16
    static ei_register_info ei[] = {
737
16
        { &ei_matter_tlv_unsupported_control,
738
16
          { "matter.tlv.control.unsupported", PI_UNDECODED, PI_WARN,
739
16
            "Unsupported Matter-TLV control byte", EXPFILL }
740
16
        },
741
16
        { &ei_matter_short_secured_payload,
742
16
          { "matter.payload.too_short", PI_MALFORMED, PI_ERROR,
743
16
            "Secured payload shorter than the message integrity code", EXPFILL }
744
16
        },
745
16
    };
746
747
    /* Register the protocol name and description */
748
16
    proto_matter = proto_register_protocol("Matter", "Matter", "matter");
749
16
    matter_handle = register_dissector("matter", dissect_matter, proto_matter);
750
16
    register_dissector("matter.tlv", dissect_matter_tlv, proto_matter);
751
752
    /* Required function calls to register the header fields and subtrees */
753
16
    proto_register_field_array(proto_matter, hf, array_length(hf));
754
16
    proto_register_subtree_array(ett, array_length(ett));
755
756
16
    expert_module_t *expert = expert_register_protocol(proto_matter);
757
16
    expert_register_field_array(expert, ei, array_length(ei));
758
759
16
}
760
761
void
762
proto_reg_handoff_matter(void)
763
16
{
764
    /* Register default Matter ports 5540 for automatic dissection */
765
16
    dissector_add_uint("udp.port", 5540, matter_handle);
766
767
    /* Allow decode-as for other ports */
768
16
    dissector_add_for_decode_as("udp.port", matter_handle);
769
16
}