]> git.ipfire.org Git - thirdparty/hostap.git/blame - tests/hwsim/test_sae.py
tests: Make *_key_lifetime_in_memory more robust
[thirdparty/hostap.git] / tests / hwsim / test_sae.py
CommitLineData
1640a2e4 1# Test cases for SAE
5b3c40a6 2# Copyright (c) 2013-2014, Jouni Malinen <j@w1.fi>
1640a2e4
JM
3#
4# This software may be distributed under the terms of the BSD license.
5# See README for more details.
6
5b3c40a6
JM
7import binascii
8import os
1640a2e4
JM
9import time
10import subprocess
11import logging
12logger = logging.getLogger()
13
14import hwsim_utils
15import hostapd
81e787b7 16from utils import HwsimSkip
5b3c40a6 17from test_ap_psk import find_wpas_process, read_process_memory, verify_not_present, get_key_locations
1640a2e4
JM
18
19def test_sae(dev, apdev):
20 """SAE with default group"""
b9749b6a
JM
21 if "SAE" not in dev[0].get_capability("auth_alg"):
22 raise HwsimSkip("SAE not supported")
1640a2e4
JM
23 params = hostapd.wpa2_params(ssid="test-sae",
24 passphrase="12345678")
25 params['wpa_key_mgmt'] = 'SAE'
65038313
JM
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)
1640a2e4
JM
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")
d463c556
JM
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'])
1640a2e4 41
19d3a6e3
JM
42def test_sae_pmksa_caching(dev, apdev):
43 """SAE and PMKSA caching"""
b9749b6a
JM
44 if "SAE" not in dev[0].get_capability("auth_alg"):
45 raise HwsimSkip("SAE not supported")
19d3a6e3
JM
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")
7cc9a81f
JM
54 ev = hapd.wait_event([ "AP-STA-CONNECTED" ], timeout=5)
55 if ev is None:
56 raise Exception("No connection event received from hostapd")
19d3a6e3 57 dev[0].request("DISCONNECT")
7cc9a81f 58 dev[0].wait_disconnected()
19d3a6e3 59 dev[0].request("RECONNECT")
5f35a5e2 60 dev[0].wait_connected(timeout=15, error="Reconnect timed out")
19d3a6e3
JM
61 if dev[0].get_status_field('sae_group') is not None:
62 raise Exception("SAE group claimed to have been used")
63
64def test_sae_pmksa_caching_disabled(dev, apdev):
65 """SAE and PMKSA caching disabled"""
b9749b6a
JM
66 if "SAE" not in dev[0].get_capability("auth_alg"):
67 raise HwsimSkip("SAE not supported")
19d3a6e3
JM
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")
7cc9a81f
JM
77 ev = hapd.wait_event([ "AP-STA-CONNECTED" ], timeout=5)
78 if ev is None:
79 raise Exception("No connection event received from hostapd")
19d3a6e3 80 dev[0].request("DISCONNECT")
7cc9a81f 81 dev[0].wait_disconnected()
19d3a6e3 82 dev[0].request("RECONNECT")
5f35a5e2 83 dev[0].wait_connected(timeout=15, error="Reconnect timed out")
19d3a6e3
JM
84 if dev[0].get_status_field('sae_group') != '19':
85 raise Exception("Expected default SAE group not used")
86
1640a2e4
JM
87def test_sae_groups(dev, apdev):
88 """SAE with all supported groups"""
b9749b6a
JM
89 if "SAE" not in dev[0].get_capability("auth_alg"):
90 raise HwsimSkip("SAE not supported")
1640a2e4
JM
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
112def test_sae_group_nego(dev, apdev):
113 """SAE group negotiation"""
b9749b6a
JM
114 if "SAE" not in dev[0].get_capability("auth_alg"):
115 raise HwsimSkip("SAE not supported")
1640a2e4
JM
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")
a6cf5cd6
JM
127
128def test_sae_anti_clogging(dev, apdev):
129 """SAE anti clogging"""
b9749b6a
JM
130 if "SAE" not in dev[0].get_capability("auth_alg"):
131 raise HwsimSkip("SAE not supported")
a6cf5cd6
JM
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):
5f35a5e2 147 dev[i].wait_connected(timeout=10)
d05ff960
JM
148
149def test_sae_forced_anti_clogging(dev, apdev):
150 """SAE anti clogging (forced)"""
b9749b6a
JM
151 if "SAE" not in dev[0].get_capability("auth_alg"):
152 raise HwsimSkip("SAE not supported")
d05ff960 153 params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678")
fd4709ff 154 params['wpa_key_mgmt'] = 'SAE WPA-PSK'
d05ff960
JM
155 params['sae_anti_clogging_threshold'] = '0'
156 hostapd.add_ap(apdev[0]['ifname'], params)
fd4709ff 157 dev[2].connect("test-sae", psk="12345678", scan_freq="2412")
d05ff960
JM
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
163def test_sae_mixed(dev, apdev):
164 """Mixed SAE and non-SAE network"""
b9749b6a
JM
165 if "SAE" not in dev[0].get_capability("auth_alg"):
166 raise HwsimSkip("SAE not supported")
d05ff960
JM
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")
acb63c75
JM
177
178def test_sae_missing_password(dev, apdev):
179 """SAE and missing password"""
b9749b6a
JM
180 if "SAE" not in dev[0].get_capability("auth_alg"):
181 raise HwsimSkip("SAE not supported")
acb63c75
JM
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")
5b3c40a6
JM
194
195
196def test_sae_key_lifetime_in_memory(dev, apdev, params):
197 """SAE and key lifetime in memory"""
b9749b6a
JM
198 if "SAE" not in dev[0].get_capability("auth_alg"):
199 raise HwsimSkip("SAE not supported")
5b3c40a6
JM
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
54f2cae2 211 time.sleep(1)
5b3c40a6
JM
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:
81e787b7 260 raise HwsimSkip("Password not found while associated")
5b3c40a6 261 if pmk not in buf:
81e787b7 262 raise HwsimSkip("PMK not found while associated")
5b3c40a6
JM
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)")