test_erp.py 17 KB

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