Skip to content

Instantly share code, notes, and snippets.

@est31
Last active May 20, 2024 15:04
Show Gist options
  • Save est31/d92d17acbb4ea152296f9b38764cd791 to your computer and use it in GitHub Desktop.
Save est31/d92d17acbb4ea152296f9b38764cd791 to your computer and use it in GitHub Desktop.

Trying to deploy WPA3 on my home network

Introduction

Recently, news broke about a new possible offline attack on WPA2 using PMKID. To summarize the attack, WPA2 protected APs can end up broadcasting PMKID values which can then be used to offline-brute-force the password.

These PMKID values are computed this way:

PMKID = HMAC-SHA1-128(PMK, "PMK Name" | MAC_AP | MAC_STA)

Where PMK is computed this way (according to this link):

PMK = PBKDF2(HMAC−SHA1, PSK, SSID, 4096, 256)

PSK is the pre-shared key password. All the other values (MAC_AP, MAC_STA, SSID) are public and known by the attacker. Thus, given the PMKID, an attacker could perform an offline brute force attack on the shared password.

Fortunately, my local WiFi network is secure because my password has higher entropy than 128 bit, making it impossible to brute force it. However, this new revelation reminded me that I was still depending on WPA2. Why is that still the case?

None of my devices on my network have official WPA3 support yet. But despite that, I attempted to obtain WPA3 support on each of them.

The post goes into a lot of detail and is a bit long. If you are not interested in the details, you may prefer to only read the summary section near the end.

WPA3 is great

First, a few notes on WPA3.

WPA3 replaces WPA2-PSK with a key agreement protocol called "SAE" which stands for "Simultaneous Authentication of Equals".

This mode is much better than WPA2-PSK because it:

  • has perfect forward secrecy (which WPA2 lacks), and
  • protects from offline brute force attacks.

The first advantage is great because it means that even if an attacker gets your password, they can't use it to decrypt any passively recorded communication of yours. With WPA2, such decryption is always possible.

As for the second advantage, most passwords used in WiFi networks (let's be honest, most passwords anywhere) are simple enough that they can be cracked using offline brute force attacks with low cost. Forcing an attack to be online (either via a MITM or via a client trying different passwords) dramatically reduces attacker capabilities.

WPA3 also adds other modes like DPP or OWE. This blog post provides some technical info on how the WPA3 modes work. But in this project, I didn't put any focus on these two.

WPA3 is maybe not great?

The comment section of the schneier.com blog post on WPA3 made me a bit cautious though. It seems that Dragonfly, the scheme that SAE bases on, has been the subject of some controversy.

This 2013 summary was linked by one of the commenters, from a time when apparently Dragonfly had been proposed for inclusion into TLS version 1.3.

I actually don't have an idea how relevant this criticism still is. Maybe all of the issues got fixed? Maybe only some of them? A paper that proves the security of SAE has been published since those E-Mails, but I doubt that this resolves all of the points. For example, are the mentioned timing attacks still relevant? No idea.

My current assumption is that if SAE were really totally insecure, there'd have been more prominent writeups about its insecurity, in more drastic words. So maybe SAE is not perfect, but it surely is better than the prior WPA2-PSK exchange protocol.

J-PAKE might have been a better choice but well...

I still think it's worth it to switch to WPA3. If the security issues turn out to be more grave, I can always go back.

All paths lead to hostap

On my private WiFi, there are three devices:

  • The WiFi AP, using OpenWRT 18.06
  • My netbook, using Kubuntu 18.04
  • My phone, using LineageOS 14.1 (Android)

All of them use some Linux distro. Even though these distros are highly different from each other (Busybox/musl on OpenWRT, GNU/glibc/systemd on Kubuntu, Android/bionic on LineageOS), they all base their WiFi implementations on the hostap project.

The hostap project is split in two components:

  • hostapd, which lets you run a WiFi access point, and
  • wpa_supplicant, which lets you connect to an existing WiFi network

The two components do share code together.

So, does hostap support WPA3 yet? This question has, in fact, been asked twice already on the mailing list: first in March, then again in June.

According to the E-Mails, hostap from the master branch should support WPA3.

The last release of hostap was version 2.6 in 2016, which is almost 2 years ago. We don't want to wait for a release of hostap in order to start to use SAE.

OpenWRT

In preparation for switching my WiFi to WPA3, I have updated my router from OpenWRT 15.05.01 to the brand-new 18.06 (released on July 30, 2018). It's the first release since the reunification of the OpenWRT project.

According to the file package/network/services/hostapd/Makefile, OpenWRT 18.06 uses hostapd commit fa617ee6a from April 2018. The E-Mail by the hostap maintainer saying that WPA3 was supported on master was from March. So OpenWRT 18.06 should theoretically have a recent enough hostapd.

hostapd's configuration is controlled by a config file with the default name hostapd.conf. This commit added documentation on how SAE support can be added: you'll have to add SAE to the wpa_key_mgmt key.

However, OpenWRT doesn't let/want you to change the config files directly. Instead, it uses UCI that acts as a layer between the user and hostapd.conf.

In particular, the OpenWRT system parses the /etc/config/wireless file and generates the hostapd.conf for you.

The file that performs this task is stored in package/network/services/hostapd/files/hostapd.sh. Let's find out where it's stored on the device:

# find / -name hostapd.sh
/lib/netifd/hostapd.sh
/overlay/upper/lib/netifd/hostapd.sh
/rom/lib/netifd/hostapd.sh

Now, let's find out where the hostapd.conf file is stored:

# find / -name *hostapd*.conf
/tmp/run/hostapd-phy0.conf

Using grep, we can determine the currently set config key:

# grep wpa_key_mgmt /tmp/run/hostapd-phy0.conf
wpa_key_mgmt=WPA-PSK

Let's edit /lib/netifd/hostapd.sh now to add SAE to this key. Inside the hostapd_append_wpa_key_mgmt function, we can conveniently add a line like:

append wpa_key_mgmt "SAE"

Now, we can run /etc/init.d/network restart to re-run the script. Note: the command will bring down the network during its operations, so if you connect to your OpenWRT box through the network, it might render the ssh session unresponsive. After all the network is being restarted and ssh uses the network. But for me the ssh session got responsive again after waiting a bit. After the command was run, the wpa_key_mgmt key should include SAE. And indeed it does:

# grep wpa_key_mgmt /tmp/run/hostapd-phy0.conf
wpa_key_mgmt=WPA-PSK SAE

But for some reason, the WiFi network isn't showing up any more. logread | grep hostapd is giving us a very suspicious log message:

daemon.err hostapd: Line 32: invalid key_mgmt 'SAE'

Looking at the source code of hostapd, it seems that SAE is treated as invalid if CONFIG_SAE is not set. Now, do we have to compile OpenWRT? Or is there a way around it?

Fortunately, looking at the Makefile again, we can see that wpad-mesh has this flag enabled.

So we only have to replace our currently installed wpad with wpad-mesh:

opkg remove wpad-mini
opkg install wpad-mesh

Now let's try /etc/init.d/network restart again. Logread now tells us that the WiFi network was set up successfully:

hostapd: wlan0: interface state UNINITIALIZED->ENABLED

After this, the network should have SAE capabilities.

Kubuntu

The next device to migrate over is my netbook. It uses wpa_supplicant which has a .conf file similar to hostapd, and just like with OpenWRT, the user doesn't edit the .conf file directly, but uses an intermediary. For (K)ubuntu, this intermediary is NetworkManager.

Sadly, NetworkManager doesn't seem to support WPA3 yet. But that shouldn't concern us right now. First, we'll need to get wpa_supplicant to support SAE.

Obtaining wpa_supplicant that supports SAE

The Ubuntu 18.04 wpa_supplicant package is pointing to 2.6, the last stable release. Therefore we need to update the package locally to a more recent git version.

Debian provides utilities/scripts for compiling existing packages locally and changing the source if desired.

As preparation, one needs to obtain the required build dependencies via sudo apt build-dep wpasupplicant. Then, you obtain the source code of the current package via the apt source wpasupplicant command. You should probably do this in a dedicated subdirectory because it'll create a lot of files in your pwd. After this, one could directly compile the source code using debuild -us -uc -i -I. But we want to do our own changes first.

Fortunately, the maintainers of the wpa package have helped us a bit with their debian/get-orig-source script. If the top entry in debian/changelog has a version formatted like:

<anything>+git<number>+<revision>

then the script will automatically download the specified revision from hostapd's git. As of writing this document the latest hostapd git commit is c773c7d5ddbc8ca031614e1c999b05bec43778aa.

Let's try it out by adding a version to debian/changelog:

export VER=2.7+git00+c773c7d5ddbc8ca031614e1c999b05bec43778aa
cd wpa-2.6
DEBFULLNAME=N EMAIL=U@H dch -v 2:$VER
debian/rules get-orig-source

The dch command will open an interactive editor for you to write something into. You must write something otherwise dch will terminate and not add the version. You can just write "SAE" or some keyboard spam, it doesn't matter. After dch ran successfully, it tells us that it renamed the directory we are inside.

Debian's guide on updating packages says that in order to update the source code, one has to move the debian directory to the new location. Let's try this by doing:

cd ..
cd ..
mv wpa-$VER/debian debian
rm -r wpa-$VER
tar xvf wpa_$VER.orig.tar.xz
cd wpa-$VER/
mv ../debian .

Then we can try running debuild -us -uc -i -I but it will give us errors about patches not applying. Turns out that a lot of patches present in the debian package were actually backports of master commits onto the stable release. We should thus remove them:

pushd debian/patches
sed -i "/000.*.patch/d" series
sed -i "/VU-228519\/.*/d" series
sed -i "/wpa_disable_eapol_key_retries.patch/d" series
sed -i "/fix-pem-decryption.patch/d" series
sed -i "/nl80211-Fix.*.patch/d" series
sed -i "/wpa_supplicant-update-MAC-when-driver-detects-a-change.patch/d" series
sed -i "/android_hal_fw_path_change.patch/d" series
popd
export QUILT_PATCHES="debian/patches"
while quilt push; do quilt refresh; done

Remember that in OpenWRT we also needed the CONFIG_SAE flag. We'll need it here as well:

echo "CONFIG_SAE=y" >> debian/config/hostapd/linux
echo "CONFIG_SAE=y" >> debian/config/wpasupplicant/linux

Now we can finally build the package:

debuild -us -uc -i -I

And install it using:

dpkg -i wpasupplicant_2.7+git00+c773c7d5ddbc8ca031614e1c999b05bec43778aa_amd64.deb

You can always install the distro provided version using sudo apt install wpasupplicant=<version>, with <version> set to the current version of the distro you have.

Convincing wpa_supplicant to use SAE

Now the new wpasupplicant can be tried out.

We can avoid talking to NetworkManager, which doesn't know about SAE, by talking to wpa_supplicant directly via the wpa_cli command:

sudo wpa_cli status

This prints out the status of the current connection. Most importantly, you can do:

sudo wpa_cli status | grep key_mgmt

And it'll print you the key management mechanism currently used, whether it's SAE, WPA-PSK or something different.

For useful links on wpa_cli, there is an invaluable list of commands here, an Arch wiki mention here, and a guide to use wpa_cli directly to connect to networks here.

wpa_supplicant maintains a list of stored networks, and uses it to connect one of the available networks. The stored networks can be listed by:

sudo wpa_cli list_networks

NetworkManager interfaces with wpa_supplicant by setting up one network and then changing it according to its own stored settings. Even if NetworkManager knows about multiple networks, it only passes one to wpa_supplicant, the one it's supposed to use. This network has the number 0.

Similarly to hostapd, wpa_supplicant uses a key_mgmt value to list the key algorithms that should be used to establish a connection. NetworkManager sets this value as well, as one can observe with:

sudo wpa_cli get_network 0 key_mgmt

We can now set this value to support SAE only and tell wpa_supplicant to re-connect to the network.

sudo wpa_cli set_network 0 key_mgmt SAE
sudo wpa_cli reassociate

If everything went well, we should see usage of SAE:

$ sudo wpa_cli status | grep key_mgmt
key_mgmt=SAE

NetworkManager's tray menu will still show something like WPA2-PSK, but this is because we talked to wpa_supplicant directly :).

This approach isn't viable however, because NetworkManager will often re-set the key_mgmt value of the current network.

As we are already patching and self-compiling wpa_supplicant, we can easily add one more change: If we change the config parsing code to treat "WPA-PSK" as if "WPA-PSK SAE" were present, we can trick wpa_supplicant into opportunistically supporting SAE, so supporting SAE where it is available. The requirement on WPA2-PSK is still present however, due to NetworkManager.

Doing this right before our debbuild step will apply the config parsing change to wpa_supplicant:

quilt new sae-if-psk.patch
quilt add wpa_supplicant/config.c
sed -i "s/WPA_KEY_MGMT_PSK;/WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_SAE;/" wpa_supplicant/config.c
quilt refresh

With this change, we can obtain the wanted key_mgmt without further involvement:

$ sudo wpa_cli status | grep key_mgmt
key_mgmt=SAE

So, the netbook is ported as well!

For reference, these were the full build instructions to for the custom-built wpa_supplicant that I used:

export VER=2.7+git00+c773c7d5ddbc8ca031614e1c999b05bec43778aa
apt source wpasupplicant
cd wpa-2.6
DEBFULLNAME=N EMAIL=U@H dch -v 2:$VER
debian/rules get-orig-source
cd ..
mv wpa-$VER/debian debian
rm -r wpa-$VER
tar xvf wpa_$VER.orig.tar.xz
cd wpa-$VER/
mv ../debian .
pushd debian/patches
sed -i "/000.*.patch/d" series
sed -i "/VU-228519\/.*/d" series
sed -i "/wpa_disable_eapol_key_retries.patch/d" series
sed -i "/fix-pem-decryption.patch/d" series
sed -i "/nl80211-Fix.*.patch/d" series
sed -i "/wpa_supplicant-update-MAC-when-driver-detects-a-change.patch/d" series
sed -i "/android_hal_fw_path_change.patch/d" series
popd
export QUILT_PATCHES="debian/patches"
while quilt push; do quilt refresh; done
echo "CONFIG_SAE=y" >> debian/config/hostapd/linux
echo "CONFIG_SAE=y" >> debian/config/wpasupplicant/linux
quilt new sae-if-psk.patch
quilt add wpa_supplicant/config.c
sed -i "s/WPA_KEY_MGMT_PSK;/WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_SAE;/" wpa_supplicant/config.c
quilt refresh
debuild -us -uc -i -I

LineageOS (Android)

The last (and probably hardest) device to be migrated is my phone, the Galaxy S2.

Android uses wpa_supplicant as well, as observable by this search. Thus, we might be able to use a similar trick as above to update wpa_supplicant and then patch it to parse WPA-PSK just like SAE.

According to this default.xml entry, this fork of wpa_supplicant is being used.

The wpa_supplicant fork seems to have been started some long time ago with a fixed version of hostapd and then patches by upstream got happily happily cherry picked on top. Seems like it's one of those ugly Android forks you keep hearing about.

But first things first. Before we can update wpa_supplicant, we need to find out how to compile it and get our modified version onto the Android device in the first place. Android, in embedded tradition, doesn't allow you to recompile individual system packages and exchange them with modified versions. This would have made our life much easier, but apparently they want you to compile the whole system OS in one piece. Thus, the first challenge is building LineageOS from source code.

Building LineageOS

Mostly I was following the device-specific (but auto generated) official build guide.

I'll mostly point out things I did or ran into that weren't mentioned in the guide. First of all, I fortunately didn't have to obtain the tools adb and repo manually, but could just use their official Ubuntu packages by doing sudo apt install adb repo. Also, my device doesn't use fastboot for flashing, so I didn't install it at all. This way I didn't need to add bin directories and didn't need any changes in my PATH env var either.

During running the brunch i9100 step, I got this error:

build/core/base_rules.mk:183: *** vendor/samsung/galaxys2-common/proprietary: MODULE.TARGET.SHARED_LIBRARIES.libUMP already defined by hardware/samsung/exynos4/hal/libUMP.

After a bit of googling I found this post. The instructions were a bit outdated, as the entire fimp stuff was gone. I only had to remove the i9100 from the line in device/samsung/galaxys2-common/extract-files.sh, as described in the post.

I also got this error:

flex-2.5.39: loadlocale.c:130: _nl_intern_locale_data: Assertion `cnt < (sizeof (_nl_value_type_LC_TIME) / sizeof (_nl_value_type_LC_TIME[0]))' failed.

This was probably because of me having a german locale on my computer. StackOverflow had a solution for my problem. All I had to do was:

export LC_ALL=C

Once the build is done, it populates the out/target/product/i9100 directory.

Flashing LineageOS

First, remember to always do your backups! Without wanting to spoil things, they turned out to be very useful down the line.

How to flash LineageOS is described on this page.

In principle, if LineageOS is already present on the phone, flashing is as easy as:

  • copying the zip file to the phone via something like adb push out/target/product/i9100/lineage-14.1-*-UNOFFICIAL-i9100.zip /sdcard0
  • rebooting to the TWRP recovery image e.g. via adb reboot recovery
  • and selecting and installing the zip file via the GUI

However, the LineageOS install that was present on my phone had been using official signing keys. This gave me an "error 7" during installation with an error message like:

Can't install this package on top of incompatible data. Please try another package or run a factory reset

The solution was to run a migration script provided by LineageOS, as described here.

During the process, I also updated my TWRP from 3.1.0-0 to 3.2.3-0 but then ran into an error of the form:

sysutil: mmap(292396813, R, PRIVATE, 21, 0) failed: Out of memory
sysutil: Map of '/sdcard0/lineage-14.1-20180814-UNOFFICIAL-i9100.zip' failed
Failed to map file '/sdcard0/lineage-14.1-20180814-UNOFFICIAL-i9100.zip'
Error installing zip file '/sdcard0/lineage-14.1-20180814-UNOFFICIAL-i9100.zip'

The problem is known. The workaround I applied was to revert to version 3.1.0-0.

Verification

There are two approaches to verify on the client whether a successful SAE connection has been established.

The first way is to use wpa_cli, similar to above. To my surprise, LineageOS actually does ship with wpa_cli per default. But attempts to use it end in wpa_cli not being able to communicate with the wpa_supplicant daemon:

$ adb shell
i9100:/ # wpa_cli
wpa_cli v2.6-devel-7.1.2
Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi> and contributors

This software may be distributed under the terms of the BSD license.
See README for more details.


Using interface 'wlan0'

Interactive mode

Could not connect to wpa_supplicant: wlan0 - re-trying

My guess is that the problem is an overzealous security policy or something like that. I found this patch for a different device, but when I tried to apply the patch to my device, the patch didn't have any effect.

So I tried a different approach: logcat. It's Android's unified logging utility. The Android fork of wpa_supplicant has been patched to output its log through this utility, including a log level translation. Really sophisticated :). We can patch wpa_supplicant to change the log level of any output we desire, to appear in logcat output.

One can search logcat output for wpa_supplicant by doing adb logcat wpa_supplicant:I *:S (without adb at the front if you are on-device). However, with default settings this command stays suspiciously silent and only reports some entries of the form:

rfkill: WLAN unblocked

I've been searching for a long time inside the wpa_supplicant codebase for the cause of this, but I was looking at the wrong codebase the whole time: It turns out that wpa_supplicant logcat logs are being filtered out by the OS, and that there's a setting in the developer options to turn that off.

Now we have all prerequisites to try out any changes we do to wpa_supplicant. I actually ran the full build with an unmodified source and flashed that onto my device in preparation, because then I'd know where to look when I ran into any issues.

Adding SAE support to wpa_supplicant

At the branch history of the LineagOS wpa_supplicant fork, the latest commit that looks like a backported one is this one. And indeed, there is an analogon of the commit in the upstream hostap project, which is this. That's from October 2017, but full SAE support arrived only in March/April 2018.

Android 9 (pie) had been released a few days ago and maybe it bases on a newer wpa_supplicant, so updating that one to add SAE support might be easier. So, let's try to just use the Android 9 version of wpa_supplicant:

cd external/wpa_supplicant_8
$ git remote add android-upstream https://android.googlesource.com/platform/external/wpa_supplicant_8
$ git remote update
$ git checkout android-9.0.0_r3

And then rebuild. We then get:

ninja: error: 'lineage/out/target/product/i9100/obj/SHARED_LIBRARIES/android.hardware.wifi.hostapd@1.0_intermediates/export_includes', needed by 'lineage/out/target/product/i9100/obj/EXECUTABLES/hostapd_intermediates/import_includes', missing and no known rule to make it

So that didn't go really well. I then tried to save the approach by taking the Android.mk from the working older version:

git checkout 55f469ac1a07c8f134de1c5a8b04b7513c928b64 hostapd/Android.mk
git checkout 55f469ac1a07c8f134de1c5a8b04b7513c928b64 wpa_supplicant/Android.mk

But no avail. You are just getting more and more build errors and you basically need to create a hybrid Android.mk of the newer and older versions and so I gave up and tried a different approach.

So my next attempt was to go a more direct route: applying all the patches between the last commit from upstream on that repo and the latest master.

git format-patch b488a12948751f57871f09baa345e59b23959a41..c773c7d5ddbc8ca031614e1c999b05bec43778aa
ls *.patch | wc -l

This gives us a giant amount of 791 patches. git am -3'ing them causes merge conflicts right in the first few patches and I don't really want to resolve each of them. So doing this directly isn't viable.

If we filter the patch names and are only taking the ones containing SAE, we have a more manageable amount: 35.

ls *SAE*.patch | wc -l

I then invoked a command like

git am -3 /path/to/hostap/*SAE*.patch

and fixed the merge conflicts for each patch. This is a real hack as it relies on the author putting "SAE" into all the commits relevant for SAE. It actually turned out that some upstream commits have been missing from the android fork of wpa_supplicant, so I had to additionally apply one SAE related patch to fix the build. Overall it was quite a bit of work but I won't go into more detail. If you are interested in the result, I've uploaded it to github here. The branch also includes changes to do more logging and to treat all WPA-PSK network config entries as if they supported SAE.

Entering Kernel space in 3, 2, 1

After getting the modified wpa_supplicant to compile successfully, I flashed it to my phone and tried to connect it to my WiFi network, hoping that it would build up this connection using SAE. However, the phone always chose WPA-PSK. After adding some log output, it seemed that this code erased the SAE from the protocol list again:

	if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE))
		sel &= ~(WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE);

So apparently some "driver" flags weren't set. In this context, driver just means the back end side of wpa_supplicant, aka the way it talks to the kernel/OS/etc. Each driver implements a different API.

On modern Linuxes, the used driver is NL80211, which talks to the kernel via the header with the same name.

This driver contains the following code:

	if (flags & NL80211_FEATURE_SAE)
		capa->flags |= WPA_DRIVER_FLAGS_SAE;

And NL80211_FEATURE_SAE is defined in Linux's nl80211.h.

Running git blame in Linux git for the file gives us this commit that introduced the value to the header. The first kernel release that included this commit was 3.8. But unfortunately, my phone uses kernel version 3.0.101-lineage-gb7ffe7f2aea, and thus it doesn't contain that value in the header. In fact, the only way the wpa_supplicant driver compiles is because it ships with a copy of the nl80211.h header.

So, it seems that not just wpa_supplicant needs SAE support, but also the kernel itself.

Android has a rich "alternative kernels" culture, where people create modified kernels and publish them for others in the community to use them. So maybe there are kernels that have updated the kernel version to something newer than 3.0.

An online search turned up a modded kernel called dorimanx which includes, as it claims, an update to Linux 3.15. But it seems that the nl80211.h header doesn't live up to this claim: in dorimanx's version of that header there is no trace of NL80211_FEATURE_SAE, neither in the enum where it'd normally live, nor the entire file. So it seems that dorimanx isn't useful to our purposes and that we have to add SAE support to the kernel ourselves.

Given the experience with wpa_supplicant, backporting the patches seems the better solution compared to updating the entire kernel.

The commit that introduced this NL80211_FEATURE_SAE value is surrounded by other wifi related commits. This is probably due to the nature of how the Linux kernel is being developed: an individual developer proposes a patch and sends it to the maintainer of that specific part of the kernel. The maintainer then reviews it and if everything is okay, it is being included in a proposed list of patches that is being sent to Linus Torvalds who then can include the patch in a future kernel release.

Out of this group of wifi related commits, there are three commits that seem to be relevant enough to motivate a backport:

  • mac80211: Take status code as parameter to ieee80211_send_auth (700e8ea6770df311)
  • cfg80211: Allow user space to specify non-IEs to SAE Authentication (e39e5b5e7206767a)
  • mac80211: Allow station mode SAE to be implemented in user space (6b8ece3a7031523a)

Backporting the first two patches was more or less easy, but the third patch is a bit bigger and it's also been changing code that was modified quite a bit by a refactor that happened since the kernel that my phone was based on, and the kernel that the patch was based on. After a bit of figuring it was possible to backport the third commit as well.

I've uploaded the resulting kernel source to a branch on github.

But for some reason, even though the code seems to be actually enabling the NL80211_FEATURE_SAE feature, wpa_supplicant is not receiving that info. I ran out of time before I could find out why this is the case.

So I gave up, and flashed back the official Lineage OS image. Due to my change to always add SAE during config file parsing if WPA-PSK was present, wpa_supplicant apparently added SAE to all WPA-PSK networks during saving. But the wpa_supplicant copy from official Lineage OS couldn't cope with that value and just deleted all networks with SAE in their name and thus I would have lost all saved WPA-PSK networks, but fortunately, I could just apply the backup :). This is precisely why you should do backups!

Summary (TLDR)

I wanted to migrate my WiFi network communication to use SAE, which is a new, more secure authentication mechanism that is part of WPA3.

All of my three devices on my WiFi run a distribution of Linux: OpenWRT, Kubuntu, Android. Also, all of them use the hostap project for WiFi authentication. I could successfully add SAE support to my OpenWRT and Kubuntu devices, but gave up with my Android device.

Only a master version of hostap supports the version of SAE that ships with WPA3. Also, it needs specific SAE build flags to be enabled. Last but not least, it needs to be told to use SAE in its configuration.

Thankfully, OpenWRT already ships a package with hostapd that uses a master version and a SAE build flag. I changed a configuration generation shell script provided by the OS to make hostapd enable SAE for the particular network.

For Kubuntu, I custom-compiled a wpa_supplicant package using the master version with the SAE build flag enabled. I also had to modify config file parsing code of wpa_supplicant to enable SAE support.

For my Android phone, I went to great lengths, in comparison to the other two devices, until I gave up. I've backported 35 SAE patches to the Android-custom wpa_supplicant fork. Then I discovered that for SAE support, kernel 3.8 is required but my kernel was 3.0. So I backported some kernel patches as well. But no avail: For some reason, my wpa_supplicant was still of the opinion that my kernel lacked SAE support. Then I gave up. During this entire process, I ran into multiple restrictions and issues in my way, that I didn't feel present in the other Linux distributions.

I worked on the project on and off during the first half of August 2018. Thanks go to @nagisa for proofreading and reviewing my post.

@mdempsky
Copy link

Thanks for the great write up! I really appreciate the effort you put into citing sources.

@tomisaacson
Copy link

@est31 I'm running OpenWrt 18.06.4 on a Netgear WNDR3700 v1 and I've followed all of your instructions. I now have the Wifi running with:

$ grep wpa_key_mgmt /tmp/run/hostapd-phy0.conf
wpa_key_mgmt=WPA-PSK SAE

But how do I actually use SAE? LuCI doesn't support WPA3 yet so it doesn't appear as an option. /etc/config/wireless shows option encryption 'psk2'. Do I need to change this or is WPA3 now on?

@est31
Copy link
Author

est31 commented Oct 5, 2019

Yes, ignore the LuCI stuff, it is misleading. It only matters what hostapd says. The next major release of OpenWRT adds support though I think.

@tomisaacson
Copy link

tomisaacson commented Oct 5, 2019

@est31 Just wasn't sure how to check. If I do sudo iw dev wlan0 scan from my laptop on WPA2 PSK I get:

RSN:	 * Version: 1
	 * Group cipher: CCMP
	 * Pairwise ciphers: CCMP
	 * Authentication suites: PSK
	 * Capabilities: 16-PTKSA-RC 1-GTKSA-RC (0x000c)

With SAE added I get:

RSN:	 * Version: 1
	 * Group cipher: CCMP
	 * Pairwise ciphers: CCMP
	 * Authentication suites: PSK 00-0f-ac:8
	 * Capabilities: 16-PTKSA-RC 1-GTKSA-RC (0x000c)

Is that what you expect? I can no longer connect my iPhone but my laptop (running Ubuntu 18.04) works fine.

I tried OpenWRT 19.07 but hostapd_append_wpa_key_mgmt() and adding append wpa_key_mgmt "SAE" didn't seem to work:
https://github.com/openwrt/openwrt/blob/openwrt-19.07/package/network/services/hostapd/files/hostapd.sh
I couldn't figure out where $auth_type is coming from.

@tomisaacson
Copy link

I asked on the OpenWRT forum:
https://forum.openwrt.org/t/auth-type-in-hostapd-sh-wpa3/45923
I ended up using @hnyman's special build of master for Netgear WNDR3700v1 and this worked fine, including LuCI support.
https://forum.openwrt.org/t/build-for-wndr3700v1-v2-wndr3800/

@est31
Copy link
Author

est31 commented Jan 11, 2020

Some updates:

  • OpenWRT 19.07 was released today and has WPA3 support included: https://openwrt.org/releases/19.07/notes-19.07.0
  • plasma-nm has gotten WPA3 support. It will arrive in Plasma version 5.18, to be released in February 2020. I think it will be included into Kubuntu 20.04 LTS. This is the last component needed for me to get WPA3 support on my Kubuntu without patches.

@Neustradamus
Copy link

Yes a good news!

@est31
Copy link
Author

est31 commented Jan 11, 2020

OK got it working with Openwrt 19.07 + Kubuntu 19.10.

Openwrt 19.07

wpad-openssl is required, as mentioned in the release announcement.

opkg update
opkg install --force-overwrite hostapd-openssl

Then I set option encryption 'sae-mixed+aes' in /etc/config/wireless. Weirdly, setting sae or sae-psk there didn't work. After running /etc/init.d/network reload, I then get this when running grep wpa_key /tmp/run/hostapd-phy0.conf :

wpa_key_mgmt=WPA-PSK WPA-PSK-SHA256 SAE

Some posts on the forum say that WPA3 support on 19.07 is incomplete. No idea whether they are outdated or I just avoided the parts where it is incomplete. For me at least it works

Kubuntu 19.10

While networkmanager and wpa_supplicant have SAE support now, plasma-nm shipped with Kubuntu 19.10 doesn't (as I noted above there is a chance that Kubuntu 20.04 will have it). Maybe Ubuntu, using gnome tools, has support. Idk. On Kubuntu, I still had to use patched wpa_supplicant.

I adjusted the sed command to do sed -i "s/WPA_KEY_MGMT_PSK;/WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_SAE;/;s/WPA_KEY_MGMT_PSK_SHA256;/WPA_KEY_MGMT_PSK_SHA256 | WPA_KEY_MGMT_SAE;/" wpa_supplicant/config.c. I also updated version numbers for hostapd 2.9. After that it worked:

$ sudo wpa_cli status | grep key_mgmt
key_mgmt=SAE

A switch to a git version of plasma-nm instead of patching hostapd would be cleaner I guess but it's only a temporary hack... I hope.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment