]> git.ipfire.org Git - thirdparty/openwrt.git/commitdiff
wifi-scripts: ucode: advertise Transition Disable on WPA3-only BSSes
authorHauke Mehrtens <hauke@hauke-m.de>
Sun, 19 Apr 2026 10:50:08 +0000 (12:50 +0200)
committerHauke Mehrtens <hauke@hauke-m.de>
Sat, 2 May 2026 18:34:22 +0000 (20:34 +0200)
WPA3 Specification v3.5 §13 defines the Transition Disable element sent
inside message 3 of the 4-way handshake.  An AP that is no longer
offering a transition mode for its SSID sets the matching bit so that
compliant STAs permanently stop falling back to WPA-PSK / WPA-EAP /
open for that SSID, hardening against downgrade attacks and against
operator mistakes where a transition-mode BSS is briefly brought up on
an SSID that previously ran WPA3-only.

Expose this as a UCI list 'transition_disable' with three classes of
entries:

  * The existing OpenWrt encryption tokens 'sae' (bit 0x01), 'sae-pk'
    (0x02), 'wpa3' (0x04) and 'owe' (0x08) OR into the bitmap.  SAE-PK
    itself is not yet wired through wifi-scripts; the token only lets
    an operator who configured SAE-PK out of band also hand the
    matching bit to hostapd.

  * 'on' derives the bitmap from the AP's auth_type ('sae' -> 0x01,
    'eap2'/'eap192' -> 0x04, pure 'owe' -> 0x08) and overrides any
    other explicit tokens in the same list.  Transition BSSes
    (psk-sae, eap-eap2, owe with owe_transition set) produce no
    bits even under 'on' because they are by definition still in
    transition.

  * 'off' unconditionally suppresses the element regardless of any
    other entries.  Operators who need to revert a WPA3-only SSID back
    to a transition mode can set this proactively, giving compliant
    STAs time to forget the permanent bit before the mode change.

Leave the list unset by default.  Advertising Transition Disable is a
one-way door -- once a compliant STA has seen the permanent bit for an
SSID it will refuse to associate to a transition-mode BSS of the same
name ever again -- so it must be opted in to per SSID, never flipped
on by a firmware bump.  This also matches the WPA3 and Wi-Fi Enhanced
Open Deployment and Implementation Guide v1.1 Table 4 requirement that
Transition Disable be MAND disabled by default on APs.

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>
package/network/config/wifi-scripts/files-ucode/usr/share/schema/wireless.wifi-iface.json
package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/ap.uc

index 97dea0e42ddc83c7cdeab40a9a2bb3712b84ca4d..c32a49170cb010da6208775276c04e7dfce00b65 100644 (file)
                        "description": "Local time zone as specified in 8.3 of IEEE Std 1003.1-2004",
                        "type": "string"
                },
+               "transition_disable": {
+                       "description": "Transition modes the AP signals as disabled per WPA3 v3.5 §13. Entries 'sae', 'sae-pk', 'wpa3', 'owe' are OR'd into the bitmap; 'on' (or '1') derives it from auth_type; 'off' (or '0') suppresses the element. Unset by default.",
+                       "type": "array",
+                       "items": {
+                               "type": "string",
+                               "enum": [ "on", "off", "0", "1", "sae", "sae-pk", "wpa3", "owe" ]
+                       }
+               },
                "uapsd": {
                        "type": "alias",
                        "default": "uapsd_advertisement_enabled"
index 400034e4a9ec6f9a7ee14b4a0e90e9624692a555..b2890ff6914454cc9adffb2ff60a8672436110c1 100644 (file)
@@ -439,6 +439,42 @@ function iface_mfp(config) {
        ]);
 }
 
+function iface_transition_disable(config) {
+       if (config.wpa < 2)
+               return;
+
+       let list = config.transition_disable;
+       if (!list || !length(list))
+               return;
+
+       for (let s in list)
+               if (s == 'off' || s == '0')
+                       return;
+
+       let bits = 0;
+       for (let s in list) {
+               if (s == 'on' || s == '1') {
+                       bits = 0;
+                       switch (config.auth_type) {
+                       case 'sae':    bits = 0x01; break;
+                       case 'eap2':
+                       case 'eap192': bits = 0x04; break;
+                       case 'owe':    if (!config.owe_transition) bits = 0x08; break;
+                       }
+                       break;
+               }
+               switch (s) {
+               case 'sae':    bits |= 0x01; break;
+               case 'sae-pk': bits |= 0x02; break;
+               case 'wpa3':   bits |= 0x04; break;
+               case 'owe':    bits |= 0x08; break;
+               }
+       }
+
+       if (bits)
+               append('transition_disable', sprintf('0x%02x', bits));
+}
+
 function iface_key_caching(config) {
        if (config.wpa < 2)
                return;
@@ -533,6 +569,8 @@ export function generate(interface, data, config, vlans, stas, phy_features) {
 
        iface_mfp(config);
 
+       iface_transition_disable(config);
+
        iface_key_caching(config);
 
        iface_hs20(config);