wifi-scripts: ucode: add WPA3-Personal Compatibility Mode

The WPA3 and Wi-Fi Enhanced Open Deployment and Implementation Guide
v1.1 §2.4 (Tables 6 and 7) defines WPA3-Personal Compatibility Mode:
the AP advertises a legacy-looking RSNE (WPA-PSK, CCMP-128, PMF
Disabled) while RSN Override Elements layered on top expose SAE and,
on EHT, SAE-EXT-KEY.  WPA2-only STAs and STAs that ignore RSN
Overriding associate unchanged; modern STAs pick up the stronger WPA3
AKM via RSNOE or RSNO2E.

Only the pairwise cipher differs between elements: RSNE and RSNOE
advertise CCMP-128, RSNO2E advertises GCMP-256 (EHT only).  Group
data (CCMP-128) and group management cipher (BIP-CMAC-128) are the
same in all three per Tables 6/7, so hostapd's BSS-wide group_cipher
and group_mgmt_cipher singletons produce the spec-correct values.

Unlike WPA3-Personal Transition Mode (sae-mixed), which puts PSK and
SAE together in the main RSNE with PMF Capable, Compatibility Mode
keeps the main RSNE strictly WPA2-shaped so clients that choke on a
mixed AKM list or PMF=Capable still see a pure WPA2 BSS.  The trade-
off is that clients without RSN Overriding support never pick up SAE.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Link: https://github.com/openwrt/openwrt/pull/23009
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
This commit is contained in:
Hauke Mehrtens 2026-04-19 21:38:56 +02:00
parent ef393caee2
commit 86b9eec8f0
2 changed files with 70 additions and 12 deletions

View File

@ -81,14 +81,26 @@ function iface_accounting_server(config) {
append_vars(config, [ 'radius_acct_req_attr' ]);
}
function iface_auth_type(config) {
function iface_auth_type(config, band) {
if (config.auth_type in [ 'sae', 'owe', 'eap2', 'eap192', 'dpp' ])
config.ieee80211w = 2;
if (config.auth_type in [ 'psk-sae', 'eap-eap2' ])
set_default(config, 'ieee80211w', 1);
if (config.auth_type in [ 'sae', 'psk-sae' ]) {
if (config.auth_type == 'psk-sae-compat') {
if (band == '6g') {
set_default(config, 'ieee80211w', 2);
} else {
set_default(config, 'ieee80211w', 0);
config.rsn_override_mfp = 2;
config.rsn_override_omit_rsnxe = 1;
}
if (config.rsn_override_pairwise_2)
config.rsn_override_mfp_2 = 2;
}
if (config.auth_type in [ 'sae', 'psk-sae', 'psk-sae-compat' ]) {
config.sae_require_mfp = 1;
if (!config.ppsk)
set_default(config, 'sae_pwe', 2);
@ -122,6 +134,7 @@ function iface_auth_type(config) {
case 'psk2':
case 'sae':
case 'psk-sae':
case 'psk-sae-compat':
config.vlan_possible = 1;
config.wps_possible = 1;
@ -137,12 +150,12 @@ function iface_auth_type(config) {
netifd.setup_failed('INVALID_WPA_PSK');
}
if (config.auth_type in [ 'psk', 'psk-sae' ]) {
if (config.auth_type in [ 'psk', 'psk-sae', 'psk-sae-compat' ] && band != '6g') {
set_default(config, 'wpa_psk_file', `/var/run/hostapd-${config.ifname}.psk`);
touch_file(config.wpa_psk_file);
}
if (config.auth_type in [ 'sae', 'psk-sae' ]) {
if (config.auth_type in [ 'sae', 'psk-sae', 'psk-sae-compat' ]) {
set_default(config, 'sae_password_file', `/var/run/hostapd-${config.ifname}.sae`);
touch_file(config.sae_password_file);
}
@ -198,7 +211,7 @@ function iface_auth_type(config) {
}
function iface_ppsk(config) {
if (!(config.auth_type in [ 'none', 'owe', 'psk', 'sae', 'psk-sae', 'wep' ]) || !config.auth_server_addr)
if (!(config.auth_type in [ 'none', 'owe', 'psk', 'sae', 'psk-sae', 'psk-sae-compat', 'wep' ]) || !config.auth_server_addr)
return;
iface_authentication_server(config);
@ -402,7 +415,9 @@ function iface_roaming(config) {
}
function iface_mfp(config) {
if (!config.ieee80211w || config.wpa < 2) {
let override_mfp = config.rsn_override_mfp || config.rsn_override_mfp_2;
if ((!config.ieee80211w && !override_mfp) || config.wpa < 2) {
append('ieee80211w', 0);
return;
}
@ -432,7 +447,7 @@ function iface_key_caching(config) {
'rsn_preauth', 'rsn_preauth_interfaces'
]);
} else {
set_default(config, 'okc', (config.auth_type in [ 'sae', 'psk-sae', 'owe' ]));
set_default(config, 'okc', (config.auth_type in [ 'sae', 'psk-sae', 'psk-sae-compat', 'owe' ]));
}
if (!config.okc && !config.fils)
@ -487,12 +502,12 @@ export function generate(interface, data, config, vlans, stas, phy_features) {
config.auth_type = 'eap2';
}
if (config.auth_type in [ 'psk', 'psk-sae' ])
if (config.auth_type in [ 'psk', 'psk-sae', 'psk-sae-compat' ] && data.config.band != '6g')
iface_wpa_stations(config, stas);
if (config.auth_type in [ 'sae', 'psk-sae' ])
if (config.auth_type in [ 'sae', 'psk-sae', 'psk-sae-compat' ])
iface_sae_stations(config, stas);
iface_auth_type(config);
iface_auth_type(config, data.config.band);
iface_accounting_server(config);
@ -520,7 +535,7 @@ export function generate(interface, data, config, vlans, stas, phy_features) {
iface_interworking(config);
iface.wpa_key_mgmt(config);
iface.wpa_key_mgmt(config, data.config.band);
append_vars(config, [
'wpa_key_mgmt',
]);
@ -541,6 +556,10 @@ export function generate(interface, data, config, vlans, stas, phy_features) {
]);
}
if (config.rsn_override_omit_rsnxe) {
append_vars(config, ['rsn_override_omit_rsnxe']);
}
/* raw options */
for (let raw in config.hostapd_bss_options)
append_raw(raw);

View File

@ -57,6 +57,15 @@ export function parse_encryption(config, dev_config) {
config.auth_type = 'psk-sae';
break;
case 'sae-compat':
config.auth_type = 'psk-sae-compat';
config.wpa_pairwise = 'CCMP';
if (dev_config.band != '6g')
config.rsn_override_pairwise = 'CCMP';
if (wildcard(dev_config.htmode ?? '', 'EHT*'))
config.rsn_override_pairwise_2 = 'GCMP-256';
break;
case 'wpa':
case 'wpa2':
case 'wpa-mixed':
@ -104,7 +113,7 @@ export function parse_encryption(config, dev_config) {
config.wpa_pairwise ??= 'CCMP';
};
export function wpa_key_mgmt(config) {
export function wpa_key_mgmt(config, band) {
if (!config.wpa)
return;
@ -174,6 +183,36 @@ export function wpa_key_mgmt(config) {
append_value(config, 'wpa_key_mgmt', 'FT-PSK');
break;
case 'psk-sae-compat':
if (band == '6g') {
append_value(config, 'wpa_key_mgmt', 'SAE');
if (config.ieee80211r)
append_value(config, 'wpa_key_mgmt', 'FT-SAE');
if (config.sae_ext_key && config.rsn_override_pairwise_2) {
append_value(config, 'rsn_override_key_mgmt_2', 'SAE-EXT-KEY');
if (config.ieee80211r)
append_value(config, 'rsn_override_key_mgmt_2', 'FT-SAE-EXT-KEY');
}
} else {
append_value(config, 'wpa_key_mgmt', 'WPA-PSK');
if (config.ieee80211w)
append_value(config, 'wpa_key_mgmt', 'WPA-PSK-SHA256');
if (config.ieee80211r)
append_value(config, 'wpa_key_mgmt', 'FT-PSK');
append_value(config, 'rsn_override_key_mgmt', 'SAE');
if (config.ieee80211r)
append_value(config, 'rsn_override_key_mgmt', 'FT-SAE');
if (config.sae_ext_key && config.rsn_override_pairwise_2) {
append_value(config, 'rsn_override_key_mgmt_2', 'SAE-EXT-KEY');
if (config.ieee80211r)
append_value(config, 'rsn_override_key_mgmt_2', 'FT-SAE-EXT-KEY');
}
}
break;
case 'owe':
append_value(config, 'wpa_key_mgmt', 'OWE');
break;