test_sae.py 12 KB

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