• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# SPDX-License-Identifier: GPL-2.0-only
2# This file is part of Scapy
3# See https://scapy.net/ for more information
4# Copyright (C) Philippe Biondi <phil@secdev.org>
5
6"""
7Extensible Authentication Protocol (EAP)
8"""
9
10
11import struct
12
13from scapy.fields import (
14    BitEnumField,
15    BitField,
16    ByteEnumField,
17    ByteField,
18    ConditionalField,
19    FieldLenField,
20    FieldListField,
21    IntField,
22    LenField,
23    LongField,
24    PacketField,
25    PacketListField,
26    PadField,
27    ShortField,
28    StrLenField,
29    XByteField,
30    XIntField,
31    XStrField,
32    XStrFixedLenField,
33    XStrLenField,
34)
35from scapy.packet import (
36    Packet,
37    Padding,
38    bind_bottom_up,
39    bind_layers,
40    bind_top_down,
41)
42from scapy.layers.l2 import SourceMACField, Ether, CookedLinux, GRE, SNAP
43from scapy.config import conf
44from scapy.compat import orb, chb
45
46#
47# EAPOL
48#
49
50#########################################################################
51#
52# EAPOL protocol version
53# IEEE Std 802.1X-2010 - Section 11.3.1
54#########################################################################
55#
56
57eapol_versions = {
58    0x1: "802.1X-2001",
59    0x2: "802.1X-2004",
60    0x3: "802.1X-2010",
61}
62
63#########################################################################
64#
65# EAPOL Packet Types
66# IEEE Std 802.1X-2010 - Table 11.3
67#########################################################################
68#
69
70eapol_types = {
71    0x0: "EAP-Packet",  # "EAPOL-EAP" in 801.1X-2010
72    0x1: "EAPOL-Start",
73    0x2: "EAPOL-Logoff",
74    0x3: "EAPOL-Key",
75    0x4: "EAPOL-Encapsulated-ASF-Alert",
76    0x5: "EAPOL-MKA",
77    0x6: "EAPOL-Announcement (Generic)",
78    0x7: "EAPOL-Announcement (Specific)",
79    0x8: "EAPOL-Announcement-Req"
80}
81
82
83class EAPOL(Packet):
84    """
85    EAPOL - IEEE Std 802.1X-2010
86    """
87
88    name = "EAPOL"
89    fields_desc = [
90        ByteEnumField("version", 1, eapol_versions),
91        ByteEnumField("type", 0, eapol_types),
92        LenField("len", None, "H")
93    ]
94
95    EAP_PACKET = 0
96    START = 1
97    LOGOFF = 2
98    KEY = 3
99    ASF = 4
100
101    def extract_padding(self, s):
102        tmp_len = self.len
103        return s[:tmp_len], s[tmp_len:]
104
105    def hashret(self):
106        return chb(self.type) + self.payload.hashret()
107
108    def answers(self, other):
109        if isinstance(other, EAPOL):
110            if ((self.type == self.EAP_PACKET) and
111                    (other.type == self.EAP_PACKET)):
112                return self.payload.answers(other.payload)
113        return 0
114
115    def mysummary(self):
116        return self.sprintf("EAPOL %EAPOL.type%")
117
118
119#
120# EAP
121#
122
123
124#########################################################################
125#
126# EAP methods types
127# http://www.iana.org/assignments/eap-numbers/eap-numbers.xhtml#eap-numbers-4
128#########################################################################
129#
130
131eap_types = {
132    0: "Reserved",
133    1: "Identity",
134    2: "Notification",
135    3: "Legacy Nak",
136    4: "MD5-Challenge",
137    5: "One-Time Password (OTP)",
138    6: "Generic Token Card (GTC)",
139    7: "Allocated - RFC3748",
140    8: "Allocated - RFC3748",
141    9: "RSA Public Key Authentication",
142    10: "DSS Unilateral",
143    11: "KEA",
144    12: "KEA-VALIDATE",
145    13: "EAP-TLS",
146    14: "Defender Token (AXENT)",
147    15: "RSA Security SecurID EAP",
148    16: "Arcot Systems EAP",
149    17: "EAP-Cisco Wireless",
150    18: "GSM Subscriber Identity Modules (EAP-SIM)",
151    19: "SRP-SHA1",
152    20: "Unassigned",
153    21: "EAP-TTLS",
154    22: "Remote Access Service",
155    23: "EAP-AKA Authentication",
156    24: "EAP-3Com Wireless",
157    25: "PEAP",
158    26: "MS-EAP-Authentication",
159    27: "Mutual Authentication w/Key Exchange (MAKE)",
160    28: "CRYPTOCard",
161    29: "EAP-MSCHAP-V2",
162    30: "DynamID",
163    31: "Rob EAP",
164    32: "Protected One-Time Password",
165    33: "MS-Authentication-TLV",
166    34: "SentriNET",
167    35: "EAP-Actiontec Wireless",
168    36: "Cogent Systems Biometrics Authentication EAP",
169    37: "AirFortress EAP",
170    38: "EAP-HTTP Digest",
171    39: "SecureSuite EAP",
172    40: "DeviceConnect EAP",
173    41: "EAP-SPEKE",
174    42: "EAP-MOBAC",
175    43: "EAP-FAST",
176    44: "ZoneLabs EAP (ZLXEAP)",
177    45: "EAP-Link",
178    46: "EAP-PAX",
179    47: "EAP-PSK",
180    48: "EAP-SAKE",
181    49: "EAP-IKEv2",
182    50: "EAP-AKA",
183    51: "EAP-GPSK",
184    52: "EAP-pwd",
185    53: "EAP-EKE Version 1",
186    54: "EAP Method Type for PT-EAP",
187    55: "TEAP",
188    254: "Reserved for the Expanded Type",
189    255: "Experimental",
190}
191
192
193#########################################################################
194#
195# EAP codes
196# http://www.iana.org/assignments/eap-numbers/eap-numbers.xhtml#eap-numbers-1
197#########################################################################
198#
199
200eap_codes = {
201    1: "Request",
202    2: "Response",
203    3: "Success",
204    4: "Failure",
205    5: "Initiate",
206    6: "Finish"
207}
208
209
210class EAP(Packet):
211    """
212    RFC 3748 - Extensible Authentication Protocol (EAP)
213    """
214
215    name = "EAP"
216    fields_desc = [
217        ByteEnumField("code", 4, eap_codes),
218        ByteField("id", 0),
219        ShortField("len", None),
220        ConditionalField(ByteEnumField("type", 0, eap_types),
221                         lambda pkt:pkt.code not in [
222                             EAP.SUCCESS, EAP.FAILURE]),
223        ConditionalField(
224            FieldListField("desired_auth_types", [],
225                           ByteEnumField("auth_type", 0, eap_types),
226                           length_from=lambda pkt: pkt.len - 4),
227            lambda pkt:pkt.code == EAP.RESPONSE and pkt.type == 3),
228        ConditionalField(
229            StrLenField("identity", '', length_from=lambda pkt: pkt.len - 5),
230            lambda pkt: pkt.code == EAP.RESPONSE and hasattr(pkt, 'type') and pkt.type == 1),  # noqa: E501
231        ConditionalField(
232            StrLenField("message", '', length_from=lambda pkt: pkt.len - 5),
233            lambda pkt: pkt.code == EAP.REQUEST and hasattr(pkt, 'type') and pkt.type == 1)  # noqa: E501
234    ]
235
236    #########################################################################
237    #
238    # EAP codes
239    # http://www.iana.org/assignments/eap-numbers/eap-numbers.xhtml#eap-numbers-1
240    #########################################################################
241    #
242
243    REQUEST = 1
244    RESPONSE = 2
245    SUCCESS = 3
246    FAILURE = 4
247    INITIATE = 5
248    FINISH = 6
249
250    registered_methods = {}
251
252    @classmethod
253    def register_variant(cls):
254        cls.registered_methods[cls.type.default] = cls
255
256    @classmethod
257    def dispatch_hook(cls, _pkt=None, *args, **kargs):
258        if _pkt:
259            c = orb(_pkt[0])
260            if c in [1, 2] and len(_pkt) >= 5:
261                t = orb(_pkt[4])
262                return cls.registered_methods.get(t, cls)
263        return cls
264
265    def answers(self, other):
266        if isinstance(other, EAP):
267            if self.code == self.REQUEST:
268                return 0
269            elif self.code == self.RESPONSE:
270                if ((other.code == self.REQUEST) and
271                        (other.type == self.type)):
272                    return 1
273            elif other.code == self.RESPONSE:
274                return 1
275        return 0
276
277    def mysummary(self):
278        summary_str = "EAP %{eap_class}.code% %{eap_class}.type%".format(
279            eap_class=self.__class__.__name__
280        )
281        if self.type == 1 and self.code == EAP.RESPONSE:
282            summary_str += " %{eap_class}.identity%".format(
283                eap_class=self.__class__.__name__
284            )
285        return self.sprintf(summary_str)
286
287    def post_build(self, p, pay):
288        if self.len is None:
289            tmp_len = len(p) + len(pay)
290            tmp_p = p[:2] + chb((tmp_len >> 8) & 0xff) + chb(tmp_len & 0xff)
291            p = tmp_p + p[4:]
292        return p + pay
293
294    def guess_payload_class(self, _):
295        return Padding
296
297
298class EAP_MD5(EAP):
299    """
300    RFC 3748 - "Extensible Authentication Protocol (EAP)"
301    """
302
303    name = "EAP-MD5"
304    match_subclass = True
305    fields_desc = [
306        ByteEnumField("code", 1, eap_codes),
307        ByteField("id", 0),
308        FieldLenField("len", None, fmt="H", length_of="optional_name",
309                      adjust=lambda p, x: x + 6 + (p.value_size or 0)),
310        ByteEnumField("type", 4, eap_types),
311        FieldLenField("value_size", None, fmt="B", length_of="value"),
312        XStrLenField("value", '', length_from=lambda p: p.value_size),
313        XStrLenField("optional_name", '', length_from=lambda p: 0 if p.len is None or p.value_size is None else (p.len - p.value_size - 6))  # noqa: E501
314    ]
315
316
317class EAP_TLS(EAP):
318    """
319    RFC 5216 - "The EAP-TLS Authentication Protocol"
320    """
321
322    name = "EAP-TLS"
323    match_subclass = True
324    fields_desc = [
325        ByteEnumField("code", 1, eap_codes),
326        ByteField("id", 0),
327        FieldLenField("len", None, fmt="H", length_of="tls_data",
328                      adjust=lambda p, x: x + 10 if p.L == 1 else x + 6),
329        ByteEnumField("type", 13, eap_types),
330        BitField('L', 0, 1),
331        BitField('M', 0, 1),
332        BitField('S', 0, 1),
333        BitField('reserved', 0, 5),
334        ConditionalField(IntField('tls_message_len', 0), lambda pkt: pkt.L == 1),  # noqa: E501
335        XStrLenField('tls_data', '', length_from=lambda pkt: 0 if pkt.len is None else pkt.len - (6 + 4 * pkt.L))  # noqa: E501
336    ]
337
338
339class EAP_TTLS(EAP):
340    """
341    RFC 5281 - "Extensible Authentication Protocol Tunneled Transport Layer
342    Security Authenticated Protocol Version 0 (EAP-TTLSv0)"
343    """
344
345    name = "EAP-TTLS"
346    match_subclass = True
347    fields_desc = [
348        ByteEnumField("code", 1, eap_codes),
349        ByteField("id", 0),
350        FieldLenField("len", None, fmt="H", length_of="data",
351                      adjust=lambda p, x: x + 10 if p.L == 1 else x + 6),
352        ByteEnumField("type", 21, eap_types),
353        BitField("L", 0, 1),
354        BitField("M", 0, 1),
355        BitField("S", 0, 1),
356        BitField("reserved", 0, 2),
357        BitField("version", 0, 3),
358        ConditionalField(IntField("message_len", 0), lambda pkt: pkt.L == 1),
359        XStrLenField("data", "", length_from=lambda pkt: 0 if pkt.len is None else pkt.len - (6 + 4 * pkt.L))  # noqa: E501
360    ]
361
362
363class EAP_PEAP(EAP):
364    """
365    draft-josefsson-pppext-eap-tls-eap-05.txt - "Protected EAP Protocol (PEAP)"
366    """
367
368    name = "PEAP"
369    match_subclass = True
370    fields_desc = [
371        ByteEnumField("code", 1, eap_codes),
372        ByteField("id", 0),
373        FieldLenField("len", None, fmt="H", length_of="tls_data",
374                      adjust=lambda p, x: x + 10 if p.L == 1 else x + 6),
375        ByteEnumField("type", 25, eap_types),
376        BitField("L", 0, 1),
377        BitField("M", 0, 1),
378        BitField("S", 0, 1),
379        BitField("reserved", 0, 3),
380        BitField("version", 1, 2),
381        ConditionalField(IntField("tls_message_len", 0), lambda pkt: pkt.L == 1),  # noqa: E501
382        XStrLenField("tls_data", "", length_from=lambda pkt: 0 if pkt.len is None else pkt.len - (6 + 4 * pkt.L))  # noqa: E501
383    ]
384
385
386class EAP_FAST(EAP):
387    """
388    RFC 4851 - "The Flexible Authentication via Secure Tunneling
389    Extensible Authentication Protocol Method (EAP-FAST)"
390    """
391
392    name = "EAP-FAST"
393    match_subclass = True
394    fields_desc = [
395        ByteEnumField("code", 1, eap_codes),
396        ByteField("id", 0),
397        FieldLenField("len", None, fmt="H", length_of="data",
398                      adjust=lambda p, x: x + 10 if p.L == 1 else x + 6),
399        ByteEnumField("type", 43, eap_types),
400        BitField('L', 0, 1),
401        BitField('M', 0, 1),
402        BitField('S', 0, 1),
403        BitField('reserved', 0, 2),
404        BitField('version', 0, 3),
405        ConditionalField(IntField('message_len', 0), lambda pkt: pkt.L == 1),
406        XStrLenField('data', '', length_from=lambda pkt: 0 if pkt.len is None else pkt.len - (6 + 4 * pkt.L))  # noqa: E501
407    ]
408
409
410class LEAP(EAP):
411    """
412    Cisco LEAP (Lightweight EAP)
413    https://freeradius.org/rfc/leap.txt
414    """
415
416    name = "Cisco LEAP"
417    match_subclass = True
418    fields_desc = [
419        ByteEnumField("code", 1, eap_codes),
420        ByteField("id", 0),
421        ShortField("len", None),
422        ByteEnumField("type", 17, eap_types),
423        ByteField('version', 1),
424        XByteField('unused', 0),
425        FieldLenField("count", None, "challenge_response", "B", adjust=lambda p, x: len(p.challenge_response)),  # noqa: E501
426        XStrLenField("challenge_response", "", length_from=lambda p: 0 or p.count),  # noqa: E501
427        StrLenField("username", "", length_from=lambda p: p.len - (8 + (0 or p.count)))  # noqa: E501
428    ]
429
430
431#############################################################################
432# IEEE 802.1X-2010 - EAPOL-Key
433#############################################################################
434
435# sect 11.9 of 802.1X-2010
436# AND sect 12.7.2 of 802.11-2016
437
438
439class EAPOL_KEY(Packet):
440    name = "EAPOL_KEY"
441    deprecated_fields = {
442        "key": ("key_data", "2.6.0"),
443        "len": ("key_length", "2.6.0"),
444    }
445    fields_desc = [
446        ByteEnumField("key_descriptor_type", 1, {1: "RC4", 2: "RSN"}),
447        # Key Information
448        BitField("res2", 0, 2),
449        BitField("smk_message", 0, 1),
450        BitField("encrypted_key_data", 0, 1),
451        BitField("request", 0, 1),
452        BitField("error", 0, 1),
453        BitField("secure", 0, 1),
454        BitField("has_key_mic", 1, 1),
455        BitField("key_ack", 0, 1),
456        BitField("install", 0, 1),
457        BitField("res", 0, 2),
458        BitEnumField("key_type", 0, 1, {0: "Group/SMK", 1: "Pairwise"}),
459        BitEnumField("key_descriptor_type_version", 0, 3, {
460            1: "HMAC-MD5+ARC4",
461            2: "HMAC-SHA1-128+AES-128",
462            3: "AES-128-CMAC+AES-128",
463        }),
464        #
465        LenField("key_length", None, "H"),
466        LongField("key_replay_counter", 0),
467        XStrFixedLenField("key_nonce", "", 32),
468        XStrFixedLenField("key_iv", "", 16),
469        XStrFixedLenField("key_rsc", "", 8),
470        XStrFixedLenField("key_id", "", 8),
471        XStrFixedLenField("key_mic", "", 16),  # XXX size can be 24
472        FieldLenField("key_data_length", None, length_of="key_data"),
473        XStrLenField("key_data", "",
474                     length_from=lambda pkt: pkt.key_data_length)
475    ]
476
477    def extract_padding(self, s):
478        return s[:self.key_length], s[self.key_length:]
479
480    def hashret(self):
481        return struct.pack("!B", self.type) + self.payload.hashret()
482
483    def answers(self, other):
484        if isinstance(other, EAPOL_KEY) and \
485                other.descriptor_type == self.descriptor_type:
486            return 1
487        return 0
488
489    def guess_key_number(self):
490        """
491        Determines 4-way handshake key number
492
493        :return: key number (1-4), or 0 if it cannot be determined
494        """
495        if self.key_type == 1:
496            if self.key_ack == 1:
497                if self.key_mic == 0:
498                    return 1
499                if self.install == 1:
500                    return 3
501            else:
502                if self.secure == 0:
503                    return 2
504                return 4
505        return 0
506
507
508#############################################################################
509# IEEE 802.1X-2010 - MACsec Key Agreement (MKA) protocol
510#############################################################################
511
512#########################################################################
513#
514# IEEE 802.1X-2010 standard
515# Section 11.11.1
516#########################################################################
517#
518
519_parameter_set_types = {
520    1: "Live Peer List",
521    2: "Potential Peer List",
522    3: "MACsec SAK Use",
523    4: "Distributed SAK",
524    5: "Distributed CAK",
525    6: "KMD",
526    7: "Announcement",
527    255: "ICV Indicator"
528}
529
530
531# Used by MKAParamSet::dispatch_hook() to instantiate the appropriate class
532_param_set_cls = {
533    1: "MKALivePeerListParamSet",
534    2: "MKAPotentialPeerListParamSet",
535    3: "MKASAKUseParamSet",
536    4: "MKADistributedSAKParamSet",
537    255: "MKAICVSet",
538}
539
540
541class MACsecSCI(Packet):
542    """
543    Secure Channel Identifier.
544    """
545
546    #########################################################################
547    #
548    # IEEE 802.1AE-2006 standard
549    # Section 9.9
550    #########################################################################
551    #
552
553    name = "SCI"
554    fields_desc = [
555        SourceMACField("system_identifier"),
556        ShortField("port_identifier", 0)
557    ]
558
559    def extract_padding(self, s):
560        return "", s
561
562
563class MKAParamSet(Packet):
564    """
565    Class from which every parameter set class inherits (except
566    MKABasicParamSet, which has no "Parameter set type" field, and must
567    come first in the list of parameter sets).
568    """
569
570    MACSEC_DEFAULT_ICV_LEN = 16
571    EAPOL_MKA_DEFAULT_KEY_WRAP_LEN = 24
572
573    @classmethod
574    def dispatch_hook(cls, _pkt=None, *args, **kargs):
575        """
576        Returns the right parameter set class.
577        """
578
579        cls = conf.raw_layer
580        if _pkt is not None:
581            ptype = orb(_pkt[0])
582            return globals().get(_param_set_cls.get(ptype), conf.raw_layer)
583
584        return cls
585
586
587class MKABasicParamSet(Packet):
588    """
589    Basic Parameter Set (802.1X-2010, section 11.11).
590    """
591
592    #########################################################################
593    #
594    # IEEE 802.1X-2010 standard
595    # Section 11.11
596    #########################################################################
597    #
598
599    name = "Basic Parameter Set"
600    fields_desc = [
601        ByteField("mka_version_id", 0),
602        ByteField("key_server_priority", 0),
603        BitField("key_server", 0, 1),
604        BitField("macsec_desired", 0, 1),
605        BitField("macsec_capability", 0, 2),
606        BitField("param_set_body_len", 0, 12),
607        PacketField("SCI", MACsecSCI(), MACsecSCI),
608        XStrFixedLenField("actor_member_id", "", length=12),
609        XIntField("actor_message_number", 0),
610        XIntField("algorithm_agility", 0),
611        PadField(
612            XStrLenField(
613                "cak_name",
614                "",
615                length_from=lambda pkt: (pkt.param_set_body_len - 28)
616            ),
617            4,
618            padwith=b"\x00"
619        )
620    ]
621
622    def extract_padding(self, s):
623        return "", s
624
625
626class MKAPeerListTuple(Packet):
627    """
628    Live / Potential Peer List parameter sets tuples (802.1X-2010, section 11.11).  # noqa: E501
629    """
630
631    name = "Peer List Tuple"
632    fields_desc = [
633        XStrFixedLenField("member_id", "", length=12),
634        XStrFixedLenField("message_number", "", length=4),
635    ]
636
637
638class MKALivePeerListParamSet(MKAParamSet):
639    """
640    Live Peer List parameter sets (802.1X-2010, section 11.11).
641    """
642
643    #########################################################################
644    #
645    # IEEE 802.1X-2010 standard
646    # Section 11.11
647    #########################################################################
648    #
649
650    name = "Live Peer List Parameter Set"
651    fields_desc = [
652        PadField(
653            ByteEnumField(
654                "param_set_type",
655                1,
656                _parameter_set_types
657            ),
658            2,
659            padwith=b"\x00"
660        ),
661        ShortField("param_set_body_len", 0),
662        PacketListField("member_id_message_num", [], MKAPeerListTuple)
663    ]
664
665
666class MKAPotentialPeerListParamSet(MKAParamSet):
667    """
668    Potential Peer List parameter sets (802.1X-2010, section 11.11).
669    """
670
671    #########################################################################
672    #
673    # IEEE 802.1X-2010 standard
674    # Section 11.11
675    #########################################################################
676    #
677
678    name = "Potential Peer List Parameter Set"
679    fields_desc = [
680        PadField(
681            ByteEnumField(
682                "param_set_type",
683                2,
684                _parameter_set_types
685            ),
686            2,
687            padwith=b"\x00"
688        ),
689        ShortField("param_set_body_len", 0),
690        PacketListField("member_id_message_num", [], MKAPeerListTuple)
691    ]
692
693
694class MKASAKUseParamSet(MKAParamSet):
695    """
696    SAK Use Parameter Set (802.1X-2010, section 11.11).
697    """
698
699    #########################################################################
700    #
701    # IEEE 802.1X-2010 standard
702    # Section 11.11
703    #########################################################################
704    #
705
706    name = "SAK Use Parameter Set"
707    fields_desc = [
708        ByteEnumField("param_set_type", 3, _parameter_set_types),
709        BitField("latest_key_an", 0, 2),
710        BitField("latest_key_tx", 0, 1),
711        BitField("latest_key_rx", 0, 1),
712        BitField("old_key_an", 0, 2),
713        BitField("old_key_tx", 0, 1),
714        BitField("old_key_rx", 0, 1),
715        BitField("plain_tx", 0, 1),
716        BitField("plain_rx", 0, 1),
717        BitField("X", 0, 1),
718        BitField("delay_protect", 0, 1),
719        BitField("param_set_body_len", 0, 12),
720        XStrFixedLenField("latest_key_key_server_member_id", "", length=12),
721        XStrFixedLenField("latest_key_key_number", "", length=4),
722        XStrFixedLenField("latest_key_lowest_acceptable_pn", "", length=4),
723        XStrFixedLenField("old_key_key_server_member_id", "", length=12),
724        XStrFixedLenField("old_key_key_number", "", length=4),
725        XStrFixedLenField("old_key_lowest_acceptable_pn", "", length=4)
726    ]
727
728
729class MKADistributedSAKParamSet(MKAParamSet):
730    """
731    Distributed SAK parameter set (802.1X-2010, section 11.11).
732    """
733
734    #########################################################################
735    #
736    # IEEE 802.1X-2010 standard
737    # Section 11.11
738    #########################################################################
739    #
740
741    name = "Distributed SAK parameter set"
742    fields_desc = [
743        ByteEnumField("param_set_type", 4, _parameter_set_types),
744        BitField("distributed_an", 0, 2),
745        BitField("confidentiality_offset", 0, 2),
746        BitField("unused", 0, 4),
747        ShortField("param_set_body_len", 0),
748        XStrFixedLenField("key_number", "", length=4),
749        ConditionalField(
750            XStrFixedLenField("macsec_cipher_suite", "", length=8),
751            lambda pkt: pkt.param_set_body_len > 28
752        ),
753        XStrFixedLenField(
754            "sak_aes_key_wrap",
755            "",
756            length=MKAParamSet.EAPOL_MKA_DEFAULT_KEY_WRAP_LEN
757        )
758    ]
759
760
761class MKADistributedCAKParamSet(MKAParamSet):
762    """
763    Distributed CAK Parameter Set (802.1X-2010, section 11.11).
764    """
765
766    #########################################################################
767    #
768    # IEEE 802.1X-2010 standard
769    # Section 11.11
770    #########################################################################
771    #
772
773    name = "Distributed CAK parameter set"
774    fields_desc = [
775        PadField(
776            ByteEnumField(
777                "param_set_type",
778                5,
779                _parameter_set_types
780            ),
781            2,
782            padwith=b"\x00"
783        ),
784        ShortField("param_set_body_len", 0),
785        XStrFixedLenField(
786            "cak_aes_key_wrap",
787            "",
788            length=MKAParamSet.EAPOL_MKA_DEFAULT_KEY_WRAP_LEN
789        ),
790        XStrField("cak_key_name", "")
791    ]
792
793
794class MKAICVSet(MKAParamSet):
795    """
796    ICV (802.1X-2010, section 11.11).
797    """
798
799    #########################################################################
800    #
801    # IEEE 802.1X-2010 standard
802    # Section 11.11
803    #########################################################################
804    #
805
806    name = "ICV"
807    fields_desc = [
808        PadField(
809            ByteEnumField(
810                "param_set_type",
811                255,
812                _parameter_set_types
813            ),
814            2,
815            padwith=b"\x00"
816        ),
817        ShortField("param_set_body_len", 0),
818        XStrFixedLenField("icv", "", length=MKAParamSet.MACSEC_DEFAULT_ICV_LEN)
819    ]
820
821
822class MKAParamSetPacketListField(PacketListField):
823    """
824    PacketListField that handles the parameter sets.
825    """
826
827    PARAM_SET_LEN_MASK = 0b0000111111111111
828
829    def m2i(self, pkt, m):
830        return MKAParamSet(m)
831
832    def getfield(self, pkt, s):
833        lst = []
834        remain = s
835
836        while remain:
837            len_bytes = struct.unpack("!H", remain[2:4])[0]
838            param_set_len = self.__class__.PARAM_SET_LEN_MASK & len_bytes
839            current = remain[:4 + param_set_len]
840            remain = remain[4 + param_set_len:]
841            current_packet = self.m2i(pkt, current)
842            lst.append(current_packet)
843
844        return remain, lst
845
846
847class MKAPDU(Packet):
848    """
849    MACsec Key Agreement Protocol Data Unit.
850    """
851
852    #########################################################################
853    #
854    # IEEE 802.1X-2010 standard
855    # Section 11.11
856    #########################################################################
857    #
858
859    name = "MKPDU"
860    fields_desc = [
861        PacketField("basic_param_set", "", MKABasicParamSet),
862        MKAParamSetPacketListField("parameter_sets", [], MKAParamSet),
863    ]
864
865    def extract_padding(self, s):
866        return "", s
867
868
869# Bind EAPOL types
870bind_layers(EAPOL, EAP, type=0)
871bind_layers(EAPOL, EAPOL_KEY, type=3)
872bind_layers(EAPOL, MKAPDU, type=5)
873
874bind_bottom_up(Ether, EAPOL, type=0x888e)
875# the reserved IEEE Std 802.1X PAE address
876bind_top_down(Ether, EAPOL, dst='01:80:c2:00:00:03', type=0x888e)
877bind_layers(CookedLinux, EAPOL, proto=0x888e)
878bind_layers(SNAP, EAPOL, code=0x888e)
879bind_layers(GRE, EAPOL, proto=0x888e)
880