]>
Commit | Line | Data |
---|---|---|
acc9a635 | 1 | # EAP Re-authentication Protocol (ERP) tests |
3b51cc63 | 2 | # Copyright (c) 2014-2015, Jouni Malinen <j@w1.fi> |
acc9a635 JM |
3 | # |
4 | # This software may be distributed under the terms of the BSD license. | |
5 | # See README for more details. | |
6 | ||
5b3c40a6 | 7 | import binascii |
acc9a635 JM |
8 | import logging |
9 | logger = logging.getLogger() | |
5b3c40a6 JM |
10 | import os |
11 | import time | |
acc9a635 JM |
12 | |
13 | import hostapd | |
81e787b7 | 14 | from utils import HwsimSkip |
acc9a635 | 15 | from test_ap_eap import int_eap_server_params |
5b3c40a6 | 16 | from test_ap_psk import find_wpas_process, read_process_memory, verify_not_present, get_key_locations |
acc9a635 | 17 | |
81e787b7 JM |
18 | def check_erp_capa(dev): |
19 | capab = dev.get_capability("erp") | |
20 | if not capab or 'ERP' not in capab: | |
21 | raise HwsimSkip("ERP not supported in the build") | |
22 | ||
acc9a635 JM |
23 | def test_erp_initiate_reauth_start(dev, apdev): |
24 | """Authenticator sending EAP-Initiate/Re-auth-Start, but ERP disabled on peer""" | |
25 | params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap") | |
26 | params['erp_send_reauth_start'] = '1' | |
27 | params['erp_domain'] = 'example.com' | |
28 | hapd = hostapd.add_ap(apdev[0]['ifname'], params) | |
29 | ||
30 | dev[0].request("ERP_FLUSH") | |
31 | dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", | |
32 | eap="PAX", identity="pax.user@example.com", | |
33 | password_hex="0123456789abcdef0123456789abcdef", | |
34 | scan_freq="2412") | |
35 | ||
36 | def test_erp_enabled_on_server(dev, apdev): | |
37 | """ERP enabled on internal EAP server, but disabled on peer""" | |
38 | params = int_eap_server_params() | |
39 | params['erp_send_reauth_start'] = '1' | |
40 | params['erp_domain'] = 'example.com' | |
41 | params['eap_server_erp'] = '1' | |
42 | hapd = hostapd.add_ap(apdev[0]['ifname'], params) | |
43 | ||
44 | dev[0].request("ERP_FLUSH") | |
45 | dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", | |
46 | eap="PAX", identity="pax.user@example.com", | |
47 | password_hex="0123456789abcdef0123456789abcdef", | |
48 | scan_freq="2412") | |
49 | ||
50 | def test_erp(dev, apdev): | |
51 | """ERP enabled on server and peer""" | |
81e787b7 | 52 | check_erp_capa(dev[0]) |
acc9a635 JM |
53 | params = int_eap_server_params() |
54 | params['erp_send_reauth_start'] = '1' | |
55 | params['erp_domain'] = 'example.com' | |
56 | params['eap_server_erp'] = '1' | |
57 | params['disable_pmksa_caching'] = '1' | |
58 | hapd = hostapd.add_ap(apdev[0]['ifname'], params) | |
59 | ||
60 | dev[0].request("ERP_FLUSH") | |
61 | dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", | |
62 | eap="PSK", identity="psk.user@example.com", | |
63 | password_hex="0123456789abcdef0123456789abcdef", | |
64 | erp="1", scan_freq="2412") | |
65 | for i in range(3): | |
66 | dev[0].request("DISCONNECT") | |
5f35a5e2 | 67 | dev[0].wait_disconnected(timeout=15) |
acc9a635 JM |
68 | dev[0].request("RECONNECT") |
69 | ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15) | |
70 | if ev is None: | |
71 | raise Exception("EAP success timed out") | |
72 | if "EAP re-authentication completed successfully" not in ev: | |
73 | raise Exception("Did not use ERP") | |
5f35a5e2 | 74 | dev[0].wait_connected(timeout=15, error="Reconnection timed out") |
acc9a635 | 75 | |
0e40d7da JM |
76 | def test_erp_server_no_match(dev, apdev): |
77 | """ERP enabled on server and peer, but server has no key match""" | |
81e787b7 | 78 | check_erp_capa(dev[0]) |
0e40d7da JM |
79 | params = int_eap_server_params() |
80 | params['erp_send_reauth_start'] = '1' | |
81 | params['erp_domain'] = 'example.com' | |
82 | params['eap_server_erp'] = '1' | |
83 | params['disable_pmksa_caching'] = '1' | |
84 | hapd = hostapd.add_ap(apdev[0]['ifname'], params) | |
85 | ||
86 | dev[0].request("ERP_FLUSH") | |
87 | id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", | |
88 | eap="PSK", identity="psk.user@example.com", | |
89 | password_hex="0123456789abcdef0123456789abcdef", | |
90 | erp="1", scan_freq="2412") | |
91 | dev[0].request("DISCONNECT") | |
5f35a5e2 | 92 | dev[0].wait_disconnected(timeout=15) |
0e40d7da JM |
93 | hapd.request("ERP_FLUSH") |
94 | dev[0].request("RECONNECT") | |
95 | ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS", | |
96 | "CTRL-EVENT-EAP-FAILURE"], timeout=15) | |
97 | if ev is None: | |
98 | raise Exception("EAP result timed out") | |
99 | if "CTRL-EVENT-EAP-SUCCESS" in ev: | |
100 | raise Exception("Unexpected EAP success") | |
101 | dev[0].request("DISCONNECT") | |
102 | dev[0].select_network(id) | |
103 | ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15) | |
104 | if ev is None: | |
105 | raise Exception("EAP success timed out") | |
106 | if "EAP re-authentication completed successfully" in ev: | |
107 | raise Exception("Unexpected use of ERP") | |
5f35a5e2 | 108 | dev[0].wait_connected(timeout=15, error="Reconnection timed out") |
0e40d7da | 109 | |
acc9a635 JM |
110 | def start_erp_as(apdev): |
111 | params = { "ssid": "as", "beacon_int": "2000", | |
112 | "radius_server_clients": "auth_serv/radius_clients.conf", | |
113 | "radius_server_auth_port": '18128', | |
114 | "eap_server": "1", | |
115 | "eap_user_file": "auth_serv/eap_user.conf", | |
116 | "ca_cert": "auth_serv/ca.pem", | |
117 | "server_cert": "auth_serv/server.pem", | |
118 | "private_key": "auth_serv/server.key", | |
119 | "eap_sim_db": "unix:/tmp/hlr_auc_gw.sock", | |
120 | "dh_file": "auth_serv/dh.conf", | |
121 | "pac_opaque_encr_key": "000102030405060708090a0b0c0d0e0f", | |
122 | "eap_fast_a_id": "101112131415161718191a1b1c1d1e1f", | |
123 | "eap_fast_a_id_info": "test server", | |
124 | "eap_server_erp": "1", | |
125 | "erp_domain": "example.com" } | |
126 | hostapd.add_ap(apdev['ifname'], params) | |
127 | ||
128 | def test_erp_radius(dev, apdev): | |
129 | """ERP enabled on RADIUS server and peer""" | |
81e787b7 | 130 | check_erp_capa(dev[0]) |
acc9a635 JM |
131 | start_erp_as(apdev[1]) |
132 | params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap") | |
133 | params['auth_server_port'] = "18128" | |
134 | params['erp_send_reauth_start'] = '1' | |
135 | params['erp_domain'] = 'example.com' | |
136 | params['disable_pmksa_caching'] = '1' | |
137 | hapd = hostapd.add_ap(apdev[0]['ifname'], params) | |
138 | ||
139 | dev[0].request("ERP_FLUSH") | |
140 | dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", | |
141 | eap="PSK", identity="psk.user@example.com", | |
142 | password_hex="0123456789abcdef0123456789abcdef", | |
143 | erp="1", scan_freq="2412") | |
144 | for i in range(3): | |
145 | dev[0].request("DISCONNECT") | |
5f35a5e2 | 146 | dev[0].wait_disconnected(timeout=15) |
acc9a635 JM |
147 | dev[0].request("RECONNECT") |
148 | ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15) | |
149 | if ev is None: | |
150 | raise Exception("EAP success timed out") | |
151 | if "EAP re-authentication completed successfully" not in ev: | |
152 | raise Exception("Did not use ERP") | |
5f35a5e2 | 153 | dev[0].wait_connected(timeout=15, error="Reconnection timed out") |
acc9a635 JM |
154 | |
155 | def erp_test(dev, hapd, **kwargs): | |
156 | hapd.dump_monitor() | |
157 | dev.dump_monitor() | |
158 | dev.request("ERP_FLUSH") | |
159 | id = dev.connect("test-wpa2-eap", key_mgmt="WPA-EAP", erp="1", | |
160 | scan_freq="2412", **kwargs) | |
161 | dev.request("DISCONNECT") | |
5f35a5e2 | 162 | dev.wait_disconnected(timeout=15) |
acc9a635 JM |
163 | hapd.dump_monitor() |
164 | dev.request("RECONNECT") | |
165 | ev = dev.wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15) | |
166 | if ev is None: | |
167 | raise Exception("EAP success timed out") | |
168 | if "EAP re-authentication completed successfully" not in ev: | |
169 | raise Exception("Did not use ERP") | |
5f35a5e2 | 170 | dev.wait_connected(timeout=15, error="Reconnection timed out") |
acc9a635 JM |
171 | ev = hapd.wait_event([ "AP-STA-CONNECTED" ], timeout=5) |
172 | if ev is None: | |
173 | raise Exception("No connection event received from hostapd") | |
174 | dev.request("DISCONNECT") | |
175 | ||
176 | def test_erp_radius_eap_methods(dev, apdev): | |
177 | """ERP enabled on RADIUS server and peer""" | |
81e787b7 | 178 | check_erp_capa(dev[0]) |
acc9a635 JM |
179 | start_erp_as(apdev[1]) |
180 | params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap") | |
181 | params['auth_server_port'] = "18128" | |
182 | params['erp_send_reauth_start'] = '1' | |
183 | params['erp_domain'] = 'example.com' | |
184 | params['disable_pmksa_caching'] = '1' | |
185 | hapd = hostapd.add_ap(apdev[0]['ifname'], params) | |
186 | ||
187 | erp_test(dev[0], hapd, eap="AKA", identity="0232010000000000@example.com", | |
188 | password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123") | |
189 | erp_test(dev[0], hapd, eap="AKA'", identity="6555444333222111@example.com", | |
190 | password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123") | |
191 | # TODO: EKE getSession | |
192 | #erp_test(dev[0], hapd, eap="EKE", identity="erp-eke@example.com", | |
193 | # password="hello") | |
3b51cc63 JM |
194 | if "FAST" in dev[0].get_capability("eap"): |
195 | erp_test(dev[0], hapd, eap="FAST", identity="erp-fast@example.com", | |
196 | password="password", ca_cert="auth_serv/ca.pem", | |
197 | phase2="auth=GTC", | |
198 | phase1="fast_provisioning=2", | |
199 | pac_file="blob://fast_pac_auth_erp") | |
acc9a635 JM |
200 | erp_test(dev[0], hapd, eap="GPSK", identity="erp-gpsk@example.com", |
201 | password="abcdefghijklmnop0123456789abcdef") | |
f41f670e JM |
202 | erp_test(dev[0], hapd, eap="IKEV2", identity="erp-ikev2@example.com", |
203 | password="password") | |
acc9a635 JM |
204 | erp_test(dev[0], hapd, eap="PAX", identity="erp-pax@example.com", |
205 | password_hex="0123456789abcdef0123456789abcdef") | |
206 | # TODO: PEAP (EMSK) | |
207 | #erp_test(dev[0], hapd, eap="PEAP", identity="erp-peap@example.com", | |
208 | # password="password", ca_cert="auth_serv/ca.pem", | |
209 | # phase2="auth=MSCHAPV2") | |
210 | erp_test(dev[0], hapd, eap="PSK", identity="erp-psk@example.com", | |
211 | password_hex="0123456789abcdef0123456789abcdef") | |
3b51cc63 JM |
212 | if "PWD" in dev[0].get_capability("eap"): |
213 | erp_test(dev[0], hapd, eap="PWD", identity="erp-pwd@example.com", | |
214 | password="secret password") | |
acc9a635 JM |
215 | erp_test(dev[0], hapd, eap="SAKE", identity="erp-sake@example.com", |
216 | password_hex="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef") | |
217 | erp_test(dev[0], hapd, eap="SIM", identity="1232010000000000@example.com", | |
218 | password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581") | |
219 | erp_test(dev[0], hapd, eap="TLS", identity="erp-tls@example.com", | |
220 | ca_cert="auth_serv/ca.pem", client_cert="auth_serv/user.pem", | |
221 | private_key="auth_serv/user.key") | |
222 | erp_test(dev[0], hapd, eap="TTLS", identity="erp-ttls@example.com", | |
223 | password="password", ca_cert="auth_serv/ca.pem", phase2="auth=PAP") | |
5b3c40a6 JM |
224 | |
225 | def test_erp_key_lifetime_in_memory(dev, apdev, params): | |
226 | """ERP and key lifetime in memory""" | |
81e787b7 | 227 | check_erp_capa(dev[0]) |
5b3c40a6 JM |
228 | p = int_eap_server_params() |
229 | p['erp_send_reauth_start'] = '1' | |
230 | p['erp_domain'] = 'example.com' | |
231 | p['eap_server_erp'] = '1' | |
232 | p['disable_pmksa_caching'] = '1' | |
233 | hapd = hostapd.add_ap(apdev[0]['ifname'], p) | |
234 | password = "63d2d21ac3c09ed567ee004a34490f1d16e7fa5835edf17ddba70a63f1a90a25" | |
235 | ||
236 | pid = find_wpas_process(dev[0]) | |
237 | ||
238 | dev[0].request("ERP_FLUSH") | |
239 | dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS", | |
240 | identity="pap-secret@example.com", password=password, | |
241 | ca_cert="auth_serv/ca.pem", phase2="auth=PAP", | |
242 | erp="1", scan_freq="2412") | |
243 | ||
54f2cae2 | 244 | time.sleep(1) |
5b3c40a6 JM |
245 | buf = read_process_memory(pid, password) |
246 | ||
247 | dev[0].request("DISCONNECT") | |
248 | dev[0].wait_disconnected(timeout=15) | |
249 | ||
250 | dev[0].relog() | |
750904dd JM |
251 | msk = None |
252 | emsk = None | |
5b3c40a6 JM |
253 | rRK = None |
254 | rIK = None | |
255 | pmk = None | |
256 | ptk = None | |
257 | gtk = None | |
258 | with open(os.path.join(params['logdir'], 'log0'), 'r') as f: | |
259 | for l in f.readlines(): | |
750904dd JM |
260 | if "EAP-TTLS: Derived key - hexdump" in l: |
261 | val = l.strip().split(':')[3].replace(' ', '') | |
262 | msk = binascii.unhexlify(val) | |
263 | if "EAP-TTLS: Derived EMSK - hexdump" in l: | |
264 | val = l.strip().split(':')[3].replace(' ', '') | |
265 | emsk = binascii.unhexlify(val) | |
5b3c40a6 JM |
266 | if "EAP: ERP rRK - hexdump" in l: |
267 | val = l.strip().split(':')[3].replace(' ', '') | |
268 | rRK = binascii.unhexlify(val) | |
269 | if "EAP: ERP rIK - hexdump" in l: | |
270 | val = l.strip().split(':')[3].replace(' ', '') | |
271 | rIK = binascii.unhexlify(val) | |
272 | if "WPA: PMK - hexdump" in l: | |
273 | val = l.strip().split(':')[3].replace(' ', '') | |
274 | pmk = binascii.unhexlify(val) | |
275 | if "WPA: PTK - hexdump" in l: | |
276 | val = l.strip().split(':')[3].replace(' ', '') | |
277 | ptk = binascii.unhexlify(val) | |
278 | if "WPA: Group Key - hexdump" in l: | |
279 | val = l.strip().split(':')[3].replace(' ', '') | |
280 | gtk = binascii.unhexlify(val) | |
750904dd | 281 | if not msk or not emsk or not rIK or not rRK or not pmk or not ptk or not gtk: |
5b3c40a6 JM |
282 | raise Exception("Could not find keys from debug log") |
283 | if len(gtk) != 16: | |
284 | raise Exception("Unexpected GTK length") | |
285 | ||
286 | kck = ptk[0:16] | |
287 | kek = ptk[16:32] | |
288 | tk = ptk[32:48] | |
289 | ||
290 | fname = os.path.join(params['logdir'], | |
291 | 'erp_key_lifetime_in_memory.memctx-') | |
292 | ||
293 | logger.info("Checking keys in memory while associated") | |
294 | get_key_locations(buf, password, "Password") | |
295 | get_key_locations(buf, pmk, "PMK") | |
750904dd JM |
296 | get_key_locations(buf, msk, "MSK") |
297 | get_key_locations(buf, emsk, "EMSK") | |
5b3c40a6 JM |
298 | get_key_locations(buf, rRK, "rRK") |
299 | get_key_locations(buf, rIK, "rIK") | |
300 | if password not in buf: | |
81e787b7 | 301 | raise HwsimSkip("Password not found while associated") |
5b3c40a6 | 302 | if pmk not in buf: |
81e787b7 | 303 | raise HwsimSkip("PMK not found while associated") |
5b3c40a6 JM |
304 | if kck not in buf: |
305 | raise Exception("KCK not found while associated") | |
306 | if kek not in buf: | |
307 | raise Exception("KEK not found while associated") | |
308 | if tk in buf: | |
309 | raise Exception("TK found from memory") | |
310 | if gtk in buf: | |
311 | raise Exception("GTK found from memory") | |
312 | ||
313 | logger.info("Checking keys in memory after disassociation") | |
314 | buf = read_process_memory(pid, password) | |
315 | ||
316 | # Note: Password is still present in network configuration | |
317 | # Note: PMK is in EAP fast re-auth data | |
318 | ||
319 | get_key_locations(buf, password, "Password") | |
320 | get_key_locations(buf, pmk, "PMK") | |
750904dd JM |
321 | get_key_locations(buf, msk, "MSK") |
322 | get_key_locations(buf, emsk, "EMSK") | |
5b3c40a6 JM |
323 | get_key_locations(buf, rRK, "rRK") |
324 | get_key_locations(buf, rIK, "rIK") | |
325 | verify_not_present(buf, kck, fname, "KCK") | |
326 | verify_not_present(buf, kek, fname, "KEK") | |
327 | verify_not_present(buf, tk, fname, "TK") | |
328 | verify_not_present(buf, gtk, fname, "GTK") | |
329 | ||
330 | dev[0].request("RECONNECT") | |
331 | ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15) | |
332 | if ev is None: | |
333 | raise Exception("EAP success timed out") | |
334 | if "EAP re-authentication completed successfully" not in ev: | |
335 | raise Exception("Did not use ERP") | |
336 | dev[0].wait_connected(timeout=15, error="Reconnection timed out") | |
337 | ||
338 | dev[0].request("DISCONNECT") | |
339 | dev[0].wait_disconnected(timeout=15) | |
340 | ||
341 | dev[0].relog() | |
342 | pmk = None | |
343 | ptk = None | |
344 | gtk = None | |
345 | with open(os.path.join(params['logdir'], 'log0'), 'r') as f: | |
346 | for l in f.readlines(): | |
347 | if "WPA: PMK - hexdump" in l: | |
348 | val = l.strip().split(':')[3].replace(' ', '') | |
349 | pmk = binascii.unhexlify(val) | |
350 | if "WPA: PTK - hexdump" in l: | |
351 | val = l.strip().split(':')[3].replace(' ', '') | |
352 | ptk = binascii.unhexlify(val) | |
353 | if "WPA: GTK in EAPOL-Key - hexdump" in l: | |
354 | val = l.strip().split(':')[3].replace(' ', '') | |
355 | gtk = binascii.unhexlify(val) | |
356 | if not pmk or not ptk or not gtk: | |
357 | raise Exception("Could not find keys from debug log") | |
358 | ||
359 | kck = ptk[0:16] | |
360 | kek = ptk[16:32] | |
361 | tk = ptk[32:48] | |
362 | ||
363 | logger.info("Checking keys in memory after ERP and disassociation") | |
364 | buf = read_process_memory(pid, password) | |
365 | ||
366 | # Note: Password is still present in network configuration | |
367 | ||
368 | get_key_locations(buf, password, "Password") | |
369 | get_key_locations(buf, pmk, "PMK") | |
750904dd JM |
370 | get_key_locations(buf, msk, "MSK") |
371 | get_key_locations(buf, emsk, "EMSK") | |
5b3c40a6 JM |
372 | get_key_locations(buf, rRK, "rRK") |
373 | get_key_locations(buf, rIK, "rIK") | |
374 | verify_not_present(buf, kck, fname, "KCK") | |
375 | verify_not_present(buf, kek, fname, "KEK") | |
376 | verify_not_present(buf, tk, fname, "TK") | |
377 | verify_not_present(buf, gtk, fname, "GTK") | |
378 | ||
379 | dev[0].request("REMOVE_NETWORK all") | |
380 | ||
381 | logger.info("Checking keys in memory after network profile removal") | |
382 | buf = read_process_memory(pid, password) | |
383 | ||
384 | # Note: rRK and rIK are still in memory | |
385 | ||
386 | get_key_locations(buf, password, "Password") | |
387 | get_key_locations(buf, pmk, "PMK") | |
750904dd JM |
388 | get_key_locations(buf, msk, "MSK") |
389 | get_key_locations(buf, emsk, "EMSK") | |
5b3c40a6 JM |
390 | get_key_locations(buf, rRK, "rRK") |
391 | get_key_locations(buf, rIK, "rIK") | |
392 | verify_not_present(buf, password, fname, "password") | |
393 | verify_not_present(buf, pmk, fname, "PMK") | |
394 | verify_not_present(buf, kck, fname, "KCK") | |
395 | verify_not_present(buf, kek, fname, "KEK") | |
396 | verify_not_present(buf, tk, fname, "TK") | |
397 | verify_not_present(buf, gtk, fname, "GTK") | |
750904dd JM |
398 | verify_not_present(buf, msk, fname, "MSK") |
399 | verify_not_present(buf, emsk, fname, "EMSK") | |
5b3c40a6 JM |
400 | |
401 | dev[0].request("ERP_FLUSH") | |
402 | logger.info("Checking keys in memory after ERP_FLUSH") | |
403 | buf = read_process_memory(pid, password) | |
404 | get_key_locations(buf, rRK, "rRK") | |
405 | get_key_locations(buf, rIK, "rIK") | |
406 | verify_not_present(buf, rRK, fname, "rRK") | |
407 | verify_not_present(buf, rIK, fname, "rIK") |