/src/wireshark/epan/dissectors/packet-nts-ke.c
Line | Count | Source |
1 | | /* packet-nts-ke.c |
2 | | * Dissector for Network Time Security Key Establishment Protocol (RFC 8915) |
3 | | * |
4 | | * Copyright (c) 2024 by Martin Mayer <martin.mayer@m2-it-solutions.de> |
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 | | #include "config.h" |
14 | | #include "packet-tcp.h" |
15 | | #include "packet-tls.h" |
16 | | #include "packet-nts-ke.h" |
17 | | #include <epan/packet.h> |
18 | | #include <epan/tfs.h> |
19 | | #include <epan/unit_strings.h> |
20 | | #include <wsutil/str_util.h> |
21 | | #include <epan/expert.h> |
22 | | #include <epan/conversation.h> |
23 | | |
24 | 14 | #define TLS_PORT 4460 |
25 | 0 | #define CRIT_TYPE_BODY_LEN 4 |
26 | 14 | #define TYPE_MASK 0x7FFF |
27 | 14 | #define CRITICAL_MASK 0x8000 |
28 | | |
29 | 0 | #define NTS_KE_EXPORTER_LABEL "EXPORTER-network-time-security" |
30 | 14 | #define NTS_KE_ALPN "ntske/1" |
31 | | |
32 | | void proto_register_nts_ke(void); |
33 | | void proto_reg_handoff_nts_ke(void); |
34 | | |
35 | | static dissector_handle_t nts_ke_handle; |
36 | | |
37 | | static int proto_nts_ke; |
38 | | |
39 | | /* Fields */ |
40 | | static int hf_nts_ke_record; |
41 | | static int hf_nts_ke_critical_bit; |
42 | | static int hf_nts_ke_record_type; |
43 | | static int hf_nts_ke_body_length; |
44 | | static int hf_nts_ke_next_proto; |
45 | | static int hf_nts_ke_error; |
46 | | static int hf_nts_ke_warning; |
47 | | static int hf_nts_ke_aead_algo; |
48 | | static int hf_nts_ke_cookie; |
49 | | static int hf_nts_ke_cookie_used_frame; |
50 | | static int hf_nts_ke_server; |
51 | | static int hf_nts_ke_port; |
52 | | static int hf_nts_ke_response_in; |
53 | | static int hf_nts_ke_response_to; |
54 | | |
55 | | /* Expert fields */ |
56 | | static expert_field ei_nts_ke_critical_bit_missing; |
57 | | static expert_field ei_nts_ke_record_after_end; |
58 | | static expert_field ei_nts_ke_end_missing; |
59 | | static expert_field ei_nts_ke_next_proto_illegal_count; |
60 | | static expert_field ei_nts_ke_body_illegal; |
61 | | static expert_field ei_nts_ke_body_length_illegal; |
62 | | static expert_field ei_nts_ke_alpn_mismatch; |
63 | | |
64 | | /* Prefs */ |
65 | | static bool nts_ke_extract_keys = true; |
66 | | static bool nts_ke_chrony_compat_mode = false; |
67 | | |
68 | | /* Trees */ |
69 | | static int ett_nts_ke; |
70 | | static int ett_nts_ke_record; |
71 | | |
72 | 0 | #define RECORD_TYPE_END 0 |
73 | 0 | #define RECORD_TYPE_NEXT 1 |
74 | 0 | #define RECORD_TYPE_ERR 2 |
75 | 0 | #define RECORD_TYPE_WARN 3 |
76 | 0 | #define RECORD_TYPE_AEAD 4 |
77 | 0 | #define RECORD_TYPE_COOKIE 5 |
78 | 0 | #define RECORD_TYPE_NEG_SRV 6 |
79 | 0 | #define RECORD_TYPE_NEG_PORT 7 |
80 | | #define RECORD_TYPE_UA_1_LOW 8 |
81 | | #define RECORD_TYPE_UA_1_HIGH 1023 |
82 | | #define RECORD_TYPE_COMP_GCM 1024 |
83 | | #define RECORD_TYPE_UA_2_LOW 1025 |
84 | | #define RECORD_TYPE_UA_2_HIGH 16383 |
85 | | #define RECORD_TYPE_RES_LOW 16384 |
86 | | #define RECORD_TYPE_RES_HIGH 32767 |
87 | | |
88 | | /* https://www.iana.org/assignments/nts/nts.xhtml#nts-key-establishment-record-types */ |
89 | | static const range_string nts_ke_record_types[] = { |
90 | | { RECORD_TYPE_END, RECORD_TYPE_END, "End of Message" }, |
91 | | { RECORD_TYPE_NEXT, RECORD_TYPE_NEXT, "NTS Next Protocol Negotiation" }, |
92 | | { RECORD_TYPE_ERR, RECORD_TYPE_ERR, "Error" }, |
93 | | { RECORD_TYPE_WARN, RECORD_TYPE_WARN, "Warning" }, |
94 | | { RECORD_TYPE_AEAD, RECORD_TYPE_AEAD, "AEAD Algorithm Negotiation" }, |
95 | | { RECORD_TYPE_COOKIE, RECORD_TYPE_COOKIE, "New Cookie for NTPv4" }, |
96 | | { RECORD_TYPE_NEG_SRV, RECORD_TYPE_NEG_SRV, "NTPv4 Server Negotiation" }, |
97 | | { RECORD_TYPE_NEG_PORT, RECORD_TYPE_NEG_PORT, "NTPv4 Port Negotiation" }, |
98 | | { RECORD_TYPE_UA_1_LOW, RECORD_TYPE_UA_1_HIGH, "Unassigned" }, |
99 | | { RECORD_TYPE_COMP_GCM, RECORD_TYPE_COMP_GCM, "Compliant AES-128-GCM-SIV Exporter Context" }, |
100 | | { RECORD_TYPE_UA_2_LOW, RECORD_TYPE_UA_2_HIGH, "Unassigned" }, |
101 | | { RECORD_TYPE_RES_LOW, RECORD_TYPE_RES_HIGH, "Reserved" }, |
102 | | { 0, 0, NULL } |
103 | | }; |
104 | | |
105 | | /* https://www.iana.org/assignments/nts/nts.xhtml#nts-error-codes */ |
106 | | static const range_string nts_ke_error_codes[] = { |
107 | | { 0, 0, "Unrecognized Critical Record" }, |
108 | | { 1, 1, "Bad Request" }, |
109 | | { 2, 2, "Internal Server Error" }, |
110 | | { 3, 32767, "Unassigned" }, |
111 | | { 32768, 65535, "Reserved" }, |
112 | | { 0, 0, NULL } |
113 | | }; |
114 | | |
115 | | /* https://www.iana.org/assignments/nts/nts.xhtml#nts-warning-codes */ |
116 | | static const range_string nts_ke_warning_codes[] = { |
117 | | { 0, 32767, "Unassigned" }, |
118 | | { 32768, 65535, "Reserved" }, |
119 | | { 0, 0, NULL } |
120 | | }; |
121 | | |
122 | | /* https://www.iana.org/assignments/nts/nts.xhtml#nts-next-protocols */ |
123 | | static const range_string nts_ke_next_proto_rvals[] = { |
124 | | { 0, 0, "NTPv4" }, |
125 | | { 1, 32767, "Unassigned" }, |
126 | | { 32768, 65535, "Reserved" }, |
127 | | { 0, 0, NULL } |
128 | | }; |
129 | | |
130 | | /* https://www.iana.org/assignments/aead-parameters/ */ |
131 | | static const range_string nts_ke_aead_rvals[] = { |
132 | | { 1, 1, "AEAD_AES_128_GCM" }, |
133 | | { 2, 2, "AEAD_AES_256_GCM" }, |
134 | | { 3, 3, "AEAD_AES_128_CCM" }, |
135 | | { 4, 4, "AEAD_AES_256_CCM" }, |
136 | | { 5, 5, "AEAD_AES_128_GCM_8" }, |
137 | | { 6, 6, "AEAD_AES_256_GCM_8" }, |
138 | | { 7, 7, "AEAD_AES_128_GCM_12" }, |
139 | | { 8, 8, "AEAD_AES_256_GCM_12" }, |
140 | | { 9, 9, "AEAD_AES_128_CCM_SHORT" }, |
141 | | { 10, 10, "AEAD_AES_256_CCM_SHORT" }, |
142 | | { 11, 11, "AEAD_AES_128_CCM_SHORT_8" }, |
143 | | { 12, 12, "AEAD_AES_256_CCM_SHORT_8" }, |
144 | | { 13, 13, "AEAD_AES_128_CCM_SHORT_12" }, |
145 | | { 14, 14, "AEAD_AES_256_CCM_SHORT_12" }, |
146 | | { 15, 15, "AEAD_AES_SIV_CMAC_256" }, |
147 | | { 16, 16, "AEAD_AES_SIV_CMAC_384" }, |
148 | | { 17, 17, "AEAD_AES_SIV_CMAC_512" }, |
149 | | { 18, 18, "AEAD_AES_128_CCM_8" }, |
150 | | { 19, 19, "AEAD_AES_256_CCM_8" }, |
151 | | { 20, 20, "AEAD_AES_128_OCB_TAGLEN128" }, |
152 | | { 21, 21, "AEAD_AES_128_OCB_TAGLEN96" }, |
153 | | { 22, 22, "AEAD_AES_128_OCB_TAGLEN64" }, |
154 | | { 23, 23, "AEAD_AES_192_OCB_TAGLEN128" }, |
155 | | { 24, 24, "AEAD_AES_192_OCB_TAGLEN96" }, |
156 | | { 25, 25, "AEAD_AES_192_OCB_TAGLEN64" }, |
157 | | { 26, 26, "AEAD_AES_256_OCB_TAGLEN128" }, |
158 | | { 27, 27, "AEAD_AES_256_OCB_TAGLEN96" }, |
159 | | { 28, 28, "AEAD_AES_256_OCB_TAGLEN64" }, |
160 | | { 29, 29, "AEAD_CHACHA20_POLY1305" }, |
161 | | { 30, 30, "AEAD_AES_128_GCM_SIV" }, |
162 | | { 31, 31, "AEAD_AES_256_GCM_SIV" }, |
163 | | { 32, 32, "AEAD_AEGIS128L" }, |
164 | | { 33, 33, "AEAD_AEGIS256" }, |
165 | | { 34, 32767, "Unassigned" }, |
166 | | { 32768, 65535, "Reserved for Private Use" }, |
167 | | { 0, 0, NULL } |
168 | | }; |
169 | | |
170 | | /* All supported AEAD |
171 | | * Note: Key length is limited in NTS_KE_TLS13_KEY_MAX_LEN |
172 | | * |
173 | | * Only the following algos have been seen in the wild and were tested. |
174 | | * Extending the supported algos can be easily done by extending this list. |
175 | | * Think of looking at NTP ntp_decrypt_nts() when adding new algos, because |
176 | | * different GCRY modes may require different handling. |
177 | | * |
178 | | * All crypto functions will need GCRYPT >= 1.10.0 because |
179 | | * GCRY_CIPHER_MODE_SIV is a mandatory algorithm. If'ing out SIV algos |
180 | | * to compile successfully without GCRYPT support. |
181 | | */ |
182 | | static const nts_aead nts_ke_aead_gcry_map[] = { |
183 | | #if GCRYPT_VERSION_NUMBER >= 0x010a00 |
184 | | { 15, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_SIV, 32, 16 }, |
185 | | { 16, GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_SIV, 48, 16 }, |
186 | | { 17, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_SIV, 64, 16 }, |
187 | | { 30, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_GCM_SIV, 16, 16 }, |
188 | | #endif |
189 | | { 0, 0, 0, 0, 0 } |
190 | | }; |
191 | | |
192 | | const nts_aead * |
193 | | nts_find_aead(uint16_t id) |
194 | 0 | { |
195 | 0 | const nts_aead *c; |
196 | 0 | for(c = nts_ke_aead_gcry_map; c->id !=0 ; c++){ |
197 | 0 | if(c->id == id){ |
198 | 0 | return c; |
199 | 0 | } |
200 | 0 | } |
201 | 0 | return NULL; |
202 | 0 | } |
203 | | |
204 | | /* Request/response tracking */ |
205 | | typedef struct _nts_ke_req_resp_t { |
206 | | uint32_t req_frame; |
207 | | uint32_t resp_frame; |
208 | | } nts_ke_req_resp_t; |
209 | | |
210 | | /* Cookie map */ |
211 | | static wmem_map_t *nts_cookies; |
212 | | |
213 | | struct nts_ke_uid_lookup { |
214 | | uint32_t uid_hash; |
215 | | nts_cookie_t *cookie; |
216 | | }; |
217 | | |
218 | | static int |
219 | | nts_find_list_callback(const void *a, const void *b) |
220 | 0 | { |
221 | 0 | const uint32_t *x = (const uint32_t*)a; |
222 | 0 | if(*x == GPOINTER_TO_UINT(b)) return 0; else return -1; |
223 | 0 | } |
224 | | |
225 | | static void |
226 | | nts_uid_lookup_callback(void *key _U_, void *val, void *userdata) |
227 | 0 | { |
228 | 0 | nts_cookie_t *current_cookie = (nts_cookie_t *)val; |
229 | 0 | struct nts_ke_uid_lookup *func_data = (struct nts_ke_uid_lookup *)userdata; |
230 | |
|
231 | 0 | if(wmem_list_find_custom(current_cookie->frames_used_uid, GUINT_TO_POINTER(func_data->uid_hash), nts_find_list_callback)) |
232 | 0 | func_data->cookie = current_cookie; |
233 | 0 | } |
234 | | |
235 | | void |
236 | | nts_append_used_frames_to_tree(void *data, void *user_data) |
237 | 0 | { |
238 | 0 | proto_item *ct; |
239 | |
|
240 | 0 | uint32_t *pnum = (uint32_t *)data; |
241 | 0 | nts_used_frames_lookup_t *func_data = (nts_used_frames_lookup_t *)user_data; |
242 | |
|
243 | 0 | ct = proto_tree_add_uint(func_data->tree, func_data->hfindex, func_data->tvb, 0, 0, *pnum); |
244 | 0 | proto_item_set_generated(ct); |
245 | 0 | } |
246 | | |
247 | | nts_cookie_t* |
248 | | nts_new_cookie(tvbuff_t *tvb, uint16_t aead, packet_info *pinfo) |
249 | 0 | { |
250 | 0 | unsigned int cookie_len = tvb_reported_length(tvb); |
251 | 0 | uint8_t *key_c2s = (uint8_t *)wmem_alloc0(pinfo->pool, NTS_KE_TLS13_KEY_MAX_LEN); |
252 | 0 | uint8_t *key_s2c = (uint8_t *)wmem_alloc0(pinfo->pool, NTS_KE_TLS13_KEY_MAX_LEN); |
253 | 0 | uint8_t *tvb_bytes; |
254 | 0 | nts_cookie_t *cookie; |
255 | 0 | uint32_t strong_hash; |
256 | 0 | const nts_aead *aead_entry; |
257 | 0 | bool k1, k2; |
258 | 0 | uint8_t ex_context_s2c[5], ex_context_c2s[5]; |
259 | | |
260 | | /* Build exporter context |
261 | | * We only support NTPv4 - Context begins with 0x0000 (NTPv4 protocol ID) |
262 | | * Followed by two bytes = AEAD ID |
263 | | * Followed by one byte (0x00 = C2S key, 0x01 = S2C key) |
264 | | * |
265 | | * RFC 8915 5.1: |
266 | | * The per-association context value [RFC5705] SHALL consist of the following five octets: |
267 | | * |
268 | | * - The first two octets SHALL be zero (the Protocol ID for NTPv4). |
269 | | * - The next two octets SHALL be the Numeric Identifier of the negotiated AEAD algorithm in network byte order. |
270 | | * - The final octet SHALL be 0x00 for the C2S key and 0x01 for the S2C key. |
271 | | * |
272 | | * Chrony is using a hard-coded context of AEAD_AES_SIV_CMAC_256 while also supporting AEAD_AES_128_GCM_SIV: |
273 | | * - C2S 0x0000000F00 |
274 | | * - S2C 0x0000000F01 |
275 | | * |
276 | | * As this is a breaking compatibility bug, offer a compatibility mode as preference. |
277 | | * See: https://gitlab.com/chrony/chrony/-/issues/12 |
278 | | */ |
279 | 0 | ex_context_c2s[0] = 0x00; |
280 | 0 | ex_context_c2s[1] = 0x00; |
281 | 0 | ex_context_c2s[2] = (uint8_t)(aead >> 8); |
282 | 0 | ex_context_c2s[3] = (uint8_t)aead; |
283 | 0 | ex_context_c2s[4] = 0x00; |
284 | |
|
285 | 0 | ex_context_s2c[0] = 0x00; |
286 | 0 | ex_context_s2c[1] = 0x00; |
287 | 0 | ex_context_s2c[2] = (uint8_t)(aead >> 8); |
288 | 0 | ex_context_s2c[3] = (uint8_t)aead; |
289 | 0 | ex_context_s2c[4] = 0x01; |
290 | |
|
291 | 0 | if(nts_ke_chrony_compat_mode && aead == 30) { |
292 | 0 | ex_context_c2s[2] = 0x00; |
293 | 0 | ex_context_c2s[3] = 0x0F; |
294 | 0 | ex_context_s2c[2] = 0x00; |
295 | 0 | ex_context_s2c[3] = 0x0F; |
296 | 0 | } |
297 | |
|
298 | 0 | aead_entry = nts_find_aead(aead); |
299 | |
|
300 | 0 | if(cookie_len < 1 || !aead_entry) |
301 | 0 | return NULL; |
302 | | |
303 | 0 | tvb_bytes = (uint8_t *)tvb_memdup(pinfo->pool, tvb, 0, cookie_len); |
304 | 0 | strong_hash = wmem_strong_hash(tvb_bytes, cookie_len); |
305 | |
|
306 | 0 | cookie = (nts_cookie_t*) wmem_map_lookup(nts_cookies, GUINT_TO_POINTER(strong_hash)); |
307 | | |
308 | | /* Cookie was not found, add it */ |
309 | 0 | if(!cookie) { |
310 | | |
311 | | /* Extract keys */ |
312 | 0 | k1 = tls13_exporter(pinfo, false, |
313 | 0 | NTS_KE_EXPORTER_LABEL, ex_context_c2s, |
314 | 0 | sizeof(ex_context_c2s), aead_entry->key_len, &key_c2s); |
315 | 0 | k2 = tls13_exporter(pinfo, false, |
316 | 0 | NTS_KE_EXPORTER_LABEL, ex_context_s2c, |
317 | 0 | sizeof(ex_context_s2c), aead_entry->key_len, &key_s2c); |
318 | |
|
319 | 0 | cookie = wmem_new(wmem_file_scope(), nts_cookie_t); |
320 | |
|
321 | 0 | cookie->frame_received = pinfo->num; |
322 | 0 | cookie->frames_used = wmem_list_new(wmem_file_scope()); |
323 | 0 | cookie->frames_used_uid = wmem_list_new(wmem_file_scope()); |
324 | 0 | cookie->aead = aead; |
325 | 0 | if(k1 && k2) { |
326 | 0 | cookie->keys_present = true; |
327 | 0 | memcpy(cookie->key_c2s, key_c2s, aead_entry->key_len); |
328 | 0 | memcpy(cookie->key_s2c, key_s2c, aead_entry->key_len); |
329 | 0 | } else { |
330 | 0 | cookie->keys_present = false; |
331 | 0 | } |
332 | |
|
333 | 0 | wmem_map_insert(nts_cookies, GUINT_TO_POINTER(strong_hash), cookie); |
334 | 0 | } |
335 | |
|
336 | 0 | return cookie; |
337 | 0 | } |
338 | | |
339 | | nts_cookie_t* |
340 | | nts_new_cookie_copy(tvbuff_t *tvb, nts_cookie_t *ref_cookie, packet_info *pinfo) |
341 | 0 | { |
342 | 0 | unsigned int cookie_len = tvb_reported_length(tvb); |
343 | 0 | uint8_t *tvb_bytes; |
344 | 0 | nts_cookie_t *cookie; |
345 | 0 | uint32_t strong_hash; |
346 | 0 | const nts_aead *aead_entry; |
347 | |
|
348 | 0 | aead_entry = nts_find_aead(ref_cookie->aead); |
349 | |
|
350 | 0 | if(cookie_len < 1 || !aead_entry) |
351 | 0 | return NULL; |
352 | | |
353 | 0 | tvb_bytes = (uint8_t *)tvb_memdup(pinfo->pool, tvb, 0, cookie_len); |
354 | 0 | strong_hash = wmem_strong_hash(tvb_bytes, cookie_len); |
355 | |
|
356 | 0 | cookie = (nts_cookie_t*) wmem_map_lookup(nts_cookies, GUINT_TO_POINTER(strong_hash)); |
357 | | |
358 | | /* Cookie was not found, add it */ |
359 | 0 | if(!cookie) { |
360 | |
|
361 | 0 | cookie = wmem_new(wmem_file_scope(), nts_cookie_t); |
362 | |
|
363 | 0 | cookie->frame_received = pinfo->num; |
364 | 0 | cookie->frames_used = wmem_list_new(wmem_file_scope()); |
365 | 0 | cookie->frames_used_uid = wmem_list_new(wmem_file_scope()); |
366 | 0 | cookie->aead = ref_cookie->aead; |
367 | 0 | if(ref_cookie->keys_present) { |
368 | 0 | cookie->keys_present = true; |
369 | 0 | memcpy(cookie->key_c2s, ref_cookie->key_c2s, aead_entry->key_len); |
370 | 0 | memcpy(cookie->key_s2c, ref_cookie->key_s2c, aead_entry->key_len); |
371 | 0 | } else { |
372 | 0 | cookie->keys_present = false; |
373 | 0 | } |
374 | |
|
375 | 0 | wmem_map_insert(nts_cookies, GUINT_TO_POINTER(strong_hash), cookie); |
376 | 0 | } |
377 | |
|
378 | 0 | return cookie; |
379 | 0 | } |
380 | | |
381 | | nts_cookie_t* nts_use_cookie(tvbuff_t *tvb_cookie, tvbuff_t *tvb_uid, packet_info *pinfo) |
382 | 0 | { |
383 | 0 | unsigned int cookie_len = tvb_reported_length(tvb_cookie); |
384 | 0 | unsigned int uid_len = tvb_reported_length(tvb_uid); |
385 | |
|
386 | 0 | uint8_t *tvb_cookie_bytes, *tvb_uid_bytes; |
387 | 0 | nts_cookie_t *cookie; |
388 | |
|
389 | 0 | uint32_t strong_hash_cookie, strong_hash_uid; |
390 | 0 | uint32_t *pnum, *uid; |
391 | |
|
392 | 0 | if(cookie_len < 1 || uid_len < 1) |
393 | 0 | return NULL; |
394 | | |
395 | | /* Hash cookie and UID */ |
396 | 0 | tvb_cookie_bytes = (uint8_t *)tvb_memdup(pinfo->pool, tvb_cookie, 0, cookie_len); |
397 | 0 | strong_hash_cookie = wmem_strong_hash(tvb_cookie_bytes, cookie_len); |
398 | |
|
399 | 0 | tvb_uid_bytes = (uint8_t *)tvb_memdup(pinfo->pool, tvb_uid, 0, uid_len); |
400 | 0 | strong_hash_uid = wmem_strong_hash(tvb_uid_bytes, uid_len); |
401 | | |
402 | | /* Find cookie by hash */ |
403 | 0 | cookie = (nts_cookie_t*) wmem_map_lookup(nts_cookies, GUINT_TO_POINTER(strong_hash_cookie)); |
404 | |
|
405 | 0 | if(cookie) { |
406 | | /* In theory a cookie can be used multiple times, so remember all packets which used it */ |
407 | 0 | if(!wmem_list_find_custom(cookie->frames_used, GUINT_TO_POINTER(pinfo->num), nts_find_list_callback)) { |
408 | 0 | pnum = wmem_new0(wmem_file_scope(), uint32_t); |
409 | 0 | wmem_list_append(cookie->frames_used, pnum); |
410 | 0 | *pnum = pinfo->num; |
411 | 0 | } |
412 | |
|
413 | 0 | if(!wmem_list_find_custom(cookie->frames_used_uid, GUINT_TO_POINTER(strong_hash_uid), nts_find_list_callback)) { |
414 | 0 | uid = wmem_new0(wmem_file_scope(), uint32_t); |
415 | 0 | wmem_list_append(cookie->frames_used_uid, uid); |
416 | 0 | *uid = strong_hash_uid; |
417 | 0 | } |
418 | 0 | } |
419 | |
|
420 | 0 | return cookie; |
421 | 0 | } |
422 | | |
423 | | nts_cookie_t* |
424 | | nts_find_cookie_by_uid(tvbuff_t *tvb_uid) |
425 | 0 | { |
426 | 0 | unsigned int uid_len = tvb_reported_length(tvb_uid); |
427 | |
|
428 | 0 | uint8_t *tvb_uid_bytes; |
429 | 0 | struct nts_ke_uid_lookup lookup; |
430 | |
|
431 | 0 | if(uid_len < 1) |
432 | 0 | return NULL; |
433 | | |
434 | | /* Hash UID */ |
435 | 0 | tvb_uid_bytes = (uint8_t *)tvb_memdup(NULL, tvb_uid, 0, uid_len); |
436 | 0 | lookup.uid_hash = wmem_strong_hash(tvb_uid_bytes, uid_len); |
437 | 0 | lookup.cookie = NULL; |
438 | 0 | wmem_free(NULL, tvb_uid_bytes); |
439 | | |
440 | | /* Find cookie by UID hash */ |
441 | 0 | wmem_map_foreach(nts_cookies, nts_uid_lookup_callback, &lookup); |
442 | |
|
443 | 0 | return lookup.cookie; |
444 | 0 | } |
445 | | |
446 | | static int |
447 | | dissect_nts_ke(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) |
448 | 0 | { |
449 | 0 | int offset; |
450 | 0 | uint16_t critical, type; |
451 | 0 | uint32_t body_length, body_counter, aead = 0; |
452 | 0 | uint32_t counter_next_proto_recs = 0, counter_aead = 0, counter_cookies = 0; |
453 | 0 | uint32_t *next_proto_list_item; |
454 | 0 | proto_item *ti, *ti_record, *rt; |
455 | 0 | proto_tree *nts_ke_tree, *record_tree; |
456 | 0 | bool critical_bool, end_record = false; |
457 | 0 | bool request, direction_determined = false; |
458 | 0 | wmem_list_t *next_protos = wmem_list_new(pinfo->pool); |
459 | 0 | conversation_t *conv; |
460 | 0 | nts_ke_req_resp_t *conv_data; |
461 | 0 | nts_cookie_t *cookie; |
462 | 0 | struct tcp_analysis *tcp_conv; |
463 | 0 | nts_used_frames_lookup_t lookup_data = {.tvb = tvb, .hfindex = hf_nts_ke_cookie_used_frame}; |
464 | |
|
465 | 0 | offset = 0; |
466 | |
|
467 | 0 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "NTS-KE"); |
468 | 0 | col_clear(pinfo->cinfo,COL_INFO); |
469 | |
|
470 | 0 | ti = proto_tree_add_item(tree, proto_nts_ke, tvb, 0, 0, ENC_NA); |
471 | 0 | nts_ke_tree = proto_item_add_subtree(ti, ett_nts_ke); |
472 | | |
473 | | /* Error on ALPN mismatch */ |
474 | 0 | if(strcmp(tls_get_alpn(pinfo), NTS_KE_ALPN) != 0) |
475 | 0 | expert_add_info(pinfo, nts_ke_tree, &ei_nts_ke_alpn_mismatch); |
476 | | |
477 | | /* Conversation init */ |
478 | 0 | conv = find_or_create_conversation(pinfo); |
479 | 0 | conv_data = (nts_ke_req_resp_t *)conversation_get_proto_data(conv, proto_nts_ke); |
480 | | |
481 | | /* As NTS-KE has no client/server distinction. We need to rely on TCP. |
482 | | * We can be sure that TCP has identified the server port, |
483 | | * so we just need to compare it with our packet's destination port |
484 | | * to identify the direction. |
485 | | */ |
486 | 0 | tcp_conv = get_tcp_conversation_data_idempotent(conv); |
487 | 0 | if(tcp_conv && pinfo->destport == tcp_conv->server_port) { |
488 | 0 | direction_determined = true; |
489 | 0 | request = true; |
490 | 0 | } else if (tcp_conv && pinfo->srcport == tcp_conv->server_port) { |
491 | 0 | direction_determined = true; |
492 | 0 | request = false; |
493 | 0 | } |
494 | |
|
495 | 0 | if (direction_determined) { |
496 | 0 | if (!conv_data) { |
497 | 0 | conv_data = wmem_new(wmem_file_scope(), nts_ke_req_resp_t); |
498 | 0 | conv_data->req_frame = request ? pinfo->num : 0; |
499 | 0 | conv_data->resp_frame = !request ? pinfo->num : 0; |
500 | 0 | conversation_add_proto_data(conv, proto_nts_ke, conv_data); |
501 | 0 | } else { |
502 | 0 | conv_data->req_frame = request ? pinfo->num : conv_data->req_frame; |
503 | 0 | conv_data->resp_frame = !request ? pinfo->num : conv_data->resp_frame; |
504 | 0 | } |
505 | 0 | } |
506 | |
|
507 | 0 | while(tvb_reported_length_remaining(tvb, offset) >= CRIT_TYPE_BODY_LEN) { |
508 | | |
509 | | /* Reset pointer */ |
510 | 0 | cookie = NULL; |
511 | |
|
512 | 0 | ti_record = proto_tree_add_item(nts_ke_tree, hf_nts_ke_record, tvb, offset, 0, ENC_NA); |
513 | 0 | record_tree = proto_item_add_subtree(ti_record, ett_nts_ke_record); |
514 | |
|
515 | 0 | critical = tvb_get_uint16(tvb, offset, ENC_BIG_ENDIAN) & CRITICAL_MASK; |
516 | 0 | critical_bool = (bool)(critical >> 15); |
517 | 0 | proto_tree_add_boolean(record_tree, hf_nts_ke_critical_bit, tvb, offset, 2, critical); |
518 | |
|
519 | 0 | type = tvb_get_uint16(tvb, offset, ENC_BIG_ENDIAN) & TYPE_MASK; |
520 | 0 | proto_tree_add_uint(record_tree, hf_nts_ke_record_type, tvb, offset, 2, type); |
521 | 0 | proto_item_append_text(ti_record, " (%s)", rval_to_str_const(type, nts_ke_record_types, "Unknown Record Type")); |
522 | 0 | offset += 2; |
523 | |
|
524 | 0 | proto_tree_add_item_ret_uint(record_tree, hf_nts_ke_body_length, tvb, offset, 2, ENC_BIG_ENDIAN, &body_length); |
525 | 0 | offset += 2; |
526 | |
|
527 | 0 | if(end_record) |
528 | 0 | expert_add_info(pinfo, record_tree, &ei_nts_ke_record_after_end); |
529 | |
|
530 | 0 | body_counter = 0; |
531 | |
|
532 | 0 | switch (type) { |
533 | 0 | case RECORD_TYPE_END: |
534 | | |
535 | | /* No body allowed */ |
536 | 0 | if(body_length > 0) { |
537 | 0 | call_data_dissector(tvb_new_subset_length(tvb, offset, body_length), pinfo, record_tree); |
538 | 0 | expert_add_info(pinfo, record_tree, &ei_nts_ke_body_illegal); |
539 | 0 | offset += body_length; |
540 | 0 | } |
541 | | |
542 | | /* Critical bit is mandatory for this type */ |
543 | 0 | if(!critical_bool) |
544 | 0 | expert_add_info(pinfo, record_tree, &ei_nts_ke_critical_bit_missing); |
545 | | |
546 | | /* Mark end record as seen */ |
547 | 0 | end_record = true; |
548 | |
|
549 | 0 | break; |
550 | | |
551 | 0 | case RECORD_TYPE_NEXT: |
552 | |
|
553 | 0 | while(body_counter < body_length) { |
554 | 0 | uint32_t next_proto; |
555 | 0 | proto_tree_add_item_ret_uint(record_tree, hf_nts_ke_next_proto, tvb, offset, 2, ENC_BIG_ENDIAN, &next_proto); |
556 | 0 | offset += 2; |
557 | 0 | body_counter += 2; |
558 | | |
559 | | /* Store list of offered/accepted next protocols */ |
560 | 0 | next_proto_list_item = wmem_new0(pinfo->pool, uint32_t); |
561 | 0 | wmem_list_append(next_protos, next_proto_list_item); |
562 | 0 | *next_proto_list_item = next_proto; |
563 | |
|
564 | 0 | col_append_str(pinfo->cinfo, COL_INFO, rval_to_str_const(next_proto, nts_ke_next_proto_rvals, "Unknown Proto")); |
565 | 0 | } |
566 | | |
567 | | /* Critical bit is mandatory for this type */ |
568 | 0 | if(!critical_bool) |
569 | 0 | expert_add_info(pinfo, record_tree, &ei_nts_ke_critical_bit_missing); |
570 | |
|
571 | 0 | counter_next_proto_recs++; |
572 | |
|
573 | 0 | break; |
574 | | |
575 | 0 | case RECORD_TYPE_ERR: |
576 | | |
577 | | /* Fixed body length */ |
578 | 0 | if(body_length == 2) { |
579 | 0 | proto_tree_add_item(record_tree, hf_nts_ke_error, tvb, offset, body_length, ENC_BIG_ENDIAN); |
580 | 0 | } else { |
581 | 0 | call_data_dissector(tvb_new_subset_length(tvb, offset, body_length), pinfo, record_tree); |
582 | 0 | expert_add_info(pinfo, record_tree, &ei_nts_ke_body_length_illegal); |
583 | 0 | } |
584 | 0 | offset += body_length; |
585 | | |
586 | | /* Critical bit is mandatory for this type */ |
587 | 0 | if(!critical_bool) |
588 | 0 | expert_add_info(pinfo, record_tree, &ei_nts_ke_critical_bit_missing); |
589 | |
|
590 | 0 | break; |
591 | | |
592 | 0 | case RECORD_TYPE_WARN: |
593 | | |
594 | | /* Fixed body length */ |
595 | 0 | if(body_length == 2) { |
596 | 0 | proto_tree_add_item(record_tree, hf_nts_ke_warning, tvb, offset, body_length, ENC_BIG_ENDIAN); |
597 | 0 | } else { |
598 | 0 | call_data_dissector(tvb_new_subset_length(tvb, offset, body_length), pinfo, record_tree); |
599 | 0 | expert_add_info(pinfo, record_tree, &ei_nts_ke_body_length_illegal); |
600 | 0 | } |
601 | 0 | offset += body_length; |
602 | | |
603 | | /* Critical bit is mandatory for this type */ |
604 | 0 | if(!critical_bool) |
605 | 0 | expert_add_info(pinfo, record_tree, &ei_nts_ke_critical_bit_missing); |
606 | |
|
607 | 0 | break; |
608 | | |
609 | 0 | case RECORD_TYPE_AEAD: |
610 | |
|
611 | 0 | while(body_counter < body_length) { |
612 | |
|
613 | 0 | proto_tree_add_item_ret_uint(record_tree, hf_nts_ke_aead_algo, tvb, offset, 2, ENC_BIG_ENDIAN, &aead); |
614 | 0 | offset += 2; |
615 | 0 | body_counter += 2; |
616 | 0 | counter_aead++; |
617 | 0 | } |
618 | |
|
619 | 0 | break; |
620 | | |
621 | 0 | case RECORD_TYPE_COOKIE: |
622 | | |
623 | | /* Arbitrary body data |
624 | | * |
625 | | * Other dissectors (e.g. NTP) need to access this data along it's extracted keys. |
626 | | * Add NTS cookies if NTP (0x00) is part of next protos |
627 | | */ |
628 | 0 | if ( |
629 | 0 | nts_ke_extract_keys && |
630 | 0 | aead > 0 && |
631 | 0 | wmem_list_find_custom(next_protos, GUINT_TO_POINTER(0x00), nts_find_list_callback) |
632 | 0 | ) { |
633 | 0 | cookie = nts_new_cookie(tvb_new_subset_length(tvb, offset, body_length), (uint16_t)aead, pinfo); |
634 | 0 | } |
635 | 0 | proto_tree_add_item(record_tree, hf_nts_ke_cookie, tvb, offset, body_length, ENC_NA); |
636 | 0 | offset += body_length; |
637 | 0 | counter_cookies++; |
638 | |
|
639 | 0 | if(cookie) { |
640 | | /* List all packets which made use of that cookie */ |
641 | 0 | lookup_data.tree = record_tree; |
642 | 0 | wmem_list_foreach(cookie->frames_used, nts_append_used_frames_to_tree, &lookup_data); |
643 | 0 | } |
644 | |
|
645 | 0 | break; |
646 | | |
647 | 0 | case RECORD_TYPE_NEG_SRV: |
648 | | |
649 | | /* Arbitrary string */ |
650 | 0 | proto_tree_add_item(record_tree, hf_nts_ke_server, tvb, offset, body_length, ENC_ASCII); |
651 | 0 | offset += body_length; |
652 | |
|
653 | 0 | break; |
654 | | |
655 | 0 | case RECORD_TYPE_NEG_PORT: |
656 | | |
657 | | /* Fixed body length */ |
658 | 0 | if(body_length == 2) { |
659 | 0 | proto_tree_add_item(record_tree, hf_nts_ke_port, tvb, offset, body_length, ENC_BIG_ENDIAN); |
660 | 0 | } else { |
661 | 0 | call_data_dissector(tvb_new_subset_length(tvb, offset, body_length), pinfo, record_tree); |
662 | 0 | expert_add_info(pinfo, record_tree, &ei_nts_ke_body_length_illegal); |
663 | 0 | } |
664 | 0 | offset += body_length; |
665 | |
|
666 | 0 | break; |
667 | | |
668 | 0 | default: |
669 | |
|
670 | 0 | call_data_dissector(tvb_new_subset_length(tvb, offset, body_length), pinfo, record_tree); |
671 | 0 | offset += body_length; |
672 | |
|
673 | 0 | break; |
674 | 0 | } |
675 | | |
676 | 0 | proto_item_set_end(ti_record, tvb, offset); |
677 | 0 | } |
678 | | |
679 | | /* Request/Response */ |
680 | 0 | if(conv_data && direction_determined) { |
681 | 0 | if(request && conv_data->resp_frame > 0) { |
682 | 0 | rt = proto_tree_add_uint(nts_ke_tree, hf_nts_ke_response_in, tvb, 0, 0, conv_data->resp_frame); |
683 | 0 | proto_item_set_generated(rt); |
684 | 0 | } else if (!request && conv_data->req_frame > 0) { |
685 | 0 | rt = proto_tree_add_uint(nts_ke_tree, hf_nts_ke_response_to, tvb, 0, 0, conv_data->req_frame); |
686 | 0 | proto_item_set_generated(rt); |
687 | 0 | } |
688 | 0 | } |
689 | | |
690 | | /* Info columns text */ |
691 | 0 | if(counter_aead > 0) |
692 | 0 | col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "%u AEAD Algorithm%s", counter_aead, plurality(counter_aead, "", "s")); |
693 | |
|
694 | 0 | if(counter_cookies > 0) |
695 | 0 | col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "%u Cookie%s", counter_cookies, plurality(counter_cookies, "", "s")); |
696 | | |
697 | | /* No end record found */ |
698 | 0 | if(!end_record) |
699 | 0 | expert_add_info(pinfo, nts_ke_tree, &ei_nts_ke_end_missing); |
700 | | |
701 | | /* Illegal AEAD record count */ |
702 | 0 | if(counter_next_proto_recs != 1) |
703 | 0 | expert_add_info(pinfo, nts_ke_tree, &ei_nts_ke_next_proto_illegal_count); |
704 | |
|
705 | 0 | proto_item_set_end(ti, tvb, offset); |
706 | |
|
707 | 0 | return offset; |
708 | 0 | } |
709 | | |
710 | | static unsigned |
711 | | get_nts_ke_message_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_) |
712 | 0 | { |
713 | |
|
714 | 0 | bool another_record = true; |
715 | 0 | unsigned size = 0; |
716 | | |
717 | | /* Concat multiple records into one protocol tree */ |
718 | 0 | while(another_record) { |
719 | | |
720 | | /* Size is body length + 4 byte (CRIT_TYPE_BODY_LEN) */ |
721 | 0 | unsigned pdu_size = tvb_get_uint16(tvb, offset + 2, ENC_BIG_ENDIAN) + CRIT_TYPE_BODY_LEN; |
722 | 0 | size += pdu_size; |
723 | |
|
724 | 0 | if (tvb_captured_length_remaining(tvb, offset + pdu_size) < CRIT_TYPE_BODY_LEN) |
725 | 0 | another_record = false; |
726 | |
|
727 | 0 | offset += pdu_size; |
728 | 0 | } |
729 | |
|
730 | 0 | return size; |
731 | |
|
732 | 0 | } |
733 | | |
734 | | static int |
735 | | dissect_nts_ke_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) |
736 | 0 | { |
737 | 0 | if (!tvb_bytes_exist(tvb, 0, CRIT_TYPE_BODY_LEN)) |
738 | 0 | return 0; |
739 | | |
740 | 0 | tcp_dissect_pdus(tvb, pinfo, tree, true, CRIT_TYPE_BODY_LEN, get_nts_ke_message_len, dissect_nts_ke, data); |
741 | 0 | return tvb_reported_length(tvb); |
742 | 0 | } |
743 | | |
744 | | void |
745 | | proto_register_nts_ke(void) |
746 | 14 | { |
747 | 14 | static hf_register_info hf[] = { |
748 | 14 | { &hf_nts_ke_record, |
749 | 14 | { "NTS-KE Record", "nts-ke.record", |
750 | 14 | FT_NONE, BASE_NONE, |
751 | 14 | NULL, 0x0, |
752 | 14 | NULL, HFILL } |
753 | 14 | }, |
754 | 14 | { &hf_nts_ke_critical_bit, |
755 | 14 | { "Critical Bit", "nts-ke.critical_bit", |
756 | 14 | FT_BOOLEAN, 16, |
757 | 14 | TFS(&tfs_set_notset), CRITICAL_MASK, |
758 | 14 | NULL, HFILL } |
759 | 14 | }, |
760 | 14 | { &hf_nts_ke_record_type, |
761 | 14 | { "Record Type", "nts-ke.type", |
762 | 14 | FT_UINT16, BASE_DEC | BASE_RANGE_STRING, |
763 | 14 | RVALS(nts_ke_record_types), TYPE_MASK, |
764 | 14 | NULL, HFILL } |
765 | 14 | }, |
766 | 14 | { &hf_nts_ke_body_length, |
767 | 14 | { "Body Length", "nts-ke.body_length", |
768 | 14 | FT_UINT16, BASE_DEC | BASE_UNIT_STRING, |
769 | 14 | UNS(&units_byte_bytes), 0x0, |
770 | 14 | NULL, HFILL } |
771 | 14 | }, |
772 | 14 | { &hf_nts_ke_next_proto, |
773 | 14 | { "Next Protocol ID", "nts-ke.next_proto", |
774 | 14 | FT_UINT16, BASE_DEC | BASE_RANGE_STRING, |
775 | 14 | RVALS(nts_ke_next_proto_rvals), 0x0, |
776 | 14 | NULL, HFILL } |
777 | 14 | }, |
778 | 14 | { &hf_nts_ke_error, |
779 | 14 | { "Error Code", "nts-ke.error", |
780 | 14 | FT_UINT16, BASE_DEC | BASE_RANGE_STRING, |
781 | 14 | RVALS(nts_ke_error_codes), 0x0, |
782 | 14 | NULL, HFILL } |
783 | 14 | }, |
784 | 14 | { &hf_nts_ke_warning, |
785 | 14 | { "Warning Code", "nts-ke.warning", |
786 | 14 | FT_UINT16, BASE_DEC | BASE_RANGE_STRING, |
787 | 14 | RVALS(nts_ke_warning_codes), 0x0, |
788 | 14 | NULL, HFILL } |
789 | 14 | }, |
790 | 14 | { &hf_nts_ke_aead_algo, |
791 | 14 | { "AEAD Algorithm", "nts-ke.aead_algo", |
792 | 14 | FT_UINT16, BASE_DEC | BASE_RANGE_STRING, |
793 | 14 | RVALS(nts_ke_aead_rvals), 0x0, |
794 | 14 | NULL, HFILL } |
795 | 14 | }, |
796 | 14 | { &hf_nts_ke_cookie, |
797 | 14 | { "Cookie Data", "nts-ke.cookie", |
798 | 14 | FT_BYTES, BASE_NONE, |
799 | 14 | NULL, 0x0, |
800 | 14 | NULL, HFILL } |
801 | 14 | }, |
802 | 14 | { &hf_nts_ke_cookie_used_frame, { |
803 | 14 | "Used cookie in", "nts-ke.cookie.use_frame", |
804 | 14 | FT_FRAMENUM, BASE_NONE, |
805 | 14 | NULL, 0, |
806 | 14 | NULL, HFILL }}, |
807 | 14 | { &hf_nts_ke_server, |
808 | 14 | { "Server", "nts-ke.server", |
809 | 14 | FT_STRING, BASE_NONE, |
810 | 14 | NULL, 0x0, |
811 | 14 | NULL, HFILL } |
812 | 14 | }, |
813 | 14 | { &hf_nts_ke_port, |
814 | 14 | { "Port", "nts-ke.port", |
815 | 14 | FT_UINT16, BASE_DEC, |
816 | 14 | NULL, 0x0, |
817 | 14 | NULL, HFILL } |
818 | 14 | }, |
819 | 14 | { &hf_nts_ke_response_in, |
820 | 14 | { "Response In", "nts-ke.response_in", |
821 | 14 | FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE), 0x0, |
822 | 14 | NULL, HFILL } |
823 | 14 | }, |
824 | 14 | { &hf_nts_ke_response_to, |
825 | 14 | { "Response To", "nts-ke.response_to", |
826 | 14 | FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_REQUEST), 0x0, |
827 | 14 | NULL, HFILL } |
828 | 14 | } |
829 | 14 | }; |
830 | | |
831 | 14 | static ei_register_info ei[] = { |
832 | 14 | { &ei_nts_ke_critical_bit_missing, |
833 | 14 | { "nts-ke.critical_bit.missing", PI_MALFORMED, PI_ERROR, |
834 | 14 | "Critical bit must be set for this record type", EXPFILL } |
835 | 14 | }, |
836 | 14 | { &ei_nts_ke_record_after_end, |
837 | 14 | { "nts-ke.record.after_end", PI_MALFORMED, PI_ERROR, |
838 | 14 | "Illegal record after end of message", EXPFILL } |
839 | 14 | }, |
840 | 14 | { &ei_nts_ke_end_missing, |
841 | 14 | { "nts-ke.end.missing", PI_MALFORMED, PI_ERROR, |
842 | 14 | "No end of message present", EXPFILL } |
843 | 14 | }, |
844 | 14 | { &ei_nts_ke_body_illegal, |
845 | 14 | { "nts-ke.body.illegal", PI_MALFORMED, PI_ERROR, |
846 | 14 | "Illegal body data present", EXPFILL } |
847 | 14 | }, |
848 | 14 | { &ei_nts_ke_body_length_illegal, |
849 | 14 | { "nts-ke.body_length.illegal", PI_MALFORMED, PI_ERROR, |
850 | 14 | "Illegal body length", EXPFILL } |
851 | 14 | }, |
852 | 14 | { &ei_nts_ke_next_proto_illegal_count, |
853 | 14 | { "nts-ke.next_proto.illegal_count", PI_MALFORMED, PI_ERROR, |
854 | 14 | "Illegal Next Protocol record count", EXPFILL } |
855 | 14 | }, |
856 | 14 | { &ei_nts_ke_alpn_mismatch, |
857 | 14 | { "nts-ke.alpn_mismatch", PI_DECRYPTION, PI_ERROR, |
858 | 14 | "TLS ALPN mismatch", EXPFILL } |
859 | 14 | } |
860 | 14 | }; |
861 | | |
862 | 14 | static int *ett[] = { |
863 | 14 | &ett_nts_ke, |
864 | 14 | &ett_nts_ke_record |
865 | 14 | }; |
866 | | |
867 | 14 | expert_module_t* expert_nts_ke; |
868 | 14 | module_t *nts_ke_module; |
869 | | |
870 | 14 | nts_cookies = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), g_direct_hash, g_direct_equal); |
871 | | |
872 | 14 | proto_nts_ke = proto_register_protocol ("NTS Key Establishment Protocol", "NTS-KE", "nts-ke"); |
873 | | |
874 | 14 | proto_register_field_array(proto_nts_ke, hf, array_length(hf)); |
875 | 14 | proto_register_subtree_array(ett, array_length(ett)); |
876 | | |
877 | 14 | expert_nts_ke = expert_register_protocol(proto_nts_ke); |
878 | 14 | expert_register_field_array(expert_nts_ke, ei, array_length(ei)); |
879 | | |
880 | 14 | nts_ke_module = prefs_register_protocol(proto_nts_ke, NULL); |
881 | 14 | prefs_register_bool_preference(nts_ke_module, "extract_keys", |
882 | 14 | "Extract S2C and C2S keys", |
883 | 14 | "Whether to extract client-to-server and server-to-client " |
884 | 14 | "keys for crypto-processing.", |
885 | 14 | &nts_ke_extract_keys); |
886 | 14 | prefs_register_bool_preference(nts_ke_module, "chrony_compat_mode", |
887 | 14 | "Chrony Compatibility Mode", |
888 | 14 | "Allows AEAD_AES_128_GCM_SIV key extraction for Chrony-based " |
889 | 14 | "NTP clients and servers.", |
890 | 14 | &nts_ke_chrony_compat_mode); |
891 | | |
892 | 14 | nts_ke_handle = register_dissector("nts-ke", dissect_nts_ke_tcp, proto_nts_ke); |
893 | 14 | } |
894 | | |
895 | | void |
896 | | proto_reg_handoff_nts_ke(void) |
897 | 14 | { |
898 | 14 | dissector_add_uint_with_preference("tls.port", TLS_PORT, nts_ke_handle); |
899 | 14 | dissector_add_string("tls.alpn", NTS_KE_ALPN, nts_ke_handle); |
900 | 14 | } |
901 | | |
902 | | /* |
903 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
904 | | * |
905 | | * Local variables: |
906 | | * c-basic-offset: 4 |
907 | | * tab-width: 8 |
908 | | * indent-tabs-mode: nil |
909 | | * End: |
910 | | * |
911 | | * vi: set shiftwidth=4 tabstop=8 expandtab: |
912 | | * :indentSize=4:tabSize=8:noTabs=true: |
913 | | */ |