93194d30ec46dfeb8bfcb0e51c53a6a26ef9179b
[mech_eap.git] / tests / hwsim / test_sae.py
1 # Test cases for SAE
2 # Copyright (c) 2013-2014, Jouni Malinen <j@w1.fi>
3 #
4 # This software may be distributed under the terms of the BSD license.
5 # See README for more details.
6
7 import binascii
8 import os
9 import time
10 import subprocess
11 import logging
12 logger = logging.getLogger()
13
14 import hwsim_utils
15 import hostapd
16 from utils import HwsimSkip
17 from test_ap_psk import find_wpas_process, read_process_memory, verify_not_present, get_key_locations
18
19 def test_sae(dev, apdev):
20     """SAE with default group"""
21     if "SAE" not in dev[0].get_capability("auth_alg"):
22         raise HwsimSkip("SAE not supported")
23     params = hostapd.wpa2_params(ssid="test-sae",
24                                  passphrase="12345678")
25     params['wpa_key_mgmt'] = 'SAE'
26     hapd = hostapd.add_ap(apdev[0]['ifname'], params)
27     key_mgmt = hapd.get_config()['key_mgmt']
28     if key_mgmt.split(' ')[0] != "SAE":
29         raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
30
31     dev[0].request("SET sae_groups ")
32     id = dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
33                         scan_freq="2412")
34     if dev[0].get_status_field('sae_group') != '19':
35             raise Exception("Expected default SAE group not used")
36     bss = dev[0].get_bss(apdev[0]['bssid'])
37     if 'flags' not in bss:
38         raise Exception("Could not get BSS flags from BSS table")
39     if "[WPA2-SAE-CCMP]" not in bss['flags']:
40         raise Exception("Unexpected BSS flags: " + bss['flags'])
41
42 def test_sae_pmksa_caching(dev, apdev):
43     """SAE and PMKSA caching"""
44     if "SAE" not in dev[0].get_capability("auth_alg"):
45         raise HwsimSkip("SAE not supported")
46     params = hostapd.wpa2_params(ssid="test-sae",
47                                  passphrase="12345678")
48     params['wpa_key_mgmt'] = 'SAE'
49     hapd = hostapd.add_ap(apdev[0]['ifname'], params)
50
51     dev[0].request("SET sae_groups ")
52     dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
53                    scan_freq="2412")
54     ev = hapd.wait_event([ "AP-STA-CONNECTED" ], timeout=5)
55     if ev is None:
56         raise Exception("No connection event received from hostapd")
57     dev[0].request("DISCONNECT")
58     dev[0].wait_disconnected()
59     dev[0].request("RECONNECT")
60     dev[0].wait_connected(timeout=15, error="Reconnect timed out")
61     if dev[0].get_status_field('sae_group') is not None:
62             raise Exception("SAE group claimed to have been used")
63
64 def test_sae_pmksa_caching_disabled(dev, apdev):
65     """SAE and PMKSA caching disabled"""
66     if "SAE" not in dev[0].get_capability("auth_alg"):
67         raise HwsimSkip("SAE not supported")
68     params = hostapd.wpa2_params(ssid="test-sae",
69                                  passphrase="12345678")
70     params['wpa_key_mgmt'] = 'SAE'
71     params['disable_pmksa_caching'] = '1'
72     hapd = hostapd.add_ap(apdev[0]['ifname'], params)
73
74     dev[0].request("SET sae_groups ")
75     dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
76                    scan_freq="2412")
77     ev = hapd.wait_event([ "AP-STA-CONNECTED" ], timeout=5)
78     if ev is None:
79         raise Exception("No connection event received from hostapd")
80     dev[0].request("DISCONNECT")
81     dev[0].wait_disconnected()
82     dev[0].request("RECONNECT")
83     dev[0].wait_connected(timeout=15, error="Reconnect timed out")
84     if dev[0].get_status_field('sae_group') != '19':
85             raise Exception("Expected default SAE group not used")
86
87 def test_sae_groups(dev, apdev):
88     """SAE with all supported groups"""
89     if "SAE" not in dev[0].get_capability("auth_alg"):
90         raise HwsimSkip("SAE not supported")
91     # This would be the full list of supported groups, but groups 14-16
92     # (2048-4096 bit MODP) are a bit too slow on some VMs and can result in
93     # hitting mac80211 authentication timeout, so skip them for now.
94     #sae_groups = [ 19, 25, 26, 20, 21, 2, 5, 14, 15, 16, 22, 23, 24 ]
95     sae_groups = [ 19, 25, 26, 20, 21, 2, 5, 22, 23, 24 ]
96     groups = [str(g) for g in sae_groups]
97     params = hostapd.wpa2_params(ssid="test-sae-groups",
98                                  passphrase="12345678")
99     params['wpa_key_mgmt'] = 'SAE'
100     params['sae_groups'] = ' '.join(groups)
101     hostapd.add_ap(apdev[0]['ifname'], params)
102
103     for g in groups:
104         logger.info("Testing SAE group " + g)
105         dev[0].request("SET sae_groups " + g)
106         id = dev[0].connect("test-sae-groups", psk="12345678", key_mgmt="SAE",
107                             scan_freq="2412")
108         if dev[0].get_status_field('sae_group') != g:
109             raise Exception("Expected SAE group not used")
110         dev[0].remove_network(id)
111
112 def test_sae_group_nego(dev, apdev):
113     """SAE group negotiation"""
114     if "SAE" not in dev[0].get_capability("auth_alg"):
115         raise HwsimSkip("SAE not supported")
116     params = hostapd.wpa2_params(ssid="test-sae-group-nego",
117                                  passphrase="12345678")
118     params['wpa_key_mgmt'] = 'SAE'
119     params['sae_groups'] = '19'
120     hostapd.add_ap(apdev[0]['ifname'], params)
121
122     dev[0].request("SET sae_groups 25 26 20 19")
123     dev[0].connect("test-sae-group-nego", psk="12345678", key_mgmt="SAE",
124                    scan_freq="2412")
125     if dev[0].get_status_field('sae_group') != '19':
126         raise Exception("Expected SAE group not used")
127
128 def test_sae_anti_clogging(dev, apdev):
129     """SAE anti clogging"""
130     if "SAE" not in dev[0].get_capability("auth_alg"):
131         raise HwsimSkip("SAE not supported")
132     params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678")
133     params['wpa_key_mgmt'] = 'SAE'
134     params['sae_anti_clogging_threshold'] = '1'
135     hostapd.add_ap(apdev[0]['ifname'], params)
136
137     dev[0].request("SET sae_groups ")
138     dev[1].request("SET sae_groups ")
139     id = {}
140     for i in range(0, 2):
141         dev[i].scan(freq="2412")
142         id[i] = dev[i].connect("test-sae", psk="12345678", key_mgmt="SAE",
143                                scan_freq="2412", only_add_network=True)
144     for i in range(0, 2):
145         dev[i].select_network(id[i])
146     for i in range(0, 2):
147         dev[i].wait_connected(timeout=10)
148
149 def test_sae_forced_anti_clogging(dev, apdev):
150     """SAE anti clogging (forced)"""
151     if "SAE" not in dev[0].get_capability("auth_alg"):
152         raise HwsimSkip("SAE not supported")
153     params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678")
154     params['wpa_key_mgmt'] = 'SAE WPA-PSK'
155     params['sae_anti_clogging_threshold'] = '0'
156     hostapd.add_ap(apdev[0]['ifname'], params)
157     dev[2].connect("test-sae", psk="12345678", scan_freq="2412")
158     for i in range(0, 2):
159         dev[i].request("SET sae_groups ")
160         dev[i].connect("test-sae", psk="12345678", key_mgmt="SAE",
161                        scan_freq="2412")
162
163 def test_sae_mixed(dev, apdev):
164     """Mixed SAE and non-SAE network"""
165     if "SAE" not in dev[0].get_capability("auth_alg"):
166         raise HwsimSkip("SAE not supported")
167     params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678")
168     params['wpa_key_mgmt'] = 'SAE WPA-PSK'
169     params['sae_anti_clogging_threshold'] = '0'
170     hostapd.add_ap(apdev[0]['ifname'], params)
171
172     dev[2].connect("test-sae", psk="12345678", scan_freq="2412")
173     for i in range(0, 2):
174         dev[i].request("SET sae_groups ")
175         dev[i].connect("test-sae", psk="12345678", key_mgmt="SAE",
176                        scan_freq="2412")
177
178 def test_sae_missing_password(dev, apdev):
179     """SAE and missing password"""
180     if "SAE" not in dev[0].get_capability("auth_alg"):
181         raise HwsimSkip("SAE not supported")
182     params = hostapd.wpa2_params(ssid="test-sae",
183                                  passphrase="12345678")
184     params['wpa_key_mgmt'] = 'SAE'
185     hapd = hostapd.add_ap(apdev[0]['ifname'], params)
186
187     dev[0].request("SET sae_groups ")
188     id = dev[0].connect("test-sae",
189                         raw_psk="46b4a73b8a951ad53ebd2e0afdb9c5483257edd4c21d12b7710759da70945858",
190                         key_mgmt="SAE", scan_freq="2412", wait_connect=False)
191     ev = dev[0].wait_event(['CTRL-EVENT-SSID-TEMP-DISABLED'], timeout=10)
192     if ev is None:
193         raise Exception("Invalid network not temporarily disabled")
194
195
196 def test_sae_key_lifetime_in_memory(dev, apdev, params):
197     """SAE and key lifetime in memory"""
198     if "SAE" not in dev[0].get_capability("auth_alg"):
199         raise HwsimSkip("SAE not supported")
200     password = "5ad144a7c1f5a5503baa6fa01dabc15b1843e8c01662d78d16b70b5cd23cf8b"
201     p = hostapd.wpa2_params(ssid="test-sae", passphrase=password)
202     p['wpa_key_mgmt'] = 'SAE'
203     hapd = hostapd.add_ap(apdev[0]['ifname'], p)
204
205     pid = find_wpas_process(dev[0])
206
207     dev[0].request("SET sae_groups ")
208     id = dev[0].connect("test-sae", psk=password, key_mgmt="SAE",
209                         scan_freq="2412")
210
211     time.sleep(0.1)
212     buf = read_process_memory(pid, password)
213
214     dev[0].request("DISCONNECT")
215     dev[0].wait_disconnected()
216
217     dev[0].relog()
218     sae_k = None
219     sae_keyseed = None
220     sae_kck = None
221     pmk = None
222     ptk = None
223     gtk = None
224     with open(os.path.join(params['logdir'], 'log0'), 'r') as f:
225         for l in f.readlines():
226             if "SAE: k - hexdump" in l:
227                 val = l.strip().split(':')[3].replace(' ', '')
228                 sae_k = binascii.unhexlify(val)
229             if "SAE: keyseed - hexdump" in l:
230                 val = l.strip().split(':')[3].replace(' ', '')
231                 sae_keyseed = binascii.unhexlify(val)
232             if "SAE: KCK - hexdump" in l:
233                 val = l.strip().split(':')[3].replace(' ', '')
234                 sae_kck = binascii.unhexlify(val)
235             if "SAE: PMK - hexdump" in l:
236                 val = l.strip().split(':')[3].replace(' ', '')
237                 pmk = binascii.unhexlify(val)
238             if "WPA: PTK - hexdump" in l:
239                 val = l.strip().split(':')[3].replace(' ', '')
240                 ptk = binascii.unhexlify(val)
241             if "WPA: Group Key - hexdump" in l:
242                 val = l.strip().split(':')[3].replace(' ', '')
243                 gtk = binascii.unhexlify(val)
244     if not sae_k or not sae_keyseed or not sae_kck or not pmk or not ptk or not gtk:
245         raise Exception("Could not find keys from debug log")
246     if len(gtk) != 16:
247         raise Exception("Unexpected GTK length")
248
249     kck = ptk[0:16]
250     kek = ptk[16:32]
251     tk = ptk[32:48]
252
253     fname = os.path.join(params['logdir'],
254                          'sae_key_lifetime_in_memory.memctx-')
255
256     logger.info("Checking keys in memory while associated")
257     get_key_locations(buf, password, "Password")
258     get_key_locations(buf, pmk, "PMK")
259     if password not in buf:
260         raise HwsimSkip("Password not found while associated")
261     if pmk not in buf:
262         raise HwsimSkip("PMK not found while associated")
263     if kck not in buf:
264         raise Exception("KCK not found while associated")
265     if kek not in buf:
266         raise Exception("KEK not found while associated")
267     if tk in buf:
268         raise Exception("TK found from memory")
269     if gtk in buf:
270         raise Exception("GTK found from memory")
271     verify_not_present(buf, sae_k, fname, "SAE(k)")
272     verify_not_present(buf, sae_keyseed, fname, "SAE(keyseed)")
273     verify_not_present(buf, sae_kck, fname, "SAE(KCK)")
274
275     logger.info("Checking keys in memory after disassociation")
276     buf = read_process_memory(pid, password)
277
278     # Note: Password is still present in network configuration
279     # Note: PMK is in PMKSA cache
280
281     get_key_locations(buf, password, "Password")
282     get_key_locations(buf, pmk, "PMK")
283     verify_not_present(buf, kck, fname, "KCK")
284     verify_not_present(buf, kek, fname, "KEK")
285     verify_not_present(buf, tk, fname, "TK")
286     verify_not_present(buf, gtk, fname, "GTK")
287     verify_not_present(buf, sae_k, fname, "SAE(k)")
288     verify_not_present(buf, sae_keyseed, fname, "SAE(keyseed)")
289     verify_not_present(buf, sae_kck, fname, "SAE(KCK)")
290
291     dev[0].request("PMKSA_FLUSH")
292     logger.info("Checking keys in memory after PMKSA cache flush")
293     buf = read_process_memory(pid, password)
294     get_key_locations(buf, password, "Password")
295     get_key_locations(buf, pmk, "PMK")
296     verify_not_present(buf, pmk, fname, "PMK")
297
298     dev[0].request("REMOVE_NETWORK all")
299
300     logger.info("Checking keys in memory after network profile removal")
301     buf = read_process_memory(pid, password)
302
303     get_key_locations(buf, password, "Password")
304     get_key_locations(buf, pmk, "PMK")
305     verify_not_present(buf, password, fname, "password")
306     verify_not_present(buf, pmk, fname, "PMK")
307     verify_not_present(buf, kck, fname, "KCK")
308     verify_not_present(buf, kek, fname, "KEK")
309     verify_not_present(buf, tk, fname, "TK")
310     verify_not_present(buf, gtk, fname, "GTK")
311     verify_not_present(buf, sae_k, fname, "SAE(k)")
312     verify_not_present(buf, sae_keyseed, fname, "SAE(keyseed)")
313     verify_not_present(buf, sae_kck, fname, "SAE(KCK)")