-
Notifications
You must be signed in to change notification settings - Fork 35
/
Copy pathtest-4wayhs.py
146 lines (111 loc) · 4.17 KB
/
test-4wayhs.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# Import dependencies and libraries.
from dependencies.libwifi.wifi import *
from dependencies.libwifi.crypto import rsn_prf_sha1, aes_wrap_key_withpad
from library.station import Authenticator
from pbkdf2 import PBKDF2
class FourWayHandshake(Authenticator):
"""Authenticator Station."""
name = "handshake-ap"
def send_msg1(self):
self.anonce = random.randbytes(32)
self.replay_counter += 1
p = EAPOL_KEY(key_descriptor_type="RSN",
has_key_mic=0,
key_ack=1,
key_type="Pairwise",
key_descriptor_type_version="HMAC-SHA1-128+AES-128",
key_replay_counter=self.replay_counter,
key_nonce=self.anonce)
p = LLC()/SNAP()/EAPOL(version="802.1X-2004", type="EAPOL-Key")/p
p = self.get_header()/p
log(STATUS, f"Sending msg1: {repr(p)}")
self.sock_mon.send(p)
def process_msg2(self, p):
self.snonce = p[EAPOL_KEY].key_nonce
log(STATUS, f"Received snonce {self.snonce}")
self.derive_ptk()
eapol = p[EAPOL].copy()
eapol.key_mic = b"\x00" * 20
mic = hmac.new(self.ptk[0:16], bytes(eapol), hashlib.sha1).digest()[0:16]
log(STATUS, f"Calculated MIC: {mic}")
log(STATUS, f"Received MIC: {p[EAPOL].key_mic}")
def send_msg3(self):
self.replay_counter += 1
# Get the RSNE from hostap
rsne = binascii.unhexlify(self.wpaspy_command("GET_RSNE"))
# Get GTK from Hostapd so it's of the correct length in all cases
gtk, gtk_index = self.wpaspy_command("GET_GTK").split()
gtk = binascii.unhexlify(gtk)
gtk_index = int(gtk_index)
# See Figure 12-36—GTK KDE format:
# - The first byte is: Key ID (2 bits), Tx (1 bit), Reserved (3 bits)
# - The second byte is Reserved (8 bits)
gtk_info = struct.pack(">B", gtk_index) + b"\x00"
key_data = bytes(Dot11EltVendorSpecific(oui=0x000fac, info=b"\x01" + gtk_info + gtk))
key_data += rsne
# Encrypt the key data
kekkey = self.ptk[16:32]
log(STATUS, f"KEK Key: {kekkey}")
log(STATUS, f"Plaintext: {key_data}")
ciphertext = aes_wrap_key_withpad(kekkey, key_data)
log(STATUS, f"ciphertext: {ciphertext}")
eapol = EAPOL_KEY(key_descriptor_type="RSN",
encrypted_key_data=1,
secure=1,
has_key_mic=1,
key_ack=1,
install=1,
key_type="Pairwise",
key_descriptor_type_version="HMAC-SHA1-128+AES-128",
len=len(gtk),
key_replay_counter=self.replay_counter,
key_nonce=self.anonce,
key_length=len(ciphertext),
key=ciphertext)
eapol = EAPOL(version="802.1X-2004", type="EAPOL-Key")/eapol
eapol.key_mic = hmac.new(self.ptk[0:16], bytes(eapol), hashlib.sha1).digest()[0:16]
p = LLC()/SNAP()/eapol
p = self.get_header()/p
log(STATUS, f"Sending frame {repr(p)}")
self.sock_mon.send(p)
def derive_ptk(self):
# TODO: Get the SSID and passphrase through the hostap control interface
ssid = "testnetwork"
passphrase = "passphrase"
pmk = PBKDF2(passphrase, ssid, 4096).read(32)
apmac = binascii.a2b_hex(self.mac.replace(":", ""))
stamac = binascii.a2b_hex(self.clientmac.replace(":", ""))
apnonce = self.anonce
stanonce = self.snonce
label = b"Pairwise key expansion"
key_data = min(apmac,stamac) + max(apmac,stamac) + min(apnonce,stanonce) + max(apnonce,stanonce)
self.ptk = rsn_prf_sha1(pmk, label, key_data, 512//8)
log(DEBUG, f"PTK: {self.ptk}")
def start_handshake(self, msg):
self.clientmac = msg.split()[1]
self.replay_counter = 0
log(STATUS, f"Starting handshake with {self.clientmac}")
self.send_msg1()
def handle_started(self):
# After hostap started, configure to skip the 4-way handshake
# so we can handle it ourselves
self.wpaspy_command("SKIP_4WAY 1")
def handle_mon(self, p):
if p.addr2 != self.clientmac or p.addr1 != self.mac:
return
if EAPOL in p:
self.handle_eapol(p)
def handle_eapol(self, p):
log(STATUS, f"Received {repr(p)} length={len(p)}")
# Message 4 can be detected because the key data field is empty:
if EAPOL_KEY in p and p[EAPOL_KEY].key_length == 0:
log(STATUS, "Received message 4, handshake complete!")
# Otherwise assume it's message 2
else:
self.process_msg2(p)
self.send_msg3()
def handle_wpaspy(self, msg):
"""Override the Station/Daemon-handler."""
log(STATUS, "daemon: " + msg)
if "AP-STA-ASSOCIATING" in msg:
self.start_handshake(msg)