Commit | Line | Data |
---|---|---|
3949f61c MW |
1 | --- -*-lua-*- |
2 | --- | |
3 | --- Wireshark protocol dissector for TrIPE | |
4 | --- | |
5 | --- (c) 2017 Straylight/Edgeware | |
6 | --- | |
7 | ||
8 | -------- Licensing notice --------------------------------------------------- | |
9 | --- | |
10 | --- This file is part of Trivial IP Encryption (TrIPE). | |
11 | --- | |
11ad66c2 MW |
12 | --- TrIPE is free software: you can redistribute it and/or modify it under |
13 | --- the terms of the GNU General Public License as published by the Free | |
14 | --- Software Foundation; either version 3 of the License, or (at your | |
15 | --- option) any later version. | |
3949f61c | 16 | --- |
11ad66c2 MW |
17 | --- TrIPE is distributed in the hope that it will be useful, but WITHOUT |
18 | --- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
19 | --- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
20 | --- for more details. | |
3949f61c MW |
21 | --- |
22 | --- You should have received a copy of the GNU General Public License | |
11ad66c2 | 23 | --- along with TrIPE. If not, see <https://www.gnu.org/licenses/>. |
3949f61c MW |
24 | |
25 | local tripe = Proto("tripe", "TrIPE VPN") | |
26 | ||
27 | ----------------------------------------------------------------------------- | |
28 | --- Configuration handling. | |
29 | ||
30 | local CONFIG = { | |
31 | -- Information about the configuration variables. This table, when it's | |
32 | -- set up, maps the internal names, which are used to refer to | |
33 | -- configuration variables in the rest of this code, to a little structure: | |
34 | -- | |
35 | -- * `var' names the variable, and is the usual key for lookups; | |
36 | -- | |
37 | -- * `name' is the label used in the dialogue box; | |
38 | -- | |
39 | -- * `type' is the type of variable, currently either `enum' or `int'; | |
40 | -- | |
41 | -- * `descr' is a longer (but generally fairly useless) description for | |
42 | -- use in a tooltip; | |
43 | -- | |
44 | -- * `allowed' is a sequence of allowed values for an `enum' variable; | |
45 | -- and | |
46 | -- | |
47 | -- * `min' and `max' are the limits on permitted values for an `int' | |
48 | -- variable (and may be omitted). | |
49 | -- | |
50 | -- More slots are added at runtime: | |
51 | -- | |
52 | -- * `map' is a table mapping string values to their integer indices, as | |
53 | -- stored in Wireshark's preferences database. | |
54 | -- | |
55 | -- Initially, though, the table is given as a sequence, so that the | |
56 | -- preferences can be populated in a consistent (and approximately logical) | |
57 | -- order. | |
58 | ||
59 | { var = "bulk", name = "Bulk transform", | |
60 | type = "enum", allowed = { "v0", "iiv", "naclbox" }, | |
61 | descr = "Bulk cryptographic transform", default = "v0" }, | |
62 | { var = "hashsz", name = "Hash length", type = "int", min = 0, | |
63 | descr = "Hash length (bytes)", default = 20 }, | |
64 | { var = "tagsz", name = "Tag length", type = "int", min = 0, | |
65 | descr = "Authentication tag length (bytes)", default = 10 }, | |
66 | { var = "ivsz", name = "IV length", type = "int", min = 0, | |
67 | descr = "Initialization vector length (bytes)", default = 8 }, | |
68 | { var = "kx", name = "Key-exchange group", | |
69 | type = "enum", allowed = { "dh", "ec", "x25519", "x448" }, | |
70 | descr = "Key-exchange group type", default = "dh" }, | |
71 | { var = "scsz", name = "Scalar length", type = "int", min = 1, | |
72 | descr = "Scalar field-element length (bytes)", default = 32 }, | |
73 | } | |
74 | ||
75 | local C = { } -- The working values of the configuration variables. | |
76 | ||
77 | local function set_config(k, v) | |
78 | -- Set configuration variable K to the value V. | |
79 | -- | |
80 | -- K is a string naming the variable to set. V is the new value, which may | |
81 | -- be a string or a number. | |
82 | -- | |
83 | -- For `int' variables, V is converted to a number if necessary, and then | |
84 | -- checked against the permitted bounds. | |
85 | -- | |
86 | -- For `enum' variables, things are more complicated. If V is a string, | |
87 | -- it's checked against the permitted values. If V is a number, it's | |
88 | -- converted back into the corresponding string. | |
89 | ||
90 | local info = CONFIG[k] | |
91 | ||
92 | if info == nil then error("unknown config key `" .. k .. "'") end | |
93 | ||
94 | if info.type == "enum" then | |
95 | if type(v) == "number" then | |
96 | local t = info.allowed[v] | |
97 | if t == nil then | |
98 | error(string.format("bad index %d for `%s'", n, k)) | |
99 | end | |
100 | v = t | |
101 | else | |
102 | if info.map[v] == nil then | |
103 | error(string.format("bad value `%s' for `%s'", v, k)) | |
104 | end | |
105 | end | |
106 | ||
107 | elseif info.type == "int" then | |
108 | local n = tonumber(v) | |
109 | if n == nil then error("bad number `" .. v .. "'") end | |
110 | if n ~= math.floor(n) then | |
111 | error("value `" .. v .. "' is not an integer") | |
112 | end | |
113 | if (info.min ~= nil and n < info.min) or | |
114 | (info.max ~= nil and n > info.max) | |
115 | then | |
116 | error(string.format("value %d out of range for `%s'", n, k)) | |
117 | end | |
118 | v = n | |
119 | end | |
120 | ||
121 | C[k] = v | |
122 | end | |
123 | ||
124 | -- Set up the configuration information. Configure preferences objects on | |
125 | -- the dissector. For `enum' variables, build the `map' slots. | |
126 | for i, v in ipairs(CONFIG) do | |
127 | local k = v.var | |
128 | CONFIG[k] = v | |
129 | if v.type == "enum" then | |
130 | local tab = { } | |
131 | v.map = { } | |
132 | for i, t in pairs(v.allowed) do | |
133 | v.map[t] = i | |
134 | tab[i] = { i, t, i } | |
135 | end | |
136 | tripe.prefs[k] = Pref.enum(v.name, v.map[v.default], v.descr, tab) | |
137 | elseif v.type == "int" then | |
138 | tripe.prefs[k] = Pref.uint(v.name, v.default, v.descr) | |
139 | end | |
140 | end | |
141 | ||
142 | local function prefs_changed() | |
143 | -- Notice that the preferences have been changed and update `C'. | |
144 | ||
145 | for k, _ in pairs(CONFIG) do | |
146 | if type(k) == "string" then set_config(k, tripe.prefs[k]) end | |
147 | end | |
148 | end | |
149 | tripe.prefs_changed = prefs_changed | |
150 | ||
151 | -- Populate the configuration table from the stored preferences or their | |
152 | -- default values. | |
153 | prefs_changed() | |
154 | ||
155 | -- Now work through arguments passed in on the command line. Annoyingly, | |
156 | -- while one can set preferences on the Wireshark command line, these are | |
157 | -- done /before/ Lua scripts are loaded, so the silly thing thinks the | |
158 | -- preference slots don't exist. So we have to do it a different way. | |
159 | for _, arg in ipairs({...}) do | |
160 | local k, v = arg:match("(.+)=(.+)") | |
161 | if k == nil or v == nil then error("bad option syntax `" .. arg .. "'") end | |
162 | se_config(k, v) | |
163 | end | |
164 | ||
165 | ----------------------------------------------------------------------------- | |
166 | --- Protocol dissection primitives. | |
167 | ||
168 | local PF = { } -- The table of protocol fields, filled in later. | |
169 | ||
170 | -- The `dissect_*' functions follow a common protocol. They parse a thing | |
171 | -- from a packet buffer BUF, of size SZ, starting from POS, and store | |
172 | -- interesting things in a given TREE; when they're done, they return the | |
173 | -- updated index where the next interesting thing might be. As a result, | |
174 | -- it's usually a simple matter to parse a packet by invoking the appropriate | |
175 | -- primitive dissectors in the right order. | |
176 | ||
177 | local function dissect_wtf(buf, tree, pos, sz) | |
178 | -- If POS is not at the end of the buffer, note that there's unexpected | |
179 | -- stuff in the packet. | |
180 | ||
181 | if pos < sz then tree:add(PF["tripe.wtf"], buf(pos, sz - pos)) end | |
182 | return sz | |
183 | end | |
184 | ||
185 | -- Dissect a ciphertext of some particular kind. | |
186 | local dissect_ct = { } | |
187 | function dissect_ct.naclbox(buf, tree, pos, sz) | |
188 | tree:add(PF["tripe.ciphertext.tag"], buf(pos, 16)); pos = pos + 16 | |
189 | tree:add(PF["tripe.ciphertext.seq"], buf(pos, 4)); pos = pos + 4 | |
190 | tree:add(PF["tripe.ciphertext.body"], buf(pos, sz - pos)) | |
191 | end | |
192 | function dissect_ct.iiv(buf, tree, pos, sz) | |
193 | tree:add(PF["tripe.ciphertext.tag"], buf(pos, C.tagsz)); pos = pos + C.tagsz | |
194 | tree:add(PF["tripe.ciphertext.seq"], buf(pos, 4)); pos = pos + 4 | |
195 | tree:add(PF["tripe.ciphertext.body"], buf(pos, sz - pos)) | |
196 | end | |
197 | function dissect_ct.v0(buf, tree, pos, sz) | |
198 | tree:add(PF["tripe.ciphertext.tag"], buf(pos, C.tagsz)); pos = pos + C.tagsz | |
199 | tree:add(PF["tripe.ciphertext.seq"], buf(pos, 4)); pos = pos + 4 | |
200 | tree:add(PF["tripe.ciphertext.iv"], buf(pos, C.ivsz)); pos = pos + C.ivsz | |
201 | tree:add(PF["tripe.ciphertext.body"], buf(pos, sz - pos)) | |
202 | end | |
203 | ||
204 | local function dissect_ciphertext(buf, tree, label, pos, sz) | |
205 | -- Dissect a ciphertext, making the whole thing be a little subtree with | |
206 | -- the given LABEL. | |
207 | ||
208 | local t = tree:add(PF[label], buf(pos, sz - pos)) | |
209 | dissect_ct[C.bulk](buf, t, pos, sz) | |
9235c5a4 | 210 | return sz |
3949f61c MW |
211 | end |
212 | ||
213 | local function dissect_packet(buf, tree, pos, sz) | |
214 | return dissect_ciphertext(buf, tree, "tripe.packet.payload", pos, sz) | |
215 | end | |
216 | ||
217 | -- Dissect a group element of some particular kind. | |
218 | local dissect_ge = { } | |
219 | function dissect_ge.dh(buf, tree, pos, sz) | |
220 | tree:add(PF["tripe.dh.len"], buf(pos, 2)) | |
221 | xsz = buf(pos, 2):uint(); pos = pos + 2 | |
222 | tree:add(PF["tripe.dh.x"], buf(pos, xsz)); pos = pos + xsz | |
223 | return pos | |
224 | end | |
225 | function dissect_ge.ec(buf, tree, pos, sz) | |
226 | tree:add(PF["tripe.ec.xlen"], buf(pos, 2)) | |
227 | xsz = buf(pos, 2):uint(); pos = pos + 2 | |
228 | tree:add(PF["tripe.ec.x"], buf(pos, xsz)); pos = pos + xsz | |
229 | tree:add(PF["tripe.ec.ylen"], buf(pos, 2)) | |
230 | ysz = buf(pos, 2):uint(); pos = pos + 2 | |
231 | tree:add(PF["tripe.ec.y"], buf(pos, ysz)); pos = pos + ysz | |
232 | return pos | |
233 | end | |
234 | function dissect_ge.x25519(buf, tree, pos, sz) | |
235 | tree:add(PF["tripe.x25519.x"], buf(pos, 32)) | |
236 | return pos + 32 | |
237 | end | |
238 | function dissect_ge.x448(buf, tree, pos, sz) | |
239 | tree:add(PF["tripe.x448.x"], buf(pos, 56)) | |
240 | return pos + 56 | |
241 | end | |
242 | ||
243 | local function dissect_my_challenge(buf, tree, pos, sz) | |
244 | -- We don't know how long the group element is going to be. We can set the | |
245 | -- length later, but (at least in older versions) it doesn't work so well | |
246 | -- to increase the length, so make it large to start out, and shrink it | |
247 | -- later. | |
248 | local t = tree:add(PF["tripe.keyexch.mychal"], buf(pos, sz - pos)) | |
249 | local q = dissect_ge[C.kx](buf, t, pos, sz) | |
250 | t:set_len(q - pos) | |
251 | return q | |
252 | end | |
253 | ||
254 | local function dissect_my_cookie(buf, tree, pos, sz) | |
255 | tree:add(PF["tripe.keyexch.mycookie"], buf(pos, C.hashsz)) | |
256 | return pos + C.hashsz | |
257 | end | |
258 | ||
259 | local function dissect_your_cookie(buf, tree, pos, sz) | |
260 | tree:add(PF["tripe.keyexch.yourcookie"], buf(pos, C.hashsz)) | |
261 | return pos + C.hashsz | |
262 | end | |
263 | ||
264 | local kx_scsz = { x25519 = 32, x448 = 56 } -- Hardwired scalar sizes. | |
265 | local function dissect_check(buf, tree, pos, sz) | |
266 | local scsz = kx_scsz[C.kx] or C.scsz | |
267 | tree:add(PF["tripe.keyexch.check"], buf(pos, scsz)) | |
268 | return pos + scsz | |
269 | end | |
270 | ||
271 | local function dissect_reply(buf, tree, pos, sz) | |
272 | return dissect_ciphertext(buf, tree, "tripe.keyexch.reply", pos, sz) | |
273 | end | |
274 | ||
275 | local function dissect_switch(buf, tree, pos, sz) | |
276 | return dissect_ciphertext(buf, tree, "tripe.keyexch.switch", pos, sz) | |
277 | end | |
278 | ||
279 | local function dissect_switchok(buf, tree, pos, sz) | |
280 | return dissect_ciphertext(buf, tree, "tripe.keyexch.switchok", pos, sz) | |
281 | end | |
282 | ||
283 | local function dissect_misc_payload(buf, tree, pos, sz) | |
284 | tree:add(PF["tripe.misc.payload"], buf(pos, sz - pos)) | |
285 | return sz | |
286 | end | |
287 | ||
288 | local function dissect_misc_ciphertext(buf, tree, pos, sz) | |
289 | return dissect_ciphertext(buf, tree, "tripe.misc.ciphertext", pos, sz) | |
290 | end | |
291 | ||
8362ac1c MW |
292 | local function dissect_chal(buf, tree, label, pos, sz) |
293 | local len = buf(pos, 2):uint() | |
294 | local t = tree:add(PF[label], buf(pos, len + 2)) | |
295 | t:add(PF["tripe.chal.len"], buf(pos, 2)); pos = pos + 2 | |
296 | t:add(PF["tripe.chal.sequence"], buf(pos, 4)); pos = pos + 4; len = len - 4 | |
297 | t:add(PF["tripe.chal.tag"], buf(pos, len)) | |
298 | return pos + len | |
299 | end | |
300 | ||
301 | local function dissect_my_chal(buf, tree, pos, sz) | |
302 | return dissect_chal(buf, tree, "tripe.knock.mychal", pos, sz) | |
303 | end | |
304 | ||
305 | local function dissect_your_chal(buf, tree, pos, sz) | |
306 | return dissect_chal(buf, tree, "tripe.knock.yourchal", pos, sz) | |
307 | end | |
308 | ||
309 | local function dissect_keyid(buf, tree, pos, sz) | |
310 | tree:add(PF["tripe.knock.keyid"], buf(pos, 4)) | |
311 | return pos + 4 | |
312 | end | |
313 | ||
314 | local function dissect_ies(buf, tree, pos, sz) | |
315 | local len = buf(pos, 2):uint() | |
316 | local lim = pos + len + 2 | |
317 | local t = tree:add(PF["tripe.knock.ies"], buf(pos, len + 2)) | |
318 | t:add(PF["tripe.ies.len"], buf(pos, 2)); pos = pos + 2 | |
319 | pos = dissect_ge[C.kx](buf, t, pos, sz) | |
320 | return dissect_ciphertext(buf, t, "tripe.ies.ciphertext", pos, lim) | |
321 | end | |
322 | ||
3949f61c MW |
323 | ----------------------------------------------------------------------------- |
324 | --- The protocol information table. | |
325 | ||
326 | local PKTINFO = { | |
327 | -- This is the main table which describes the protocol. The top level maps | |
328 | -- category codes to structures: | |
329 | -- | |
330 | -- * `label' is the category code's symbolic name; | |
331 | -- | |
332 | -- * `subtype' is the field name for the subtype code; | |
333 | -- | |
334 | -- * `info' is a prefix for the information column display; and | |
335 | -- | |
336 | -- * `sub' is a table describing the individual subtypes. | |
337 | -- | |
338 | -- The subtype table similarly maps subtype codes to structures: | |
339 | -- | |
340 | -- * `label' is the subtype code's symbolic name; | |
341 | -- | |
342 | -- * `info' is the suffix for the information column display; and | |
343 | -- | |
344 | -- * `dissect' is a sequence of primitive dissectors to run in order to | |
345 | -- parse the rest of the packet. | |
346 | ||
347 | [0] = { | |
348 | label = "MSG_PACKET", subtype = "tripe.packet.type", | |
349 | info = "Packet data", | |
350 | sub = { | |
351 | [0] = { label = "PACKET_IP", info = "encapsulated IP datagram", | |
352 | dissect = { dissect_packet} } | |
353 | } | |
354 | }, | |
355 | ||
356 | [1] = { | |
357 | label = "MSG_KEYEXCH", subtype = "tripe.keyexch.type", | |
358 | info = "Key exchange", | |
359 | sub = { | |
360 | [0] = { label = "KX_PRECHAL", info = "pre-challenge", | |
361 | dissect = { dissect_my_challenge, | |
362 | dissect_wtf } }, | |
363 | [1] = { label = "KX_CHAL", info = "challenge", | |
364 | dissect = { dissect_my_challenge, | |
365 | dissect_your_cookie, | |
366 | dissect_check, | |
367 | dissect_wtf } }, | |
368 | [2] = { label = "KX_REPLY", info = "reply", | |
369 | dissect = { dissect_my_challenge, | |
370 | dissect_your_cookie, | |
371 | dissect_check, | |
372 | dissect_reply } }, | |
373 | [3] = { label = "KX_SWITCH", info = "switch", | |
374 | dissect = { dissect_my_cookie, | |
375 | dissect_your_cookie, | |
376 | dissect_switch } }, | |
377 | [4] = { label = "KX_SWITCHOK", info = "switch-ok", | |
378 | dissect = { dissect_switchok } }, | |
8362ac1c MW |
379 | [5] = { label = "KX_TOKENRQ", info = "token-rq", |
380 | dissect = { dissect_my_chal, | |
381 | dissect_keyid, | |
382 | dissect_ies } }, | |
383 | [6] = { label = "KX_TOKEN", info = "token", | |
384 | dissect = { dissect_your_chal, | |
385 | dissect_my_chal, | |
386 | dissect_ies } }, | |
387 | [7] = { label = "KX_KNOCK", info = "knock", | |
388 | dissect = { dissect_your_chal, | |
389 | dissect_keyid, | |
390 | dissect_ies, | |
391 | dissect_my_challenge } } | |
3949f61c MW |
392 | } |
393 | }, | |
394 | ||
395 | [2] = { | |
396 | label = "MSG_MISC", subtype = "tripe.misc.type", | |
397 | info = "Miscellaneous", | |
398 | sub = { | |
399 | [0] = { label = "MISC_NOP", info = "no-operation (keepalive)", | |
400 | dissect = { dissect_misc_payload } }, | |
401 | [1] = { label = "MISC_PING", info = "transport-level ping", | |
402 | dissect = { dissect_misc_payload } }, | |
403 | [2] = { label = "MISC_PONG", info = "transport-level ping reply", | |
404 | dissect = { dissect_misc_payload } }, | |
405 | [3] = { label = "MISC_EPING", info = "crypto-level ping", | |
406 | dissect = { dissect_misc_ciphertext } }, | |
407 | [4] = { label = "MISC_EPONG", info = "crypto-level ping reply", | |
408 | dissect = { dissect_misc_ciphertext } }, | |
409 | [5] = { label = "MISC_GREET", info = "greeting", | |
410 | dissect = { dissect_misc_payload } }, | |
067aa5f0 MW |
411 | [6] = { label = "MISC_BYE", info = "disconnect notification", |
412 | dissect = { dissect_misc_ciphertext } }, | |
3949f61c MW |
413 | } |
414 | } | |
415 | } | |
416 | ||
417 | do | |
418 | -- Work through the master table and build `cattab' and `subtab' tables, | |
419 | -- mapping category and subtype codes to their symbolic names for | |
420 | -- presentation. The `subtab' is a two-level table, needing two layers of | |
421 | -- indexing. | |
422 | local cattab = { } | |
423 | local subtab = { } | |
424 | for i, v in pairs(PKTINFO) do | |
425 | cattab[i] = v.label | |
426 | if v.sub ~= nil then | |
427 | subtab[i] = { } | |
428 | for j, w in pairs(v.sub) do | |
429 | subtab[i][j] = w.label | |
430 | end | |
431 | end | |
432 | end | |
433 | ||
434 | local ftab = { | |
435 | -- The protocol fields. This table maps the field names to structures | |
436 | -- used to build the fields, which are then stored in `PF' (declared way | |
437 | -- above): | |
438 | -- | |
439 | -- * `name' is the field name to show in the dissector tree view; | |
440 | -- | |
441 | -- * `type' is the field type; | |
442 | -- | |
443 | -- * `base' is a tweak describing how the field should be formatted; | |
444 | -- | |
445 | -- * `mask' is used to single out a piece of a larger bitfield; and | |
446 | -- | |
447 | -- * `tab' names a mapping table used to convert numerical values to | |
448 | -- symbolic names. | |
449 | ||
450 | ["tripe.type"] = { | |
451 | name = "Message type", type = ftypes.UINT8, base = base.HEX | |
452 | }, | |
453 | ["tripe.cat"] = { | |
454 | name = "Message category", type = ftypes.UINT8, base = base.DEC, | |
455 | mask = 0xf0, tab = cattab | |
456 | }, | |
457 | ["tripe.packet.type"] = { | |
458 | name = "Packet subcode", type = ftypes.UINT8, base = base.DEC, | |
459 | mask = 0x0f, tab = subtab[0] | |
460 | }, | |
461 | ["tripe.packet.payload"] = { | |
462 | name = "Encrypted packet", type = ftypes.NONE | |
463 | }, | |
8362ac1c MW |
464 | ["tripe.knock.keyid"] = { |
465 | name = "Short key indicator", type = ftypes.UINT32, base = base.HEX | |
466 | }, | |
467 | ["tripe.knock.mychal"] = { | |
468 | name = "Sender's one-time challenge", type = ftypes.NONE | |
469 | }, | |
470 | ["tripe.knock.yourchal"] = { | |
471 | name = "Recipient's one-time challenge", type = ftypes.NONE | |
472 | }, | |
473 | ["tripe.chal.len"] = { | |
474 | name = "Challenge length", type = ftypes.UINT16, base = base.DEC | |
475 | }, | |
476 | ["tripe.chal.sequence"] = { | |
477 | name = "Challenge sequence number", | |
478 | type = ftypes.UINT32, base = base.DEC | |
479 | }, | |
480 | ["tripe.chal.tag"] = { | |
481 | name = "Challenge tag", type = ftypes.BYTES, base = base.SPACE | |
482 | }, | |
483 | ["tripe.knock.ies"] = { | |
484 | name = "Encrypted message", type = ftypes.NONE | |
485 | }, | |
486 | ["tripe.ies.len"] = { | |
487 | name = "Encrypted message length", | |
488 | type = ftypes.UINT16, base = base.DEC | |
489 | }, | |
490 | ["tripe.ies.clue"] = { | |
491 | name = "Encrypted message KEM clue", | |
492 | type = ftypes.BYTES, base = base.SPACE | |
493 | }, | |
494 | ["tripe.ies.ciphertext"] = { | |
495 | name = "Encrypted message ciphertext", | |
496 | type = ftypes.BYTES, base = base.SPACE | |
497 | }, | |
3949f61c MW |
498 | ["tripe.keyexch.type"] = { |
499 | name = "Key-exchange subcode", type = ftypes.UINT8, base = base.DEC, | |
500 | mask = 0x0f, tab = subtab[1] | |
501 | }, | |
502 | ["tripe.keyexch.mychal"] = { | |
503 | name = "Sender's challenge R = r P", type = ftypes.NONE | |
504 | }, | |
505 | ["tripe.keyexch.mycookie"] = { | |
506 | name = "Hash of recipient's challenge = H(R, ...)", | |
507 | type = ftypes.BYTES, base = base.SPACE | |
508 | }, | |
509 | ["tripe.keyexch.yourcookie"] = { | |
510 | name = "Hash of sender's challenge = H(R', ...)", | |
511 | type = ftypes.BYTES, base = base.SPACE | |
512 | }, | |
513 | ["tripe.keyexch.reply"] = { | |
514 | name = "Encrypted reply = k R'", type = ftypes.NONE | |
515 | }, | |
516 | ["tripe.keyexch.switch"] = { | |
517 | name = "Encrypted reply and switch request = k R', H(...)", | |
518 | type = ftypes.NONE | |
519 | }, | |
520 | ["tripe.keyexch.switchok"] = { | |
521 | name = "Encrypted switch confirmation = H(...)", type = ftypes.NONE | |
522 | }, | |
523 | ["tripe.misc.type"] = { | |
524 | name = "Miscellenaous subcode", type = ftypes.UINT8, base = base.DEC, | |
525 | mask = 0x0f, tab = subtab[2] | |
526 | }, | |
527 | ["tripe.misc.payload"] = { | |
528 | name = "Miscellaneous payload", | |
529 | type = ftypes.BYTES, base = base.SPACE | |
530 | }, | |
531 | ["tripe.misc.ciphertext"] = { | |
532 | name = "Miscellaneous encrypted payload", type = ftypes.NONE | |
533 | }, | |
534 | ["tripe.wtf"] = { | |
535 | name = "Unexpected trailing data", | |
536 | type = ftypes.BYTES, base = base.SPACE | |
537 | }, | |
538 | ["tripe.keyexch.check"] = { | |
539 | name = "Sender's challenge check value = r XOR H(r K', ...)", | |
540 | type = ftypes.BYTES, base = base.SPACE | |
541 | }, | |
542 | ["tripe.ciphertext.seq"] = { | |
543 | name = "Sequence number", type = ftypes.UINT32, base = base.DEC | |
544 | }, | |
545 | ["tripe.ciphertext.iv"] = { | |
546 | name = "Initialization vector", type = ftypes.BYTES, base = base.SPACE | |
547 | }, | |
548 | ["tripe.ciphertext.tag"] = { | |
549 | name = "Authentication tag", type = ftypes.BYTES, base = base.SPACE | |
550 | }, | |
551 | ["tripe.ciphertext.body"] = { | |
552 | name = "Encrypted data", type = ftypes.BYTES, base = base.SPACE | |
553 | }, | |
554 | ["tripe.dh.len"] = { | |
555 | name = "DH group element length", | |
556 | type = ftypes.UINT16, base = base.DEC | |
557 | }, | |
558 | ["tripe.dh.x"] = { | |
559 | name = "DH group element value", | |
560 | type = ftypes.BYTES, base = base.SPACE | |
561 | }, | |
562 | ["tripe.ec.xlen"] = { | |
563 | name = "Elliptic curve x-coordinate length", | |
564 | type = ftypes.UINT16, base = base.DEC | |
565 | }, | |
566 | ["tripe.ec.x"] = { | |
567 | name = "Elliptic curve x-coordinate value", | |
568 | type = ftypes.BYTES, base = base.SPACE | |
569 | }, | |
570 | ["tripe.ec.ylen"] = { | |
571 | name = "Elliptic curve y-coordinate length", | |
572 | type = ftypes.UINT16, base = base.DEC | |
573 | }, | |
574 | ["tripe.ec.y"] = { | |
575 | name = "Elliptic curve y-coordinate value", | |
576 | type = ftypes.BYTES, base = base.SPACE | |
577 | }, | |
578 | ["tripe.x25519.x"] = { | |
579 | name = "X25519 x-coordinate", | |
580 | type = ftypes.BYTES, base = base.SPACE | |
581 | }, | |
582 | ["tripe.x448.x"] = { | |
583 | name = "X448 x-coordinate", | |
584 | type = ftypes.BYTES, base = base.SPACE | |
585 | }, | |
586 | } | |
587 | ||
588 | -- Convert this table into the protocol fields, and populate `PF'. | |
589 | local ff = { } | |
590 | local i = 1 | |
591 | ||
592 | -- Figure out whether we can use `none' fields (see below). | |
593 | -- probe for this easily | |
594 | local use_none_p = rawget(ProtoField, 'none') ~= nil | |
595 | for abbr, args in pairs(ftab) do | |
596 | ||
597 | -- An annoying hack. Older versions of Wireshark don't allow setting | |
598 | -- fields with type `none', which is a shame because they're ideal as | |
599 | -- internal tree nodes. | |
600 | ty = args.type | |
601 | b = args.base | |
602 | if ty == ftypes.NONE and not use_none_p then | |
603 | ty = ftypes.BYTES | |
604 | b = base.SPACE | |
605 | end | |
606 | ||
607 | -- Go make the field. | |
608 | local f = ProtoField.new(args.name, abbr, ty, | |
609 | args.tab, b, args.mask, args.descr) | |
610 | PF[abbr] = f | |
611 | ff[i] = f; i = i + 1 | |
612 | end | |
613 | tripe.fields = PF | |
614 | end | |
615 | ||
616 | ----------------------------------------------------------------------------- | |
617 | --- The main dissector. | |
618 | ||
619 | function tripe.dissector(buf, pinfo, tree) | |
620 | ||
621 | -- Fill in the obvious stuff. | |
622 | pinfo.cols.protocol = "TrIPE" | |
623 | ||
624 | local sz = buf:reported_length_remaining() | |
625 | local sub = tree:add(tripe, buf(0, sz), "TrIPE packet") | |
626 | local p = 1 | |
627 | ||
628 | -- Decode the packet type octet. | |
629 | local tycode = buf(0, 1):uint() | |
630 | local ty = sub:add(PF["tripe.type"], buf(0, 1)) | |
631 | ty:add(PF["tripe.cat"], buf(0, 1)) | |
632 | local cat = bit.rshift(bit.band(tycode, 0xf0), 4) | |
633 | local subty = bit.band(tycode, 0x0f) | |
634 | local info = PKTINFO[cat] | |
635 | ||
636 | -- Dispatch using the master protocol table. | |
637 | if info == nil then | |
638 | pinfo.cols.info = string.format("Unknown category code %u, " .. | |
639 | "unknown type code %u", | |
640 | cat, subty) | |
641 | else | |
642 | ty:add(PF[info.subtype], buf(0, 1)) | |
643 | local subinfo = info.sub[subty] | |
644 | if subinfo == nil then | |
645 | pinfo.cols.info = string.format("%s, unknown type code %u", | |
646 | info.info, subty) | |
647 | else | |
648 | pinfo.cols.info = string.format("%s, %s", info.info, subinfo.info) | |
649 | p = 1 | |
650 | for _, d in ipairs(subinfo.dissect) do p = d(buf, sub, p, sz) end | |
651 | end | |
652 | end | |
653 | ||
654 | -- Return the final position we reached. | |
655 | return p | |
656 | end | |
657 | ||
658 | -- We're done. Register the dissector. | |
659 | DissectorTable.get("udp.port"):add(4070, tripe) | |
660 | ||
661 | -------- That's all, folks -------------------------------------------------- |