diff --git a/scapy/fields.py b/scapy/fields.py index 0a92c3f5e5290c7c5a9ff2b09f0765a30b173620..0dbe6910eba412c44c8f5ead1712f173fc499e3b 100644 --- a/scapy/fields.py +++ b/scapy/fields.py @@ -554,6 +554,30 @@ class StrLenField(StrField): l = self.length_from(pkt) return s[l:], self.m2i(pkt,s[:l]) +class XStrField(StrField): + """ + StrField which value is printed as hexadecimal. + """ + + def i2repr(self, pkt, x): + return x.encode("hex") + +class XStrLenField(StrLenField): + """ + StrLenField which value is printed as hexadecimal. + """ + + def i2repr(self, pkt, x): + return x[:self.length_from(pkt)].encode("hex") + +class XStrFixedLenField(StrFixedLenField): + """ + StrFixedLenField which value is printed as hexadecimal. + """ + + def i2repr(self, pkt, x): + return x[:self.length_from(pkt)].encode("hex") + class StrLenFieldUtf16(StrLenField): def h2i(self, pkt, x): return x.encode('utf-16')[2:] diff --git a/scapy/layers/l2.py b/scapy/layers/l2.py index b26ff106b90580cb5853a282aaec881cad4f5684..40f44518b641cde92fae5af56dbf31d214170a55 100644 --- a/scapy/layers/l2.py +++ b/scapy/layers/l2.py @@ -543,6 +543,366 @@ class EAP_FAST(Packet): ] +############################################################################# +##### IEEE 802.1X-2010 - MACsec Key Agreement (MKA) protocol +############################################################################# + +#________________________________________________________________________ +# +# IEEE 802.1X-2010 standard +# Section 11.11.1 +#________________________________________________________________________ +# + +_parameter_set_types = { + 1: "Live Peer List", + 2: "Potential Peer List", + 3: "MACsec SAK Use", + 4: "Distributed SAK", + 5: "Distributed CAK", + 6: "KMD", + 7: "Announcement", + 255: "ICV Indicator" +} + + +# Used by MKAParamSet::dispatch_hook() to instantiate the appropriate class +_param_set_cls = { + 1: "MKALivePeerListParamSet", + 2: "MKAPotentialPeerListParamSet", + 3: "MKASAKUseParamSet", + 4: "MKADistributedSAKParamSet", + 255: "MKAICVSet", +} + + +class MACsecSCI(Packet): + """ + Secure Channel Identifier. + """ + + #________________________________________________________________________ + # + # IEEE 802.1AE-2006 standard + # Section 9.9 + #________________________________________________________________________ + # + + name = "SCI" + fields_desc = [ + SourceMACField("system_identifier"), + ShortField("port_identifier", 0) + ] + + def extract_padding(self, s): + return "", s + + +class MKAParamSet(Packet): + """ + Class from which every parameter set class inherits (except + MKABasicParamSet, which has no "Parameter set type" field, and must + come first in the list of parameter sets). + """ + + MACSEC_DEFAULT_ICV_LEN = 16 + EAPOL_MKA_DEFAULT_KEY_WRAP_LEN = 24 + + @classmethod + def dispatch_hook(cls, _pkt=None, *args, **kargs): + """ + Returns the right parameter set class. + """ + + cls = conf.raw_layer + if _pkt is not None: + ptype = struct.unpack("!B", _pkt[0])[0] + return globals().get(_param_set_cls.get(ptype), conf.raw_layer) + + return cls + + +class MKABasicParamSet(Packet): + """ + Basic Parameter Set (802.1X-2010, section 11.11). + """ + + #________________________________________________________________________ + # + # IEEE 802.1X-2010 standard + # Section 11.11 + #________________________________________________________________________ + # + + name = "Basic Parameter Set" + fields_desc = [ + ByteField("mka_version_id", 0), + ByteField("key_server_priority", 0), + BitField("key_server", 0, 1), + BitField("macsec_desired", 0, 1), + BitField("macsec_capability", 0, 2), + BitField("param_set_body_len", 0, 12), + PacketField("SCI", MACsecSCI(), MACsecSCI), + XStrFixedLenField("actor_member_id", "", length=12), + XIntField("actor_message_number", 0), + XIntField("algorithm_agility", 0), + PadField( + XStrLenField( + "cak_name", + "", + length_from=lambda pkt: (pkt.param_set_body_len - 28) + ), + 4, + padwith="\x00" + ) + ] + + def extract_padding(self, s): + return "", s + + +class MKAPeerListTuple(Packet): + """ + Live / Potential Peer List parameter sets tuples (802.1X-2010, section 11.11). + """ + + name = "Peer List Tuple" + fields_desc = [ + XStrFixedLenField("member_id", "", length=12), + XStrFixedLenField("message_number", "", length=4), + ] + + +class MKALivePeerListParamSet(MKAParamSet): + """ + Live Peer List parameter sets (802.1X-2010, section 11.11). + """ + + #________________________________________________________________________ + # + # IEEE 802.1X-2010 standard + # Section 11.11 + #________________________________________________________________________ + # + + name = "Live Peer List Parameter Set" + fields_desc = [ + PadField( + ByteEnumField( + "param_set_type", + 1, + _parameter_set_types + ), + 2, + padwith="\x00" + ), + ShortField("param_set_body_len", 0), + PacketListField("member_id_message_num", [], MKAPeerListTuple) + ] + + +class MKAPotentialPeerListParamSet(MKAParamSet): + """ + Potential Peer List parameter sets (802.1X-2010, section 11.11). + """ + + #________________________________________________________________________ + # + # IEEE 802.1X-2010 standard + # Section 11.11 + #________________________________________________________________________ + # + + name = "Potential Peer List Parameter Set" + fields_desc = [ + PadField( + ByteEnumField( + "param_set_type", + 2, + _parameter_set_types + ), + 2, + padwith="\x00" + ), + ShortField("param_set_body_len", 0), + PacketListField("member_id_message_num", [], MKAPeerListTuple) + ] + + +class MKASAKUseParamSet(MKAParamSet): + """ + SAK Use Parameter Set (802.1X-2010, section 11.11). + """ + + #________________________________________________________________________ + # + # IEEE 802.1X-2010 standard + # Section 11.11 + #________________________________________________________________________ + # + + name = "SAK Use Parameter Set" + fields_desc = [ + ByteEnumField("param_set_type", 3, _parameter_set_types), + BitField("latest_key_an", 0, 2), + BitField("latest_key_tx", 0, 1), + BitField("latest_key_rx", 0, 1), + BitField("old_key_an", 0, 2), + BitField("old_key_tx", 0, 1), + BitField("old_key_rx", 0, 1), + BitField("plain_tx", 0, 1), + BitField("plain_rx", 0, 1), + BitField("X", 0, 1), + BitField("delay_protect", 0, 1), + BitField("param_set_body_len", 0, 12), + XStrFixedLenField("latest_key_key_server_member_id", "", length=12), + XStrFixedLenField("latest_key_key_number", "", length=4), + XStrFixedLenField("latest_key_lowest_acceptable_pn", "", length=4), + XStrFixedLenField("old_key_key_server_member_id", "", length=12), + XStrFixedLenField("old_key_key_number", "", length=4), + XStrFixedLenField("old_key_lowest_acceptable_pn", "", length=4) + ] + + +class MKADistributedSAKParamSet(MKAParamSet): + """ + Distributed SAK parameter set (802.1X-2010, section 11.11). + """ + + #________________________________________________________________________ + # + # IEEE 802.1X-2010 standard + # Section 11.11 + #________________________________________________________________________ + # + + name = "Distributed SAK parameter set" + fields_desc = [ + ByteEnumField("param_set_type", 4, _parameter_set_types), + BitField("distributed_an", 0, 2), + BitField("confidentiality_offset", 0, 2), + BitField("unused", 0, 4), + ShortField("param_set_body_len", 0), + XStrFixedLenField("key_number", "", length=4), + ConditionalField( + XStrFixedLenField("macsec_cipher_suite", "", length=8), + lambda pkt: pkt.param_set_body_len > 28 + ), + XStrFixedLenField( + "sak_aes_key_wrap", + "", + length=MKAParamSet.EAPOL_MKA_DEFAULT_KEY_WRAP_LEN + ) + ] + + +class MKADistributedCAKParamSet(MKAParamSet): + """ + Distributed CAK Parameter Set (802.1X-2010, section 11.11). + """ + + #________________________________________________________________________ + # + # IEEE 802.1X-2010 standard + # Section 11.11 + #________________________________________________________________________ + # + + name = "Distributed CAK parameter set" + fields_desc = [ + PadField( + ByteEnumField( + "param_set_type", + 5, + _parameter_set_types + ), + 2, + padwith="\x00" + ), + ShortField("param_set_body_len", 0), + XStrFixedLenField( + "cak_aes_key_wrap", + "", + length=MKAParamSet.EAPOL_MKA_DEFAULT_KEY_WRAP_LEN + ), + XStrField("cak_key_name", "") + ] + + +class MKAICVSet(MKAParamSet): + """ + ICV (802.1X-2010, section 11.11). + """ + + #________________________________________________________________________ + # + # IEEE 802.1X-2010 standard + # Section 11.11 + #________________________________________________________________________ + # + + name = "ICV" + fields_desc = [ + PadField( + ByteEnumField( + "param_set_type", + 255, + _parameter_set_types + ), + 2, + padwith="\x00" + ), + ShortField("param_set_body_len", 0), + XStrFixedLenField("icv", "", length=MKAParamSet.MACSEC_DEFAULT_ICV_LEN) + ] + + +class MKAParamSetPacketListField(PacketListField): + """ + PacketListField that handles the parameter sets. + """ + + PARAM_SET_LEN_MASK = 0b0000111111111111 + + def m2i(self, pkt, m): + return MKAParamSet(m) + + def getfield(self, pkt, s): + lst = [] + remain = s + + while remain: + len_bytes = struct.unpack("!H", remain[2:4])[0] + param_set_len = self.__class__.PARAM_SET_LEN_MASK & len_bytes + current = remain[:4 + param_set_len] + remain = remain[4 + param_set_len:] + current_packet = self.m2i(pkt, current) + lst.append(current_packet) + + return remain, lst + + +class MKAPDU(Packet): + """ + MACsec Key Agreement Protocol Data Unit. + """ + + #________________________________________________________________________ + # + # IEEE 802.1X-2010 standard + # Section 11.11 + #________________________________________________________________________ + # + + name = "MKPDU" + fields_desc = [ + PacketField("basic_param_set", "", MKABasicParamSet), + MKAParamSetPacketListField("parameter_sets", [], MKAParamSet), + ] + + def extract_padding(self, s): + return "", s + class ARP(Packet): name = "ARP" @@ -668,9 +1028,10 @@ bind_layers( GRE, GRErouting, { "routing_present" : 1 } ) bind_layers( GRErouting, conf.raw_layer,{ "address_family" : 0, "SRE_len" : 0 }) bind_layers( GRErouting, GRErouting, { } ) bind_layers( EAPOL, EAP, type=0) -bind_layers( EAP, EAP_MD5, type=4) +bind_layers( EAPOL, MKAPDU, type=5) bind_layers(EAP, EAP_TLS, type=13) bind_layers(EAP, EAP_FAST, type=43) +bind_layers( EAP, EAP_MD5, type=4) bind_layers( LLC, STP, dsap=66, ssap=66, ctrl=3) bind_layers( LLC, SNAP, dsap=170, ssap=170, ctrl=3) bind_layers( SNAP, Dot1Q, code=33024) diff --git a/test/regression.uts b/test/regression.uts index b6fd6f085f4da75dd96bd65ebda4a6aecc6d37f8..a6ca36aedc5e66e8bfc646e4a2fe9d99f380b85e 100644 --- a/test/regression.uts +++ b/test/regression.uts @@ -5597,6 +5597,87 @@ assert(eapol.len == 60) assert(eapol.haslayer(EAP_FAST)) +############ +############ ++ EAPOL-MKA class tests + += EAPOL-MKA - With Basic parameter set - Dissection +eapol = None +s = '\x03\x05\x00T\x01\xff\xf0<\x00Bh\xa8\x1e\x03\x00\n\xbcj\x00\x96Ywz\x82:\x90\xd9\xe7\x00\x00\x00\x01\x00\x80\xc2\x01\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\xff\x00\x00\x10\xe5\xf5j\x86V\\\xb1\xcc\xa9\xb95\x04m*Cj' +eapol = EAPOL(s) +assert(eapol.version == 3) +assert(eapol.type == 5) +assert(eapol.len == 84) +assert(eapol.haslayer(MKAPDU)) +assert(eapol[MKAPDU].basic_param_set.actor_member_id == "\xbcj\x00\x96Ywz\x82:\x90\xd9\xe7") +assert(eapol[MKAPDU].haslayer(MKAICVSet)) +assert(eapol[MKAPDU][MKAICVSet].icv == "\xe5\xf5j\x86V\\\xb1\xcc\xa9\xb95\x04m*Cj") + + += EAPOL-MKA - With Potential Peer List parameter set - Dissection +eapol = None +s = '\x03\x05\x00h\x01\x10\xe0<\xccN$\xc4\xf7\x7f\x00\x80q\x8b\x8a9\x86k/X\x14\xc9\xdc\xf6\x00\x00\x00}\x00\x80\xc2\x01\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x02\x00\x00\x10\xbcj\x00\x96Ywz\x82:\x90\xd9\xe7\x00\x00\x00\x01\xff\x00\x00\x105\x01\xdc)\xfd\xd1\xff\xd55\x9c_o\xc9\x9c\xca\xc0' +eapol = EAPOL(s) +assert(eapol.version == 3) +assert(eapol.type == 5) +assert(eapol.len == 104) +assert(eapol.haslayer(MKAPDU)) +assert(eapol[MKAPDU].basic_param_set.actor_member_id == "q\x8b\x8a9\x86k/X\x14\xc9\xdc\xf6") +assert(eapol.haslayer(MKAPotentialPeerListParamSet)) +assert(eapol[MKAPDU][MKAPotentialPeerListParamSet].member_id_message_num[0].member_id == "\xbcj\x00\x96Ywz\x82:\x90\xd9\xe7") +assert(eapol[MKAPDU].haslayer(MKAICVSet)) +assert(eapol[MKAPDU][MKAICVSet].icv == "5\x01\xdc)\xfd\xd1\xff\xd55\x9c_o\xc9\x9c\xca\xc0") + += EAPOL-MKA - With Live Peer List parameter set - Dissection +eapol = None +s = "\x03\x05\x00h\x01\xffp<\x00Bh\xa8\x1e\x03\x00\n\xbcj\x00\x96Ywz\x82:\x90\xd9\xe7\x00\x00\x00\x02\x00\x80\xc2\x01\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x01\x00\x00\x10q\x8b\x8a9\x86k/X\x14\xc9\xdc\xf6\x00\x00\x00\x80\xff\x00\x00\x10\xf4\xa1d\x18\tD\xa2}\x8e'\x0c/\xda,\xea\xb7" +eapol = EAPOL(s) +assert(eapol.version == 3) +assert(eapol.type == 5) +assert(eapol.len == 104) +assert(eapol.haslayer(MKAPDU)) +assert(eapol[MKAPDU].basic_param_set.actor_member_id == '\xbcj\x00\x96Ywz\x82:\x90\xd9\xe7') +assert(eapol.haslayer(MKALivePeerListParamSet)) +assert(eapol[MKAPDU][MKALivePeerListParamSet].member_id_message_num[0].member_id == "q\x8b\x8a9\x86k/X\x14\xc9\xdc\xf6") +assert(eapol[MKAPDU].haslayer(MKAICVSet)) +assert(eapol[MKAPDU][MKAICVSet].icv == "\xf4\xa1d\x18\tD\xa2}\x8e'\x0c/\xda,\xea\xb7") + += EAPOL-MKA - With SAK Use parameter set - Dissection +eapol = None +s = '\x03\x05\x00\x94\x01\xffp<\x00Bh\xa8\x1e\x03\x00\n\xbcj\x00\x96Ywz\x82:\x90\xd9\xe7\x00\x00\x00\x03\x00\x80\xc2\x01\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x03\x10\x00(q\x8b\x8a9\x86k/X\x14\xc9\xdc\xf6\x00\x00\x00\x01\x00\x00\x00\x00q\x8b\x8a9\x86k/X\x14\xc9\xdc\xf6\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x10q\x8b\x8a9\x86k/X\x14\xc9\xdc\xf6\x00\x00\x00\x83\xff\x00\x00\x10OF\x84\xf1@%\x95\xe6Fw9\x1a\xfa\x03(\xae' +eapol = EAPOL(s) +assert(eapol.version == 3) +assert(eapol.type == 5) +assert(eapol.len == 148) +assert(eapol.haslayer(MKAPDU)) +assert(eapol[MKAPDU].basic_param_set.actor_member_id == '\xbcj\x00\x96Ywz\x82:\x90\xd9\xe7') +assert(eapol.haslayer(MKASAKUseParamSet)) +assert(eapol[MKAPDU][MKASAKUseParamSet].latest_key_key_server_member_id == "q\x8b\x8a9\x86k/X\x14\xc9\xdc\xf6") +assert(eapol.haslayer(MKALivePeerListParamSet)) +assert(eapol[MKAPDU][MKALivePeerListParamSet].member_id_message_num[0].member_id == "q\x8b\x8a9\x86k/X\x14\xc9\xdc\xf6") +assert(eapol[MKAPDU].haslayer(MKAICVSet)) +assert(eapol[MKAPDU][MKAICVSet].icv == "OF\x84\xf1@%\x95\xe6Fw9\x1a\xfa\x03(\xae") + += EAPOL-MKA - With Distributed SAK parameter set - Dissection +eapol = None +s = "\x03\x05\x00\xb4\x01\x10\xe0<\xccN$\xc4\xf7\x7f\x00\x80q\x8b\x8a9\x86k/X\x14\xc9\xdc\xf6\x00\x00\x00\x81\x00\x80\xc2\x01\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x01\x00\x00\x10\xbcj\x00\x96Ywz\x82:\x90\xd9\xe7\x00\x00\x00\x02\x03\x10\x00(q\x8b\x8a9\x86k/X\x14\xc9\xdc\xf6\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x10\x00\x1c\x00\x00\x00\x01Cz\x05\x88\x9f\xe8-\x94W+?\x13~\xfb\x016yVB?\xbd\xa1\x9fu\xff\x00\x00\x10\xb0H\xcf\xe0:\xa1\x94RD'\x03\xe67\xe1Ur" +eapol = EAPOL(s) +assert(eapol.version == 3) +assert(eapol.type == 5) +assert(eapol.len == 180) +assert(eapol.haslayer(MKAPDU)) +assert(eapol[MKAPDU].basic_param_set.actor_member_id == "q\x8b\x8a9\x86k/X\x14\xc9\xdc\xf6") +assert(eapol.haslayer(MKASAKUseParamSet)) +assert(eapol[MKAPDU][MKASAKUseParamSet].latest_key_key_server_member_id == "q\x8b\x8a9\x86k/X\x14\xc9\xdc\xf6") +assert(eapol.haslayer(MKALivePeerListParamSet)) +assert(eapol[MKAPDU][MKALivePeerListParamSet].member_id_message_num[0].member_id == "\xbcj\x00\x96Ywz\x82:\x90\xd9\xe7") +assert(eapol.haslayer(MKADistributedSAKParamSet)) +assert(eapol[MKADistributedSAKParamSet].sak_aes_key_wrap == "Cz\x05\x88\x9f\xe8-\x94W+?\x13~\xfb\x016yVB?\xbd\xa1\x9fu") +assert(eapol[MKAPDU].haslayer(MKAICVSet)) +assert(eapol[MKAPDU][MKAICVSet].icv == "\xb0H\xcf\xe0:\xa1\x94RD'\x03\xe67\xe1Ur") + + +############ ############ ############ + EAP class tests