From 68165c85c83a50e18254abef9c0866c8b8a2bcd8 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Sun, 19 Apr 2026 16:50:37 +0200 Subject: [PATCH] wifi-scripts: ucode: default BIP cipher from wpa_pairwise The WPA3 and Wi-Fi Enhanced Open Deployment and Implementation Guide v1.1 (Tables 4, 5, 6) requires the group-management cipher (BIP) to match the mode and strength of the pairwise cipher: GCM-mode pairwise ciphers pair with BIP-GMAC integrity, CCM-mode pairwise ciphers with BIP-CMAC integrity. The ucode pipeline hard-coded group_mgmt_cipher to AES-128-CMAC (BIP-CMAC-128) regardless of the pairwise cipher, except for the eap192 special case that already forced BIP-GMAC-256. An EHT WPA3-Personal BSS therefore emitted wpa_pairwise=GCMP-256 alongside group_mgmt_cipher=AES-128-CMAC -- the integrity cipher two steps weaker than the data cipher and a spec violation on EHT. hostapd has a single group_mgmt_cipher knob, so the selected BIP has to be compatible with every pairwise cipher in wpa_pairwise. Picking from the first token would mis-select on mixed lists -- e.g. wpa_pairwise=\"GCMP-256 CCMP\" would yield BIP-GMAC-256, which a CCMP-only STA cannot negotiate. Walk the wpa_pairwise tokens and pick the BIP that matches the weakest cipher present: CCMP / TKIP -> AES-128-CMAC (BIP-CMAC-128) CCMP-256 -> BIP-CMAC-256 GCMP -> BIP-GMAC-128 GCMP-256 -> BIP-GMAC-256 Token matching uses fnmatch wildcards against a copy of wpa_pairwise that is padded with leading and trailing spaces, so each token is space-bounded regardless of its position in the list. The RSN override pairwise lists are not consulted: in the only caller that sets them (WPA3-Personal Compatibility Mode), Tables 6 and 7 require BIP-CMAC-128 across RSNE/RSNOE/RSNO2E even when the override lists advertise GCMP-256, so wpa_pairwise=CCMP already yields the correct BIP. An explicit ieee80211w_mgmt_cipher UCI value still wins over the derived default. Co-Authored-By: Claude Opus 4.7 Link: https://github.com/openwrt/openwrt/pull/23009 Signed-off-by: Hauke Mehrtens --- .../files-ucode/usr/share/ucode/wifi/ap.uc | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/ap.uc b/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/ap.uc index b2890ff691..f9b676197e 100644 --- a/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/ap.uc +++ b/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/ap.uc @@ -418,6 +418,20 @@ function iface_roaming(config) { ]); } +function default_group_mgmt_cipher(config) { + let p = ' ' + (config.wpa_pairwise ?? '') + ' '; + + if (wildcard(p, '* CCMP *') || wildcard(p, '* TKIP *')) + return 'AES-128-CMAC'; + if (wildcard(p, '* CCMP-256 *')) + return 'BIP-CMAC-256'; + if (wildcard(p, '* GCMP *')) + return 'BIP-GMAC-128'; + if (wildcard(p, '* GCMP-256 *')) + return 'BIP-GMAC-256'; + return 'AES-128-CMAC'; +} + function iface_mfp(config) { let override_mfp = config.rsn_override_mfp || config.rsn_override_mfp_2; @@ -426,10 +440,7 @@ function iface_mfp(config) { return; } - if (config.auth_type == 'eap192') - config.group_mgmt_cipher = 'BIP-GMAC-256'; - else - config.group_mgmt_cipher = config.ieee80211w_mgmt_cipher ?? 'AES-128-CMAC'; + config.group_mgmt_cipher = config.ieee80211w_mgmt_cipher ?? default_group_mgmt_cipher(config); set_default(config, 'beacon_prot', 1);