From patchwork Mon Feb 24 09:15:29 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Peer, Ilan" X-Patchwork-Id: 1242962 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=hostap-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=intel.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.a=rsa-sha256 header.s=bombadil.20170209 header.b=CvgGbpXW; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 48QxyQ6ZxYz9sPk for ; Mon, 24 Feb 2020 20:45:50 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:MIME-Version:Cc:List-Subscribe: List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id:References: In-Reply-To:Message-Id:Date:Subject:To:From:Reply-To:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Owner; bh=CcdjPnD1Jv8KvZ0CBpAWByxZoWWkEqHKtYUn61+AmTE=; b=CvgGbpXWOI8BKVPRKHREmfKA7g UxOnk/S7tyXbO/xhD3s38JQ7slTTu/k95/JpVnEcybZ2puYVERTU7YjlpOPfG0JTrodCWlRQsx9gR O97VQZscg/3yQqvBzkp19/PtwQA/K+1dFQkn+KFJHw2NZ65CjMoNZtufq5NUvQCex7/mejU8tBggJ Xfb72QjRvWPjwdQWT5VuN8gPwEctNMUImyxEiEkIQPVXZLoJMC8ejPeFkZCtW9hwgOkAvAJWDsc9T rJObUJ46Nb1rbn7UiVNbUS77rIDgbBPv4h/0yecwS54erCgfe9yW0PkSx/aHBRDacgc/JPk/E6n+h ensO9lqw==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1j6AJ6-0007x6-7z; Mon, 24 Feb 2020 09:45:44 +0000 Received: from mga17.intel.com ([192.55.52.151]) by bombadil.infradead.org with esmtps (Exim 4.92.3 #3 (Red Hat Linux)) id 1j69qS-0007oi-Hv for hostap@lists.infradead.org; Mon, 24 Feb 2020 09:16:17 +0000 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga007.jf.intel.com ([10.7.209.58]) by fmsmga107.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 24 Feb 2020 01:15:55 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.70,479,1574150400"; d="scan'208";a="225914559" Received: from jed01681.jer.intel.com ([10.12.190.127]) by orsmga007.jf.intel.com with ESMTP; 24 Feb 2020 01:15:53 -0800 From: Ilan Peer To: hostap@lists.infradead.org Subject: [PATCH 14/14] tests: Add PASN test coverage Date: Mon, 24 Feb 2020 11:15:29 +0200 Message-Id: <20200224091529.15259-15-ilan.peer@intel.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200224091529.15259-1-ilan.peer@intel.com> References: <20200224091529.15259-1-ilan.peer@intel.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20200224_011608_665449_49317E2E X-CRM114-Status: GOOD ( 14.21 ) X-Spam-Score: -2.3 (--) X-Spam-Report: SpamAssassin version 3.4.3 on bombadil.infradead.org summary: Content analysis details: (-2.3 points) pts rule name description ---- ---------------------- -------------------------------------------------- -2.3 RCVD_IN_DNSWL_MED RBL: Sender listed at https://www.dnswl.org/, medium trust [192.55.52.151 listed in list.dnswl.org] 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record -0.0 SPF_PASS SPF: sender matches SPF record X-BeenThere: hostap@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Ilan Peer MIME-Version: 1.0 Sender: "Hostap" Errors-To: hostap-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Signed-off-by: Ilan Peer --- tests/hwsim/example-hostapd.config | 2 + tests/hwsim/example-wpa_supplicant.config | 2 + tests/hwsim/hostapd.py | 17 ++ tests/hwsim/test_pasn.py | 344 ++++++++++++++++++++++ tests/hwsim/wpasupplicant.py | 20 ++ 5 files changed, 385 insertions(+) create mode 100644 tests/hwsim/test_pasn.py diff --git a/tests/hwsim/example-hostapd.config b/tests/hwsim/example-hostapd.config index 942134a513..ca25023ef8 100644 --- a/tests/hwsim/example-hostapd.config +++ b/tests/hwsim/example-hostapd.config @@ -110,3 +110,5 @@ CONFIG_FILS_SK_PFS=y CONFIG_OWE=y CONFIG_DPP=y CONFIG_DPP2=y + +CONFIG_PASN=y diff --git a/tests/hwsim/example-wpa_supplicant.config b/tests/hwsim/example-wpa_supplicant.config index d494caf864..77e522f228 100644 --- a/tests/hwsim/example-wpa_supplicant.config +++ b/tests/hwsim/example-wpa_supplicant.config @@ -153,3 +153,5 @@ CONFIG_PMKSA_CACHE_EXTERNAL=y CONFIG_OWE=y CONFIG_DPP=y CONFIG_DPP2=y + +CONFIG_PASN=y diff --git a/tests/hwsim/hostapd.py b/tests/hwsim/hostapd.py index 62b3302beb..13fb0d4b36 100644 --- a/tests/hwsim/hostapd.py +++ b/tests/hwsim/hostapd.py @@ -532,6 +532,23 @@ class Hostapd: def send_file(self, src, dst): self.host.send_file(src, dst) + def get_ptksa(self, bssid, cipher): + res = self.request("PTKSA_CACHE_LIST") + lines = res.splitlines() + for l in lines: + if bssid not in l or cipher not in l: + continue + vals = dict() + [index, addr, cipher, expiration, tk, hltk] = l.split(' ', 5) + vals['index'] = index + vals['addr'] = addr + vals['cipher'] = cipher + vals['expiration'] = expiration + vals['tk'] = tk + vals['hltk'] = hltk + return vals + return None + def add_ap(apdev, params, wait_enabled=True, no_enable=False, timeout=30, global_ctrl_override=None, driver=False): if isinstance(apdev, dict): diff --git a/tests/hwsim/test_pasn.py b/tests/hwsim/test_pasn.py new file mode 100644 index 0000000000..72a5f51bc0 --- /dev/null +++ b/tests/hwsim/test_pasn.py @@ -0,0 +1,344 @@ +# Test cases for PASN +# Copyright (C) 2019 Intel Corporation +# +# This software may be distributed under the terms of the BSD license. +# See README for more details. + +from remotehost import remote_compatible +import binascii +import os +import time +import logging +logger = logging.getLogger() +import socket +import struct +import subprocess + +import hwsim_utils +import hostapd +from wpasupplicant import WpaSupplicant +from utils import HwsimSkip +from hwsim import HWSimRadio +from test_erp import check_erp_capa, start_erp_as +from test_fils import check_fils_capa + +def check_pasn_capab(dev): + if "PASN" not in dev.get_capability("auth_alg"): + raise HwsimSkip("PASN not supported") + +def pasn_ap_params(akmp="PASN", cipher="CCMP", group="19"): + params = {"ssid": "test-wpa2-pasn", + "wpa_passphrase": "12345678", + "wpa": "2", + "ieee80211w": "2", + "wpa_key_mgmt": "WPA-PSK " + akmp, + "rsn_pairwise": cipher, + "pasn_groups" : group, + } + + return params + +def check_pasn_ptk(dev, hapd, cipher): + sta_ptksa = dev.get_ptksa(hapd.own_addr(), cipher) + ap_ptksa = hapd.get_ptksa(dev.own_addr(), cipher) + if not (sta_ptksa and ap_ptksa): + raise Exception("Could not get PTKSA entry") + + logger.info("sta: TK: %s HLTK: %s" % (sta_ptksa['tk'], sta_ptksa['hltk'])) + logger.info("ap : TK: %s HLTK: %s" % (ap_ptksa['tk'], ap_ptksa['hltk'])) + + if sta_ptksa['tk'] != ap_ptksa['tk'] or sta_ptksa['hltk'] != ap_ptksa['hltk']: + raise Exception("TK/HLTK mismatch") + +def check_pasn_akmp_cipher(dev, hapd, akmp="PASN", cipher="CCMP", + group="19", status=0, fail=0): + dev.scan(type="ONLY", freq=2412) + + cmd = "PASN_START bssid=%s akmp=%s cipher=%s group=%s" % (hapd.own_addr(), akmp, cipher, group) + + resp = dev.request(cmd) + if fail: + if "OK" in resp: + raise Exception("Unexpected success to start PASN authentication") + return + + if "OK" not in resp: + raise Exception("Failed to start PASN authentication") + + ev = dev.wait_event(["PASN-AUTH-STATUS"], 3) + if not ev: + raise Exception("PASN: PASN-AUTH-STATUS not seen") + + if hapd.own_addr() + " akmp=" + akmp + ", status=" + str(status) not in ev: + raise Exception("PASN: unexpected status") + + if status: + return + + check_pasn_ptk(dev, hapd, cipher) + +@remote_compatible +def test_pasn_ccmp(dev, apdev): + """PASN authentication with WPA2/CCMP AP""" + check_pasn_capab(dev[0]) + + params = pasn_ap_params("PASN", "CCMP", "19") + hapd = hostapd.add_ap(apdev[0], params) + + check_pasn_akmp_cipher(dev[0], hapd, "PASN", "CCMP") + +@remote_compatible +def test_pasn_gcmp(dev, apdev): + """PASN authentication with WPA2/GCMP AP""" + check_pasn_capab(dev[0]) + + params = pasn_ap_params("PASN", "GCMP", "19") + hapd = hostapd.add_ap(apdev[0], params) + + check_pasn_akmp_cipher(dev[0], hapd, "PASN", "GCMP") + +@remote_compatible +def test_pasn_ccmp_256(dev, apdev): + """PASN authentication with WPA2/CCMP256 AP""" + check_pasn_capab(dev[0]) + + params = pasn_ap_params("PASN", "CCMP-256", "19") + hapd = hostapd.add_ap(apdev[0], params) + + check_pasn_akmp_cipher(dev[0], hapd, "PASN", "CCMP-256") + +@remote_compatible +def test_pasn_gcmp_256(dev, apdev): + """PASN authentication with WPA2/GCMP-256 AP""" + check_pasn_capab(dev[0]) + + params = pasn_ap_params("PASN", "GCMP-256", "19") + hapd = hostapd.add_ap(apdev[0], params) + + check_pasn_akmp_cipher(dev[0], hapd, "PASN", "GCMP-256") + +@remote_compatible +def test_pasn_group_mismatch(dev, apdev): + """PASN authentication with WPA2/CCMP AP with group mismatch""" + check_pasn_capab(dev[0]) + + params = pasn_ap_params("PASN", "CCMP", "20") + hapd = hostapd.add_ap(apdev[0], params) + + check_pasn_akmp_cipher(dev[0], hapd, "PASN", "CCMP", status=77) + +@remote_compatible +def test_pasn_channel_mismatch(dev, apdev): + """PASN authentication with WPA2/CCMP AP with channel mismatch""" + check_pasn_capab(dev[0]) + + params = pasn_ap_params("PASN", "CCMP") + params['channel'] = "6" + hapd = hostapd.add_ap(apdev[0], params) + + check_pasn_akmp_cipher(dev[0], hapd, "PASN", "CCMP", fail=1) + +@remote_compatible +def test_pasn_while_connected_same_channel(dev, apdev): + """PASN authentication with WPA2/CCMP AP while connected same channel""" + check_pasn_capab(dev[0]) + + ssid = "test-wpa2-psk" + psk = '602e323e077bc63bd80307ef4745b754b0ae0a925c2638ecd13a794b9527b9e6' + params = hostapd.wpa2_params(ssid=ssid) + params['wpa_psk'] = psk + hapd = hostapd.add_ap(apdev[0], params) + + dev[0].connect(ssid, raw_psk=psk, scan_freq="2412") + + params = pasn_ap_params("PASN", "CCMP") + hapd = hostapd.add_ap(apdev[1], params) + + check_pasn_akmp_cipher(dev[0], hapd, "PASN", "CCMP") + +@remote_compatible +def test_pasn_while_connected_same_ap(dev, apdev): + """PASN authentication with WPA2/CCMP AP while connected to it""" + check_pasn_capab(dev[0]) + + params = hostapd.wpa2_params(ssid="test-wpa2-psk", + passphrase="12345678") + hapd = hostapd.add_ap(apdev[0], params) + + dev[0].connect("test-wpa2-psk", psk="12345678", scan_freq="2412") + + check_pasn_akmp_cipher(dev[0], hapd, "PASN", "CCMP", fail=1) + +@remote_compatible +def test_pasn_while_connected_diff_channel(dev, apdev): + """PASN authentication with WPA2/CCMP AP while connected diff channel""" + check_pasn_capab(dev[0]) + + with HWSimRadio(n_channels=2) as (radio, iface): + wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5') + wpas.interface_add(iface) + + if wpas.get_mcc() < 2: + raise HwsimSkip("PASN: New radio does not support MCC") + + params = hostapd.wpa2_params(ssid="test-wpa2-psk", + passphrase="12345678") + params['channel'] = "6" + hapd = hostapd.add_ap(apdev[0], params) + wpas.connect("test-wpa2-psk", psk="12345678", scan_freq="2437") + + params = pasn_ap_params("PASN", "CCMP") + hapd2 = hostapd.add_ap(apdev[1], params) + + check_pasn_akmp_cipher(wpas, hapd2, "PASN", "CCMP") + +@remote_compatible +def test_pasn_sae_pmksa_cache(dev, apdev): + """PASN authentication with SAE AP with PMKSA caching""" + check_pasn_capab(dev[0]) + + if "SAE" not in dev[0].get_capability("auth_alg"): + raise HwsimSkip("SAE not supported") + + params = hostapd.wpa2_params(ssid="test-sae", + passphrase="12345678") + params['wpa_key_mgmt'] = 'SAE PASN' + hapd = hostapd.add_ap(apdev[0], params) + + dev[0].request("SET sae_groups 19") + dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE", scan_freq="2412") + + hwsim_utils.test_connectivity(dev[0], hapd) + + dev[0].request("DISCONNECT") + dev[0].wait_disconnected() + + check_pasn_akmp_cipher(dev[0], hapd, "SAE", "CCMP") + +def check_pasn_fils_pmksa_cache(dev, apdev, params, key_mgmt): + check_fils_capa(dev[0]) + check_erp_capa(dev[0]) + check_pasn_capab(dev[0]) + + start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst")) + + bssid = apdev[0]['bssid'] + params = hostapd.wpa2_eap_params(ssid="fils") + params['wpa_key_mgmt'] = key_mgmt + " PASN" + params['auth_server_port'] = "18128" + params['erp_domain'] = 'example.com' + params['fils_realm'] = 'example.com' + hapd = hostapd.add_ap(apdev[0]['ifname'], params) + + dev[0].scan_for_bss(bssid, freq=2412) + dev[0].request("ERP_FLUSH") + + id = dev[0].connect("fils", key_mgmt=key_mgmt, + eap="PSK", identity="psk.user@example.com", + password_hex="0123456789abcdef0123456789abcdef", + erp="1", scan_freq="2412") + pmksa = dev[0].get_pmksa(bssid) + if pmksa is None: + raise Exception("No PMKSA cache entry created") + + hwsim_utils.test_connectivity(dev[0], hapd) + + dev[0].request("DISCONNECT") + dev[0].wait_disconnected() + + check_pasn_akmp_cipher(dev[0], hapd, key_mgmt, "CCMP") + +@remote_compatible +def test_pasn_fils_sha256_pmksa_cache(dev, apdev, params): + """PASN authentication with FILS-SHA256 with PMKSA caching""" + check_pasn_fils_pmksa_cache(dev, apdev, params, "FILS-SHA256") + +@remote_compatible +def test_pasn_fils_sha384_pmksa_cache(dev, apdev, params): + """PASN authentication with FILS-SHA384 with PMKSA caching""" + check_pasn_fils_pmksa_cache(dev, apdev, params, "FILS-SHA384") + +@remote_compatible +def test_pasn_sae_hltk(dev, apdev): + """Station authentication with SAE AP with HLTK derivation during connection""" + check_pasn_capab(dev[0]) + + if "SAE" not in dev[0].get_capability("auth_alg"): + raise HwsimSkip("SAE not supported") + + try: + params = hostapd.wpa2_params(ssid="test-sae", + passphrase="12345678") + params['wpa_key_mgmt'] = 'SAE PASN' + params['force_hltk_derivation'] = "1" + hapd = hostapd.add_ap(apdev[0], params) + + dev[0].request("SET force_hltk_derivation 1") + dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE", scan_freq="2412") + + check_pasn_ptk(dev[0], hapd, "CCMP") + finally: + dev[0].request("SET force_hltk_derivation 0") + + +def check_pasn_fils_hltk(dev, apdev, params, key_mgmt): + check_fils_capa(dev[0]) + check_erp_capa(dev[0]) + check_pasn_capab(dev[0]) + + start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst")) + + try: + bssid = apdev[0]['bssid'] + params = hostapd.wpa2_eap_params(ssid="fils") + params['wpa_key_mgmt'] = key_mgmt + params['auth_server_port'] = "18128" + params['erp_domain'] = 'example.com' + params['fils_realm'] = 'example.com' + params['disable_pmksa_caching'] = '1' + params['force_hltk_derivation'] = "1" + hapd = hostapd.add_ap(apdev[0]['ifname'], params) + + dev[0].scan_for_bss(bssid, freq=2412) + dev[0].request("ERP_FLUSH") + dev[0].request("SET force_hltk_derivation 1") + + id = dev[0].connect("fils", key_mgmt=key_mgmt, + eap="PSK", identity="psk.user@example.com", + password_hex="0123456789abcdef0123456789abcdef", + erp="1", scan_freq="2412") + + hwsim_utils.test_connectivity(dev[0], hapd) + + check_pasn_ptk(dev[0], hapd, "CCMP") + + dev[0].request("DISCONNECT") + dev[0].wait_disconnected() + + dev[0].dump_monitor() + dev[0].select_network(id, freq=2412) + ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED", + "EVENT-ASSOC-REJECT", + "CTRL-EVENT-CONNECTED"], timeout=10) + if ev is None: + raise Exception("Connection using FILS/ERP timed out") + if "CTRL-EVENT-EAP-STARTED" in ev: + raise Exception("Unexpected EAP exchange") + if "EVENT-ASSOC-REJECT" in ev: + raise Exception("Association failed") + + hwsim_utils.test_connectivity(dev[0], hapd) + + check_pasn_ptk(dev[0], hapd, "CCMP") + finally: + dev[0].request("SET force_hltk_derivation 0") + +@remote_compatible +def test_pasn_fils_sha256_hltk(dev, apdev, params): + """Station authentication with FILS-SHA256 with HLTK derivation during connection""" + check_pasn_fils_hltk(dev, apdev, params, "FILS-SHA256") + +@remote_compatible +def test_pasn_fils_sha384_hltk(dev, apdev, params): + """Station authentication with FILS-SHA384 with HLTK derivation during connection""" + check_pasn_fils_hltk(dev, apdev, params, "FILS-SHA384") diff --git a/tests/hwsim/wpasupplicant.py b/tests/hwsim/wpasupplicant.py index d1c249328c..61f413526a 100644 --- a/tests/hwsim/wpasupplicant.py +++ b/tests/hwsim/wpasupplicant.py @@ -1581,3 +1581,23 @@ class WpaSupplicant: res = self.request("DPP_CONFIGURATOR_REMOVE %d" % conf_id) if "OK" not in res: raise Exception("DPP_CONFIGURATOR_REMOVE failed") + + def get_ptksa(self, bssid, cipher): + res = self.request("PTKSA_CACHE_LIST") + lines = res.splitlines() + for l in lines: + if bssid not in l or cipher not in l: + continue + + vals = dict() + [index, addr, cipher, expiration, tk, hltk] = l.split(' ', 5) + vals['index'] = index + vals['addr'] = addr + vals['cipher'] = cipher + vals['expiration'] = expiration + vals['tk'] = tk + vals['hltk'] = hltk + return vals + return None + +