1# Test cases for RSNE/RSNXE overriding
2# Copyright (c) 2023-2024, Qualcomm Innovation Center, Inc.
3#
4# This software may be distributed under the terms of the BSD license.
5# See README for more details.
6
7import hostapd
8from utils import *
9from hwsim import HWSimRadio
10from wpasupplicant import WpaSupplicant
11from test_eht import eht_mld_enable_ap, eht_verify_status, eht_verify_wifi_version, traffic_test
12
13def test_rsn_override(dev, apdev):
14    """RSNE=WPA2-Personal/PMF-optional override=WPA3-Personal/PMF-required (with MLO parameters)"""
15    check_sae_capab(dev[0])
16
17    ssid = "test-rsn-override"
18    params = hostapd.wpa2_params(ssid=ssid,
19                                 passphrase="12345678",
20                                 ieee80211w='1')
21    params['rsn_override_key_mgmt'] = 'SAE SAE-EXT-KEY'
22    params['rsn_override_pairwise'] = 'CCMP GCMP-256'
23    params['rsn_override_mfp'] = '2'
24    params['beacon_prot'] = '1'
25    params['sae_groups'] = '19 20'
26    params['sae_require_mfp'] = '1'
27    params['sae_pwe'] = '2'
28    hapd = hostapd.add_ap(apdev[0], params)
29    bssid = hapd.own_addr()
30
31    try:
32        dev[0].set("rsn_overriding", "1")
33        dev[0].scan_for_bss(bssid, freq=2412)
34        bss = dev[0].get_bss(bssid)
35        flags = bss['flags']
36        if "PSK" in flags:
37            raise Exception("Unexpected BSS flags: " + flags)
38        if "-SAE+SAE-EXT-KEY-" not in flags:
39            raise Exception("Unexpected BSS flags: " + flags)
40        if "-GCMP-256+CCMP" not in flags:
41            raise Exception("Unexpected BSS flags: " + flags)
42
43        dev[0].set("sae_pwe", "2")
44        dev[0].set("sae_groups", "")
45        dev[0].connect(ssid, sae_password="12345678", key_mgmt="SAE",
46                       ieee80211w="2", scan_freq="2412")
47    finally:
48        dev[0].set("sae_pwe", "0")
49        dev[0].set("rsn_overriding", "0")
50
51def test_rsn_override2(dev, apdev):
52    """RSNE=WPA2-Personal/PMF-disabled override=WPA3-Personal/PMF-required (with MLO parameters)"""
53    check_sae_capab(dev[0])
54
55    ssid = "test-rsn-override"
56    params = hostapd.wpa2_params(ssid=ssid,
57                                 passphrase="12345678",
58                                 ieee80211w='0')
59    params['rsn_override_key_mgmt'] = 'SAE SAE-EXT-KEY'
60    params['rsn_override_pairwise'] = 'CCMP GCMP-256'
61    params['rsn_override_mfp'] = '2'
62    params['beacon_prot'] = '1'
63    params['sae_groups'] = '19 20'
64    params['sae_require_mfp'] = '1'
65    params['sae_pwe'] = '2'
66    hapd = hostapd.add_ap(apdev[0], params)
67    bssid = hapd.own_addr()
68
69    try:
70        dev[0].set("rsn_overriding", "1")
71        dev[0].scan_for_bss(bssid, freq=2412)
72        bss = dev[0].get_bss(bssid)
73        flags = bss['flags']
74        if "PSK" in flags:
75            raise Exception("Unexpected BSS flags: " + flags)
76        if "-SAE+SAE-EXT-KEY-" not in flags:
77            raise Exception("Unexpected BSS flags: " + flags)
78        if "-GCMP-256+CCMP" not in flags:
79            raise Exception("Unexpected BSS flags: " + flags)
80
81        dev[0].set("sae_pwe", "2")
82        dev[0].set("sae_groups", "")
83        dev[0].connect(ssid, sae_password="12345678", key_mgmt="SAE",
84                       ieee80211w="2", scan_freq="2412")
85    finally:
86        dev[0].set("sae_pwe", "0")
87        dev[0].set("rsn_overriding", "0")
88
89def test_rsn_override_no_pairwise(dev, apdev):
90    """RSN overriding and no pairwise cipher match in RSNEO"""
91    check_sae_capab(dev[0])
92
93    ssid = "test-rsn-override"
94    params = hostapd.wpa2_params(ssid=ssid,
95                                 passphrase="12345678",
96                                 ieee80211w='1')
97    params['rsn_override_key_mgmt'] = 'SAE SAE-EXT-KEY'
98    params['rsn_override_pairwise'] = 'GCMP-256'
99    params['rsn_override_mfp'] = '2'
100    params['beacon_prot'] = '1'
101    params['sae_groups'] = '19 20'
102    params['sae_require_mfp'] = '1'
103    hapd = hostapd.add_ap(apdev[0], params)
104    bssid = hapd.own_addr()
105
106    try:
107        dev[0].set("rsn_overriding", "1")
108        dev[0].scan_for_bss(bssid, freq=2412)
109
110        dev[0].set("sae_groups", "")
111        dev[0].connect(ssid, psk="12345678", key_mgmt="WPA-PSK SAE",
112                       pairwise="CCMP", ieee80211w="1", scan_freq="2412")
113    finally:
114        dev[0].set("sae_pwe", "0")
115        dev[0].set("rsn_overriding", "0")
116
117def test_rsn_override_mld(dev, apdev):
118    """AP MLD and RSNE=WPA2-Personal/PMF-disabled override=WPA3-Personal/PMF-required"""
119    run_rsn_override_mld(dev, apdev, False)
120
121def test_rsn_override_mld_mixed(dev, apdev):
122    """AP MLD and RSNE=WPA2-Personal/PMF-disabled override=WPA3-Personal/PMF-required on one link"""
123    run_rsn_override_mld(dev, apdev, True)
124
125def test_rsn_override_mld_only_sta(dev, apdev):
126    """AP MLD and RSN overriding only on STA"""
127    run_rsn_override_mld(dev, apdev, False, only_sta=True)
128
129def test_rsn_override_mld_too_long_elems(dev, apdev):
130    """AP MLD and RSN overriding with too long elements"""
131    run_rsn_override_mld(dev, apdev, False, too_long_elems=True)
132
133def run_rsn_override_mld(dev, apdev, mixed, only_sta=False,
134                         too_long_elems=False):
135    with HWSimRadio(use_mlo=True) as (hapd_radio, hapd_iface), \
136        HWSimRadio(use_mlo=True) as (wpas_radio, wpas_iface):
137
138        wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
139        wpas.interface_add(wpas_iface)
140        check_sae_capab(wpas)
141
142        passphrase = 'qwertyuiop'
143        ssid = "AP MLD RSN override"
144        params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
145        params['ieee80211n'] = '1'
146        params['ieee80211ax'] = '1'
147        params['ieee80211be'] = '1'
148        params['channel'] = '1'
149        params['hw_mode'] = 'g'
150        params['beacon_prot'] = '1'
151        params['sae_groups'] = '19 20'
152        params['sae_require_mfp'] = '1'
153        params['sae_pwe'] = '2'
154        if only_sta:
155            params['wpa_key_mgmt'] = 'SAE SAE-EXT-KEY'
156            params['rsn_pairwise'] = 'CCMP GCMP-256'
157            params['ieee80211w'] = '2'
158        elif not mixed:
159            params['rsn_override_key_mgmt'] = 'SAE'
160            params['rsn_override_key_mgmt_2'] = 'SAE-EXT-KEY'
161            params['rsn_override_pairwise'] = 'CCMP'
162            params['rsn_override_pairwise_2'] = 'GCMP-256'
163            params['rsn_override_mfp'] = '1'
164            params['rsn_override_mfp_2'] = '2'
165
166        params1 = dict(params)
167
168        if mixed:
169            params['wpa_key_mgmt'] = 'SAE SAE-EXT-KEY'
170            params['rsn_pairwise'] = 'CCMP GCMP-256'
171            params['ieee80211w'] = '2'
172            params['rsn_override_key_mgmt_2'] = 'SAE SAE-EXT-KEY'
173            params['rsn_override_pairwise_2'] = 'CCMP GCMP-256'
174            params['rsn_override_mfp_2'] = '2'
175
176            params1['rsn_override_key_mgmt_2'] = 'SAE SAE-EXT-KEY'
177            params1['rsn_override_pairwise_2'] = 'CCMP GCMP-256'
178            params1['rsn_override_mfp_2'] = '2'
179
180        hapd0 = eht_mld_enable_ap(hapd_iface, 0, params)
181
182        params1['channel'] = '6'
183        if too_long_elems:
184            params1['rsnoe_override'] = 'ddff506f9a29' + 251*'cc'
185        hapd1 = eht_mld_enable_ap(hapd_iface, 1, params1)
186
187        wpas.set("sae_pwe", "1")
188        wpas.set("rsn_overriding", "1")
189        wpas.connect(ssid, sae_password=passphrase, scan_freq="2412 2437",
190                     key_mgmt="SAE-EXT-KEY", ieee80211w="2", beacon_prot="1",
191                     pairwise="GCMP-256 CCMP", wait_connect=not too_long_elems)
192        if too_long_elems:
193            ev = wpas.wait_event(['Associated with'], timeout=10)
194            if ev is None:
195                raise Exception("Association not reported")
196            ev = wpas.wait_event(['EAPOL-RX'], timeout=1)
197            if ev is None:
198                raise Exception("EAPOL-Key M1 not reported")
199            ev = wpas.wait_event(['EAPOL-RX', 'CTRL-EVENT-DISCONNECTED'],
200                                 timeout=20)
201            if ev is None:
202                raise Exception("Disconnection not reported")
203            # The AP is expected to fail to send M3 due to RSNOE/RSNO2E/RSNXOE
204            # being too long to fit into the RSN Override Link KDE.
205            if 'EAPOL-RX' in ev:
206                raise Exception("Unexpected EAPOL-Key M3 reported")
207            return
208
209        eht_verify_status(wpas, hapd0, 2412, 20, is_ht=True, mld=True,
210                          valid_links=3, active_links=3)
211        eht_verify_wifi_version(wpas)
212        traffic_test(wpas, hapd0)
213        traffic_test(wpas, hapd1)
214        if only_sta:
215            return
216
217        dev[0].set("rsn_overriding", "0")
218        dev[0].connect(ssid, psk=passphrase, key_mgmt="WPA-PSK",
219                       scan_freq="2412 2437")
220
221        status = wpas.get_status()
222        logger.debug("wpas STATUS:\n" + str(status))
223        if status['key_mgmt'] != 'SAE-EXT-KEY' or \
224           'pmf' not in status or \
225           status['pmf'] != '2' or \
226           status['pairwise_cipher'] != 'GCMP-256':
227            raise Exception("Unexpected result for new STA")
228
229        status = dev[0].get_status()
230        logger.debug("dev[0] STATUS:\n" + str(status))
231        if status['key_mgmt'] != 'WPA2-PSK' or \
232           status['pairwise_cipher'] != 'CCMP':
233            raise Exception("Unexpected result for legacy STA")
234
235def test_rsn_override_connect_cmd(dev, apdev):
236    """RSNE=WPA2-Personal/PMF-optional override=WPA3-Personal/PMF-required using cfg80211 connect command"""
237    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
238    wpas.interface_add("wlan5", drv_params="force_connect_cmd=1 rsn_override_in_driver=1")
239    check_sae_capab(wpas)
240
241    ssid = "test-rsn-override"
242    params = hostapd.wpa2_params(ssid=ssid,
243                                 passphrase="12345678",
244                                 ieee80211w='1')
245    params['rsn_override_key_mgmt'] = 'WPA-PSK-SHA256'
246    params['rsn_override_pairwise'] = 'CCMP GCMP-256'
247    params['rsn_override_mfp'] = '2'
248    params['beacon_prot'] = '1'
249    hapd = hostapd.add_ap(apdev[0], params)
250
251    wpas.set("rsn_overriding", "1")
252    wpas.connect(ssid, psk="12345678", key_mgmt="WPA-PSK-SHA256",
253                 ieee80211w="2", scan_freq="2412")
254
255def test_rsn_override_omit_rsnxe(dev, apdev):
256    """RSN overriding with RSNXE explicitly omitted"""
257    check_sae_capab(dev[0])
258
259    ssid = "test-rsn-override"
260    params = hostapd.wpa2_params(ssid=ssid,
261                                 passphrase="12345678",
262                                 ieee80211w='1')
263    params['rsn_override_key_mgmt'] = 'SAE SAE-EXT-KEY'
264    params['rsn_override_pairwise'] = 'CCMP GCMP-256'
265    params['rsn_override_mfp'] = '2'
266    params['beacon_prot'] = '1'
267    params['sae_groups'] = '19 20'
268    params['sae_require_mfp'] = '1'
269    params['sae_pwe'] = '2'
270    params['ssid_protection'] = '1'
271    params['rsn_override_omit_rsnxe'] = '1'
272    hapd = hostapd.add_ap(apdev[0], params)
273    bssid = hapd.own_addr()
274
275    try:
276        dev[0].set("rsn_overriding", "1")
277        dev[0].scan_for_bss(bssid, freq=2412)
278        dev[0].set("sae_pwe", "2")
279        dev[0].set("sae_groups", "")
280        dev[0].connect(ssid, sae_password="12345678", key_mgmt="SAE",
281                       ieee80211w="2", ssid_protection="1",
282                       scan_freq="2412")
283    finally:
284        dev[0].set("sae_pwe", "0")
285        dev[0].set("rsn_overriding", "0")
286
287def test_rsn_override_replace_ies(dev, apdev):
288    """RSN overriding and replaced AP IEs"""
289    check_sae_capab(dev[0])
290
291    ssid = "test-rsn-override"
292    params = hostapd.wpa2_params(ssid=ssid,
293                                 passphrase="12345678",
294                                 ieee80211w='1')
295    params['rsn_override_key_mgmt'] = 'SAE'
296    params['rsn_override_key_mgmt_2'] = 'SAE-EXT-KEY'
297    params['rsn_override_pairwise'] = 'CCMP'
298    params['rsn_override_pairwise_2'] = 'GCMP-256'
299    params['rsn_override_mfp'] = '1'
300    params['rsn_override_mfp_2'] = '2'
301    params['beacon_prot'] = '1'
302    params['sae_groups'] = '19 20'
303    params['sae_require_mfp'] = '1'
304    params['sae_pwe'] = '2'
305    params['ssid_protection'] = '1'
306    params['rsne_override'] = '30180100000fac040100000fac040200000facff000fac020c00'
307    params['rsnxe_override'] = 'f40320eeee'
308    params['rsnoe_override'] = 'dd1c506f9a290100000fac040100000fac040200000facff000fac088c00'
309    params['rsno2e_override'] = 'dd1c506f9a2a0100000fac040100000fac090200000facff000fac18cc00'
310    params['rsnxoe_override'] = 'dd07506f9a2b20bbbb'
311    hapd = hostapd.add_ap(apdev[0], params)
312    bssid = hapd.own_addr()
313
314    try:
315        dev[0].set("rsn_overriding", "1")
316        dev[0].scan_for_bss(bssid, freq=2412)
317        dev[0].set("sae_pwe", "2")
318        dev[0].set("sae_groups", "")
319        dev[0].connect(ssid, sae_password="12345678", key_mgmt="SAE",
320                       ieee80211w="2", ssid_protection="1",
321                       scan_freq="2412")
322    finally:
323        dev[0].set("sae_pwe", "0")
324        dev[0].set("rsn_overriding", "0")
325
326def test_rsn_override_rsnxe_extensibility(dev, apdev):
327    """RSN overriding and RSNXE extensibility"""
328    check_sae_capab(dev[0])
329
330    ssid = "test-rsn-override"
331    params = hostapd.wpa2_params(ssid=ssid,
332                                 passphrase="12345678",
333                                 ieee80211w='1')
334    params['rsn_override_key_mgmt'] = 'SAE SAE-EXT-KEY'
335    params['rsn_override_pairwise'] = 'CCMP GCMP-256'
336    params['rsn_override_mfp'] = '2'
337    params['beacon_prot'] = '1'
338    params['sae_groups'] = '19 20'
339    params['sae_require_mfp'] = '1'
340    params['sae_pwe'] = '2'
341    params['rsnxe_override'] = 'f4182f0000ffffffffffffffffffffffffffeeeeeeeeeeeeeeee'
342    params['rsnxoe_override'] = 'dd1c506f9a2b2f0000ffffffffffffffffffffffffffeeeeeeeeeeeeeeee'
343    hapd = hostapd.add_ap(apdev[0], params)
344    bssid = hapd.own_addr()
345
346    try:
347        dev[0].set("rsn_overriding", "1")
348        dev[0].scan_for_bss(bssid, freq=2412)
349        dev[0].set("sae_pwe", "2")
350        dev[0].set("sae_groups", "")
351        dev[0].connect(ssid, sae_password="12345678", key_mgmt="SAE",
352                       ieee80211w="2", ssid_protection="1",
353                       scan_freq="2412")
354    finally:
355        dev[0].set("sae_pwe", "0")
356        dev[0].set("rsn_overriding", "0")
357
358def test_rsn_override_sta_only(dev, apdev):
359    """RSN overriding enabled only on the STA"""
360    check_sae_capab(dev[0])
361    params = hostapd.wpa2_params(ssid="test-sae",
362                                 passphrase="12345678")
363    params['wpa_key_mgmt'] = 'SAE'
364    hapd = hostapd.add_ap(apdev[0], params)
365
366    dev[0].set("sae_groups", "")
367    try:
368        dev[0].set("rsn_overriding", "1")
369        dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
370                       scan_freq="2412")
371    finally:
372        dev[0].set("rsn_overriding", "0")
373
374def test_rsn_override_compatibility_mode(dev, apdev):
375    """RSN overriding and WPA3-Personal Compatibility Mode"""
376    check_sae_capab(dev[0])
377
378    ssid = "test-rsn-override"
379    params = hostapd.wpa2_params(ssid=ssid,
380                                 passphrase="12345678")
381    params['rsn_override_key_mgmt'] = 'SAE'
382    params['rsn_override_key_mgmt_2'] = 'SAE-EXT-KEY'
383    params['rsn_override_pairwise'] = 'CCMP'
384    params['rsn_override_pairwise_2'] = 'GCMP-256'
385    params['rsn_override_mfp'] = '2'
386    params['rsn_override_mfp_2'] = '2'
387    params['beacon_prot'] = '1'
388    params['sae_groups'] = '19 20'
389    params['sae_require_mfp'] = '1'
390    params['sae_pwe'] = '2'
391    hapd = hostapd.add_ap(apdev[0], params)
392    bssid = hapd.own_addr()
393
394    try:
395        logger.info("RSN overriding capable STA using RSNO2E")
396        dev[0].set("rsn_overriding", "1")
397        dev[0].scan_for_bss(bssid, freq=2412)
398        dev[0].set("sae_pwe", "2")
399        dev[0].set("sae_groups", "")
400        dev[0].connect(ssid, sae_password="12345678",
401                       pairwise="GCMP-256", key_mgmt="SAE-EXT-KEY",
402                       ieee80211w="2", scan_freq="2412")
403        hapd.wait_sta()
404        dev[0].request("REMOVE_NETWORK all")
405        dev[0].wait_disconnected()
406        hapd.wait_sta_disconnect()
407
408        logger.info("RSN overriding capable STA using RSNOE")
409        dev[0].set("sae_pwe", "0")
410        dev[0].connect(ssid, sae_password="12345678",
411                       pairwise="CCMP", key_mgmt="SAE",
412                       ieee80211w="2", scan_freq="2412")
413        hapd.wait_sta()
414        dev[0].request("REMOVE_NETWORK all")
415        dev[0].wait_disconnected()
416        hapd.wait_sta_disconnect()
417
418        logger.info("RSN overriding capable STA using RSNE")
419        dev[0].connect(ssid, psk="12345678",
420                       pairwise="CCMP", key_mgmt="WPA-PSK",
421                       ieee80211w="0", scan_freq="2412")
422        hapd.wait_sta()
423        dev[0].request("REMOVE_NETWORK all")
424        dev[0].wait_disconnected()
425        hapd.wait_sta_disconnect()
426
427        logger.info("RSN overriding uncapable STA using RSNE")
428        dev[0].set("rsn_overriding", "0")
429        dev[0].connect(ssid, psk="12345678",
430                       pairwise="CCMP", key_mgmt="WPA-PSK",
431                       ieee80211w="0", scan_freq="2412")
432        hapd.wait_sta()
433        dev[0].request("REMOVE_NETWORK all")
434        dev[0].wait_disconnected()
435        hapd.wait_sta_disconnect()
436    finally:
437        dev[0].set("sae_pwe", "0")
438        dev[0].set("rsn_overriding", "0")
439