8000 esp32: add WPA2 enterprise support by h-milz · Pull Request #16463 · micropython/micropython · GitHub
[go: up one dir, main page]

Skip to content

esp32: add WPA2 enterprise support #16463

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed

Conversation

h-milz
Copy link
@h-milz h-milz commented Dec 20, 2024

Summary

This PR adds WPA2 Enterprise support to the ESP32 port. In particular, the patch supports EAP-PWD, EAP-PEAP, and EAP-TTLS with MSCHAPv2 and PAP.

Testing

The patch was developed and tested in the Technical University of Munich eduroam network on an ESP32_GENERIC_S3 board, namely, a CrowPanel 5.0"-HMI ESP32 Display board with a ESP32-S3-WROOM-1-N4R8 module.

Usage example:

import network

wlan = network.WLAN(network.STA_IF)
wlan.active(True)

identity = "anonymous@eduroam.mwn.de"
username = "my_username@eduroam.mwn.de" 
password = "my_password"
certfile = "/T-TeleSec_GlobalRoot_Class_2.pem"  # needs to be uploaded first
ssid = "eduroam"
method = wlan.EAP_method
        
with open (certfile, 'rb') as file:
    ca_cert = file.read()
            
try:
    if method == wlan.EAP_PWD:
        wlan.eap_connect(ssid=ssid, eap_method=method, 
                        username=username, password=password)
    elif method == wlan.EAP_PEAP:
        wlan.eap_connect(ssid=ssid, eap_method=method, 
                        username=username, password=password, 
                        identity=identity, ca_cert=ca_cert)        
    elif method == wlan.EAP_TTLS:  
        wlan.eap_connect(ssid=ssid, eap_method=method, 
                        username=username, password=password, 
                        identity=identity, ca_cert=ca_cert,
                        ttls_phase2_method=ttls_phase2_method)
except Exception as e:
    print (f"error: {e}")

Trade-offs and Alternatives

If your board does not have a hardware RTC, odds are that the server certificate validation for EAP-PEAP, -TTLS and potentially -TLS will fail due to the system time being way off. As a workaround, you can set the system time to build time on system start like this:

import sys
import machine

(year, month, day) = sys.version.split(" on ")[1].split("-")
rtc = machine.RTC()
date_time = (int(year), int(month), int(day), 0, 0, 0, 0, 0)
rtc.init(date_time) 

EAP-TTLS with MSCHAP or CHAP could not be tested so far, neither could EAP-TLS, lacking a corresponding network. User testing and feedback is highly appreciated!

Development and testing were done with ESP-IDF 5.2.2 and MPY 1.23.0 because later versions or master gave me too many runtime or compilation errors (in other places than this patch).

If requested, I can add more documentation for the README or the MPY documentation.

@h-milz h-milz force-pushed the ports/esp32/add-wpa2-enterprise branch 3 times, most recently from b4e6d4a to 0daff50 Compare December 20, 2024 16:10
@Carglglz
Copy link
Contributor

related #8819 #8670 #5705 #12143

@h-milz
Copy link
Author
h-milz commented Dec 21, 2024

I would like to add that I am aware that my time setting workaround is really an ugly hack that is only for testing but should not be used in a production environment. The ESP32 RTC does not survive a reboot because it is not battery buffered. Hence, when the system boots, the system time will restart with the build date using the hack. In case a server certificate is renewed (they are usually valid for a year or even less, e.g. for LetsEncrypt), then the validation will fail. In this case you will have to build a new image and reflash the chip, or set a manual time in your Python code.

(Mind you that in eduroam or other wifi networks supporting EAP-PWD this no issue at all - in this case it should just work. The rule of thumb is that if your IT department documents how to set up Android phones with EAP-PWD it will be just fine. But I'm told that not all eduroam networks do that which is why I wrote the code for PEAP et al. in the first place.)

The better solution whenever you deal with SSL certificates in production is to have a battery buffered RTC, which you can set once before shipping and then e.g. once per day using NTP.

The function esp_eap_client_set_disable_time_check() cannot be used to circumvent this issue because it only suppresses the validity check for the local client certificate when using EAP-TLS. IMHO, this is a hack. It's better to (automatically) renew client certificates before expiry, also because the server may refuse validation when presented an expired client cert.

This being said, although I cannot test ESP-TLS myself I would be surprised if my code did not work out of the box, because all I did is wrap the ESP-IDF wpa_supplicant API. And yes, it is port specific and will not help the STM32 or RP2 guys e.g. using CYW43 chips. ¯_(ツ)_/¯

@Carglglz
Copy link
Contributor

I would like to add that I am aware that my time setting workaround is really an ugly hack that is only for testing but should not be used in a production environment. The ESP32 RTC does not survive a reboot because it is not battery buffered. Hence, when the system boots, the system time will restart with the build date using the hack.

An alternative could be saving current date/time in NVS periodically (see https://docs.micropython.org/en/latest/library/esp32.html#esp32.NVS)

@Josverl
Copy link
Contributor
Josverl commented Feb 20, 2025

I would recommend to drop any and all support for MSCHAPv2 and CHAP,
As they are weak, are vulnerable to dictionary attracts, and are therefore rarely used anymore.

@zcattacz
Copy link
zcattacz commented Mar 10, 2025

Future proof maybe a good point , and CHAP does seem to have die out, but MSCHAPv2 is still not rare.
Not sure if by any chance, the sync clock timestamp can be captured from WiFi beacon management frames. Long time ago, I used to try this for WIFI timesync in one project with the esp8266 sniffing hack, but the field turned out to be useless with random home routers. Managed APs may be different?

@Josverl
Copy link
Contributor
Josverl commented Mar 12, 2025

Support Status
MSCHAPv2 is still a supported protocol, but its usage is becoming increasingly limited due to security concerns. Microsoft has recommended moving away from MSCHAPv2-based connections to more secure, certificate-based authentication methods such as PEAP-TLS or EAP-TLS

Security Risks
MSCHAPv2 has several known security vulnerabilities:

  • Weak Encryption: The DES encryption used in MSCHAPv2 is considered weak and can be easily broken with modern hardware1.
  • Credential Theft: MSCHAPv2 is vulnerable to credential theft due to misconfigurations and weak encryption. Attackers can exploit misconfigured devices by imitating legitimate networks and decrypting user credentials2.
  • Evil Twin Attacks: MSCHAPv2 is susceptible to Evil Twin attacks, where attackers set up a rogue access point to intercept and decrypt user credentials3.

Recommendations
Given these vulnerabilities, it is highly recommended to transition to more secure authentication methods:

  • EAP-TLS: This method uses certificate-based authentication, eliminating password-related risks and increasing security3.
  • PEAP-TLS: Similar to EAP-TLS, this method also uses certificates for authentication, providing a secure alternative to MSCHAPv23.

My Conclusion
While MSCHAPv2 is still supported, its security risks make it an unsuitable choice for modern networks. Transitioning to EAP-TLS or PEAP-TLS is strongly recommended.

@dpgeorge
Copy link
Member

Thanks for the contribution, this is a very nice feature to add.

And yes, it is port specific and will not help the STM32 or RP2 guys

Right, so this is the main thing to discuss and get right first: how this API will generalise to other WLAN drivers.

Should it be a new method like eap_connect(), or should the existing connect() method be reused and new security arguments/options added to it? The latter seems the more obvious path to me.

Then, it would be great to try implementing this on at least one other WLAN driver, to see how general it can be.

@dpgeorge dpgeorge added this to the release-1.26.0 milestone Mar 13, 2025
@zcattacz
Copy link

It's unlikely a client side choice. In order to plug their loopholes, organizations need to prioritize spendings on IT infrastructure, accelerate old device depreciation, enforce robust and consistent network policy.

8000
@Josverl
Copy link
Contributor
Josverl commented Mar 13, 2025

organizations need to prioritize spendings on IT infrastructure, accelerate old device depreciation,

Fully agree, let's give these orgs more reasons to do the right thing

@h-milz
Copy link
Author
h-milz commented Apr 26, 2025

organizations need to prioritize spendings on IT infrastructure, accelerate old device depreciation,

Fully agree, let's give these orgs more reasons to do the right thing

As if universities or anyone else changed their policies because Micropython decided to not implement something. This stance is one of the reasons why people have a hard time using Micropython,

In more than 30 years in open source, one thing I learned is that it's all about choice. Tell people what this is, and let them make an informed decision. But don't patronize them.

A couple of people approached me in the meantime and asked if and how they could add the PR to their codebase because the MP developers seemed not to be willing to merge it. Maybe I should cancel the PR and enable people to more easily integrate it on their own.

@h-milz h-milz closed this Apr 26, 2025
@Josverl
Copy link
Contributor
Josverl commented Apr 26, 2025

@h-milz ,
Did you see that this PR was planned for inclusion in the next release?

@robert-hh
Copy link
Contributor

In that case it should be re-opened.

@robert-hh robert-hh reopened this Apr 26, 2025
@h-milz
Copy link
Author
h-milz commented Apr 27, 2025

@h-milz ,
Did you see that this PR was planned for inclusion in the next release?

No but in this case I'll be more than happy to contribute.

As I wrote above, some of the methods are so far untested anyway and I'd declare them experimental. There needs to be some documentation written, too. I'd suggest to extend ports/esp32/README.md. Likewise https://docs.micropython.org/en/.../esp32/quickref.html?

Shall I create a PR for these too?

@robert-hh
Copy link
Contributor
robert-hh commented Apr 27, 2025

some of the methods are so far untested anyway and I'd declare them experimental

Please test them. No untested code please.

Shall I create a PR for these too?

You can add commits for the documentation to this PR. No need to create another PR. Just add the commits to you local branch and (force-)push it to your micropython fork. Then the existing PR will be updated.

@h-milz
Copy link
Author
h-milz commented Apr 28, 2025

some of the methods are so far untested anyway and I'd declare the 10000 m experimental

Please test them. No untested code please.

I do understand your concern, but as much as I would like to - as I wrote above, I do not have access to any network infrastructure offering anything other than EAP-PWD and EAP-PEAP. My uni does not implement anything else, and the only way I see to get this tested is to get the code in the hands of people who can. This is not going to happen when we remove the corresponding code.

Also, please note that I did not implement any of the network and authentication modes myself. My code is a wrapper around functions provided by the ESP-IDF, where these methods are officially supported.

How about this. I'd restrict the code to EAP-PWD and EAP-PEAP by setting the appropriate max value for eap_method to WIFI_AUTH_EAP_PEAP in

    // parameter check
    if (eap_method < WIFI_AUTH_EAP_PWD || eap_method > WIFI_AUTH_EAP_TLS) {
        mp_raise_ValueError(MP_ERROR_TEXT("unknown config value for eap_method"));
    }

in ports/esp32/network_wlan.c , leaving the other code as-is and documenting properly how to deal with it. That would facilitate others who want to test the code to do that in any easy way without jeopardizing the integrity of the next MP release.

Alternatively, when a user selects EAP-TTLS or EAP-TLS, issue a proper warning that the code is experimental, and on top mark it clearly as experimental in the documentation (i.e., ports/esp32/README.md). For the reasons given, I'd prefer this route.

Please advise.

Actually, I will have some time tomorrow to implement and test the code including minor changes, given that my PR was created against v1.23.0, and re-commit. BTW what is the deadline for inclusion in v1.26.0?

@robert-hh
Copy link
Contributor

It's good that you documented what you have tested and what not. Maybe someone else follows your call to test other methods. You have section in network_wlan_eap_connect() for EAP-TTLS or EAP-TLS, so even if your network does not support these, you can test if the call handling in your code is working as expected.
You should rebase your PR on top of the actual repository state, and adding a section for the documentation is needed.
I do not know of a deadline for v1.26. It's on the list of commits to be included. (see https://github.com/micropython/micropython/milestone/10) So you will get feedback when the review for the inclusion happens.

@Josverl
Copy link
Contributor
Josverl commented Apr 28, 2025

To get clarification on what is tested, and where help in testing is needed I'd suggest editing the initial message in this PR , to add a list of configs to test, and check the ones that have been tested.
That makes it simpler for others to discover what still needs testing.

danicampora and others added 5 commits May 1, 2025 11:50
Simple implementation in-line with the rest of the MicroPython ports

Tested on the nRF52832 and the nRF5340.

Signed-off-by: danicampora <danicampora@gmail.com>
This allows running `py/makeqstrdata.py` with MicroPython itself.

Signed-off-by: Volodymyr Shymanskyy <vshymanskyi@gmail.com>
Signed-off-by: Angus Gratton <angus@redyak.com.au>
This allows running mpy-tool using MicroPython itself.

An appropriate test is added to CI to make sure it continues to work.

Signed-off-by: Volodymyr Shymanskyy <vshymanskyi@gmail.com>
Signed-off-by: Angus Gratton <angus@redyak.com.au>
Signed-off-by: Volodymyr Shymanskyy <vshymanskyi@gmail.com>
Signed-off-by: Angus Gratton <angus@redyak.com.au>
This commit introduces the ability to obtain a list of stations
connected to the device when in soft-AP mode.

A new parameter ("stations") to pass to WLAN.status is supported,
returning a tuple of (bssid, ipv4) entries, one per connected station.
An empty tuple is returned if no stations are connected, and an
exception is raised if an error occurred whilst building the python
objects to return to the interpreter.

Documentation is also updated to cover the new parameter.

This fixes #5395.

Signed-off-by: Alessandro Gatti <a.gatti@frob.it>
agatti and others added 25 commits May 1, 2025 11:50
This commit makes a slight change to the vfs_posix test suite to let it
pass on Android.

On Android, non-root processes can perform most filesystem operations
only on a restricted set of directories.  The vfs_posix test suite
attempted to enumerate the filesystem root directory, and said directory
happens to be restricted for non-root processes.  This would raise
an EACCES OSError and terminate the test with a unexpected failure.

To fix this, rather than enumerating the filesystem root directory the
enumeration target is the internal shared storage area root - which
doesn't have enumeration restrictions for non-root processes.  The path
is hardcoded because it is guaranteed to be there on pretty much any
recent-ish device for now (it stayed the same for more than a decade for
compatibility reasons).  The proper way would be to query the storage
subsystem via a JNI round-trip call, but this introduces too much
complexity for something that is unlikely to break going forward.

Signed-off-by: Alessandro Gatti <a.gatti@frob.it>
The output of 'idf.py size' has changed, plus some other cleanups around
build dir name, etc.  Can now run on v5.2.2 and v5.4.1, probably other
versions.

Signed-off-by: Angus Gratton <angus@redyak.com.au>
Because the `wbits` parameter was only added to `zlib.compress` in
CPython 3.11.  Using `zlib.compressobj` makes the code compatible with much
older CPython versions.

Fixes issue #17140.

Signed-off-by: Damien George <damien@micropython.org>
This updates lwIP from STABLE-2_2_0_RELEASE, which was released in
September 2023.  The latest STABLE-2_2_1_RELEASE was released in February
2025.

Signed-off-by: Damien George <damien@micropython.org>
This requires explicitly naming and initializing all members so add that
where needed and possible.  For MP_DEFINE_NLR_JUMP_CALLBACK_FUNCTION_1
this would require initializing the .callback member, but that's a bit
of a waste since the macro is always followed by a call to
nlr_push_jump_callback() to initialize exactly that member, so rewrite
the macro without initializers.

Signed-off-by: stijn <stijn@ignitron.net>
Add code using all relevant macros to make sure they initialize
structs correctly.

Signed-off-by: stijn <stijn@ignitron.net>
Signed-off-by: Anson Mansfield <amansfield@mantaro.com>
This rewrites the code that previously manually emitted and caught various
OSError subclasses with equivalent code that uses the errno name dictionary
to do this generically, and updates the exception handler in do_filesystem
to catch them in a similarly-generic fashion using os.strerror to retrieve
an appropriate error message text equivalent to the current messages.

Note that in the CPython environments where mpremote runs, the call to the
OSError constructor already returns an instance of the corresponding mapped
exception subtype.

Signed-off-by: Anson Mansfield <amansfield@mantaro.com>
Signed-off-by: Anson Mansfield <amansfield@mantaro.com>
Signed-off-by: Jos Verlinde <jos_verlinde@hotmail.com>
Removes the risk of inadvertently deleting files on the host by preventing
the deletion of files via `rm -r` on the `/remote` vfs mount point.

Fixes issue #17147.

Signed-off-by: Jos Verlinde <jos_verlinde@hotmail.com>
The (deprecated) kconfig option NET_SOCKETS_POSIX_NAMES was removed in
commit abad505bdeed6102061767f45acd63323973f564 so remove it from our
configuration.

As the option has been deprecated longer, this also works for v3.7 and
v4.0 the other still supported versions.

Signed-off-by: Detlev Zundel <dzu@member.fsf.org>
Commit 1b6e0f64796dfd6f86a8679ea6d24e1fca1e63a8 for Zephyr v4.0.0
changed the function "thread_analyzer_print" to require a cpu argument
and allow thread analysis on each cpu separately. The argument is
ignored when THREAD_ANALYZER_AUTO_SEPARATE_CORES=n which is the
default on single core machines.

Promote this change to the MicroPython zephyr module.

Signed-off-by: Detlev Zundel <dzu@member.fsf.org>
Signed-off-by: Maureen Helm <maureen.helm@analog.com>
Commit 07a8e3253a2d8a2076c9c83c4ed4158fa3fbb2a2 removes
CONFIG_MMC_VOLUME_NAME from the Kconfig space. Instead we need to use
the device tree to find the "disk-name" property of "zephyr,mmc-disk"
devices.

Signed-off-by: Detlev Zundel <dzu@member.fsf.org>
Updates the Zephyr port build instructions. The CI is updated to use
Zephyr docker image 0.27.4, SDK 0.17.0 and the latest Zephyr release
tag.

Tested on max32690fthr and frdm_k64f.

Signed-off-by: Maureen Helm <maureen.helm@analog.com>
Signed-off-by: Detlev Zundel <dzu@member.fsf.org>
Implement PWM support using standard zephyr APIs, exposed as the standard
MicroPython `machine.PWM` class.

Signed-off-by: Ayush Singh <ayush@beagleboard.org>
Add docs for PWM support.

Signed-off-by: Ayush Singh <ayush@beagleboard.org>
Enable PWM config for bcf.

Signed-off-by: Ayush Singh <ayush@beagleboard.org>
Enables the ability to use frozen modules in the zephyr port.

Enabled by adding `CONFIG_MICROPY_FROZEN_MODULES` to the board
configuration file.  Manually set manifest path with
`CONFIG_MICROPY_FROZEN_MANIFEST`.

Signed-off-by: Vdragon <mail@massdriver.space>
Add support for the nrf5340dk.  This DK has a MX25R64 8mb external QSPI
flash chip.

Compile using:

    $ west build -b nrf5340dk/nrf5340/cpuapp

Signed-off-by: Patrick Joy <patrick@thinktransit.com.au>
Add support for the nrf9151dk.  This DK has a GD25WB256 32mb external QSPI
flash chip.

Signed-off-by: Patrick Joy <patrick@thinktransit.com.au>
Only a few ports have TCP/IP loopback enabled in their network stack, and
this test will only pass on those ports.  There's not really any good way
to do a feature check for loopback mode without actually running the test
and seeing if it passes/fails, so add an explicit check that the test is
running on a port known to support loopback.

(Enabling loopback on lwIP, eg RPI_PICO_W, costs +568 code and +272 bss and
is a rarely used feature, so not worth unconditionally enabling.)

Signed-off-by: Damien George <damien@micropython.org>
This test is rather complicated and benefits from being a unittest.

Signed-off-by: Damien George <damien@micropython.org>
Changes in this commit:
- Allow the DMA instance to be any instance, not just DMA(0); eg WLAN may
  be using DMA(0).
- Make the DMA timing test run a little faster by preloading `dma.active`.
- Run the DMA timing test 10 times and take the average time taken as the
  test result, to eliminate any big effects of caching.
- Change the expected time to `range(30, 80)` to cover RP2040, RP2350,
  RISC-V variants, and both bytecode and native emitter.
- Add a `sleep_ms(1)` after waiting for the IRQ to fire, so that any
  scheduled code gets a chance to run when the test is compiled with the
  native emitter.

With these changes this test passes reliably on RPI_PICO, RPI_PICO_W,
RPI_PICO2, RPI_PICO2_W, RPI_PICO2-RISCV and RPI_PICO2_W-RISCV, in both
bytecode and native emitter mode, with and without WLAN enabled.

Signed-off-by: Damien George <damien@micropython.org>
@robert-hh
Copy link
Contributor

That's quite a few commits. Did you rebase your branch before pushing and resolve the conflicts? Then the number of commits should be few.

@h-milz
Copy link
Author
h-milz commented May 1, 2025

That's quite a few commits. Did you rebase your branch before pushing and resolve the conflicts? Then the number of commits should be few.

I'm afraid something went wrong when I tried to do this. I may have to start over. It should in fact only be 3 commits.

@h-milz h-milz closed this by deleting the head repository May 1, 2025
@h-milz
Copy link
Author
h-milz commented May 1, 2025

This PR is FUBAR and superseded by #17234.

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

Successfully merging this pull request may close these issues.

0