aboutsummaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
authorRalf Nasilowski <Ralf.Nasilowski@ise.de>2018-08-16 10:49:31 +0200
committerRoland Knall <rknall@gmail.com>2018-10-16 09:03:43 +0000
commit9769df50efd830254aef0310562cdf47edd4ada3 (patch)
tree4e0b386f619e5a85c44a13dac9cf00071bfece1c /test
parent84fd2d79682278927ce07361d901faed35bd1202 (diff)
KNX-IP: new KNXnet/IP dissector
The new KNXnet/IP dissector replaces the old KNXnet/IP dissector. The new KNXnet/IP dissector supports the new KNX features - A_MemoryExtended services - A_PropertyExt services - KNX Data Security - KNXnet/IP Core V2 - KNXnet/IP Device Management V2 - KNXnet/IP Tunneling V2 - KNXnet/IP Routing V2 - KNXnet/IP Security Change-Id: I3d1d716ef03d16d2720e6a1fcb23c2243d1cd956 Reviewed-on: https://code.wireshark.org/review/29155 Petri-Dish: Roland Knall <rknall@gmail.com> Tested-by: Petri Dish Buildbot Reviewed-by: Peter Wu <peter@lekensteyn.nl> Reviewed-by: Roland Knall <rknall@gmail.com>
Diffstat (limited to 'test')
-rw-r--r--test/captures/knxip_DataSec.pcapbin0 -> 125 bytes
-rw-r--r--test/captures/knxip_SecureWrapper.pcapbin0 -> 137 bytes
-rw-r--r--test/captures/knxip_TimerNotify.pcapbin0 -> 118 bytes
-rw-r--r--test/keys/knx_keyring.xml58
-rw-r--r--test/suite_decryption.py115
5 files changed, 173 insertions, 0 deletions
diff --git a/test/captures/knxip_DataSec.pcap b/test/captures/knxip_DataSec.pcap
new file mode 100644
index 0000000000..7e28cd40e5
--- /dev/null
+++ b/test/captures/knxip_DataSec.pcap
Binary files differ
diff --git a/test/captures/knxip_SecureWrapper.pcap b/test/captures/knxip_SecureWrapper.pcap
new file mode 100644
index 0000000000..0be64d8c1a
--- /dev/null
+++ b/test/captures/knxip_SecureWrapper.pcap
Binary files differ
diff --git a/test/captures/knxip_TimerNotify.pcap b/test/captures/knxip_TimerNotify.pcap
new file mode 100644
index 0000000000..4e7ae3781b
--- /dev/null
+++ b/test/captures/knxip_TimerNotify.pcap
Binary files differ
diff --git a/test/keys/knx_keyring.xml b/test/keys/knx_keyring.xml
new file mode 100644
index 0000000000..c9b3cdd299
--- /dev/null
+++ b/test/keys/knx_keyring.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- All attributes of the Keyring element are mandatory. For details on how to calculate/verify the Signature attribute, see below -->
+<!-- "oKGio6SlpqeoqaqrrK2urw==" = $ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF -->
+<!-- "sLGys7S1tre4ubq7vL2+vw==" = $ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF -->
+<Keyring xmlns="http://knx.org/xml/keyring/1" Project="Family Home" Created="2015-03-04T20:55:58.0160546Z" CreatedBy="ETS 5.5 Build 456" Signature="MTIzNDU2Nzg5MDEyMzQ1Ng==">
+ <!-- A backbone element is included if the project has a secure IP backbone or the keyring contains at least one IP Routing interface -->
+ <!-- The Latency and BackboneKey attributes are only included if the project has a secure IP backbone -->
+ <!-- BackboneKey := Base64( AES128-CBC( PBKDF2( HMAC-SHA256, KeyringPassword, "1.keyring.ets.knx.org", 65536, 128), MSB128(SHA256(this.Keyring.Created), Project.BackboneKey as byte[])) --> (1)
+ <Backbone MulticastAddress="224.0.23.12" Latency="1000" Key="oKGio6SlpqeoqaqrrK2urw=="/>
+ <!-- There might be 0..n interface elements. Possible types are "USB", "Tunneling" or "Routing". There is no support for "Eiblib/IP". -->
+ <!-- Depending on the type of interface different interface attributes are possible. -->
+ <!-- USB interfaces only appear in the keying if used for Data Security communication. They only have the mandatory attribute "IndividualAddress". -->
+ <Interface Type="USB" IndividualAddress="2.1.56">
+ <!-- Interfaces used for Data Security communication have a list of group addresses which will be communicated over this interface. -->
+ <!-- Each interface group has a mandatory list of trusted senders that are allowed to send telegrams to this group address. -->
+ <Group Address="3971" Senders="1.1.1 1.1.3 1.1.4"/>
+ <Group Address="3972" Senders="1.1.2 1.1.4"/>
+ <Group Address="14271" Senders="1.1.1"/>
+ </Interface>
+ <!-- Tunneling interfaces might appear in the keying because either they are used for Data Security communication or access to the interface itself is protected (or both). -->
+ <!-- KNXnet/IP Tunneling interfaces have as a mandatary attribute the KNX individual address of the host device. -->
+ <!-- If the Tunneling interface is used for Data Security communication, the attribute "IndividualAddress" is mandatory, otherwise it's optional. -->
+ <!-- Secured Tunneling interfaces need to have the "UserID" and "Password" attributes. An "Authentication" attribute containing the interface's device authenication code is optional. -->
+ <!-- Password := Base64( AES128-CBC( PBKDF2( HMAC-SHA256, KeyringPassword, "1.keyring.ets.knx.org", 65536, 128), MSB128(SHA256(this.Keyring.Created)), RandomBytes(8)+PKCS#7Padding( Device.BusAccess(IA).Password, 24) as byte[])) --> (1)
+ <!-- Authentication := Base64( AES128-CBC( PBKDF2( HMAC-SHA256, KeyringPassword, "1.keyring.ets.knx.org", 65536, 128), MSB128(SHA256(this.Keyring.Created)), RandomBytes(8)+PKCS#7Padding( Device.AthenticationCode, 24) as byte[])) --> (1)
+ <Interface Type="Tunneling" Host="2.1.0" IndividualAddress="2.1.56" UserID="3" Password="A3BzNDU2Nzg5MDEyMzQnUA3BzNDU2Nzg5MDEyMzQnUw=" Authentication="A3BzNDU2Nzg5MDEyMzQnUA3BzNDU2Nzg5MDEyMzQnUw="/>
+ <!-- If the Backbone (KNXnet/IP Routing) interface is used for Data Security communication, the attribute "IndividualAddress" is mandatory, otherwise it's optional. -->
+ <!-- If no "IndividualAddress" attribute is present, backbone interface elements shall be omitted altogether as all necessary information is part of the Backbone element already. -->
+ <Interface Type="Backbone" IndividualAddress="0.0.123">
+ <Group Address="256" Senders="1.1.1 1.1.3 1.1.4"/>
+ <Group Address="3972" Senders="1.1.2 1.1.4"/>
+ </Interface>
+ <!-- The "GroupAddresses" collection shall only be present if the keyring contains interface groups for Data Security communication. -->
+ <!-- The "Address" and "Key" attributes are mandatory for every "GroupAddresses/Group" element. -->
+ <!-- Key := Base64( AES128-CBC( PBKDF2( HMAC-SHA256, KeyringPassword, "1.keyring.ets.knx.org", 65536, 128), MSB128(SHA256(this.Keyring.Created)), GroupAddress.Key as byte[])) --> (1)
+ <GroupAddresses>
+ <Group Address="256" Key="oKGio6SlpqeoqaqrrK2urw=="/>
+ <Group Address="3971" Key="oKGio6SlpqeoqaqrrK2urw=="/>
+ <Group Address="3972" Key="oKGio6SlpqeoqaqrrK2urw=="/>
+ <Group Address="14271" Key="oKGio6SlpqeoqaqrrK2urw=="/>
+ </GroupAddresses>
+ <!-- Keyings exported for the whole project (for backup or diagnostic purposes) shall contain one "Device" entry for every secure device. -->
+ <!-- For interface keyings the "Devices" list is optional and shall only be present if sequence numbers are known for devices referenced as interface group senders. -->
+ <!-- The "ToolKey" attribute shall only be present if the keying was exported for the whole project. -->
+ <!-- The "SeqNr" attribute is optional and shall only be present if a received sequence number for a given device is known. -->
+ <!-- ToolKey := Base64( AES128-CBC( PBKDF2( HMAC-SHA256, KeyringPassword, "1.keyring.ets.knx.org", 65536, 128), MSB128(SHA256(this.Keyring.Created)), Device.ToolKey as byte[])) --> (1)
+ <!-- Secured IP-enabled devices need to have the "ManagementPassword" attribute. An "Authentication" attribute containing the device's authenication code is optional. -->
+ <!-- ManagementPassword := Base64( AES128-CBC( PBKDF2( HMAC-SHA256, KeyringPassword, "1.keyring.ets.knx.org", 65536, 128), MSB128(SHA256(this.Keyring.Created)), RandomBytes(8)+PKCS#7Padding( Device.ManagementPassword, 24) as byte[])) --> (1)
+ <!-- Authentication := Base64( AES128-CBC( PBKDF2( HMAC-SHA256, KeyringPassword, "1.keyring.ets.knx.org", 65536, 128), MSB128(SHA256(this.Keyring.Created)), RandomBytes(8)+PKCS#7Padding( Device.AthenticationCode, 24) as byte[])) --> (1)
+ <!-- The "ManagementPassword" and "Authentication" attributes shall only be present if the keying was exported for the whole project. -->
+ <Devices>
+ <Device IndividualAddress="1.1.1" ToolKey="sLGys7S1tre4ubq7vL2+vw==" SequenceNumber="45678"/>
+ <Device IndividualAddress="1.1.2" ToolKey="sLGys7S1tre4ubq7vL2+vw==" SequenceNumber="34567"/>
+ <Device IndividualAddress="1.1.3" ToolKey="sLGys7S1tre4ubq7vL2+vw==" SequenceNumber="23456"/>
+ <Device IndividualAddress="1.1.4" ToolKey="sLGys7S1tre4ubq7vL2+vw==" SequenceNumber="12345"/>
+ <Device IndividualAddress="2.1.0" ToolKey="sLGys7S1tre4ubq7vL2+vw==" SequenceNumber="1234" ManagementPassword="A3BzNDU2Nzg5MDEyMzQnUA3BzNDU2Nzg5MDEyMzQnUw=" Authentication="A3BzNDU2Nzg5MDEyMzQnUA3BzNDU2Nzg5MDEyMzQnUw="/>
+ </Devices>
+</Keyring> \ No newline at end of file
diff --git a/test/suite_decryption.py b/test/suite_decryption.py
index e393bf4d93..88e92438bb 100644
--- a/test/suite_decryption.py
+++ b/test/suite_decryption.py
@@ -760,3 +760,118 @@ class case_decrypt_wireguard(subprocesstest.SubprocessTestCase):
], pcap_file='wireguard-psk.pcap')
self.assertIn('2\t0', lines)
self.assertIn('4\t0', lines)
+
+class case_decrypt_knxip(subprocesstest.SubprocessTestCase):
+ # Capture files for these tests contain single telegrams.
+ # For realistic (live captured) KNX/IP telegram sequences, see:
+ # https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=14825
+
+ def test_knxip_data_security_decryption_ok(self):
+ '''KNX/IP: Data Security decryption OK'''
+ # capture_file contains KNX/IP ConfigReq DataSec PropExtValueWriteCon telegram
+ capture_file = os.path.join(config.capture_dir, 'knxip_DataSec.pcap')
+ self.runProcess((config.cmd_tshark,
+ '-r', capture_file,
+ '-o', 'kip.key_1:00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F',
+ ),
+ env=config.test_env)
+ self.assertTrue(self.grepOutput(' DataSec '))
+ self.assertTrue(self.grepOutput(' PropExtValueWriteCon '))
+
+ def test_knxip_data_security_decryption_fails(self):
+ '''KNX/IP: Data Security decryption fails'''
+ # capture_file contains KNX/IP ConfigReq DataSec PropExtValueWriteCon telegram
+ capture_file = os.path.join(config.capture_dir, 'knxip_DataSec.pcap')
+ self.runProcess((config.cmd_tshark,
+ '-r', capture_file,
+ '-o', 'kip.key_1:""', # "" is really necessary, otherwise test fails
+ ),
+ env=config.test_env)
+ self.assertTrue(self.grepOutput(' DataSec '))
+ self.assertFalse(self.grepOutput(' PropExtValueWriteCon '))
+
+ def test_knxip_secure_wrapper_decryption_ok(self):
+ '''KNX/IP: SecureWrapper decryption OK'''
+ # capture_file contains KNX/IP SecureWrapper RoutingInd telegram
+ capture_file = os.path.join(config.capture_dir, 'knxip_SecureWrapper.pcap')
+ self.runProcess((config.cmd_tshark,
+ '-r', capture_file,
+ '-o', 'kip.key_1:00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F',
+ ),
+ env=config.test_env)
+ self.assertTrue(self.grepOutput(' SecureWrapper '))
+ self.assertTrue(self.grepOutput(' RoutingInd '))
+
+ def test_knxip_secure_wrapper_decryption_fails(self):
+ '''KNX/IP: SecureWrapper decryption fails'''
+ # capture_file contains KNX/IP SecureWrapper RoutingInd telegram
+ capture_file = os.path.join(config.capture_dir, 'knxip_SecureWrapper.pcap')
+ self.runProcess((config.cmd_tshark,
+ '-r', capture_file,
+ '-o', 'kip.key_1:""', # "" is really necessary, otherwise test fails
+ ),
+ env=config.test_env)
+ self.assertTrue(self.grepOutput(' SecureWrapper '))
+ self.assertFalse(self.grepOutput(' RoutingInd '))
+
+ def test_knxip_timer_notify_authentication_ok(self):
+ '''KNX/IP: TimerNotify authentication OK'''
+ # capture_file contains KNX/IP TimerNotify telegram
+ capture_file = os.path.join(config.capture_dir, 'knxip_TimerNotify.pcap')
+ self.runProcess((config.cmd_tshark,
+ '-r', capture_file,
+ '-o', 'kip.key_1:00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F',
+ ),
+ env=config.test_env)
+ self.assertTrue(self.grepOutput(' TimerNotify '))
+ self.assertTrue(self.grepOutput(' OK$'))
+
+ def test_knxip_timer_notify_authentication_fails(self):
+ '''KNX/IP: TimerNotify authentication fails'''
+ # capture_file contains KNX/IP TimerNotify telegram
+ capture_file = os.path.join(config.capture_dir, 'knxip_TimerNotify.pcap')
+ self.runProcess((config.cmd_tshark,
+ '-r', capture_file,
+ '-o', 'kip.key_1:""', # "" is really necessary, otherwise test fails
+ ),
+ env=config.test_env)
+ self.assertTrue(self.grepOutput(' TimerNotify '))
+ self.assertFalse(self.grepOutput(' OK$'))
+
+ def test_knxip_keyring_xml_import(self):
+ '''KNX/IP: keyring.xml import'''
+ # key_file "keyring.xml" contains KNX decryption keys
+ key_file = os.path.join(config.key_dir, 'knx_keyring.xml')
+ # capture_file is empty
+ capture_file = os.path.join(config.capture_dir, 'empty.pcap')
+ # Write extracted key info to stdout
+ self.runProcess((config.cmd_tshark,
+ '-o', 'kip.key_file:' + key_file,
+ '-o', 'kip.key_info_file:-',
+ '-r', capture_file,
+ ),
+ env=config.test_env)
+ self.assertTrue(self.grepOutput('^MCA 224[.]0[.]23[.]12 key A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF$'))
+ self.assertTrue(self.grepOutput('^GA 1/7/131 sender 1[.]1[.]1$'))
+ self.assertTrue(self.grepOutput('^GA 1/7/131 sender 1[.]1[.]3$'))
+ self.assertTrue(self.grepOutput('^GA 1/7/131 sender 1[.]1[.]4$'))
+ self.assertTrue(self.grepOutput('^GA 1/7/132 sender 1[.]1[.]2$'))
+ self.assertTrue(self.grepOutput('^GA 1/7/132 sender 1[.]1[.]4$'))
+ self.assertTrue(self.grepOutput('^GA 6/7/191 sender 1[.]1[.]1$'))
+ self.assertTrue(self.grepOutput('^GA 0/1/0 sender 1[.]1[.]1$'))
+ self.assertTrue(self.grepOutput('^GA 0/1/0 sender 1[.]1[.]3$'))
+ self.assertTrue(self.grepOutput('^GA 0/1/0 sender 1[.]1[.]4$'))
+ self.assertTrue(self.grepOutput('^GA 0/1/0 key A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF$'))
+ self.assertTrue(self.grepOutput('^GA 1/7/131 key A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF$'))
+ self.assertTrue(self.grepOutput('^GA 1/7/132 key A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF$'))
+ self.assertTrue(self.grepOutput('^GA 6/7/191 key A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF$'))
+ self.assertTrue(self.grepOutput('^IA 1[.]1[.]1 key B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF$'))
+ self.assertTrue(self.grepOutput('^IA 1[.]1[.]1 SeqNr 45678$'))
+ self.assertTrue(self.grepOutput('^IA 1[.]1[.]2 key B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF$'))
+ self.assertTrue(self.grepOutput('^IA 1[.]1[.]2 SeqNr 34567$'))
+ self.assertTrue(self.grepOutput('^IA 1[.]1[.]3 key B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF$'))
+ self.assertTrue(self.grepOutput('^IA 1[.]1[.]3 SeqNr 23456$'))
+ self.assertTrue(self.grepOutput('^IA 1[.]1[.]4 key B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF$'))
+ self.assertTrue(self.grepOutput('^IA 1[.]1[.]4 SeqNr 12345$'))
+ self.assertTrue(self.grepOutput('^IA 2[.]1[.]0 key B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF$'))
+ self.assertTrue(self.grepOutput('^IA 2[.]1[.]0 SeqNr 1234$'))