| 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 | --- |
| 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. |
| 16 | --- |
| 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. |
| 21 | --- |
| 22 | --- You should have received a copy of the GNU General Public License |
| 23 | --- along with TrIPE. If not, see <https://www.gnu.org/licenses/>. |
| 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) |
| 210 | return sz |
| 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 | |
| 292 | ----------------------------------------------------------------------------- |
| 293 | --- The protocol information table. |
| 294 | |
| 295 | local PKTINFO = { |
| 296 | -- This is the main table which describes the protocol. The top level maps |
| 297 | -- category codes to structures: |
| 298 | -- |
| 299 | -- * `label' is the category code's symbolic name; |
| 300 | -- |
| 301 | -- * `subtype' is the field name for the subtype code; |
| 302 | -- |
| 303 | -- * `info' is a prefix for the information column display; and |
| 304 | -- |
| 305 | -- * `sub' is a table describing the individual subtypes. |
| 306 | -- |
| 307 | -- The subtype table similarly maps subtype codes to structures: |
| 308 | -- |
| 309 | -- * `label' is the subtype code's symbolic name; |
| 310 | -- |
| 311 | -- * `info' is the suffix for the information column display; and |
| 312 | -- |
| 313 | -- * `dissect' is a sequence of primitive dissectors to run in order to |
| 314 | -- parse the rest of the packet. |
| 315 | |
| 316 | [0] = { |
| 317 | label = "MSG_PACKET", subtype = "tripe.packet.type", |
| 318 | info = "Packet data", |
| 319 | sub = { |
| 320 | [0] = { label = "PACKET_IP", info = "encapsulated IP datagram", |
| 321 | dissect = { dissect_packet} } |
| 322 | } |
| 323 | }, |
| 324 | |
| 325 | [1] = { |
| 326 | label = "MSG_KEYEXCH", subtype = "tripe.keyexch.type", |
| 327 | info = "Key exchange", |
| 328 | sub = { |
| 329 | [0] = { label = "KX_PRECHAL", info = "pre-challenge", |
| 330 | dissect = { dissect_my_challenge, |
| 331 | dissect_wtf } }, |
| 332 | [1] = { label = "KX_CHAL", info = "challenge", |
| 333 | dissect = { dissect_my_challenge, |
| 334 | dissect_your_cookie, |
| 335 | dissect_check, |
| 336 | dissect_wtf } }, |
| 337 | [2] = { label = "KX_REPLY", info = "reply", |
| 338 | dissect = { dissect_my_challenge, |
| 339 | dissect_your_cookie, |
| 340 | dissect_check, |
| 341 | dissect_reply } }, |
| 342 | [3] = { label = "KX_SWITCH", info = "switch", |
| 343 | dissect = { dissect_my_cookie, |
| 344 | dissect_your_cookie, |
| 345 | dissect_switch } }, |
| 346 | [4] = { label = "KX_SWITCHOK", info = "switch-ok", |
| 347 | dissect = { dissect_switchok } }, |
| 348 | } |
| 349 | }, |
| 350 | |
| 351 | [2] = { |
| 352 | label = "MSG_MISC", subtype = "tripe.misc.type", |
| 353 | info = "Miscellaneous", |
| 354 | sub = { |
| 355 | [0] = { label = "MISC_NOP", info = "no-operation (keepalive)", |
| 356 | dissect = { dissect_misc_payload } }, |
| 357 | [1] = { label = "MISC_PING", info = "transport-level ping", |
| 358 | dissect = { dissect_misc_payload } }, |
| 359 | [2] = { label = "MISC_PONG", info = "transport-level ping reply", |
| 360 | dissect = { dissect_misc_payload } }, |
| 361 | [3] = { label = "MISC_EPING", info = "crypto-level ping", |
| 362 | dissect = { dissect_misc_ciphertext } }, |
| 363 | [4] = { label = "MISC_EPONG", info = "crypto-level ping reply", |
| 364 | dissect = { dissect_misc_ciphertext } }, |
| 365 | [5] = { label = "MISC_GREET", info = "greeting", |
| 366 | dissect = { dissect_misc_payload } }, |
| 367 | } |
| 368 | } |
| 369 | } |
| 370 | |
| 371 | do |
| 372 | -- Work through the master table and build `cattab' and `subtab' tables, |
| 373 | -- mapping category and subtype codes to their symbolic names for |
| 374 | -- presentation. The `subtab' is a two-level table, needing two layers of |
| 375 | -- indexing. |
| 376 | local cattab = { } |
| 377 | local subtab = { } |
| 378 | for i, v in pairs(PKTINFO) do |
| 379 | cattab[i] = v.label |
| 380 | if v.sub ~= nil then |
| 381 | subtab[i] = { } |
| 382 | for j, w in pairs(v.sub) do |
| 383 | subtab[i][j] = w.label |
| 384 | end |
| 385 | end |
| 386 | end |
| 387 | |
| 388 | local ftab = { |
| 389 | -- The protocol fields. This table maps the field names to structures |
| 390 | -- used to build the fields, which are then stored in `PF' (declared way |
| 391 | -- above): |
| 392 | -- |
| 393 | -- * `name' is the field name to show in the dissector tree view; |
| 394 | -- |
| 395 | -- * `type' is the field type; |
| 396 | -- |
| 397 | -- * `base' is a tweak describing how the field should be formatted; |
| 398 | -- |
| 399 | -- * `mask' is used to single out a piece of a larger bitfield; and |
| 400 | -- |
| 401 | -- * `tab' names a mapping table used to convert numerical values to |
| 402 | -- symbolic names. |
| 403 | |
| 404 | ["tripe.type"] = { |
| 405 | name = "Message type", type = ftypes.UINT8, base = base.HEX |
| 406 | }, |
| 407 | ["tripe.cat"] = { |
| 408 | name = "Message category", type = ftypes.UINT8, base = base.DEC, |
| 409 | mask = 0xf0, tab = cattab |
| 410 | }, |
| 411 | ["tripe.packet.type"] = { |
| 412 | name = "Packet subcode", type = ftypes.UINT8, base = base.DEC, |
| 413 | mask = 0x0f, tab = subtab[0] |
| 414 | }, |
| 415 | ["tripe.packet.payload"] = { |
| 416 | name = "Encrypted packet", type = ftypes.NONE |
| 417 | }, |
| 418 | ["tripe.keyexch.type"] = { |
| 419 | name = "Key-exchange subcode", type = ftypes.UINT8, base = base.DEC, |
| 420 | mask = 0x0f, tab = subtab[1] |
| 421 | }, |
| 422 | ["tripe.keyexch.mychal"] = { |
| 423 | name = "Sender's challenge R = r P", type = ftypes.NONE |
| 424 | }, |
| 425 | ["tripe.keyexch.mycookie"] = { |
| 426 | name = "Hash of recipient's challenge = H(R, ...)", |
| 427 | type = ftypes.BYTES, base = base.SPACE |
| 428 | }, |
| 429 | ["tripe.keyexch.yourcookie"] = { |
| 430 | name = "Hash of sender's challenge = H(R', ...)", |
| 431 | type = ftypes.BYTES, base = base.SPACE |
| 432 | }, |
| 433 | ["tripe.keyexch.reply"] = { |
| 434 | name = "Encrypted reply = k R'", type = ftypes.NONE |
| 435 | }, |
| 436 | ["tripe.keyexch.switch"] = { |
| 437 | name = "Encrypted reply and switch request = k R', H(...)", |
| 438 | type = ftypes.NONE |
| 439 | }, |
| 440 | ["tripe.keyexch.switchok"] = { |
| 441 | name = "Encrypted switch confirmation = H(...)", type = ftypes.NONE |
| 442 | }, |
| 443 | ["tripe.misc.type"] = { |
| 444 | name = "Miscellenaous subcode", type = ftypes.UINT8, base = base.DEC, |
| 445 | mask = 0x0f, tab = subtab[2] |
| 446 | }, |
| 447 | ["tripe.misc.payload"] = { |
| 448 | name = "Miscellaneous payload", |
| 449 | type = ftypes.BYTES, base = base.SPACE |
| 450 | }, |
| 451 | ["tripe.misc.ciphertext"] = { |
| 452 | name = "Miscellaneous encrypted payload", type = ftypes.NONE |
| 453 | }, |
| 454 | ["tripe.wtf"] = { |
| 455 | name = "Unexpected trailing data", |
| 456 | type = ftypes.BYTES, base = base.SPACE |
| 457 | }, |
| 458 | ["tripe.keyexch.check"] = { |
| 459 | name = "Sender's challenge check value = r XOR H(r K', ...)", |
| 460 | type = ftypes.BYTES, base = base.SPACE |
| 461 | }, |
| 462 | ["tripe.ciphertext.seq"] = { |
| 463 | name = "Sequence number", type = ftypes.UINT32, base = base.DEC |
| 464 | }, |
| 465 | ["tripe.ciphertext.iv"] = { |
| 466 | name = "Initialization vector", type = ftypes.BYTES, base = base.SPACE |
| 467 | }, |
| 468 | ["tripe.ciphertext.tag"] = { |
| 469 | name = "Authentication tag", type = ftypes.BYTES, base = base.SPACE |
| 470 | }, |
| 471 | ["tripe.ciphertext.body"] = { |
| 472 | name = "Encrypted data", type = ftypes.BYTES, base = base.SPACE |
| 473 | }, |
| 474 | ["tripe.dh.len"] = { |
| 475 | name = "DH group element length", |
| 476 | type = ftypes.UINT16, base = base.DEC |
| 477 | }, |
| 478 | ["tripe.dh.x"] = { |
| 479 | name = "DH group element value", |
| 480 | type = ftypes.BYTES, base = base.SPACE |
| 481 | }, |
| 482 | ["tripe.ec.xlen"] = { |
| 483 | name = "Elliptic curve x-coordinate length", |
| 484 | type = ftypes.UINT16, base = base.DEC |
| 485 | }, |
| 486 | ["tripe.ec.x"] = { |
| 487 | name = "Elliptic curve x-coordinate value", |
| 488 | type = ftypes.BYTES, base = base.SPACE |
| 489 | }, |
| 490 | ["tripe.ec.ylen"] = { |
| 491 | name = "Elliptic curve y-coordinate length", |
| 492 | type = ftypes.UINT16, base = base.DEC |
| 493 | }, |
| 494 | ["tripe.ec.y"] = { |
| 495 | name = "Elliptic curve y-coordinate value", |
| 496 | type = ftypes.BYTES, base = base.SPACE |
| 497 | }, |
| 498 | ["tripe.x25519.x"] = { |
| 499 | name = "X25519 x-coordinate", |
| 500 | type = ftypes.BYTES, base = base.SPACE |
| 501 | }, |
| 502 | ["tripe.x448.x"] = { |
| 503 | name = "X448 x-coordinate", |
| 504 | type = ftypes.BYTES, base = base.SPACE |
| 505 | }, |
| 506 | } |
| 507 | |
| 508 | -- Convert this table into the protocol fields, and populate `PF'. |
| 509 | local ff = { } |
| 510 | local i = 1 |
| 511 | |
| 512 | -- Figure out whether we can use `none' fields (see below). |
| 513 | -- probe for this easily |
| 514 | local use_none_p = rawget(ProtoField, 'none') ~= nil |
| 515 | for abbr, args in pairs(ftab) do |
| 516 | |
| 517 | -- An annoying hack. Older versions of Wireshark don't allow setting |
| 518 | -- fields with type `none', which is a shame because they're ideal as |
| 519 | -- internal tree nodes. |
| 520 | ty = args.type |
| 521 | b = args.base |
| 522 | if ty == ftypes.NONE and not use_none_p then |
| 523 | ty = ftypes.BYTES |
| 524 | b = base.SPACE |
| 525 | end |
| 526 | |
| 527 | -- Go make the field. |
| 528 | local f = ProtoField.new(args.name, abbr, ty, |
| 529 | args.tab, b, args.mask, args.descr) |
| 530 | PF[abbr] = f |
| 531 | ff[i] = f; i = i + 1 |
| 532 | end |
| 533 | tripe.fields = PF |
| 534 | end |
| 535 | |
| 536 | ----------------------------------------------------------------------------- |
| 537 | --- The main dissector. |
| 538 | |
| 539 | function tripe.dissector(buf, pinfo, tree) |
| 540 | |
| 541 | -- Fill in the obvious stuff. |
| 542 | pinfo.cols.protocol = "TrIPE" |
| 543 | |
| 544 | local sz = buf:reported_length_remaining() |
| 545 | local sub = tree:add(tripe, buf(0, sz), "TrIPE packet") |
| 546 | local p = 1 |
| 547 | |
| 548 | -- Decode the packet type octet. |
| 549 | local tycode = buf(0, 1):uint() |
| 550 | local ty = sub:add(PF["tripe.type"], buf(0, 1)) |
| 551 | ty:add(PF["tripe.cat"], buf(0, 1)) |
| 552 | local cat = bit.rshift(bit.band(tycode, 0xf0), 4) |
| 553 | local subty = bit.band(tycode, 0x0f) |
| 554 | local info = PKTINFO[cat] |
| 555 | |
| 556 | -- Dispatch using the master protocol table. |
| 557 | if info == nil then |
| 558 | pinfo.cols.info = string.format("Unknown category code %u, " .. |
| 559 | "unknown type code %u", |
| 560 | cat, subty) |
| 561 | else |
| 562 | ty:add(PF[info.subtype], buf(0, 1)) |
| 563 | local subinfo = info.sub[subty] |
| 564 | if subinfo == nil then |
| 565 | pinfo.cols.info = string.format("%s, unknown type code %u", |
| 566 | info.info, subty) |
| 567 | else |
| 568 | pinfo.cols.info = string.format("%s, %s", info.info, subinfo.info) |
| 569 | p = 1 |
| 570 | for _, d in ipairs(subinfo.dissect) do p = d(buf, sub, p, sz) end |
| 571 | end |
| 572 | end |
| 573 | |
| 574 | -- Return the final position we reached. |
| 575 | return p |
| 576 | end |
| 577 | |
| 578 | -- We're done. Register the dissector. |
| 579 | DissectorTable.get("udp.port"):add(4070, tripe) |
| 580 | |
| 581 | -------- That's all, folks -------------------------------------------------- |