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 <j@w1.fi>
This commit is contained in:
parent
4e70bbf1c6
commit
5b3c40a65b
6 changed files with 662 additions and 3 deletions
|
@ -74,6 +74,8 @@ radius_accept_attr=27:d:3
|
||||||
"8"* AKA' [2]
|
"8"* AKA' [2]
|
||||||
|
|
||||||
"pap user" TTLS-PAP "password" [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]
|
"chap user" TTLS-CHAP "password" [2]
|
||||||
"mschap user" TTLS-MSCHAP "password" [2]
|
"mschap user" TTLS-MSCHAP "password" [2]
|
||||||
"DOMAIN\mschapv2 user" TTLS-MSCHAPV2 hash:8846f7eaee8fb117ad06bdd830b7586c [2]
|
"DOMAIN\mschapv2 user" TTLS-MSCHAPV2 hash:8846f7eaee8fb117ad06bdd830b7586c [2]
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
# See README for more details.
|
# See README for more details.
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
|
import binascii
|
||||||
import time
|
import time
|
||||||
import subprocess
|
import subprocess
|
||||||
import logging
|
import logging
|
||||||
|
@ -14,7 +15,7 @@ import os
|
||||||
|
|
||||||
import hwsim_utils
|
import hwsim_utils
|
||||||
import hostapd
|
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):
|
def read_pem(fname):
|
||||||
with open(fname, "r") as f:
|
with open(fname, "r") as f:
|
||||||
|
@ -2243,3 +2244,98 @@ def test_openssl_cipher_suite_config_hapd(dev, apdev):
|
||||||
anonymous_identity="ttls", password="password",
|
anonymous_identity="ttls", password="password",
|
||||||
openssl_ciphers="HIGH:!ADH",
|
openssl_ciphers="HIGH:!ADH",
|
||||||
ca_cert="auth_serv/ca.pem", phase2="auth=PAP")
|
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")
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
# This software may be distributed under the terms of the BSD license.
|
# This software may be distributed under the terms of the BSD license.
|
||||||
# See README for more details.
|
# See README for more details.
|
||||||
|
|
||||||
|
import binascii
|
||||||
|
import os
|
||||||
import time
|
import time
|
||||||
import subprocess
|
import subprocess
|
||||||
import logging
|
import logging
|
||||||
|
@ -12,7 +14,7 @@ logger = logging.getLogger()
|
||||||
import hwsim_utils
|
import hwsim_utils
|
||||||
import hostapd
|
import hostapd
|
||||||
from wlantest import Wlantest
|
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():
|
def ft_base_rsn():
|
||||||
params = { "wpa": "2",
|
params = { "wpa": "2",
|
||||||
|
@ -456,3 +458,108 @@ def test_ap_ft_gtk_rekey(dev, apdev):
|
||||||
if ev is None:
|
if ev is None:
|
||||||
raise Exception("GTK rekey timed out after FT protocol")
|
raise Exception("GTK rekey timed out after FT protocol")
|
||||||
hwsim_utils.test_connectivity(dev[0], hapd1)
|
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")
|
||||||
|
|
|
@ -10,6 +10,7 @@ import hmac
|
||||||
import logging
|
import logging
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import struct
|
import struct
|
||||||
import subprocess
|
import subprocess
|
||||||
import time
|
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)
|
reply_eapol("4/4", hapd, addr, msg, 0x030a, None, None, kck)
|
||||||
hapd_connected(hapd)
|
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")
|
||||||
|
|
|
@ -4,11 +4,15 @@
|
||||||
# This software may be distributed under the terms of the BSD license.
|
# This software may be distributed under the terms of the BSD license.
|
||||||
# See README for more details.
|
# See README for more details.
|
||||||
|
|
||||||
|
import binascii
|
||||||
import logging
|
import logging
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
|
||||||
import hostapd
|
import hostapd
|
||||||
from test_ap_eap import int_eap_server_params
|
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):
|
def test_erp_initiate_reauth_start(dev, apdev):
|
||||||
"""Authenticator sending EAP-Initiate/Re-auth-Start, but ERP disabled on peer"""
|
"""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")
|
private_key="auth_serv/user.key")
|
||||||
erp_test(dev[0], hapd, eap="TTLS", identity="erp-ttls@example.com",
|
erp_test(dev[0], hapd, eap="TTLS", identity="erp-ttls@example.com",
|
||||||
password="password", ca_cert="auth_serv/ca.pem", phase2="auth=PAP")
|
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")
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
# Test cases for SAE
|
# Test cases for SAE
|
||||||
# Copyright (c) 2013, Jouni Malinen <j@w1.fi>
|
# Copyright (c) 2013-2014, Jouni Malinen <j@w1.fi>
|
||||||
#
|
#
|
||||||
# This software may be distributed under the terms of the BSD license.
|
# This software may be distributed under the terms of the BSD license.
|
||||||
# See README for more details.
|
# See README for more details.
|
||||||
|
|
||||||
|
import binascii
|
||||||
|
import os
|
||||||
import time
|
import time
|
||||||
import subprocess
|
import subprocess
|
||||||
import logging
|
import logging
|
||||||
|
@ -11,6 +13,7 @@ logger = logging.getLogger()
|
||||||
|
|
||||||
import hwsim_utils
|
import hwsim_utils
|
||||||
import hostapd
|
import hostapd
|
||||||
|
from test_ap_psk import find_wpas_process, read_process_memory, verify_not_present, get_key_locations
|
||||||
|
|
||||||
def test_sae(dev, apdev):
|
def test_sae(dev, apdev):
|
||||||
"""SAE with default group"""
|
"""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)
|
ev = dev[0].wait_event(['CTRL-EVENT-SSID-TEMP-DISABLED'], timeout=10)
|
||||||
if ev is None:
|
if ev is None:
|
||||||
raise Exception("Invalid network not temporarily disabled")
|
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)")
|
||||||
|
|
Loading…
Add table
Reference in a new issue