WiFi Enterprise Authentication

Chapter 42: WiFi Enterprise Authentication

Chapter Objectives

By the end of this chapter, you will be able to:

  • Understand the need for enterprise-grade WiFi security and its distinction from personal WiFi security.
  • Describe the WPA2/WPA3-Enterprise security protocols and the role of IEEE 802.1X.
  • Explain the Extensible Authentication Protocol (EAP) framework and its main components: Supplicant, Authenticator, and Authentication Server.
  • Understand the function of a RADIUS (Remote Authentication Dial-In User Service) server in enterprise authentication.
  • Identify and differentiate common EAP methods, including EAP-PEAP, EAP-TLS, and EAP-TTLS.
  • Configure the ESP32 to connect to a WPA2/WPA3-Enterprise network using EAP-PEAP with username/password credentials.
  • Configure the ESP32 to connect to a WPA2-Enterprise network using EAP-TLS with client and server certificates.
  • Discuss the basics of certificate management for EAP-TLS on the ESP32.
  • Troubleshoot common issues encountered during enterprise WiFi setup.

Introduction

In previous chapters, we primarily focused on connecting the ESP32 to WiFi networks secured with WPA/WPA2/WPA3-Personal, which uses a Pre-Shared Key (PSK) known to all devices on the network. While suitable for home and small office environments, this approach lacks the granular control, individual accountability, and scalability required by larger organizations such as corporations, universities, and public institutions.

These environments typically employ WPA2/WPA3-Enterprise, which provides a much more robust security framework. Instead of a shared password, each user or device authenticates with unique credentials, often managed by a central server. This chapter delves into the world of WiFi Enterprise authentication, exploring the underlying protocols like EAP and RADIUS, and demonstrating how to configure your ESP32 to securely connect to these more complex networks using ESP-IDF. This capability is crucial for IoT devices deployed in enterprise settings.

Theory

WPA/WPA2/WPA3-Enterprise

WPA-Enterprise, WPA2-Enterprise, and WPA3-Enterprise are WiFi security modes designed for organizational use. Unlike their “Personal” counterparts that use a single Pre-Shared Key (PSK) for all users, Enterprise modes utilize the IEEE 802.1X standard for port-based Network Access Control. This means each device or user must individually authenticate before gaining network access.

Feature WPA/WPA2/WPA3-Personal WPA/WPA2/WPA3-Enterprise
Primary Authentication Pre-Shared Key (PSK) – a single password for all users. IEEE 802.1X using EAP methods. Each user/device authenticates individually.
Credentials Shared password. Unique per user/device (e.g., username/password, digital certificates).
Authentication Server None (authentication handled by the Access Point). Centralized server (typically RADIUS).
Key Management Single encryption key derived from PSK (though WPA3 improves this with SAE). Dynamic, per-user, per-session encryption keys.
Scalability Suitable for small environments. Managing PSK becomes difficult with many users. Highly scalable for large organizations.
Accountability Low (actions are tied to the shared PSK, not an individual). High (actions can be traced to individual credentials).
Typical Use Case Home networks, small offices. Corporations, universities, government institutions, large public venues.
Complexity Simpler to set up. More complex to set up and manage (requires RADIUS server, potentially CA).

Key features of WPA-Enterprise:

  • Individual Credentials: Each user/device has unique credentials (e.g., username/password, digital certificates). This enhances security as compromising one set of credentials doesn’t compromise the entire network.
  • Centralized Authentication: Authentication is typically handled by a dedicated server, most commonly a RADIUS server.
  • Dynamic Key Management: Encryption keys are dynamically generated and distributed on a per-user, per-session basis, further strengthening security.
  • Scalability: Easily manage access for a large number of users and devices.

Extensible Authentication Protocol (EAP)

The Extensible Authentication Protocol (EAP), defined in RFC 3748, is an authentication framework, not a specific authentication mechanism. It provides a standardized way for different authentication methods (known as EAP methods) to be used within various network access technologies, including WiFi.

EAP involves three main roles:

  1. Supplicant: The client device attempting to gain network access (e.g., your ESP32).
  2. Authenticator: The network access device that controls network access (e.g., the WiFi Access Point – AP). The authenticator acts as a pass-through for EAP messages between the supplicant and the authentication server; it doesn’t understand the content of most EAP messages.
  3. Authentication Server (AS): The server that makes the authentication decision (e.g., a RADIUS server). It validates the supplicant’s credentials and informs the authenticator whether to grant or deny access.
%%{ init: { 'theme': 'base', 'themeVariables': { 'fontFamily': 'Open Sans' } } }%%
sequenceDiagram
    actor Supplicant as Supplicant (ESP32)
    participant Authenticator as Authenticator (WiFi AP)
    participant AuthServer as Authentication Server (RADIUS)

    %% Define styles based on provided color scheme
    %% Supplicant (initiator) - using primary node style
    %% Authenticator (intermediary) - using process node style
    %% AuthServer (decision maker) - using a distinct style, perhaps primary or a variation

    Supplicant->>Authenticator: Initiates Connection (e.g., Association Request)
    Authenticator-->>Supplicant: EAP-Request/Identity
    Supplicant->>Authenticator: EAP-Response/Identity (provides its identity)
    
    activate Authenticator
    Authenticator->>AuthServer: RADIUS Access-Request (contains EAP-Response/Identity)
    deactivate Authenticator
    
    activate AuthServer
    %% EAP Method Negotiation and Challenge/Response Loop
    loop EAP Method Specific Exchange
        AuthServer-->>Authenticator: RADIUS Access-Challenge (contains EAP-Request for chosen method)
        activate Authenticator
        Authenticator-->>Supplicant: EAP-Request (specific to EAP method)
        deactivate Authenticator
        
        Supplicant->>Authenticator: EAP-Response (to challenge)
        activate Authenticator
        Authenticator->>AuthServer: RADIUS Access-Request (contains EAP-Response)
        deactivate Authenticator
    end
    
    alt Authentication Successful
        AuthServer-->>Authenticator: RADIUS Access-Accept (contains EAP-Success message & encryption keys)
        activate Authenticator
        Authenticator-->>Supplicant: EAP-Success
        Authenticator-->>Supplicant: Grants Network Access (e.g., using derived keys)
        deactivate Authenticator
        Supplicant-->>AuthServer: (Secure Communication Established)
    else Authentication Failed
        AuthServer-->>Authenticator: RADIUS Access-Reject (contains EAP-Failure message)
        activate Authenticator
        Authenticator-->>Supplicant: EAP-Failure
        Authenticator-->>Supplicant: Denies Network Access
        deactivate Authenticator
    end
    deactivate AuthServer

EAP messages are encapsulated within other protocols. For WiFi, EAP messages between the supplicant and the AP are typically carried using EAP over LAN (EAPoL). The AP then usually communicates with the RADIUS server using the RADIUS protocol.

RADIUS (Remote Authentication Dial-In User Service)

RADIUS (RFC 2865) is a client-server protocol that provides centralized Authentication, Authorization, and Accounting (AAA) management for users and devices connecting to a network service. In the context of WPA-Enterprise WiFi:

  • Authentication: The RADIUS server verifies the supplicant’s credentials (e.g., username/password or certificate) against its database.
  • Authorization: If authentication is successful, the RADIUS server can authorize specific network access levels or policies (e.g., assign to a particular VLAN, apply bandwidth limits).
  • Accounting: (Optional) The RADIUS server can log usage statistics (e.g., connection duration, data transferred).

The WiFi Access Point acts as a RADIUS client, forwarding authentication requests from supplicants to the RADIUS server.

Common EAP Methods

EAP supports a wide variety of authentication methods. The choice of EAP method depends on the security requirements and the types of credentials available. Here are some common ones relevant to ESP32:

  1. EAP-PEAP (Protected EAP)
    • How it works: PEAP creates a secure TLS (Transport Layer Security) tunnel between the supplicant (ESP32) and the RADIUS server. Inside this encrypted tunnel, a “Phase 2” authentication method is used. The most common Phase 2 method is MSCHAPv2 (Microsoft Challenge-Handshake Authentication Protocol version 2), which uses username/password credentials.
    • Credentials: Typically a username and password.
    • Server Authentication: The supplicant authenticates the RADIUS server by validating its certificate. This requires the supplicant to have the RADIUS server’s CA (Certificate Authority) certificate (or the CA certificate that signed the RADIUS server’s certificate).
    • Client Authentication: The client (ESP32) is authenticated using the inner method (e.g., MSCHAPv2). Client certificates are not typically used with PEAP.
    • Pros: Widely supported, relatively easy to implement with username/password.
    • Cons: Relies on the strength of the user’s password. Requires server CA certificate on the client.
  2. EAP-TLS (EAP-Transport Layer Security)
    • How it works: EAP-TLS provides mutual authentication using X.509 certificates. Both the supplicant (ESP32) and the RADIUS server must have a certificate and corresponding private key. They authenticate each other by validating these certificates.
    • Credentials:
      • Client: Client certificate and its private key.
      • Server: Server certificate (verified by the client using a CA certificate).
    • Server Authentication: The supplicant validates the RADIUS server’s certificate using a trusted CA certificate.
    • Client Authentication: The RADIUS server validates the supplicant’s client certificate.
    • Pros: Considered one of the most secure EAP methods due to strong mutual certificate-based authentication.
    • Cons: Requires robust certificate management infrastructure (issuing, distributing, and revoking client certificates). Managing private keys securely on IoT devices can be challenging.
  3. EAP-TTLS (EAP-Tunneled TLS)
    • How it works: Similar to PEAP, EAP-TTLS first establishes a secure TLS tunnel between the supplicant and the RADIUS server. Inside this tunnel, various legacy or simpler authentication protocols (Phase 2 methods) can be used to authenticate the client, such as PAP (Password Authentication Protocol), CHAP, MSCHAP, MSCHAPv2.
    • Credentials: Can be username/password (for PAP, MSCHAP, etc.) or sometimes certificates, depending on the inner method.
    • Server Authentication: The supplicant authenticates the RADIUS server by validating its certificate (requires CA certificate).
    • Client Authentication: Uses the chosen inner method. Client certificates are optional and less common than username/password.
    • Pros: Flexible due to support for various inner methods.
    • Cons: Security depends on the strength of the inner authentication protocol. PAP, for instance, sends passwords in clear text within the tunnel.
EAP Method Mechanism Overview Credentials Used Server Auth Client Auth (Primary) Pros Cons
EAP-PEAP
(Protected EAP)
Creates a TLS tunnel, then uses an inner authentication method (commonly MSCHAPv2). Username & Password (for inner method). CA certificate for server validation. Yes (Server certificate validated by client using CA cert). Inner method (e.g., MSCHAPv2 with username/password). Widely supported; uses familiar username/password. Security relies on password strength; requires server CA cert on client.
EAP-TLS
(EAP-Transport Layer Security)
Mutual authentication using X.509 certificates for both client and server. Client: Certificate & Private Key. Server: Certificate. CA certificate for mutual validation. Yes (Server certificate validated by client using CA cert). Client certificate validated by server. Very strong security (mutual certificate auth). Complex certificate management (issuing, distributing, revoking client certs). Secure key storage on IoT devices is vital.
EAP-TTLS
(EAP-Tunneled TLS)
Establishes a TLS tunnel, then uses various simpler/legacy protocols for client authentication inside the tunnel. Varies by inner method (e.g., Username/Password for PAP, CHAP, MSCHAPv2). CA certificate for server validation. Yes (Server certificate validated by client using CA cert). Inner method (e.g., PAP, CHAP, MSCHAPv2). Client certificates optional. Flexible due to many inner method options. Security depends on the inner protocol’s strength (e.g., PAP is weak).

Certificates in EAP

Digital certificates play a crucial role in EAP methods like PEAP, EAP-TLS, and EAP-TTLS.

  • CA Certificate (Root CA Certificate): This certificate is used by the supplicant (ESP32) to verify the authenticity of the RADIUS server’s certificate. The ESP32 must be configured with the CA certificate that issued the RADIUS server’s certificate, or a trusted root CA certificate that is part of the RADIUS server’s certificate chain.
  • Client Certificate: In EAP-TLS, the supplicant (ESP32) uses a client certificate to prove its identity to the RADIUS server. This certificate is issued by a CA that the RADIUS server trusts.
  • Client Private Key: Associated with the client certificate, this key must be kept secret on the ESP32 and is used to sign data, proving possession of the certificate.
  • Server Certificate: The RADIUS server uses this to prove its identity to supplicants.
Certificate / Key Type Purpose Used By Required for EAP Methods
CA Certificate (Root CA) Used to verify the authenticity of another entity’s certificate (typically the server’s certificate). Establishes a chain of trust. Supplicant (ESP32) EAP-PEAP, EAP-TLS, EAP-TTLS (to validate RADIUS server)
Server Certificate Used by the Authentication Server (RADIUS) to prove its identity to the Supplicant. Authentication Server (RADIUS) EAP-PEAP, EAP-TLS, EAP-TTLS
Client Certificate Used by the Supplicant (ESP32) to prove its identity to the Authentication Server. Supplicant (ESP32) EAP-TLS (mandatory). Optional/Rare for EAP-TTLS depending on inner method.
Client Private Key The secret key paired with the Client Certificate. Used by the Supplicant to sign data, proving it possesses the certificate and is who it claims to be. Must be kept secure. Supplicant (ESP32) EAP-TLS (mandatory). Optional/Rare for EAP-TTLS.

Embedding Certificates in ESP32 Firmware:

For ESP32 applications, certificates and private keys (which are typically in PEM format – a Base64 encoded text format) need to be made available to the firmware. Common methods include:

  1. Embedding directly in code: Certificates can be stored as C strings within the source code.
  2. Embedding as binary data: The ESP-IDF build system allows embedding arbitrary binary files (like .pem files) into the application binary. These can then be accessed via symbols (_binary_filename_pem_start, _binary_filename_pem_end). This is generally preferred over C strings for cleaner management.
  3. Storing in a flash filesystem: For applications with more complex certificate management needs (e.g., updatable certificates), certificates can be stored in a SPIFFS or FAT filesystem on the ESP32’s flash memory.

For simplicity, our examples will use the binary data embedding method.

Embedding Method Description Pros Cons
Embedding Directly in Code Certificates (PEM format) are stored as C-style null-terminated strings directly within the source code files (e.g., const char* ca_cert = "-----BEGIN CERTIFICATE-----...";). Simple for very small certificates or quick tests. No build system changes needed beyond including the string. Can clutter code. Prone to formatting errors. Less secure if source code is widely accessible. Difficult to update certificates without recompiling.
Embedding as Binary Data (via Build System) Certificate files (e.g., ca.pem) are added to the project, and the build system (like ESP-IDF’s CMake component target_add_binary_data) embeds them into the application binary. Accessed via _binary_filename_start and _binary_filename_end symbols. Cleaner separation of certificates from code. Generally preferred method for ESP-IDF. Less prone to string formatting issues. Requires build system configuration. Certificates are still part of the main firmware binary; updates require reflashing the firmware.
Storing in a Flash Filesystem (e.g., SPIFFS, LittleFS, FAT) Certificates are stored as files in a dedicated filesystem partition on the ESP32’s flash memory. The application reads them from the filesystem at runtime. Allows for dynamic updates of certificates without reflashing the entire application firmware (only update the filesystem). Good for managing multiple certificates or when certificates change frequently. More complex to implement (requires filesystem initialization and file I/O operations). Consumes more flash space for the filesystem itself. Slower access compared to direct embedding. Security of the filesystem and key storage needs careful consideration.

Important Note on RADIUS Server Setup: Setting up and configuring a RADIUS server (like FreeRADIUS) and the associated Certificate Authority (CA) for EAP-TLS is a complex task beyond the scope of this chapter. You will typically need assistance from your IT department if connecting to an existing enterprise network, or you’ll need to consult detailed guides for setting up your own test environment.

Practical Examples

We will demonstrate connecting the ESP32 to a WPA2-Enterprise network using two common EAP methods: EAP-PEAP and EAP-TLS.

Prerequisites:

  • An ESP32 development board.
  • ESP-IDF v5.x installed with VS Code.
  • A WiFi Access Point configured for WPA2/WPA3-Enterprise.
  • A RADIUS server configured to support the EAP method you intend to use.
  • For EAP-PEAP:
    • Username and password for authentication.
    • The CA certificate of your RADIUS server (in PEM format).
  • For EAP-TLS:
    • The CA certificate of your RADIUS server (in PEM format).
    • A client certificate for your ESP32 (in PEM format).
    • The private key corresponding to the client certificate (in PEM format), unencrypted.
    • An identity string (often derived from the client certificate, e.g., Common Name).

1. Project Setup

  1. Create a new ESP-IDF project in VS Code or copy a basic WiFi station example.
  2. Enable WPA2 Enterprise support in menuconfig:
    • Run idf.py menuconfig.
    • Navigate to Component config -> Wi-Fi.
    • Ensure WiFi Station Enable is checked.
    • Navigate to Component config -> ESP NETIF Adapter -> Support WPA2 Enterprise Authentication. Enable this option (CONFIG_ESP_WIFI_WPA2_ENT_SUPPORT).
    • Save and exit.
  3. Create a certs directory inside your project’s main directory (e.g., my_project/main/certs/). Place your CA certificate (ca.pem), client certificate (client.crt), and client key (client.key) files here as needed for the examples.

2. Embedding Certificates

Modify your main/CMakeLists.txt file to embed the certificate files into the application binary. Add the following lines (adjust filenames as needed):

C
# main/CMakeLists.txt

# ... other configurations ...

# Embed certificates for WPA2 Enterprise
# Ensure these files exist in a 'certs' subdirectory within 'main'
# For EAP-PEAP (only ca.pem is strictly needed from this list for PEAP)
target_add_binary_data(${COMPONENT_TARGET} "certs/ca.pem" TEXT DESTINATION_DIR certs)

# For EAP-TLS (all three are typically needed)
target_add_binary_data(${COMPONENT_TARGET} "certs/client.crt" TEXT DESTINATION_DIR certs)
target_add_binary_data(${COMPONENT_TARGET} "certs/client.key" TEXT DESTINATION_DIR certs)

# ... other configurations ...

This makes the certificate content accessible in your C code.

In your C code, you’ll declare extern symbols to access this embedded data:

C
// For ca.pem
extern const uint8_t ca_pem_start[] asm("_binary_ca_pem_start");
extern const uint8_t ca_pem_end[]   asm("_binary_ca_pem_end");

// For client.crt
extern const uint8_t client_crt_start[] asm("_binary_client_crt_start");
extern const uint8_t client_crt_end[]   asm("_binary_client_crt_end");

// For client.key
extern const uint8_t client_key_start[] asm("_binary_client_key_start");
extern const uint8_t client_key_end[]   asm("_binary_client_key_end");

3. Common Code: WiFi Initialization and Event Handling

The following code provides basic WiFi initialization, event handling, and global definitions that will be used by both EAP-PEAP and EAP-TLS examples.

C
// main.c
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "esp_wpa2.h" // For WPA2 Enterprise

static const char *TAG = "WIFI_ENTERPRISE";

// Event group to signal WiFi connection status
static EventGroupHandle_t s_wifi_event_group;
const int WIFI_CONNECTED_BIT = BIT0;
const int WIFI_FAIL_BIT = BIT1;

// --- Configuration Placeholders ---
// Replace with your actual network SSID
#define EAP_SSID "YOUR_ENTERPRISE_SSID"

// For EAP-PEAP
#define EAP_PEAP_ID "your_identity" // Often your username, or anonymous@realm
#define EAP_PEAP_USERNAME "your_username"
#define EAP_PEAP_PASSWORD "your_password"

// For EAP-TLS
#define EAP_TLS_IDENTITY "your_tls_identity" // Often the CN from your client certificate

// Certificate data (will be linked from embedded binary data)
extern const uint8_t ca_pem_start[] asm("_binary_ca_pem_start");
extern const uint8_t ca_pem_end[]   asm("_binary_ca_pem_end");

extern const uint8_t client_crt_start[] asm("_binary_client_crt_start");
extern const uint8_t client_crt_end[]   asm("_binary_client_crt_end");

extern const uint8_t client_key_start[] asm("_binary_client_key_start");
extern const uint8_t client_key_end[]   asm("_binary_client_key_end");


static void event_handler(void* arg, esp_event_base_t event_base,
                          int32_t event_id, void* event_data)
{
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
        ESP_LOGI(TAG, "WIFI_EVENT_STA_START: Station mode started, attempting to connect...");
        esp_wifi_connect();
    } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
        wifi_event_sta_disconnected_t* disconnected_event = (wifi_event_sta_disconnected_t*) event_data;
        ESP_LOGE(TAG, "WIFI_EVENT_STA_DISCONNECTED: Disconnected from AP, reason: %d", disconnected_event->reason);
        // Reasons are defined in esp_wifi_types.h, e.g., WIFI_REASON_AUTH_FAIL
        if (disconnected_event->reason == WIFI_REASON_AUTH_FAIL) {
            ESP_LOGE(TAG, "Authentication failed. Check credentials, certificates, and RADIUS server configuration.");
        }
        xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
        // Optional: attempt to reconnect or re-initialize
        // esp_wifi_connect();
    } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
        ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
        ESP_LOGI(TAG, "IP_EVENT_STA_GOT_IP: Got IP address: " IPSTR, IP2STR(&event->ip_info.ip));
        xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
    } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_WPS_ER_PIN) {
        // This event can sometimes be triggered with certain auth failures in enterprise.
        // Log it for debugging, but it's not directly WPS related here.
        ESP_LOGW(TAG, "WIFI_EVENT_STA_WPS_ER_PIN received. This might indicate an EAP issue.");
    } else {
        ESP_LOGD(TAG, "Received unhandled event: base=%s, id=%ld", event_base, event_id);
    }
}

static void wifi_init_common(void)
{
    s_wifi_event_group = xEventGroupCreate();

    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
    assert(sta_netif);

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));

    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
}

4. Example 1: EAP-PEAP (MSCHAPv2) Implementation

This example shows how to connect using EAP-PEAP with MSCHAPv2 as the inner authentication method, using a username and password.

C
// main.c (continued after common code)

static void wifi_init_sta_peap(void)
{
    wifi_init_common(); // Initialize common WiFi parts

    wifi_config_t wifi_config = {
        .sta = {
            .ssid = EAP_SSID,
            // Password field is not used for WPA2-Enterprise with EAP methods directly in wifi_config_t
            // It's set via esp_wifi_sta_wpa2_ent_set_password()
        },
    };
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));

    ESP_LOGI(TAG, "Enabling WPA2 Enterprise for PEAP...");
    ESP_ERROR_CHECK(esp_wifi_sta_wpa2_ent_enable()); // Enable WPA2 Enterprise mode

    ESP_LOGI(TAG, "Setting EAP method to PEAP...");
    ESP_ERROR_CHECK(esp_wifi_sta_wpa2_ent_set_eap_method(ESP_WPA2_EAP_METHOD_PEAP));

    ESP_LOGI(TAG, "Setting PEAP Phase 2 method to MSCHAPV2...");
    ESP_ERROR_CHECK(esp_wifi_sta_wpa2_ent_set_peap_phase2_method(ESP_EAP_TTLS_PHASE2_MSCHAPV2)); // Also used for PEAP

    ESP_LOGI(TAG, "Setting EAP Identity: %s", EAP_PEAP_ID);
    ESP_ERROR_CHECK(esp_wifi_sta_wpa2_ent_set_identity((unsigned char *)EAP_PEAP_ID, strlen(EAP_PEAP_ID)));

    ESP_LOGI(TAG, "Setting EAP Username: %s", EAP_PEAP_USERNAME);
    ESP_ERROR_CHECK(esp_wifi_sta_wpa2_ent_set_username((unsigned char *)EAP_PEAP_USERNAME, strlen(EAP_PEAP_USERNAME)));
    
    ESP_LOGI(TAG, "Setting EAP Password...");
    ESP_ERROR_CHECK(esp_wifi_sta_wpa2_ent_set_password((unsigned char *)EAP_PEAP_PASSWORD, strlen(EAP_PEAP_PASSWORD)));

    ESP_LOGI(TAG, "Setting CA Certificate for PEAP...");
    // Calculate length of embedded CA certificate
    unsigned int ca_pem_bytes = ca_pem_end - ca_pem_start;
    ESP_ERROR_CHECK(esp_wifi_sta_wpa2_ent_set_ca_cert(ca_pem_start, ca_pem_bytes));
    
    // Client cert and key are not used for PEAP
    // ESP_ERROR_CHECK(esp_wifi_sta_wpa2_ent_set_client_cert_and_key(NULL, 0, NULL, 0, NULL, 0));


    ESP_LOGI(TAG, "Starting WiFi driver for PEAP connection...");
    ESP_ERROR_CHECK(esp_wifi_start()); // Start WiFi, event_handler will call esp_wifi_connect()
}

void app_main_peap(void) // Rename or select this main for PEAP
{
    // Initialize NVS
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
      ESP_ERROR_CHECK(nvs_flash_erase());
      ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);

    ESP_LOGI(TAG, "ESP32 WPA2-Enterprise PEAP Example");
    wifi_init_sta_peap();

    // Wait for connection or failure
    EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
                                           WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
                                           pdFALSE,
                                           pdFALSE,
                                           portMAX_DELAY);

    if (bits & WIFI_CONNECTED_BIT) {
        ESP_LOGI(TAG, "Successfully connected to %s (PEAP)", EAP_SSID);
    } else if (bits & WIFI_FAIL_BIT) {
        ESP_LOGE(TAG, "Failed to connect to %s (PEAP)", EAP_SSID);
    } else {
        ESP_LOGE(TAG, "UNEXPECTED EVENT");
    }
    // The event_handler will have more details on failure reasons.
}
%%{ init: { 'theme': 'base', 'themeVariables': { 'fontFamily': 'Open Sans' } } }%%
graph TD
    %% Define styles for node types
    classDef startNode fill:#EDE9FE,stroke:#5B21B6,stroke-width:2px,color:#5B21B6;
    classDef processNode fill:#DBEAFE,stroke:#2563EB,stroke-width:1px,color:#1E40AF;
    classDef decisionNode fill:#FEF3C7,stroke:#D97706,stroke-width:1px,color:#92400E;
    classDef ioNode fill:#F3E5F5,stroke:#8E24AA,stroke-width:1px,color:#6A1B9A; 
    classDef endNode fill:#D1FAE5,stroke:#059669,stroke-width:2px,color:#065F46;
    classDef checkNode fill:#FEE2E2,stroke:#DC2626,stroke-width:1px,color:#991B1B;


    A(Start PEAP Setup):::startNode --> B("Initialize Common WiFi Parts<br><code class='code-mermaid'>wifi_init_common()</code>");
    class B processNode;
    B --> C("Set WiFi Config: SSID<br><code class='code-mermaid'>esp_wifi_set_config()</code>");
    class C processNode;
    C --> D("Enable WPA2 Enterprise<br><code class='code-mermaid'>esp_wifi_sta_wpa2_ent_enable()</code>");
    class D processNode;
    D --> E("Set EAP Method to PEAP<br><code class='code-mermaid'>esp_wifi_sta_wpa2_ent_set_eap_method(ESP_WPA2_EAP_METHOD_PEAP)</code>");
    class E processNode;
    E --> F("Set PEAP Phase 2 Method<br>(e.g., MSCHAPV2)<br><code class='code-mermaid'>esp_wifi_sta_wpa2_ent_set_peap_phase2_method(...)</code>");
    class F processNode;
    F --> G("Set EAP Identity<br><code class='code-mermaid'>esp_wifi_sta_wpa2_ent_set_identity()</code>");
    class G processNode;
    G --> H("Set EAP Username<br><code class='code-mermaid'>esp_wifi_sta_wpa2_ent_set_username()</code>");
    class H processNode;
    H --> I("Set EAP Password<br><code class='code-mermaid'>esp_wifi_sta_wpa2_ent_set_password()</code>");
    class I processNode;
    I --> J("Set CA Certificate<br><code class='code-mermaid'>esp_wifi_sta_wpa2_ent_set_ca_cert()</code><br>from embedded <code class='code-mermaid'>ca_pem_start</code>");
    class J ioNode;
    J --> K("Start WiFi Driver<br><code class='code-mermaid'>esp_wifi_start()</code>");
    class K processNode;
    K --> L{"Event Handler Calls<br><code class='code-mermaid'>esp_wifi_connect()</code>"};
    class L processNode;
    L --> M(Attempt Connection...):::endNode;

    D -- Error --> Err1(Handle Error):::checkNode;
    E -- Error --> Err2(Handle Error):::checkNode;
    F -- Error --> Err3(Handle Error):::checkNode;
    G -- Error --> Err4(Handle Error):::checkNode;
    H -- Error --> Err5(Handle Error):::checkNode;
    I -- Error --> Err6(Handle Error):::checkNode;
    J -- Error --> Err7(Handle Error):::checkNode;
    K -- Error --> Err8(Handle Error):::checkNode;


5. Example 2: EAP-TLS Implementation

This example shows how to connect using EAP-TLS, which requires a client certificate and private key.

C
// main.c (continued after common code, or in a separate main function)

static void wifi_init_sta_tls(void)
{
    wifi_init_common(); // Initialize common WiFi parts

    wifi_config_t wifi_config = {
        .sta = {
            .ssid = EAP_SSID,
        },
    };
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));

    ESP_LOGI(TAG, "Enabling WPA2 Enterprise for TLS...");
    ESP_ERROR_CHECK(esp_wifi_sta_wpa2_ent_enable());

    ESP_LOGI(TAG, "Setting EAP method to TLS...");
    ESP_ERROR_CHECK(esp_wifi_sta_wpa2_ent_set_eap_method(ESP_WPA2_EAP_METHOD_TLS));

    ESP_LOGI(TAG, "Setting EAP Identity for TLS: %s", EAP_TLS_IDENTITY);
    ESP_ERROR_CHECK(esp_wifi_sta_wpa2_ent_set_identity((unsigned char *)EAP_TLS_IDENTITY, strlen(EAP_TLS_IDENTITY)));

    ESP_LOGI(TAG, "Setting CA Certificate for TLS...");
    unsigned int ca_pem_bytes = ca_pem_end - ca_pem_start;
    ESP_ERROR_CHECK(esp_wifi_sta_wpa2_ent_set_ca_cert(ca_pem_start, ca_pem_bytes));

    ESP_LOGI(TAG, "Setting Client Certificate and Key for TLS...");
    unsigned int client_crt_bytes = client_crt_end - client_crt_start;
    unsigned int client_key_bytes = client_key_end - client_key_start;
    // Last two arguments are for private key password and its length, NULL and 0 if key is not encrypted.
    ESP_ERROR_CHECK(esp_wifi_sta_wpa2_ent_set_client_cert_and_key(client_crt_start, client_crt_bytes,
                                                                client_key_start, client_key_bytes,
                                                                NULL, 0));
    
    // Username and password are not typically used for EAP-TLS when client cert is primary auth
    // ESP_ERROR_CHECK(esp_wifi_sta_wpa2_ent_set_username(NULL, 0));
    // ESP_ERROR_CHECK(esp_wifi_sta_wpa2_ent_set_password(NULL, 0));

    ESP_LOGI(TAG, "Starting WiFi driver for TLS connection...");
    ESP_ERROR_CHECK(esp_wifi_start());
}

void app_main_tls(void) // Rename or select this main for TLS
{
    // Initialize NVS
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
      ESP_ERROR_CHECK(nvs_flash_erase());
      ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);

    ESP_LOGI(TAG, "ESP32 WPA2-Enterprise TLS Example");
    wifi_init_sta_tls();

    // Wait for connection or failure
    EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
                                           WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
                                           pdFALSE,
                                           pdFALSE,
                                           portMAX_DELAY);

    if (bits & WIFI_CONNECTED_BIT) {
        ESP_LOGI(TAG, "Successfully connected to %s (TLS)", EAP_SSID);
    } else if (bits & WIFI_FAIL_BIT) {
        ESP_LOGE(TAG, "Failed to connect to %s (TLS)", EAP_SSID);
    } else {
        ESP_LOGE(TAG, "UNEXPECTED EVENT");
    }
}

To use one of these examples:

  • Rename app_main_peap or app_main_tls to app_main.
  • Fill in the placeholder defines (EAP_SSID, credentials, etc.) with your actual values.
  • Ensure your certificate files are correctly placed in main/certs/ and referenced in main/CMakeLists.txt.

6. Build, Flash, and Observe

  1. Build: Use idf.py build or the VS Code build button.
  2. Flash: Use idf.py -p (YOUR_PORT) flash or the VS Code flash button.
  3. Monitor: Use idf.py -p (YOUR_PORT) monitor or the VS Code monitor button.

Observe the serial monitor output. You should see logs indicating the EAP method being used, attempts to connect, and hopefully, a successful connection with an IP address. If connection fails, the WIFI_EVENT_STA_DISCONNECTED event reason code can provide clues.

Variant Notes

  • WPA2/WPA3 Enterprise authentication, including EAP-PEAP and EAP-TLS, is supported by the ESP-IDF WiFi stack on all ESP32 variants that feature WiFi connectivity: ESP32, ESP32-S2, ESP32-S3, ESP32-C3, and ESP32-C6, ESP32-C5, ESP32-C61.
  • The API calls (esp_wpa2.h) and general behavior are consistent across these variants.
  • The cryptographic operations involved in TLS handshakes and EAP methods can be computationally intensive. While all listed variants are capable, slight performance differences in connection time might be observable, but this typically doesn’t affect functionality.
  • ESP32-H2 does not have WiFi capabilities and therefore does not support WPA2/WPA3-Enterprise authentication.

Common Mistakes & Troubleshooting Tips

Mistake / Issue Symptom(s) Troubleshooting / Solution
Incorrect Credentials or Identity (PEAP/TTLS) Authentication failures (WIFI_REASON_AUTH_FAIL). RADIUS server logs might show “unknown user” or “password mismatch”. Fix: Double-check username, password, and EAP identity string. For PEAP, the “identity” (outer identity) might differ from the “username” (inner identity, e.g., anonymous@realm vs user). Consult network admin for exact formats.
RADIUS Server CA Certificate Issues Connection fails, often during TLS handshake phase. ESP32 logs might show SSL/TLS errors. WIFI_REASON_AUTH_FAIL. Fix: Ensure the CA certificate (ca.pem) embedded in ESP32 firmware is the correct one that signed the RADIUS server’s certificate (or is a trusted root in its chain). Verify it’s in correct PEM format and not expired.
Client Certificate/Key Problems (EAP-TLS) Authentication failures. RADIUS logs may indicate client certificate validation failure, untrusted CA, or key mismatch. WIFI_REASON_AUTH_FAIL. Fix:
1. Client certificate must be issued by a CA trusted by the RADIUS server.
2. Private key (client.key) must match the client certificate (client.crt).
3. Ensure private key is unencrypted if no password is provided to ESP-IDF API.
4. Check certificate (client and CA) validity periods (not expired, not before valid).
ESP32 System Time Not Synchronized Certificate validation errors (certificates appear expired or not yet valid), especially with EAP-TLS or PEAP/TTLS server cert validation. Fix: Implement SNTP client on ESP32 to synchronize system time before initiating WiFi connection. This is crucial for certificate validity checks.
RADIUS Server Misconfiguration ESP32 fails to connect. AP might connect but ESP32 gets no IP. RADIUS server logs are key here. Fix: Verify user/device account on RADIUS. Ensure RADIUS is configured to allow the specific EAP method (PEAP, TLS, TTLS) and Phase 2 methods. Check network path between AP and RADIUS. Check RADIUS server logs for detailed error messages.
Forgetting WPA2 Enterprise Enable Flags Connection attempts might fall back to PSK or fail silently. API calls for EAP settings might return errors. Fix: Ensure CONFIG_ESP_WIFI_WPA2_ENT_SUPPORT is enabled in menuconfig. Call esp_wifi_sta_wpa2_ent_enable() in your code before setting other EAP parameters.
Incorrect EAP Method Configuration Authentication fails. RADIUS server might log mismatched EAP types. Fix: Ensure the EAP method set on ESP32 (e.g., esp_wifi_sta_wpa2_ent_set_eap_method()) matches what the RADIUS server expects for that user/network. Similarly for Phase 2 methods in PEAP/TTLS.
Certificate Format or Embedding Errors ESP32 fails to parse certificates. SSL/TLS errors. API calls for setting certs might fail. Fix: Ensure certificates are in correct PEM format (Base64 encoded, with -----BEGIN...----- and -----END...----- markers). Verify they are correctly embedded using target_add_binary_data and accessed with correct _binary_..._start/_end symbols. Check for extra characters or truncation.

Exercises

  1. Implement EAP-TTLS:
    • Research the necessary esp_wifi_sta_wpa2_ent_set_... functions for EAP-TTLS.
    • Configure your ESP32 to connect using EAP-TTLS with PAP (Password Authentication Protocol) as the inner authentication method. You’ll need username/password and the RADIUS CA certificate. (Note: PAP is less secure than MSCHAPv2, use for educational purposes in a controlled environment).
  2. Dynamic Credential Input:
    • Modify one of the examples (e.g., EAP-PEAP) to accept the SSID, EAP identity, username, and password via the serial console (UART) at runtime instead of hardcoding them. This allows for easier testing with different networks/credentials without reflashing. Store them temporarily in variables.
  3. SNTP Time Synchronization for Robust EAP-TLS:
    • Integrate SNTP time synchronization into the EAP-TLS example.
    • Initialize SNTP and wait for time synchronization before calling esp_wifi_start() to initiate the EAP-TLS connection. Log the current time to verify. This makes certificate validation more reliable.

Summary

  • WPA/WPA2/WPA3-Enterprise provides stronger security for organizational WiFi networks by using IEEE 802.1X for individual authentication.
  • The Extensible Authentication Protocol (EAP) is a framework enabling various authentication methods between a Supplicant (ESP32), Authenticator (AP), and Authentication Server (RADIUS).
  • RADIUS servers handle centralized authentication, authorization, and accounting.
  • Common EAP methods include EAP-PEAP (username/password within a TLS tunnel), EAP-TLS (mutual certificate-based authentication), and EAP-TTLS (tunneled authentication with various inner methods).
  • ESP-IDF (esp_wpa2.h) provides APIs to configure the ESP32 for enterprise authentication, including setting EAP methods, credentials, and certificates.
  • Proper management and embedding of certificates (CA, client certificate, client key) are critical, especially for EAP-TLS.
  • Troubleshooting often involves checking credentials, certificate validity, RADIUS server configuration, and ESP32 system time.

Further Reading

  • ESP-IDF WPA2 Enterprise Example: Located in your ESP-IDF installation path, typically at $IDF_PATH/examples/wifi/wifi_enterprise/. This is an excellent official resource.
  • ESP-IDF API Reference – esp_wifi.h and esp_wpa2.h:
  • RFC 3748: Extensible Authentication Protocol (EAP) – For a deep dive into the EAP framework.
  • RFC 5216: The EAP-TLS Authentication Protocol – For details on EAP-TLS.
  • RFC 5281: Extensible Authentication Protocol Tunneled TLS Authentication Protocol (EAP-TTLS) Version 0.
  • Microsoft PEAP Documentation: Search for “Protected EAP Protocol (PEAP)” on Microsoft’s documentation sites for details on PEAP.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top