From 5b3c40a65bf14b00746f061570f9376fee38fbf3 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Mon, 29 Dec 2014 13:13:25 +0200 Subject: [PATCH] tests: Verify that wpa_supplicant clears keys from memory Check that PMK and PTK and not left in memory (heap or stack) unnecessarily after they are not needed anymore. Signed-off-by: Jouni Malinen --- tests/hwsim/auth_serv/eap_user.conf | 2 + tests/hwsim/test_ap_eap.py | 98 +++++++++++++++- tests/hwsim/test_ap_ft.py | 109 ++++++++++++++++- tests/hwsim/test_ap_psk.py | 157 +++++++++++++++++++++++++ tests/hwsim/test_erp.py | 174 ++++++++++++++++++++++++++++ tests/hwsim/test_sae.py | 125 +++++++++++++++++++- 6 files changed, 662 insertions(+), 3 deletions(-) diff --git a/tests/hwsim/auth_serv/eap_user.conf b/tests/hwsim/auth_serv/eap_user.conf index 55569e217..873cf4862 100644 --- a/tests/hwsim/auth_serv/eap_user.conf +++ b/tests/hwsim/auth_serv/eap_user.conf @@ -74,6 +74,8 @@ radius_accept_attr=27:d:3 "8"* AKA' [2] "pap user" TTLS-PAP "password" [2] +"pap-secret" TTLS-PAP "63d2d21ac3c09ed567ee004a34490f1d16e7fa5835edf17ddba70a63f1a90a25" [2] +"pap-secret@example.com" TTLS-PAP "63d2d21ac3c09ed567ee004a34490f1d16e7fa5835edf17ddba70a63f1a90a25" [2] "chap user" TTLS-CHAP "password" [2] "mschap user" TTLS-MSCHAP "password" [2] "DOMAIN\mschapv2 user" TTLS-MSCHAPV2 hash:8846f7eaee8fb117ad06bdd830b7586c [2] diff --git a/tests/hwsim/test_ap_eap.py b/tests/hwsim/test_ap_eap.py index e14b470da..b1dcd8f58 100644 --- a/tests/hwsim/test_ap_eap.py +++ b/tests/hwsim/test_ap_eap.py @@ -6,6 +6,7 @@ # See README for more details. import base64 +import binascii import time import subprocess import logging @@ -14,7 +15,7 @@ import os import hwsim_utils import hostapd -from test_ap_psk import check_mib +from test_ap_psk import check_mib, find_wpas_process, read_process_memory, verify_not_present, get_key_locations def read_pem(fname): with open(fname, "r") as f: @@ -2243,3 +2244,98 @@ def test_openssl_cipher_suite_config_hapd(dev, apdev): anonymous_identity="ttls", password="password", openssl_ciphers="HIGH:!ADH", ca_cert="auth_serv/ca.pem", phase2="auth=PAP") + +def test_wpa2_eap_ttls_pap_key_lifetime_in_memory(dev, apdev, params): + """Key lifetime in memory with WPA2-Enterprise using EAP-TTLS/PAP""" + p = hostapd.wpa2_eap_params(ssid="test-wpa2-eap") + hapd = hostapd.add_ap(apdev[0]['ifname'], p) + password = "63d2d21ac3c09ed567ee004a34490f1d16e7fa5835edf17ddba70a63f1a90a25" + pid = find_wpas_process(dev[0]) + id = eap_connect(dev[0], apdev[0], "TTLS", "pap-secret", + anonymous_identity="ttls", password=password, + ca_cert="auth_serv/ca.pem", phase2="auth=PAP") + time.sleep(0.1) + buf = read_process_memory(pid, password) + + dev[0].request("DISCONNECT") + dev[0].wait_disconnected() + + dev[0].relog() + pmk = None + ptk = None + gtk = None + with open(os.path.join(params['logdir'], 'log0'), 'r') as f: + for l in f.readlines(): + if "WPA: PMK - hexdump" in l: + val = l.strip().split(':')[3].replace(' ', '') + pmk = binascii.unhexlify(val) + if "WPA: PTK - hexdump" in l: + val = l.strip().split(':')[3].replace(' ', '') + ptk = binascii.unhexlify(val) + if "WPA: Group Key - hexdump" in l: + val = l.strip().split(':')[3].replace(' ', '') + gtk = binascii.unhexlify(val) + if not pmk or not ptk or not gtk: + raise Exception("Could not find keys from debug log") + if len(gtk) != 16: + raise Exception("Unexpected GTK length") + + kck = ptk[0:16] + kek = ptk[16:32] + tk = ptk[32:48] + + fname = os.path.join(params['logdir'], + 'wpa2_eap_ttls_pap_key_lifetime_in_memory.memctx-') + + logger.info("Checking keys in memory while associated") + get_key_locations(buf, password, "Password") + get_key_locations(buf, pmk, "PMK") + if password not in buf: + print("Password not found while associated") + return "skip" + if pmk not in buf: + print("PMK not found while associated") + return "skip" + if kck not in buf: + raise Exception("KCK not found while associated") + if kek not in buf: + raise Exception("KEK not found while associated") + if tk in buf: + raise Exception("TK found from memory") + if gtk in buf: + raise Exception("GTK found from memory") + + logger.info("Checking keys in memory after disassociation") + buf = read_process_memory(pid, password) + + # Note: Password is still present in network configuration + # Note: PMK is in PMKSA cache and EAP fast re-auth data + + get_key_locations(buf, password, "Password") + get_key_locations(buf, pmk, "PMK") + verify_not_present(buf, kck, fname, "KCK") + verify_not_present(buf, kek, fname, "KEK") + verify_not_present(buf, tk, fname, "TK") + verify_not_present(buf, gtk, fname, "GTK") + + dev[0].request("PMKSA_FLUSH") + dev[0].set_network_quoted(id, "identity", "foo") + logger.info("Checking keys in memory after PMKSA cache and EAP fast reauth flush") + buf = read_process_memory(pid, password) + get_key_locations(buf, password, "Password") + get_key_locations(buf, pmk, "PMK") + verify_not_present(buf, pmk, fname, "PMK") + + dev[0].request("REMOVE_NETWORK all") + + logger.info("Checking keys in memory after network profile removal") + buf = read_process_memory(pid, password) + + get_key_locations(buf, password, "Password") + get_key_locations(buf, pmk, "PMK") + verify_not_present(buf, password, fname, "password") + verify_not_present(buf, pmk, fname, "PMK") + verify_not_present(buf, kck, fname, "KCK") + verify_not_present(buf, kek, fname, "KEK") + verify_not_present(buf, tk, fname, "TK") + verify_not_present(buf, gtk, fname, "GTK") diff --git a/tests/hwsim/test_ap_ft.py b/tests/hwsim/test_ap_ft.py index fe0c6bb5e..6ec0e97c7 100644 --- a/tests/hwsim/test_ap_ft.py +++ b/tests/hwsim/test_ap_ft.py @@ -4,6 +4,8 @@ # This software may be distributed under the terms of the BSD license. # See README for more details. +import binascii +import os import time import subprocess import logging @@ -12,7 +14,7 @@ logger = logging.getLogger() import hwsim_utils import hostapd from wlantest import Wlantest -from test_ap_psk import check_mib +from test_ap_psk import check_mib, find_wpas_process, read_process_memory, verify_not_present, get_key_locations def ft_base_rsn(): params = { "wpa": "2", @@ -456,3 +458,108 @@ def test_ap_ft_gtk_rekey(dev, apdev): if ev is None: raise Exception("GTK rekey timed out after FT protocol") hwsim_utils.test_connectivity(dev[0], hapd1) + +def test_ft_psk_key_lifetime_in_memory(dev, apdev, params): + """WPA2-PSK-FT and key lifetime in memory""" + ssid = "test-ft" + passphrase="04c2726b4b8d5f1b4db9c07aa4d9e9d8f765cb5d25ec817e6cc4fcdd5255db0" + psk = '93c90846ff67af9037ed83fb72b63dbeddaa81d47f926c20909b5886f1d9358d' + pmk = binascii.unhexlify(psk) + p = ft_params1(ssid=ssid, passphrase=passphrase) + hapd0 = hostapd.add_ap(apdev[0]['ifname'], p) + p = ft_params2(ssid=ssid, passphrase=passphrase) + hapd1 = hostapd.add_ap(apdev[1]['ifname'], p) + + pid = find_wpas_process(dev[0]) + + dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2", + scan_freq="2412") + time.sleep(0.1) + + buf = read_process_memory(pid, pmk) + + dev[0].request("DISCONNECT") + dev[0].wait_disconnected() + + dev[0].relog() + pmkr0 = None + pmkr1 = None + ptk = None + gtk = None + with open(os.path.join(params['logdir'], 'log0'), 'r') as f: + for l in f.readlines(): + if "FT: PMK-R0 - hexdump" in l: + val = l.strip().split(':')[3].replace(' ', '') + pmkr0 = binascii.unhexlify(val) + if "FT: PMK-R1 - hexdump" in l: + val = l.strip().split(':')[3].replace(' ', '') + pmkr1 = binascii.unhexlify(val) + if "FT: PTK - hexdump" in l: + val = l.strip().split(':')[3].replace(' ', '') + ptk = binascii.unhexlify(val) + if "WPA: Group Key - hexdump" in l: + val = l.strip().split(':')[3].replace(' ', '') + gtk = binascii.unhexlify(val) + if not pmkr0 or not pmkr1 or not ptk or not gtk: + raise Exception("Could not find keys from debug log") + if len(gtk) != 16: + raise Exception("Unexpected GTK length") + + kck = ptk[0:16] + kek = ptk[16:32] + tk = ptk[32:48] + + logger.info("Checking keys in memory while associated") + get_key_locations(buf, pmk, "PMK") + get_key_locations(buf, pmkr0, "PMK-R0") + get_key_locations(buf, pmkr1, "PMK-R1") + if pmk not in buf: + print("PMK not found while associated") + return "skip" + if pmkr0 not in buf: + print("PMK-R0 not found while associated") + return "skip" + if pmkr1 not in buf: + print("PMK-R1 not found while associated") + return "skip" + if kck not in buf: + raise Exception("KCK not found while associated") + if kek not in buf: + raise Exception("KEK not found while associated") + if tk in buf: + raise Exception("TK found from memory") + if gtk in buf: + raise Exception("GTK found from memory") + + logger.info("Checking keys in memory after disassociation") + buf = read_process_memory(pid, pmk) + get_key_locations(buf, pmk, "PMK") + get_key_locations(buf, pmkr0, "PMK-R0") + get_key_locations(buf, pmkr1, "PMK-R1") + + # Note: PMK/PSK is still present in network configuration + + fname = os.path.join(params['logdir'], + 'ft_psk_key_lifetime_in_memory.memctx-') + verify_not_present(buf, pmkr0, fname, "PMK-R0") + verify_not_present(buf, pmkr1, fname, "PMK-R1") + verify_not_present(buf, kck, fname, "KCK") + verify_not_present(buf, kek, fname, "KEK") + verify_not_present(buf, tk, fname, "TK") + verify_not_present(buf, gtk, fname, "GTK") + + dev[0].request("REMOVE_NETWORK all") + + logger.info("Checking keys in memory after network profile removal") + buf = read_process_memory(pid, pmk) + get_key_locations(buf, pmk, "PMK") + get_key_locations(buf, pmkr0, "PMK-R0") + get_key_locations(buf, pmkr1, "PMK-R1") + + verify_not_present(buf, pmk, fname, "PMK") + verify_not_present(buf, pmkr0, fname, "PMK-R0") + verify_not_present(buf, pmkr1, fname, "PMK-R1") + verify_not_present(buf, kck, fname, "KCK") + verify_not_present(buf, kek, fname, "KEK") + verify_not_present(buf, tk, fname, "TK") + verify_not_present(buf, gtk, fname, "GTK") diff --git a/tests/hwsim/test_ap_psk.py b/tests/hwsim/test_ap_psk.py index b3a81b7b2..5baa7a379 100644 --- a/tests/hwsim/test_ap_psk.py +++ b/tests/hwsim/test_ap_psk.py @@ -10,6 +10,7 @@ import hmac import logging logger = logging.getLogger() import os +import re import struct import subprocess import time @@ -818,3 +819,159 @@ def test_ap_wpa2_psk_ext_eapol_key_info(dev, apdev): reply_eapol("4/4", hapd, addr, msg, 0x030a, None, None, kck) hapd_connected(hapd) + +def find_wpas_process(dev): + ifname = dev.ifname + cmd = subprocess.Popen(['ps', 'ax'], stdout=subprocess.PIPE) + (data,err) = cmd.communicate() + for l in data.splitlines(): + if "wpa_supplicant" not in l: + continue + if "-i" + ifname not in l: + continue + return int(l.strip().split(' ')[0]) + raise Exception("Could not find wpa_supplicant process") + +def read_process_memory(pid, key=None): + buf = bytes() + with open('/proc/%d/maps' % pid, 'r') as maps, \ + open('/proc/%d/mem' % pid, 'r') as mem: + for l in maps.readlines(): + m = re.match(r'([0-9a-f]+)-([0-9a-f]+) ([-r][-w][-x][-p])', l) + if not m: + continue + start = int(m.group(1), 16) + end = int(m.group(2), 16) + perm = m.group(3) + if start > 0xffffffffffff: + continue + if end < start: + continue + if not perm.startswith('rw'): + continue + mem.seek(start) + data = mem.read(end - start) + buf += data + if key and key in data: + logger.info("Key found in " + l) + return buf + +def verify_not_present(buf, key, fname, keyname): + pos = buf.find(key) + if pos < 0: + return + + prefix = 2048 if pos > 2048 else pos + with open(fname + keyname, 'w') as f: + f.write(buf[pos - prefix:pos + 2048]) + raise Exception(keyname + " found after disassociation") + +def get_key_locations(buf, key, keyname): + count = 0 + pos = 0 + while True: + pos = buf.find(key, pos) + if pos < 0: + break + logger.info("Found %s at %d" % (keyname, pos)) + count += 1 + pos += len(key) + return count + +def test_wpa2_psk_key_lifetime_in_memory(dev, apdev, params): + """WPA2-PSK and PSK/PTK lifetime in memory""" + ssid = "test-wpa2-psk" + passphrase = 'qwertyuiop' + psk = '602e323e077bc63bd80307ef4745b754b0ae0a925c2638ecd13a794b9527b9e6' + pmk = binascii.unhexlify(psk) + p = hostapd.wpa2_params(ssid=ssid) + p['wpa_psk'] = psk + hapd = hostapd.add_ap(apdev[0]['ifname'], p) + + pid = find_wpas_process(dev[0]) + + id = dev[0].connect(ssid, raw_psk=psk, scan_freq="2412", + only_add_network=True) + + logger.info("Checking keys in memory after network profile configuration") + buf = read_process_memory(pid, pmk) + get_key_locations(buf, pmk, "PMK") + + dev[0].request("REMOVE_NETWORK all") + logger.info("Checking keys in memory after network profile removal") + buf = read_process_memory(pid, pmk) + get_key_locations(buf, pmk, "PMK") + + id = dev[0].connect(ssid, psk=passphrase, scan_freq="2412", + only_add_network=True) + + logger.info("Checking keys in memory before connection") + buf = read_process_memory(pid, pmk) + get_key_locations(buf, pmk, "PMK") + + dev[0].connect_network(id, timeout=20) + time.sleep(0.1) + + buf = read_process_memory(pid, pmk) + + dev[0].request("DISCONNECT") + dev[0].wait_disconnected() + + dev[0].relog() + ptk = None + gtk = None + with open(os.path.join(params['logdir'], 'log0'), 'r') as f: + for l in f.readlines(): + if "WPA: PTK - hexdump" in l: + val = l.strip().split(':')[3].replace(' ', '') + ptk = binascii.unhexlify(val) + if "WPA: Group Key - hexdump" in l: + val = l.strip().split(':')[3].replace(' ', '') + gtk = binascii.unhexlify(val) + if not pmk or not ptk or not gtk: + raise Exception("Could not find keys from debug log") + if len(gtk) != 16: + raise Exception("Unexpected GTK length") + + kck = ptk[0:16] + kek = ptk[16:32] + tk = ptk[32:48] + + logger.info("Checking keys in memory while associated") + get_key_locations(buf, pmk, "PMK") + if pmk not in buf: + print("PMK not found while associated") + return "skip" + if kck not in buf: + raise Exception("KCK not found while associated") + if kek not in buf: + raise Exception("KEK not found while associated") + if tk in buf: + raise Exception("TK found from memory") + if gtk in buf: + raise Exception("GTK found from memory") + + logger.info("Checking keys in memory after disassociation") + buf = read_process_memory(pid, pmk) + get_key_locations(buf, pmk, "PMK") + + # Note: PMK/PSK is still present in network configuration + + fname = os.path.join(params['logdir'], + 'wpa2_psk_key_lifetime_in_memory.memctx-') + verify_not_present(buf, kck, fname, "KCK") + verify_not_present(buf, kek, fname, "KEK") + verify_not_present(buf, tk, fname, "TK") + verify_not_present(buf, gtk, fname, "GTK") + + dev[0].request("REMOVE_NETWORK all") + + logger.info("Checking keys in memory after network profile removal") + buf = read_process_memory(pid, pmk) + get_key_locations(buf, pmk, "PMK") + + verify_not_present(buf, pmk, fname, "PMK") + verify_not_present(buf, kck, fname, "KCK") + verify_not_present(buf, kek, fname, "KEK") + verify_not_present(buf, tk, fname, "TK") + verify_not_present(buf, gtk, fname, "GTK") diff --git a/tests/hwsim/test_erp.py b/tests/hwsim/test_erp.py index 5073622c7..9a1827e09 100644 --- a/tests/hwsim/test_erp.py +++ b/tests/hwsim/test_erp.py @@ -4,11 +4,15 @@ # This software may be distributed under the terms of the BSD license. # See README for more details. +import binascii import logging logger = logging.getLogger() +import os +import time import hostapd from test_ap_eap import int_eap_server_params +from test_ap_psk import find_wpas_process, read_process_memory, verify_not_present, get_key_locations def test_erp_initiate_reauth_start(dev, apdev): """Authenticator sending EAP-Initiate/Re-auth-Start, but ERP disabled on peer""" @@ -215,3 +219,173 @@ def test_erp_radius_eap_methods(dev, apdev): private_key="auth_serv/user.key") erp_test(dev[0], hapd, eap="TTLS", identity="erp-ttls@example.com", password="password", ca_cert="auth_serv/ca.pem", phase2="auth=PAP") + +def test_erp_key_lifetime_in_memory(dev, apdev, params): + """ERP and key lifetime in memory""" + capab = dev[0].get_capability("erp") + if not capab or 'ERP' not in capab: + return "skip" + p = int_eap_server_params() + p['erp_send_reauth_start'] = '1' + p['erp_domain'] = 'example.com' + p['eap_server_erp'] = '1' + p['disable_pmksa_caching'] = '1' + hapd = hostapd.add_ap(apdev[0]['ifname'], p) + password = "63d2d21ac3c09ed567ee004a34490f1d16e7fa5835edf17ddba70a63f1a90a25" + + pid = find_wpas_process(dev[0]) + + dev[0].request("ERP_FLUSH") + dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS", + identity="pap-secret@example.com", password=password, + ca_cert="auth_serv/ca.pem", phase2="auth=PAP", + erp="1", scan_freq="2412") + + time.sleep(0.1) + buf = read_process_memory(pid, password) + + dev[0].request("DISCONNECT") + dev[0].wait_disconnected(timeout=15) + + dev[0].relog() + rRK = None + rIK = None + pmk = None + ptk = None + gtk = None + with open(os.path.join(params['logdir'], 'log0'), 'r') as f: + for l in f.readlines(): + if "EAP: ERP rRK - hexdump" in l: + val = l.strip().split(':')[3].replace(' ', '') + rRK = binascii.unhexlify(val) + if "EAP: ERP rIK - hexdump" in l: + val = l.strip().split(':')[3].replace(' ', '') + rIK = binascii.unhexlify(val) + if "WPA: PMK - hexdump" in l: + val = l.strip().split(':')[3].replace(' ', '') + pmk = binascii.unhexlify(val) + if "WPA: PTK - hexdump" in l: + val = l.strip().split(':')[3].replace(' ', '') + ptk = binascii.unhexlify(val) + if "WPA: Group Key - hexdump" in l: + val = l.strip().split(':')[3].replace(' ', '') + gtk = binascii.unhexlify(val) + if not rIK or not rRK or not pmk or not ptk or not gtk: + raise Exception("Could not find keys from debug log") + if len(gtk) != 16: + raise Exception("Unexpected GTK length") + + kck = ptk[0:16] + kek = ptk[16:32] + tk = ptk[32:48] + + fname = os.path.join(params['logdir'], + 'erp_key_lifetime_in_memory.memctx-') + + logger.info("Checking keys in memory while associated") + get_key_locations(buf, password, "Password") + get_key_locations(buf, pmk, "PMK") + get_key_locations(buf, rRK, "rRK") + get_key_locations(buf, rIK, "rIK") + if password not in buf: + print("Password not found while associated") + return "skip" + if pmk not in buf: + print("PMK not found while associated") + return "skip" + if kck not in buf: + raise Exception("KCK not found while associated") + if kek not in buf: + raise Exception("KEK not found while associated") + if tk in buf: + raise Exception("TK found from memory") + if gtk in buf: + raise Exception("GTK found from memory") + + logger.info("Checking keys in memory after disassociation") + buf = read_process_memory(pid, password) + + # Note: Password is still present in network configuration + # Note: PMK is in EAP fast re-auth data + + get_key_locations(buf, password, "Password") + get_key_locations(buf, pmk, "PMK") + get_key_locations(buf, rRK, "rRK") + get_key_locations(buf, rIK, "rIK") + verify_not_present(buf, kck, fname, "KCK") + verify_not_present(buf, kek, fname, "KEK") + verify_not_present(buf, tk, fname, "TK") + verify_not_present(buf, gtk, fname, "GTK") + + dev[0].request("RECONNECT") + ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15) + if ev is None: + raise Exception("EAP success timed out") + if "EAP re-authentication completed successfully" not in ev: + raise Exception("Did not use ERP") + dev[0].wait_connected(timeout=15, error="Reconnection timed out") + + dev[0].request("DISCONNECT") + dev[0].wait_disconnected(timeout=15) + + dev[0].relog() + pmk = None + ptk = None + gtk = None + with open(os.path.join(params['logdir'], 'log0'), 'r') as f: + for l in f.readlines(): + if "WPA: PMK - hexdump" in l: + val = l.strip().split(':')[3].replace(' ', '') + pmk = binascii.unhexlify(val) + if "WPA: PTK - hexdump" in l: + val = l.strip().split(':')[3].replace(' ', '') + ptk = binascii.unhexlify(val) + if "WPA: GTK in EAPOL-Key - hexdump" in l: + val = l.strip().split(':')[3].replace(' ', '') + gtk = binascii.unhexlify(val) + if not pmk or not ptk or not gtk: + raise Exception("Could not find keys from debug log") + + kck = ptk[0:16] + kek = ptk[16:32] + tk = ptk[32:48] + + logger.info("Checking keys in memory after ERP and disassociation") + buf = read_process_memory(pid, password) + + # Note: Password is still present in network configuration + + get_key_locations(buf, password, "Password") + get_key_locations(buf, pmk, "PMK") + get_key_locations(buf, rRK, "rRK") + get_key_locations(buf, rIK, "rIK") + verify_not_present(buf, kck, fname, "KCK") + verify_not_present(buf, kek, fname, "KEK") + verify_not_present(buf, tk, fname, "TK") + verify_not_present(buf, gtk, fname, "GTK") + + dev[0].request("REMOVE_NETWORK all") + + logger.info("Checking keys in memory after network profile removal") + buf = read_process_memory(pid, password) + + # Note: rRK and rIK are still in memory + + get_key_locations(buf, password, "Password") + get_key_locations(buf, pmk, "PMK") + get_key_locations(buf, rRK, "rRK") + get_key_locations(buf, rIK, "rIK") + verify_not_present(buf, password, fname, "password") + verify_not_present(buf, pmk, fname, "PMK") + verify_not_present(buf, kck, fname, "KCK") + verify_not_present(buf, kek, fname, "KEK") + verify_not_present(buf, tk, fname, "TK") + verify_not_present(buf, gtk, fname, "GTK") + + dev[0].request("ERP_FLUSH") + logger.info("Checking keys in memory after ERP_FLUSH") + buf = read_process_memory(pid, password) + get_key_locations(buf, rRK, "rRK") + get_key_locations(buf, rIK, "rIK") + verify_not_present(buf, rRK, fname, "rRK") + verify_not_present(buf, rIK, fname, "rIK") diff --git a/tests/hwsim/test_sae.py b/tests/hwsim/test_sae.py index 74bd07968..07afb1fea 100644 --- a/tests/hwsim/test_sae.py +++ b/tests/hwsim/test_sae.py @@ -1,9 +1,11 @@ # Test cases for SAE -# Copyright (c) 2013, Jouni Malinen +# Copyright (c) 2013-2014, Jouni Malinen # # This software may be distributed under the terms of the BSD license. # See README for more details. +import binascii +import os import time import subprocess import logging @@ -11,6 +13,7 @@ logger = logging.getLogger() import hwsim_utils import hostapd +from test_ap_psk import find_wpas_process, read_process_memory, verify_not_present, get_key_locations def test_sae(dev, apdev): """SAE with default group""" @@ -161,3 +164,123 @@ def test_sae_missing_password(dev, apdev): ev = dev[0].wait_event(['CTRL-EVENT-SSID-TEMP-DISABLED'], timeout=10) if ev is None: raise Exception("Invalid network not temporarily disabled") + + +def test_sae_key_lifetime_in_memory(dev, apdev, params): + """SAE and key lifetime in memory""" + password = "5ad144a7c1f5a5503baa6fa01dabc15b1843e8c01662d78d16b70b5cd23cf8b" + p = hostapd.wpa2_params(ssid="test-sae", passphrase=password) + p['wpa_key_mgmt'] = 'SAE' + hapd = hostapd.add_ap(apdev[0]['ifname'], p) + + pid = find_wpas_process(dev[0]) + + dev[0].request("SET sae_groups ") + id = dev[0].connect("test-sae", psk=password, key_mgmt="SAE", + scan_freq="2412") + + time.sleep(0.1) + buf = read_process_memory(pid, password) + + dev[0].request("DISCONNECT") + dev[0].wait_disconnected() + + dev[0].relog() + sae_k = None + sae_keyseed = None + sae_kck = None + pmk = None + ptk = None + gtk = None + with open(os.path.join(params['logdir'], 'log0'), 'r') as f: + for l in f.readlines(): + if "SAE: k - hexdump" in l: + val = l.strip().split(':')[3].replace(' ', '') + sae_k = binascii.unhexlify(val) + if "SAE: keyseed - hexdump" in l: + val = l.strip().split(':')[3].replace(' ', '') + sae_keyseed = binascii.unhexlify(val) + if "SAE: KCK - hexdump" in l: + val = l.strip().split(':')[3].replace(' ', '') + sae_kck = binascii.unhexlify(val) + if "SAE: PMK - hexdump" in l: + val = l.strip().split(':')[3].replace(' ', '') + pmk = binascii.unhexlify(val) + if "WPA: PTK - hexdump" in l: + val = l.strip().split(':')[3].replace(' ', '') + ptk = binascii.unhexlify(val) + if "WPA: Group Key - hexdump" in l: + val = l.strip().split(':')[3].replace(' ', '') + gtk = binascii.unhexlify(val) + if not sae_k or not sae_keyseed or not sae_kck or not pmk or not ptk or not gtk: + raise Exception("Could not find keys from debug log") + if len(gtk) != 16: + raise Exception("Unexpected GTK length") + + kck = ptk[0:16] + kek = ptk[16:32] + tk = ptk[32:48] + + fname = os.path.join(params['logdir'], + 'sae_key_lifetime_in_memory.memctx-') + + logger.info("Checking keys in memory while associated") + get_key_locations(buf, password, "Password") + get_key_locations(buf, pmk, "PMK") + if password not in buf: + print("Password not found while associated") + return "skip" + if pmk not in buf: + print("PMK not found while associated") + return "skip" + if kck not in buf: + raise Exception("KCK not found while associated") + if kek not in buf: + raise Exception("KEK not found while associated") + if tk in buf: + raise Exception("TK found from memory") + if gtk in buf: + raise Exception("GTK found from memory") + verify_not_present(buf, sae_k, fname, "SAE(k)") + verify_not_present(buf, sae_keyseed, fname, "SAE(keyseed)") + verify_not_present(buf, sae_kck, fname, "SAE(KCK)") + + logger.info("Checking keys in memory after disassociation") + buf = read_process_memory(pid, password) + + # Note: Password is still present in network configuration + # Note: PMK is in PMKSA cache + + get_key_locations(buf, password, "Password") + get_key_locations(buf, pmk, "PMK") + verify_not_present(buf, kck, fname, "KCK") + verify_not_present(buf, kek, fname, "KEK") + verify_not_present(buf, tk, fname, "TK") + verify_not_present(buf, gtk, fname, "GTK") + verify_not_present(buf, sae_k, fname, "SAE(k)") + verify_not_present(buf, sae_keyseed, fname, "SAE(keyseed)") + verify_not_present(buf, sae_kck, fname, "SAE(KCK)") + + dev[0].request("PMKSA_FLUSH") + logger.info("Checking keys in memory after PMKSA cache flush") + buf = read_process_memory(pid, password) + get_key_locations(buf, password, "Password") + get_key_locations(buf, pmk, "PMK") + verify_not_present(buf, pmk, fname, "PMK") + + dev[0].request("REMOVE_NETWORK all") + + logger.info("Checking keys in memory after network profile removal") + buf = read_process_memory(pid, password) + + get_key_locations(buf, password, "Password") + get_key_locations(buf, pmk, "PMK") + verify_not_present(buf, password, fname, "password") + verify_not_present(buf, pmk, fname, "PMK") + verify_not_present(buf, kck, fname, "KCK") + verify_not_present(buf, kek, fname, "KEK") + verify_not_present(buf, tk, fname, "TK") + verify_not_present(buf, gtk, fname, "GTK") + verify_not_present(buf, sae_k, fname, "SAE(k)") + verify_not_present(buf, sae_keyseed, fname, "SAE(keyseed)") + verify_not_present(buf, sae_kck, fname, "SAE(KCK)")