init
This commit is contained in:
420
fido_u2f_dissector.lua
Normal file
420
fido_u2f_dissector.lua
Normal file
@@ -0,0 +1,420 @@
|
||||
-- started based on https://gist.github.com/z4yx/218116240e2759759b239d16fed787ca
|
||||
|
||||
cbor = Dissector.get("cbor")
|
||||
iso7816 = Dissector.get("iso7816")
|
||||
|
||||
ctaphid_proto = Proto("CTAPHID","FIDO Client to Authenticator Protocol over USB HID")
|
||||
ctaphidfield_cid = ProtoField.uint32("ctaphid.cid", "Channel ID", base.HEX)
|
||||
ctaphidfield_cmd = ProtoField.uint8("ctaphid.cmd", "Command", base.HEX)
|
||||
ctaphidfield_bcnt = ProtoField.uint16("ctaphid.bcnt", "Payload Length", base.DEC_HEX)
|
||||
ctaphidfield_seq = ProtoField.uint8("ctaphid.seq", "Packet Sequence", base.HEX)
|
||||
ctaphidfield_data = ProtoField.bytes("ctaphid.data", "Data")
|
||||
ctaphid_proto.fields = { ctaphidfield_cid, ctaphidfield_cmd, ctaphidfield_bcnt, ctaphidfield_seq, ctaphidfield_data }
|
||||
|
||||
u2f_proto = Proto("u2f","FIDO CTAP1/U2F Protocol")
|
||||
u2ffield_cla = ProtoField.uint8("u2f.request.cla", "Class", base.HEX)
|
||||
u2ffield_ins = ProtoField.uint8("u2f.request.ins", "U2F command code", base.HEX)
|
||||
u2ffield_p1 = ProtoField.uint8("u2f.request.p1", "U2F command parameter 1", base.HEX)
|
||||
u2ffield_p2 = ProtoField.uint8("u2f.request.p2", "U2F command parameter 2", base.HEX)
|
||||
u2ffield_reqlen = ProtoField.uint24("u2f.request.lc", "U2F request data length", base.HEX)
|
||||
u2ffield_expreslen = ProtoField.uint24("u2f.request.le", "U2F expected response data length", base.HEX)
|
||||
u2ffield_reqdata = ProtoField.bytes("u2f.request.data", "U2F request data")
|
||||
u2ffield_status = ProtoField.uint16("u2f.response.status", "U2F response status", base.HEX)
|
||||
u2ffield_respdata = ProtoField.bytes("u2f.response.data", "U2F response data")
|
||||
u2f_proto.fields = { u2ffield_cla, u2ffield_ins, u2ffield_p1, u2ffield_p2, u2ffield_reqlen, u2ffield_reqdata, u2ffield_expreslen, u2ffield_status, u2ffield_respdata }
|
||||
|
||||
|
||||
-- Field Extractor
|
||||
field_usb_bus = Field.new("usb.bus_id")
|
||||
field_usb_device = Field.new("usb.device_address")
|
||||
field_usb_endpoint = Field.new("usb.endpoint_address")
|
||||
field_usb_endpointdir = Field.new("usb.endpoint_address.direction")
|
||||
field_usb_datalen = Field.new("usb.data_len")
|
||||
field_iso7816_ins = Field.new("iso7816.apdu.ins")
|
||||
field_iso7816_p1 = Field.new("iso7816.apdu.p1")
|
||||
field_iso7816_p2 = Field.new("iso7816.apdu.p2")
|
||||
field_iso7816_sw1 = Field.new("iso7816.apdu.sw1")
|
||||
field_iso7816_sw2 = Field.new("iso7816.apdu.sw2")
|
||||
field_iso7816_lc = Field.new("iso7816.apdu.lc")
|
||||
field_iso7816_le = Field.new("iso7816.apdu.le")
|
||||
field_iso7816_data = Field.new("iso7816.application_data")
|
||||
|
||||
CTAPHID_COMMANDS = {
|
||||
CTAPHID_MSG = 0x03,
|
||||
CTAPHID_CBOR = 0x10,
|
||||
CTAPHID_INIT = 0x06,
|
||||
CTAPHID_PING = 0x01,
|
||||
CTAPHID_CANCEL = 0x11,
|
||||
CTAPHID_ERROR = 0x3F,
|
||||
CTAPHID_KEEPALIVE = 0x3B,
|
||||
CTAPHID_WINK = 0x08,
|
||||
CTAPHID_LOCK = 0x04,
|
||||
CTAPHID_VENDOR_FIRST = 0x40,
|
||||
CTAPHID_VENDOR_LAST = 0x7F
|
||||
}
|
||||
|
||||
CTAPHID_COMMAND_STRINGS = {
|
||||
[0x03] = 'CTAPHID_MSG',
|
||||
[0x10] = 'CTAPHID_CBOR',
|
||||
[0x06] = 'CTAPHID_INIT',
|
||||
[0x01] = 'CTAPHID_PING',
|
||||
[0x11] = 'CTAPHID_CANCEL',
|
||||
[0x3F] = 'CTAPHID_ERROR',
|
||||
[0x3B] = 'CTAPHID_KEEPALIVE',
|
||||
[0x08] = 'CTAPHID_WINK',
|
||||
[0x04] = 'CTAPHID_LOCK',
|
||||
[0x40] = 'VENDOR_FIRST',
|
||||
[0x7F] = 'VENDOR_LAST',
|
||||
}
|
||||
|
||||
U2F_INS_STRINGS = {
|
||||
[0x01] = 'U2F_REGISTER',
|
||||
[0x02] = 'U2F_AUTHENTICATE',
|
||||
[0x03] = 'U2F_VERSION',
|
||||
[0x40] = 'VENDOR_FIRST',
|
||||
[0xBF] = 'VENDOR_LAST'
|
||||
}
|
||||
|
||||
U2F_P1_STRINGS = {
|
||||
[0x03] = 'ENFORCE-USER-PRESENCE-AND-SIGN',
|
||||
[0x07] = 'CHECK-ONLY',
|
||||
[0x08] = 'DONT-ENFORCE-USER-PRESENCE-AND-SIGN',
|
||||
}
|
||||
|
||||
U2F_STATUS_STRINGS = {
|
||||
[0x9000] = 'SW_NO_ERROR',
|
||||
[0x6985] = 'SW_CONDITIONS_NOT_SATISFIED',
|
||||
[0x6A80] = 'SW_WRONG_DATA',
|
||||
[0x6700] = 'SW_WRONG_LENGTH',
|
||||
[0x6E00] = 'SW_CLA_NOT_SUPPORTED',
|
||||
[0x6D00] = 'SW_INS_NOT_SUPPORTED'
|
||||
}
|
||||
|
||||
CTAP_COMMAND_CODE = {
|
||||
[0x01]='authenticatorMakeCredential',
|
||||
[0x02]='authenticatorGetAssertion',
|
||||
[0x04]='authenticatorGetInfo',
|
||||
[0x06]='authenticatorClientPIN',
|
||||
[0x07]='authenticatorReset',
|
||||
[0x08]='authenticatorGetNextAssertion',
|
||||
[0x40]='authenticatorVendorFirst',
|
||||
[0xBF]='authenticatorVendorLast'
|
||||
}
|
||||
CTAP_RESPONSE_CODE = {
|
||||
[0x00]='CTAP1_ERR_SUCCESS',
|
||||
[0x01]='CTAP1_ERR_INVALID_COMMAND',
|
||||
[0x02]='CTAP1_ERR_INVALID_PARAMETER',
|
||||
[0x03]='CTAP1_ERR_INVALID_LENGTH',
|
||||
[0x04]='CTAP1_ERR_INVALID_SEQ',
|
||||
[0x05]='CTAP1_ERR_TIMEOUT',
|
||||
[0x06]='CTAP1_ERR_CHANNEL_BUSY',
|
||||
[0x0A]='CTAP1_ERR_LOCK_REQUIRED',
|
||||
[0x0B]='CTAP1_ERR_INVALID_CHANNEL',
|
||||
[0x11]='CTAP2_ERR_CBOR_UNEXPECTED_TYPE',
|
||||
[0x12]='CTAP2_ERR_INVALID_CBOR',
|
||||
[0x14]='CTAP2_ERR_MISSING_PARAMETER',
|
||||
[0x15]='CTAP2_ERR_LIMIT_EXCEEDED',
|
||||
[0x16]='CTAP2_ERR_UNSUPPORTED_EXTENSION',
|
||||
[0x19]='CTAP2_ERR_CREDENTIAL_EXCLUDED',
|
||||
[0x21]='CTAP2_ERR_PROCESSING',
|
||||
[0x22]='CTAP2_ERR_INVALID_CREDENTIAL',
|
||||
[0x23]='CTAP2_ERR_USER_ACTION_PENDING',
|
||||
[0x24]='CTAP2_ERR_OPERATION_PENDING',
|
||||
[0x25]='CTAP2_ERR_NO_OPERATIONS',
|
||||
[0x26]='CTAP2_ERR_UNSUPPORTED_ALGORITHM',
|
||||
[0x27]='CTAP2_ERR_OPERATION_DENIED',
|
||||
[0x28]='CTAP2_ERR_KEY_STORE_FULL',
|
||||
[0x29]='CTAP2_ERR_NOT_BUSY',
|
||||
[0x2A]='CTAP2_ERR_NO_OPERATION_PENDING',
|
||||
[0x2B]='CTAP2_ERR_UNSUPPORTED_OPTION',
|
||||
[0x2C]='CTAP2_ERR_INVALID_OPTION',
|
||||
[0x2D]='CTAP2_ERR_KEEPALIVE_CANCEL',
|
||||
[0x2E]='CTAP2_ERR_NO_CREDENTIALS',
|
||||
[0x2F]='CTAP2_ERR_USER_ACTION_TIMEOUT',
|
||||
[0x30]='CTAP2_ERR_NOT_ALLOWED',
|
||||
[0x31]='CTAP2_ERR_PIN_INVALID',
|
||||
[0x32]='CTAP2_ERR_PIN_BLOCKED',
|
||||
[0x33]='CTAP2_ERR_PIN_AUTH_INVALID',
|
||||
[0x34]='CTAP2_ERR_PIN_AUTH_BLOCKED',
|
||||
[0x35]='CTAP2_ERR_PIN_NOT_SET',
|
||||
[0x36]='CTAP2_ERR_PIN_REQUIRED',
|
||||
[0x37]='CTAP2_ERR_PIN_POLICY_VIOLATION',
|
||||
[0x38]='CTAP2_ERR_PIN_TOKEN_EXPIRED',
|
||||
[0x39]='CTAP2_ERR_REQUEST_TOO_LARGE',
|
||||
[0x3A]='CTAP2_ERR_ACTION_TIMEOUT',
|
||||
[0x3B]='CTAP2_ERR_UP_REQUIRED',
|
||||
[0x7F]='CTAP1_ERR_OTHER',
|
||||
[0xDF]='CTAP2_ERR_SPEC_LAST',
|
||||
[0xE0]='CTAP2_ERR_EXTENSION_FIRST',
|
||||
[0xEF]='CTAP2_ERR_EXTENSION_LAST',
|
||||
[0xF0]='CTAP2_ERR_VENDOR_FIRST',
|
||||
[0xFF]='CTAP2_ERR_VENDOR_LAST'
|
||||
}
|
||||
|
||||
function dissect_ctaphid_payload(cmd, buffer, pinfo, tree)
|
||||
if buffer:len() == 0 then return end -- && usb.function == 0x0008 && select correct endpoint/etc.
|
||||
if cmd == CTAPHID_COMMANDS.CTAPHID_MSG then
|
||||
local isotree = tree:add("iso")
|
||||
iso7816:call(buffer, pinfo, isotree)
|
||||
isotree.hidden = true
|
||||
-- print(field_iso7816_ins().value, field_iso7816_p1().value, field_iso7816_p2().value)
|
||||
Dissector.get("u2f"):call(buffer, pinfo, tree)
|
||||
-- pinfo.cols.protocol = u2f_proto.name
|
||||
-- local subtree = tree:add(ctaphid_proto,buffer(),"CTAP1/U2F")
|
||||
-- local is_request = (field_usb_endpointdir().value == 0)
|
||||
-- print(field_usb_endpointdir().value)
|
||||
-- print(is_request)
|
||||
-- print(Dissector.get("u2f"))
|
||||
-- if is_request then -- this is a request
|
||||
-- local u2f_command = buffer(1,1):uint()
|
||||
-- subtree:append_text(" Request")
|
||||
-- pinfo.cols.info = "U2F Request (" .. u2f_command_label(u2f_command, true) .. ")"
|
||||
-- subtree:add(u2ffield_cla, buffer(0,1))
|
||||
-- subtree:add(u2ffield_ins, buffer(1,1), u2f_command, "Command: " .. u2f_command_label(u2f_command))
|
||||
-- subtree:add(u2ffield_p1, buffer(2,1))
|
||||
-- subtree:add(u2ffield_p2, buffer(3,1))
|
||||
-- local request_length = buffer(4,3):uint()
|
||||
-- subtree:add(u2ffield_reqlen, buffer(4,3))
|
||||
-- subtree:add(u2ffield_reqdata, buffer(7, request_length))
|
||||
-- else -- response
|
||||
-- local u2f_status = buffer(buffer:len()-2,2):uint()
|
||||
-- subtree:append_text(" Response")
|
||||
-- pinfo.cols.info = "U2F Response (" .. u2f_status_label(u2f_status, true) .. ")"
|
||||
-- subtree:add(u2ffield_status, u2f_status, u2f_status, "Status: " .. u2f_status_label(u2f_status))
|
||||
-- if buffer:len() > 2 then
|
||||
-- subtree:add(u2ffield_respdata, buffer(0, buffer:len()-2))
|
||||
-- end
|
||||
-- end
|
||||
elseif cmd == CTAPHID_COMMANDS.CTAPHID_CBOR then
|
||||
local subtree = tree:add(buffer(0),"FIDO2 Payload")
|
||||
local ctap_cmd = buffer(0,1):uint()
|
||||
local text = nil
|
||||
if is_request then
|
||||
text = CTAP_COMMAND_CODE[ctap_cmd]
|
||||
else
|
||||
text = CTAP_RESPONSE_CODE[ctap_cmd]
|
||||
end
|
||||
pinfo.cols.protocol = "CTAP " .. text
|
||||
subtree:add(buffer(0,1),string.format('CTAP CMD/Status: %s (0x%02x)', text, ctap_cmd))
|
||||
if buffer(1):len() > 0 then
|
||||
cbor:call(buffer(1):tvb(), pinfo, subtree)
|
||||
end
|
||||
elseif cmd == CTAPHID_COMMANDS.CTAPHID_INIT then
|
||||
elseif cmd == CTAPHID_COMMANDS.CTAPHID_PING then
|
||||
elseif cmd == CTAPHID_COMMANDS.CTAPHID_CANCEL then
|
||||
elseif cmd == CTAPHID_COMMANDS.CTAPHID_ERROR then
|
||||
elseif cmd == CTAPHID_COMMANDS.CTAPHID_KEEPALIVE then
|
||||
elseif cmd == CTAPHID_COMMANDS.CTAPHID_WINK then
|
||||
elseif cmd == CTAPHID_COMMANDS.CTAPHID_LOCK then
|
||||
elseif cmd >= CTAPHID_COMMANDS.CTAPHID_VENDOR_FIRST and cmd <= CTAPHID_COMMANDS.CTAPHID_VENDOR_LAST then
|
||||
else
|
||||
tree:add(ctaphidfield_data, buffer(0)):prepend_text("Unknown payload ")
|
||||
end
|
||||
end
|
||||
|
||||
function u2f_command_label(cmd, abbrev)
|
||||
if abbrev ~= true then
|
||||
abbrev = false
|
||||
end
|
||||
local command_string = U2F_INS_STRINGS[cmd]
|
||||
if command_string ~= nil and not abbrev then
|
||||
command_string = command_string .. string.format(" (0x%02x)", cmd)
|
||||
elseif command_string == nil then
|
||||
command_string = string.format("0x%02x", cmd)
|
||||
end
|
||||
return command_string
|
||||
end
|
||||
|
||||
function u2f_p1_label(p1, abbrev)
|
||||
if abbrev ~= true then
|
||||
abbrev = false
|
||||
end
|
||||
local p1_string = U2F_P1_STRINGS[p1]
|
||||
if p1_string ~= nil and not abbrev then
|
||||
p1_string = p1_string .. string.format(" (0x%02x)", p1)
|
||||
elseif p1_string == nil then
|
||||
p1_string = string.format("0x%02x", p1)
|
||||
end
|
||||
return p1_string
|
||||
end
|
||||
|
||||
function u2f_status_label(status, abbrev)
|
||||
if abbrev ~= true then
|
||||
abbrev = false
|
||||
end
|
||||
local status_string = U2F_STATUS_STRINGS[status]
|
||||
if status_string ~= nil and not abbrev then
|
||||
status_string = status_string .. string.format(" (0x%02x)", status)
|
||||
elseif status_string == nil then
|
||||
status_string = string.format("0x%02x", status)
|
||||
end
|
||||
return status_string
|
||||
end
|
||||
|
||||
function ctaphid_command_label(cmd)
|
||||
local command_string = CTAPHID_COMMAND_STRINGS[cmd]
|
||||
if command_string ~= nil then
|
||||
command_string = command_string .. string.format(" (0x%02x)", cmd)
|
||||
else
|
||||
command_string = string.format("0x%02x", cmd)
|
||||
if cmd >= CTAPHID_COMMANDS.CTAPHID_VENDOR_FIRST and cmd <= CTAPHID_COMMANDS.CTAPHID_VENDOR_LAST then
|
||||
command_string = command_string .. " [Vendor specific]"
|
||||
end
|
||||
end
|
||||
return command_string
|
||||
end
|
||||
|
||||
function channel_state_key(channel_id)
|
||||
local key = Struct.pack(">I2I2I1", field_usb_bus().value, field_usb_device().value, field_usb_endpoint().value) .. channel_id:bytes():raw()
|
||||
return Struct.tohex(key)
|
||||
end
|
||||
|
||||
packet_state = {} -- { packet_number => { cmd = uint, buffer = bytearray, complete = bool } }
|
||||
channel_state = {} -- { channel_state_key => { cmd = uint, payload_length = uint, buffer = bytearray } }
|
||||
|
||||
function dump(o)
|
||||
if type(o) == 'table' then
|
||||
local s = '{ '
|
||||
for k,v in pairs(o) do
|
||||
if type(k) ~= 'number' then k = '"'..k..'"' end
|
||||
s = s .. '['..k..'] = ' .. dump(v) .. ','
|
||||
end
|
||||
return s .. '} '
|
||||
else
|
||||
return tostring(o)
|
||||
end
|
||||
end
|
||||
|
||||
function u2f_proto.dissector(buffer,pinfo,tree)
|
||||
if buffer:len() == 0 then return end -- && usb.function == 0x0008 && select correct endpoint/etc.
|
||||
print("u2f_before", pinfo.curr_proto)
|
||||
pinfo.cols.protocol = u2f_proto.name -- FIXME why can't I filter against this?
|
||||
print("u2f_after", pinfo.curr_proto)
|
||||
local subtree = tree:add(ctaphid_proto,buffer(),"CTAP1/U2F")
|
||||
local is_request = (field_usb_endpointdir().value == 0)
|
||||
if is_request then -- this is a request
|
||||
local u2f_command = buffer(1,1):uint()
|
||||
local p1_value = buffer(2,1):uint()
|
||||
subtree:append_text(" Request")
|
||||
if u2f_command == 0x2 then
|
||||
pinfo.cols.info = "U2F Request (" .. u2f_command_label(u2f_command, true) .. ", " .. u2f_p1_label(p1_value, true) .. ")"
|
||||
else
|
||||
pinfo.cols.info = "U2F Request (" .. u2f_command_label(u2f_command, true) .. ")"
|
||||
end
|
||||
subtree:add(u2ffield_cla, buffer(0,1))
|
||||
subtree:add(u2ffield_ins, buffer(1,1), u2f_command, "Command: " .. u2f_command_label(u2f_command))
|
||||
if u2f_command == 0x2 then
|
||||
subtree:add(u2ffield_p1, buffer(2,1), p1_value, "U2F command parameter 1: " .. u2f_p1_label(p1_value))
|
||||
else
|
||||
subtree:add(u2ffield_p1, buffer(2,1))
|
||||
end
|
||||
subtree:add(u2ffield_p2, buffer(3,1))
|
||||
local request_length = buffer(4,3):uint()
|
||||
subtree:add(u2ffield_reqlen, buffer(4,3))
|
||||
subtree:add(u2ffield_reqdata, buffer(7, request_length))
|
||||
if buffer:len() > (request_length + 7) then
|
||||
subtree:add(u2ffield_expreslen, buffer(7 + request_length, buffer:len() - (request_length + 7)))
|
||||
end
|
||||
else -- response
|
||||
local u2f_status = buffer(buffer:len()-2,2):uint()
|
||||
subtree:append_text(" Response")
|
||||
pinfo.cols.info = "U2F Response (" .. u2f_status_label(u2f_status, true) .. ")"
|
||||
subtree:add(u2ffield_status, u2f_status, u2f_status, "Status: " .. u2f_status_label(u2f_status))
|
||||
if buffer:len() > 2 then
|
||||
subtree:add(u2ffield_respdata, buffer(0, buffer:len()-2))
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function ctaphid_proto.init()
|
||||
packet_state = {}
|
||||
channel_state = {}
|
||||
end
|
||||
|
||||
function ctaphid_proto.dissector(buffer,pinfo,tree)
|
||||
if buffer:len() == 0 then return end -- && usb.function == 0x0008 && select correct endpoint/etc.
|
||||
print("hid_before", pinfo.curr_proto)
|
||||
pinfo.cols.protocol = ctaphid_proto.name
|
||||
print("hid_after", pinfo.curr_proto)
|
||||
|
||||
local channel_id = buffer(0,4)
|
||||
local payload = nil
|
||||
local cmd_or_seq = buffer(4,1):uint()
|
||||
local is_init_packet = (bit.band(cmd_or_seq, 0x80) == 0x80)
|
||||
local cmd = nil
|
||||
local payload_length = nil
|
||||
local sequence = nil
|
||||
|
||||
-- extract relevant fields for each packet type
|
||||
if is_init_packet then
|
||||
cmd = bit.band(cmd_or_seq, 0x7f) -- ignore first bit of command field on initialization packets
|
||||
payload_length = buffer(5,2):uint()
|
||||
payload = buffer(7)
|
||||
else
|
||||
sequence = cmd_or_seq
|
||||
payload = buffer(5)
|
||||
end
|
||||
|
||||
-- keep track of state across packets to combine segmented packets
|
||||
local pstate = packet_state[pinfo.number]
|
||||
local cstate = nil
|
||||
if pstate == nil then
|
||||
pstate = {}
|
||||
cstate = channel_state[channel_state_key(channel_id)]
|
||||
if cstate == nil then
|
||||
assert(is_init_packet)
|
||||
cstate = {}
|
||||
cstate.buffer = payload:bytes()
|
||||
cstate.cmd = cmd
|
||||
cstate.payload_length = payload_length
|
||||
channel_state[channel_state_key(channel_id)] = cstate
|
||||
else
|
||||
cstate.buffer:append(payload:bytes())
|
||||
--buffer = ByteArray.tvb(cstate.buffer, "Command") -- create new tvb for packet
|
||||
end
|
||||
|
||||
if cstate.payload_length > cstate.buffer:len() then -- packet incomplete
|
||||
pstate.complete = false
|
||||
pstate.cmd = cstate.cmd
|
||||
else
|
||||
cstate.buffer:set_size(cstate.payload_length) -- usbpcap always returns full packets so we need to truncate them
|
||||
pstate.complete = true
|
||||
pstate.cmd = cstate.cmd
|
||||
pstate.buffer = cstate.buffer
|
||||
channel_state[channel_state_key(channel_id)] = nil
|
||||
end
|
||||
packet_state[pinfo.number] = pstate
|
||||
end
|
||||
|
||||
-- generate CTAPHID subtree
|
||||
local subtree = tree:add(ctaphid_proto,buffer())
|
||||
|
||||
if is_init_packet then
|
||||
local packet_text = "CTAPHID Initialization Packet"
|
||||
pinfo.cols.info = packet_text
|
||||
subtree:set_text(packet_text)
|
||||
subtree:add(ctaphidfield_cid, channel_id)
|
||||
subtree:add(ctaphidfield_cmd, buffer(4,1), cmd, "Command: " .. ctaphid_command_label(cmd))
|
||||
subtree:add(ctaphidfield_bcnt, buffer(5,2))
|
||||
subtree:add(ctaphidfield_data, payload)
|
||||
else
|
||||
local packet_text ="CTAPHID Continuation Packet"
|
||||
pinfo.cols.info = packet_text
|
||||
subtree:set_text(packet_text)
|
||||
subtree:add(ctaphidfield_cid, channel_id)
|
||||
subtree:add("Command: " .. ctaphid_command_label(pstate.cmd)):set_generated(true)
|
||||
subtree:add(ctaphidfield_seq, buffer(4,1))
|
||||
subtree:add(ctaphidfield_data, payload)
|
||||
end
|
||||
|
||||
if pstate.complete then
|
||||
dissect_ctaphid_payload(pstate.cmd, pstate.buffer:tvb("CTAPHID data"), pinfo, tree)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
usb_table = DissectorTable.get("usb.product")
|
||||
usb_table:add(0x10500407,ctaphid_proto) -- VID/PID of Yubikey
|
||||
usb_table:add(0x096e0858,ctaphid_proto) -- VID/PID of Feitian key
|
||||
usb_table:add_for_decode_as(u2f_proto)
|
||||
Reference in New Issue
Block a user