Skip to content

wolfSupplicant: clean-room WPA/WPA2/WPA3 supplicant (PSK 4-way, EAP-TLS, PEAP/MSCHAPv2, SAE with H2E)#123

Draft
dgarske wants to merge 5 commits into
wolfSSL:masterfrom
dgarske:supplicant
Draft

wolfSupplicant: clean-room WPA/WPA2/WPA3 supplicant (PSK 4-way, EAP-TLS, PEAP/MSCHAPv2, SAE with H2E)#123
dgarske wants to merge 5 commits into
wolfSSL:masterfrom
dgarske:supplicant

Conversation

@dgarske

@dgarske dgarske commented May 20, 2026

Copy link
Copy Markdown
Member

wolfSupplicant: clean-room WPA/WPA2/WPA3 supplicant (PSK 4-way, EAP-TLS, PEAP/MSCHAPv2, SAE with H2E)
wolfSupplicant: real-authenticator interop harness (hostapd + mac80211_hwsim) for PSK, EAP-TLS, PEAP, SAE

@dgarske dgarske self-assigned this May 20, 2026
In-tree host supplicant (src/supplicant/) implementing WPA2-Personal PSK
4-way, WPA2-Enterprise EAP-TLS and PEAPv0/MSCHAPv2 (wolfSSL native IO),
and WPA3-Personal SAE dragonfly with hunt-and-peck + RFC 9380
Hash-to-Element PWE (groups 19/20/21). Includes in-process tests and a
hostapd + mac80211_hwsim interop harness (PSK / EAP-TLS / PEAP / SAE,
including sae_pwe=2 H2E).

Footprint / robustness pass:
  - caller-allocated wolfip_supplicant_init() (no malloc on the MCU path)
  - pre-allocated sae_ctx confirm scratch
  - MFP / 802.11w surfaced in the RSN IE (cfg.mfp_capable / mfp_required)
  - WOLFIP_ENABLE_SAE_HNP gate (H2E-only builds drop hunt-and-peck)
  - cfg.psk_pmk + wolfip_supplicant_get_pmk() to skip PBKDF2 across boots
  - PSK PMKSA cache: re-init on the same context for the same SSID reuses
    the PMK and skips PBKDF2; wolfip_supplicant_pmksa_clear() flushes it
  - EAP-TLS reassembly buffer default 2048 (was 4096), still overridable

wolfip.h gains the wolfIP_wifi_ops vtable and src/wolfip.c demuxes EAPOL
(ethertype 0x888E) to the supplicant.
Copilot AI review requested due to automatic review settings June 10, 2026 00:07

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a clean-room WPA/WPA2/WPA3 supplicant (“wolfSupplicant”) to wolfIP and introduces Linux hostapd-based interop harnesses (wired + mac80211_hwsim) to validate EAP-TLS/PEAP, WPA2-PSK 4-way, and WPA3-SAE flows against a real authenticator.

Changes:

  • Introduces an optional Wi‑Fi control vtable (wolfIP_wifi_ops) and an EAPOL (0x888E) RX demux hook for supplicant integration.
  • Adds the supplicant implementation (src/supplicant/*) including EAP-TLS, PEAP/MSCHAPv2, RSN parsing, WPA(2) 4-way, and SAE (incl. H2E) plus test binaries.
  • Adds hostapd/mac80211_hwsim scripts + templates and an nl80211_connect helper, and wires new build/test targets into the Makefile.

Reviewed changes

Copilot reviewed 50 out of 50 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
wolfip.h Adds Wi‑Fi ops vtable + public EAPOL handler registration API.
src/wolfip.c Implements EAPOL handler registration and EAPOL ethertype demux in RX path.
src/test/unit/unit_tests_dns_dhcp.c Adds unit coverage for EAPOL handler register/unregister behavior.
src/test/unit/unit.c Registers the new unit test in the suite.
Makefile Adds supplicant build outputs and hostapd/hwsim interop targets (+ nl80211 helper build).
src/supplicant/supplicant.c Core supplicant state machine (PSK/EAP/SAE integration).
src/supplicant/supplicant.h Supplicant public interface/types.
src/supplicant/eapol.c 802.1X/EAPOL framing helpers.
src/supplicant/eapol.h EAPOL constants and APIs.
src/supplicant/eap.c EAP packet parsing/building.
src/supplicant/eap.h EAP structs/APIs and documentation for EAP parsing/building.
src/supplicant/eap_tls.c EAP-TLS outer method implementation.
src/supplicant/eap_tls.h EAP-TLS API/types.
src/supplicant/eap_tls_engine.c TLS engine glue (wolfSSL IO/exporter integration) for EAP-TLS/PEAP.
src/supplicant/eap_tls_engine.h TLS engine API/types.
src/supplicant/eap_peap.c PEAP outer method implementation (when enabled).
src/supplicant/eap_peap.h PEAP API/types.
src/supplicant/mschapv2.c MSCHAPv2 inner method implementation (when enabled).
src/supplicant/mschapv2.h MSCHAPv2 API/types.
src/supplicant/rsn_ie.c RSN IE parse/build utilities (AKM/ciphers).
src/supplicant/rsn_ie.h RSN IE API/types.
src/supplicant/wpa_crypto.c WPA(2) key derivation/PRFs and related crypto helpers.
src/supplicant/wpa_crypto.h WPA crypto API/types.
src/supplicant/sae_crypto.c SAE (dragonfly) + H2E crypto/state helpers.
src/supplicant/sae_crypto.h SAE crypto API/types.
src/supplicant/test_eap_certs.h Test certificate material helpers for EAP-TLS/PEAP tests.
src/supplicant/test_eap_framing.c Unit tests for EAP framing/parsing.
src/supplicant/test_eap_tls_engine.c Unit tests for EAP-TLS engine behavior.
src/supplicant/test_mschapv2.c Unit tests for MSCHAPv2 vectors.
src/supplicant/test_wpa_crypto.c Unit tests for WPA crypto routines.
src/supplicant/test_supplicant_4way.c In-process tests for 4-way handshake state machine.
src/supplicant/test_supplicant_eap_tls.c In-process tests for EAP-TLS supplicant flow.
src/supplicant/test_sae_crypto.c Unit tests/vectors for SAE crypto (incl. H2E).
src/supplicant/test_supplicant_sae.c In-process tests for SAE handshake state machine.
src/supplicant/test_supplicant_hostapd.c Wired hostapd interop test binary for EAP-TLS.
src/supplicant/test_supplicant_hostapd_psk.c hostapd interop test binary for WPA2-PSK path.
src/supplicant/test_supplicant_hostapd_peap.c hostapd interop test binary for PEAP/MSCHAPv2 path.
src/supplicant/test_supplicant_hostapd_sae.c nl80211 external-auth hostapd interop test binary for SAE.
tools/hostapd/README.md Documents hostapd-based interop harness setup/targets/flags.
tools/hostapd/run_hostapd_test.sh Runner for wired hostapd interop (veth pair).
tools/hostapd/run_hwsim_psk_test.sh Runner for mac80211_hwsim WPA2-PSK interop (nl80211 + hostapd).
tools/hostapd/run_hwsim_sae_test.sh Runner for mac80211_hwsim SAE interop attempt (documents SoftMAC limitation).
tools/hostapd/hostapd.conf.template hostapd wired-mode EAP server template for interop harness.
tools/hostapd/hostapd_psk.conf.template hostapd PSK template for wired path (documented limitation).
tools/hostapd/hostapd_psk_hwsim.conf.template hostapd nl80211 WPA2-PSK AP template for hwsim path.
tools/hostapd/hostapd_sae_hwsim.conf.template hostapd nl80211 WPA3-SAE AP template for hwsim path.
tools/hostapd/eap_users EAP users file for EAP-TLS interop.
tools/hostapd/eap_users_peap EAP users file for PEAP/MSCHAPv2 interop.
tools/hostapd/nl80211_connect.c Minimal libnl nl80211 client to associate STA with CONTROL_PORT for external EAPOL handling.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/wolfip.c
Comment on lines +8956 to +8968
void wolfIP_register_eapol_handler(struct wolfIP *s,
int (*handler)(void *ctx,
unsigned int if_idx,
const uint8_t *frame,
uint32_t len),
void *ctx)
{
if (s == NULL) {
return;
}
s->eapol_handler = handler;
s->eapol_handler_ctx = ctx;
}
Comment on lines +765 to +772
wolfIP_register_eapol_handler(&s, test_eapol_cb, &sentinel);
ck_assert_ptr_eq((void *)s.eapol_handler, (void *)test_eapol_cb);
ck_assert_ptr_eq(s.eapol_handler_ctx, &sentinel);

/* Unregister: passing NULL handler clears it. */
wolfIP_register_eapol_handler(&s, NULL, NULL);
ck_assert_ptr_eq((void *)s.eapol_handler, NULL);
ck_assert_ptr_eq(s.eapol_handler_ctx, NULL);
Comment on lines +4 to +6
# Drive the wolfIP supplicant against a real hostapd EAP server over a
# Linux TAP device. Validates EAP-TLS framing, identity exchange, TLS
# handshake, and EAP-Success against a non-wolfSSL implementation.
Comment on lines +9 to +14
# - hostapd installed (apt install hostapd)
# - root (or CAP_NET_ADMIN + CAP_NET_RAW) for TAP + raw socket
# - openssl (used by the test binary to mint certs into
# /tmp/wolfip_eap_certs/)
#
# Cleanup is best-effort: hostapd is killed, the TAP is removed.
Comment on lines +3 to +6
# IEEE 802.1X "wired" mode for EAP-TLS interop testing of the wolfIP
# supplicant. Bound to a TAP device; no radio, no 4-way handshake -
# just the EAP server side. Placeholders in @...@ are substituted by
# run_hostapd_test.sh.
Comment on lines +5 to +15
* Real-authenticator interop test. Drives the wolfIP supplicant over a
* Linux TAP device against a hostapd-in-wired-mode EAP server. Validates
* EAP-TLS framing, identity exchange, TLS handshake, fragmentation, and
* EAP-Success against a non-wolfSSL implementation of the authenticator.
*
* Usage:
* sudo ./test-supplicant-hostapd <ifname>
*
* The TAP is expected to be already created and brought up
* (tools/hostapd/run_hostapd_test.sh does this). The hostapd EAP server
* is also expected to be running and bound to the same TAP.
Comment thread src/supplicant/eap.h
Comment on lines +68 to +70
/* Parse an EAP packet. body / body_len point at the byte immediately
* after the 802.1X header (i.e. EAPOL packet-type byte must already be
* 0x00 EAP_PACKET; body itself starts at the EAP Code byte).
Comment thread src/supplicant/eap.h
Comment on lines +76 to +84
/* Build the 802.1X header + EAPOL-type byte + EAP payload into out.
* - eapol_type is one of EAPOL_TYPE_*. For EAP carriage, pass
* EAPOL_TYPE_EAP_PACKET; payload then contains the full EAP packet
* (code, id, length, type, type-data).
* - For EAPOL-Start, eapol_type = EAPOL_TYPE_EAPOL_START, payload NULL,
* payload_len 0.
*
* out_cap must be >= 4 + payload_len.
* Returns 0 on success and writes total bytes into *out_total_len.
New port under src/port/rp2350_cyw43439/ for the Raspberry Pi Pico 2 W,
building for both RP2350 cores (Cortex-M33 and Hazard3 RISC-V).

Hardware-validated end-to-end on a real Pico 2 W against a WPA2 AP: on a
keyed boot the radio authenticates, the CYW43439 firmware runs the
WPA2-PSK 4-way internally (WLC_E_PSK_SUP), DHCP binds a lease, and IP is
bidirectional (ARP replies + ICMP echo 5/5, ~9 ms RTT). The data plane
(BDC 802.3 TX/RX, inbound BDC data_offset handling) is exercised by the
DHCP + ICMP traffic.

WPA2-PSK is firmware-offload: the CYW43439 is FullMAC, so the host pushes
the passphrase (WLC_SET_WSEC_PMK + sup_wpa=1) and the firmware owns the
4-way. Host-run PSK is not supported on this firmware. The in-tree
src/supplicant/, cross-built for the M33 and linked under
WOLFIP_WITH_SUPPLICANT (user_settings.h + rp2350_rng.c ROSC seed), is for
the paths where EAPOL reaches the host: WPA3-SAE external-auth and
802.1X/EAP. It is staged and linked, not on the PSK data path.

Bring-up details proven earlier and retained: RP2350 boot (vectors at
image base, XOSC/clock init, UART0 console), clean-room CYW43439 gSPI
transport over a PIO state machine, 32-bit bus mode, backplane + ALP
clock, firmware blob download (ARM RUNNING), and the SDPCM/CDC ioctl path
(WLC_UP + STA MAC read).

Key driver/app fixes that made the data path work:
- gSPI interrupt servicing (gspi_service_irq): W1C-acknowledge the F0
  interrupt register (0x04) and, on a FIFO over/underflow, abort the
  corrupt in-flight frame via SPI_FRAME_CONTROL. The firmware otherwise
  gates F2 RX after a burst overflow and never recovers.
- Patient DHCP with no data-path re-join: re-issuing the join while
  associated desyncs the firmware immediately, so once associated the
  link is held and only the DHCP client is re-kicked.
- UDP echo binds the leased IP, not INADDR_ANY: wolfIP only matches a
  0-bound UDP socket while DHCP is running.

Open robustness milestone (documented in the port README): boot-to-boot
4-way keying variance (WLC_E_PSK_SUP fires on ~half of boots) and an
intermittent RX stall on keyed boots (pushed out by the overflow
recovery, not yet eliminated). The UDP-echo demo is blocked only on
catching a keyed live window, not on a wolfIP bug.

The ~225 KB Infineon firmware blob is NOT committed: weak accessors
return NULL when it is absent (the build links and stops cleanly after
the backplane stage), and a present fw_local/cyw43_fw_blob.c is compiled
in. Bring-up logging is gated behind DEBUG_BRINGUP (default on).
dgarske added 3 commits June 15, 2026 15:37
- cyw43439_wifi.c: "re-used" -> "reused" (codespell).
- main.c: rework the LFSR step so the entropy mix does not trip
  cppcheck's integerOverflow on the signed negation.
- Makefile: add cppcheck suppressions for the RP2350 startup/syscalls
  pointer-comparison false positives.
A host-generated wolfssl/options.h can define HAVE___UINT128_T, which
pulls in __attribute__((mode(TI))) and fails to compile on 32-bit
targets such as Cortex-M33. Wrap every wolfssl/options.h include in the
supplicant sources with #ifndef WOLFSSL_NO_OPTIONS_H so an MCU build can
suppress it (-DWOLFSSL_NO_OPTIONS_H) and provide its own user_settings.h.
Host builds, which do not define the macro, are unchanged.
Add a wired 802.1X demo that drives the in-tree wolfSupplicant EAP-TLS
path on real STM32H563 hardware, authenticating against a Linux host
running hostapd as the authenticator (driver=wired with its integrated
EAP server). This exercises the supplicant code end to end with no RF
variance, unlike a FullMAC Wi-Fi part where the radio firmware owns the
handshake.

Build with ENABLE_DOT1X=1 (auto-enables TLS, TZEN=0). The new
dot1x_client.{c,h} opens a wolfIP AF_PACKET/SOCK_RAW socket bound to the
EAPOL ethertype (0x888E), configures the supplicant for EAP-TLS with EC
P-256 certs embedded from dot1x_certs.h, and runs the exchange at layer
2 (no IP needed). On EAP-Success the supplicant derives the MSK-based
PMK; wired has no WPA 4-way, so the terminal success state is
SUPP_STATE_4WAY_M1_WAIT.

dot1x/ holds the host side: gen_certs.sh produces a shared EC P-256 test
PKI (one CA signing both the firmware client cert and the hostapd server
cert), plus hostapd.conf, the EAP user file, and a README with the full
procedure. Generated certs and host keys are git-ignored.

Supporting changes:
- syscalls.c: derive a coarse wall-clock from __DATE__ so X.509
  notBefore/notAfter checks pass (the board has no RTC).
- user_settings.h: guard NO_PWDBASED behind HAVE_PBKDF2 so EAP-TLS key
  derivation links.
- Makefile: ENABLE_DOT1X build block (supplicant sources, EAP-TLS,
  PBKDF2/AES-KEYWRAP, wolfIP packet sockets, WOLFSSL_NO_OPTIONS_H).
- main.c: launch the demo after the netif is up.

Hardware-validated: board reaches AUTHENTICATED and hostapd logs an
EAP-TLS success.

EAPOL is sent to the authenticator's unicast MAC rather than the PAE
group multicast (01:80:C2:00:00:03), which 802.1D switches do not
forward; hostapd use_pae_group_addr=0 replies in kind. Override the
authenticator MAC with -DDOT1X_AUTH_MAC0..5.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants