Skip to content

Commit

Permalink
v1.4.0, CP State diagnostics, TlsSecret decryption updated
Browse files Browse the repository at this point in the history
* added dissector for homeplug-av cp state packets
* more reliable tls disclosure packets handling
* version 1.4.0
  • Loading branch information
TGruett authored May 31, 2024
1 parent 3fd5df0 commit 53be2a9
Show file tree
Hide file tree
Showing 13 changed files with 295 additions and 95 deletions.
Binary file added Images/HPAV_LLC_AC.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 15 additions & 8 deletions Installer/InstallerScript.iss
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// DO NOT CHANGE VERSION HERE! Run update_version.bat
#define AppVer "1.3.1"
#define AppVer "1.4.0"
#define AppId "dsV2Gshark"

[Setup]
Expand Down Expand Up @@ -43,18 +43,20 @@ Name: "plugin/decoder"; Description: "EXI decoder (powered by chargebyte cbexige
Name: "plugin/decoder/din"; Description: "DIN 70121 support"; Types: full custom; Flags: fixed
Name: "plugin/decoder/iso2"; Description: "ISO 15118-2 support"; Types: full custom; Flags: fixed
Name: "plugin/decoder/iso20"; Description: "ISO 15118-20 support (experimental)"; Types: full custom; Flags: fixed
Name: "plugin/autoschema"; Description: "Automatic schema detection"; Types: full custom; Flags: fixed
Name: "plugin/autoschema"; Description: "Automatic schema detection"; Types: full custom; Flags: fixed
Name: "plugin/autodecrypt"; Description: "Live TLS decryption with disclosed master secret from UDP packet"; Types: full custom;
Name: "plugin/llc_diagnostic"; Description: "Additional dissector for CP-State related Homeplug AV packets"; Types: full custom;
Name: "buttons"; Description: "Add filter buttons to Wireshark (current user only)"; Types: full
Name: "colorfilters"; Description: "Highlight V2G messages in Wireshark (current user only)"; Types: full
Name: "iograph"; Description: "Prepare Wireshark I/O Graphs for V2G messages{cm:Linebreak}(current user only, may override I/O Graph preferences)"; Types: full

[Files]
Source: "..\Wireshark\plugins\v2gshared.lua"; DestDir: "{app}\plugins"; Flags: ignoreversion recursesubdirs; Components: plugin/dissectors
Source: "..\Wireshark\plugins\v2gcommon.lua"; DestDir: "{app}\plugins"; Flags: ignoreversion recursesubdirs; Components: plugin/dissectors
Source: "..\Wireshark\plugins\v2gmsg.lua"; DestDir: "{app}\plugins"; Flags: ignoreversion recursesubdirs; Components: plugin/dissectors
Source: "..\Wireshark\plugins\v2gtp.lua"; DestDir: "{app}\plugins"; Flags: ignoreversion recursesubdirs; Components: plugin/dissectors
Source: "..\Wireshark\plugins\v2gsdp.lua"; DestDir: "{app}\plugins"; Flags: ignoreversion recursesubdirs; Components: plugin/dissectors
Source: "..\Wireshark\plugins\v2gtlssecret.lua"; DestDir: "{app}\plugins"; Flags: ignoreversion recursesubdirs; Components: plugin/autodecrypt
Source: "..\Wireshark\plugins\v2gllc.lua"; DestDir: "{app}\plugins"; Flags: ignoreversion recursesubdirs; Components: plugin/llc_diagnostic
Source: "..\Wireshark\*.dll"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: plugin/decoder
Source: "..\LICENSE"; DestDir: "{app}"; DestName: "dsV2Gshark_LICENSE.txt"; Flags: ignoreversion;
Source: "..\OSSAcknowledgements.txt"; DestDir: "{app}"; DestName: "dsV2Gshark_OSSAcknowledgements.txt"; Flags: ignoreversion recursesubdirs;
Expand All @@ -64,6 +66,7 @@ Source: "dsV2Gshark_README.txt"; DestDir: "{app}"; DestName: "dsV2Gshark_README.
Type: filesandordirs; Name: "{app}\luaV2Gdecoder.dll"
Type: filesandordirs; Name: "{app}\X509CertInfos.dll"
Type: filesandordirs; Name: "{app}\plugins\v2gmsg_generic.lua"
Type: filesandordirs; Name: "{app}\plugins\v2gshared.lua"

[Code]
function HasWriteAccessToApp: Boolean;
Expand Down Expand Up @@ -212,10 +215,10 @@ var
i: Integer;
begin
// check version of lua files
StringVersionPrefix := 'v2gshared.DS_V2GSHARK_VERSION = "';
StringVersionPrefix := 'v2gcommon.DS_V2GSHARK_VERSION = "';
stringVersionSuffix := '" -- DO NOT CHANGE';
ExtractTemporaryFile('v2gshared.lua');
if LoadStringsFromFile(ExpandConstant('{tmp}\v2gshared.lua'), Lines) then
ExtractTemporaryFile('v2gcommon.lua');
if LoadStringsFromFile(ExpandConstant('{tmp}\v2gcommon.lua'), Lines) then
begin
for i := 0 to GetArrayLength(Lines) - 1 do
begin
Expand Down Expand Up @@ -271,7 +274,7 @@ procedure CurStepChanged(CurStep: TSetupStep);
var
FileName: string;
Lines: TArrayOfString;
StringsToCheck, StringsToAdd: TArrayOfString;
StringsToCheck, StringsToAdd, StringsToRemove: TArrayOfString;
begin
// add wireshark filter buttons after installation
if (CurStep = ssPostInstall) then
Expand Down Expand Up @@ -335,9 +338,13 @@ begin
'"Disabled","[ISO20] Max Current EV","","#2E3436","Line","AVG(Y Field)","v2gmsg.xml.iograph.EVMaximumChargeCurrent","None","1"',
'"Disabled","[ISO20] Max Voltage EVSE","","#2E3436","Line","AVG(Y Field)","v2gmsg.xml.iograph.EVSEMaximumVoltage","None","1"',
'"Disabled","[ISO20] Max Current EVSE","","#2E3436","Line","AVG(Y Field)","v2gmsg.xml.iograph.EVSEMaximumChargeCurrent","None","1"',
'"Disabled","CP State","","#2E3436","Dot","AVG(Y Field)","homeplug_av.st_iotecha.cpstate.state","None","20"'];
'"Disabled","CP State","","#2E3436","Dot","AVG(Y Field)","homeplug-av-llc.cpstate","None","20"',
'"Disabled","(disabled_filter_button1)","v2gtp or v2gtlssecret or tls.handshake or tls.alert_message or tls.change_cipher_spec or tcp.flags.syn == 1 or tcp.flags.fin == 1 or homeplug or homeplug-av ","#FFFFFF","Line","Packets","","None","1"',
'"Disabled","(disabled_filter_button2)","v2gtp or v2gtlssecret","#FFFFFF","Line","Packets","","None","1"'];
StringsToRemove := ['"Disabled","CP State","","#2E3436","Dot","AVG(Y Field)","homeplug_av.st_iotecha.cpstate.state","None","20"'];
if FileExists(FileName) then
begin
RemoveFromFile(FileName, StringsToRemove);
if not PrependStringsToFile(FileName, StringsToAdd) then
MsgBox('Failed to add I/O Graph presets to Wireshark!', mbError, MB_OK);
end
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ This Wireshark plugin allows to analyze and decode packets between electric vehi
- Validation of V2G messages according to XSD specification
- Certificate information details for Plug & Charge (PnC)
- Live TLS decryption
- LLC diagnostics via HomePlug AV packets for sniffer and debug packets
- Automatic schema detection
- Detect schema automatically in case of missing SDP or SAP
- Color filter for V2G packets
Expand Down Expand Up @@ -91,3 +92,5 @@ Click on a packet in the graph to inspect it in the Wireshark main window. Press
![Plugin Preferences](Images/WS_Preferences.png)
### Wireshark I/O Graph
![I/O Graph](Images/IO_Graph.png)
### HomePlug AV LLC Diagnostics
![I/O Graph](Images/HPAV_LLC_AC.png)
8 changes: 4 additions & 4 deletions V2G_Libraries/CertificateInfos/main.rc
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
#include <windows.h>

#define VER_FILEVERSION 1,3,1,0
#define VER_FILEVERSION_STR "1.3.1.0\0"
#define VER_FILEVERSION 1,4,0,0
#define VER_FILEVERSION_STR "1.4.0.0\0"
#define VER_COMPANYNAME_STR "dSPACE GmbH"
#define VER_PRODUCTNAME_STR "V2gCertificateInfos"
#define VER_PRODUCTVERSION 1,3,1,0
#define VER_PRODUCTVERSION_STR "1.3.1.0\0"
#define VER_PRODUCTVERSION 1,4,0,0
#define VER_PRODUCTVERSION_STR "1.4.0.0\0"

VS_VERSION_INFO VERSIONINFO
FILEVERSION VER_FILEVERSION
Expand Down
8 changes: 4 additions & 4 deletions V2G_Libraries/V2GDecoder/main.rc
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
#include <windows.h>

#define VER_FILEVERSION 1,3,1,0
#define VER_FILEVERSION_STR "1.3.1.0\0"
#define VER_FILEVERSION 1,4,0,0
#define VER_FILEVERSION_STR "1.4.0.0\0"
#define VER_COMPANYNAME_STR "dSPACE GmbH"
#define VER_PRODUCTNAME_STR "V2gDecoder"
#define VER_PRODUCTVERSION 1,3,1,0
#define VER_PRODUCTVERSION_STR "1.3.1.0\0"
#define VER_PRODUCTVERSION 1,4,0,0
#define VER_PRODUCTVERSION_STR "1.4.0.0\0"

VS_VERSION_INFO VERSIONINFO
FILEVERSION VER_FILEVERSION
Expand Down
33 changes: 33 additions & 0 deletions Wireshark/plugins/v2gcommon.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
--
-- Copyright 2024, dSPACE GmbH. All rights reserved.
--
-- See license file (dsV2Gshark_LICENSE.txt)
--

-- do OS specific stuff, required to properly load v2g libs
local plugins_path -- path to the plugins directory of this script
local lib_pattern
if package.config:sub(1, 1) == "\\" then
-- WINDOWS
plugins_path = debug.getinfo(1, "S").source:sub(2):match("(.*[/\\])") or "./"
lib_pattern = "?.dll"
else
-- UNIX
plugins_path = debug.getinfo(1, "S").source:sub(2):match("(.*/)") or "./"
lib_pattern = "?.so"
end
local wireshark_path = plugins_path .. "../"
if not string.find(plugins_path, package.path) then
-- extend path (where to load .lua files)
package.path = package.path .. ";" .. plugins_path .. "?.lua"
end
if not string.find(wireshark_path, package.cpath) then
-- extend cpath (where to load .so files)
package.cpath = package.cpath .. ";" .. wireshark_path .. lib_pattern .. ";" .. plugins_path .. lib_pattern
end

local v2gcommon = {}

v2gcommon.DS_V2GSHARK_VERSION = "1.4.0" -- DO NOT CHANGE

return v2gcommon
183 changes: 183 additions & 0 deletions Wireshark/plugins/v2gllc.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
--
-- Copyright 2024, dSPACE GmbH. All rights reserved.
--
-- This dissector adds functionality to the standard HomePlug AV dissector
-- to allow displaying CP State indication packets
--
-- See license file (dsV2Gshark_LICENSE.txt)
--
local v2gcommon = require("v2gcommon")

p_hpav_llc = Proto("homeplug-av-llc", "HomePlug AV protocol LLC diagnostics")
local p_hpav_llc_info = {
version = v2gcommon.DS_V2GSHARK_VERSION,
author = "dSPACE GmbH",
repository = "https://github.com/dspace-group/dsV2Gshark"
}
set_plugin_info(p_hpav_llc_info)

local f_freq = ProtoField.int16("homeplug-av-llc.freq", "Frequency", base.DEC)
local f_dutycycle = ProtoField.float("homeplug-av-llc.dutycycle", "Duty cycle", base.DEC)
local f_voltage = ProtoField.float("homeplug-av-llc.voltage", "Voltage", base.DEC)
local f_cpstate = ProtoField.int8("homeplug-av-llc.cpstate", "CP State")
local f_acmax = ProtoField.string("homeplug-av-llc.ac_max", "AC max current")
local f_result = ProtoField.string("homeplug-av-llc.result", "Result")

p_hpav_llc.fields = {f_freq, f_dutycycle, f_voltage, f_cpstate, f_acmax, f_result}

local function extract_infos_spidcom(buf)
local freq = buf(9, 2):le_int()
local dutycycle = buf(11, 2):le_int() / 10
local voltage = buf(13, 2):le_int() / 1000
local result = buf(8, 1):int()
return freq, dutycycle, voltage, result
end

local function extract_infos_iotecha(buf)
local freq = buf(15, 2):le_int()
local dutycycle = buf(14, 1):le_int()
local voltage = buf(17, 2):le_int() / 1000
return freq, dutycycle, voltage, -1
end

local function get_ac_max_current(dutycycle)
if dutycycle < 8 then
return -1
elseif dutycycle < 10 then
return 6
elseif dutycycle < 85 then
return dutycycle * 0.6
elseif dutycycle < 96 then
return (dutycycle - 64) * 2.5
elseif dutycycle < 97 then
return 80
else
return -1
end
end

local function get_cp_state(freq, dutycycle, voltage)
local StateA = 12
local StateB = 9
local StateC = 6
local StateD = 3
local StateEF = 0
local Tolerance = 1

if (dutycycle == 0 or dutycycle == 100) and freq > 0 then
return "-"
end

local cp_state_out
if voltage <= (StateA + Tolerance) and voltage >= (StateA - Tolerance) then
cp_state_out = "A"
elseif voltage <= (StateB + Tolerance) and voltage >= (StateB - Tolerance) then
cp_state_out = "B"
elseif voltage <= (StateC + Tolerance) and voltage >= (StateC - Tolerance) then
cp_state_out = "C"
elseif voltage <= (StateD + Tolerance) and voltage >= (StateD - Tolerance) then
cp_state_out = "D"
elseif voltage <= (StateEF + Tolerance) then
return "E/F"
else
return "-"
end

local pwm_active = dutycycle > 0 and dutycycle < 100 and freq > 950 and freq < 1050
if pwm_active then
return cp_state_out .. "2"
else
return cp_state_out .. "1"
end
end

local function cp_state_to_int(cp_state)
--- used for I/O Graph plotting of CP State
if cp_state == "A1" then
return 1
elseif cp_state == "A2" then
return 2
elseif cp_state == "B1" then
return 3
elseif cp_state == "B2" then
return 4
elseif cp_state == "C1" then
return 5
elseif cp_state == "C2" then
return 6
elseif cp_state == "D1" then
return 7
elseif cp_state == "D2" then
return 8
elseif cp_state == "E/F" then
return -1
end
return 0
end

function p_hpav_llc.dissector(buf, pinfo, root)
if buf:len() == 0 then
return 0
end

-- always call default homeplug-av dissector first
local hpav_dissector = Dissector.get("homeplug-av")
local consumed_bytes_hpav
if hpav_dissector ~= nil then
consumed_bytes_hpav = hpav_dissector:call(buf, pinfo, root)
else
-- some older wireshark versions does not have a homeplug-av dissector
consumed_bytes_hpav = Dissector.get("data"):call(buf, pinfo, root)
pinfo.cols.info = "Homeplug AV"
end

local mac_mme_type = buf(1, 2):le_uint()
local mme_vendor = buf(5, 3):uint()
local freq, dutycycle, voltage, result
if mme_vendor == 0x0013D7 and mac_mme_type == 0xA10E then -- Vendor OUI: SPIDCOM Technologies SA
freq, dutycycle, voltage, result = extract_infos_spidcom(buf)
elseif mme_vendor == 0x0080E1 and mac_mme_type == 0xA22E then -- Vendor OUI: ST/IoTecha
freq, dutycycle, voltage, result = extract_infos_iotecha(buf)
else
-- default homeplug-av packet
return consumed_bytes_hpav
end

pinfo.cols.protocol = "HomePlug AV LLC"
local subtree = root:add(p_hpav_llc, buf(0))

local elem_frequency = subtree:add(f_freq, freq)
elem_frequency:append_text("Hz")
-- frequency bitmask: 001
if result == 1 or result == 3 or result == 5 or result == 7 then
elem_frequency:append_text(" (changed)")
end

local elem_dutycycle = subtree:add(f_dutycycle, dutycycle)
elem_dutycycle:append_text("%")
-- duty cycle bitmask: 010
if result == 2 or result == 3 or result == 6 or result == 7 then
elem_dutycycle:append_text(" (changed)")
end

local elem_voltage = subtree:add(f_voltage, voltage)
elem_voltage:append_text("V")
-- voltage bitmask: 100
if result == 4 or result == 5 or result == 6 or result == 7 then
elem_voltage:append_text(" (changed)")
end

local cp_state = get_cp_state(freq, dutycycle, voltage)
subtree:add(f_cpstate, cp_state_to_int(cp_state)):set_text("CP State: " .. cp_state) -- store as int to allow plotting

local ac_max_current = get_ac_max_current(dutycycle)
if ac_max_current ~= -1 then
subtree:add(f_acmax, ac_max_current):append_text("A")
pinfo.cols.info = "CP State: " .. cp_state .. " [AC max current: " .. ac_max_current .. "A]"
else
pinfo.cols.info = "CP State: " .. cp_state
end
return buf:len()
end

DissectorTable.get("ethertype"):add(0x88e1, p_hpav_llc)
Loading

0 comments on commit 53be2a9

Please sign in to comment.