WPA-EAP (Enterprise)

WPA-EAP (802.1X) is used by corporate and university networks that require individual user credentials rather than a shared password. nmrs supports PEAP and EAP-TTLS with configurable inner authentication methods.

Quick Start

use nmrs::{NetworkManager, WifiSecurity, EapOptions, EapMethod, Phase2};

#[tokio::main]
async fn main() -> nmrs::Result<()> {
    let nm = NetworkManager::new().await?;

    let eap = EapOptions::new("user@company.com", "my_password")
        .with_method(EapMethod::Peap)
        .with_phase2(Phase2::Mschapv2);

    nm.connect("CorpWiFi", WifiSecurity::WpaEap { opts: eap }).await?;

    println!("Connected to enterprise WiFi!");
    Ok(())
}

EAP Methods

nmrs supports two outer EAP methods:

MethodDescriptionCommon Use
EapMethod::PeapProtected EAP — tunnels inner auth in TLSCorporate networks
EapMethod::TtlsTunneled TLS — flexible inner authUniversities, ISPs

Phase 2 (Inner Authentication)

The inner authentication runs inside the TLS tunnel established by the outer method:

MethodDescriptionTypical Pairing
Phase2::Mschapv2MS-CHAPv2 — challenge-responsePEAP
Phase2::PapPAP — plaintext (protected by TLS tunnel)TTLS

Building EAP Options

Direct Construction

#![allow(unused)]
fn main() {
use nmrs::{EapOptions, EapMethod, Phase2};

let eap = EapOptions::new("user@company.com", "password")
    .with_method(EapMethod::Peap)
    .with_phase2(Phase2::Mschapv2)
    .with_anonymous_identity("anonymous@company.com")
    .with_domain_suffix_match("company.com")
    .with_system_ca_certs(true);
}

Builder Pattern

For complex configurations, the builder pattern makes each option explicit:

#![allow(unused)]
fn main() {
use nmrs::{EapOptions, EapMethod, Phase2};

let eap = EapOptions::builder()
    .identity("user@company.com")
    .password("my_password")
    .method(EapMethod::Peap)
    .phase2(Phase2::Mschapv2)
    .anonymous_identity("anonymous@company.com")
    .domain_suffix_match("company.com")
    .system_ca_certs(true)
    .build();
}

Configuration Reference

OptionRequiredDescription
identityYesUsername (usually email)
passwordYesUser password
methodYesOuter EAP method (PEAP or TTLS)
phase2YesInner authentication (MSCHAPv2 or PAP)
anonymous_identityNoOuter identity for privacy (sent in the clear)
domain_suffix_matchNoVerify server certificate domain
ca_cert_pathNoPath to CA certificate (file:// URL)
system_ca_certsNoUse system CA store (default: false)

Certificate Validation

For security, you should validate the authentication server's certificate. There are two approaches:

System CA Certificates

Use the operating system's trusted certificate store:

#![allow(unused)]
fn main() {
let eap = EapOptions::new("user@company.com", "password")
    .with_system_ca_certs(true)
    .with_domain_suffix_match("company.com")
    .with_method(EapMethod::Peap)
    .with_phase2(Phase2::Mschapv2);
}

Custom CA Certificate

Point to a specific CA certificate file:

#![allow(unused)]
fn main() {
let eap = EapOptions::new("user@company.com", "password")
    .with_ca_cert_path("file:///etc/ssl/certs/company-ca.pem")
    .with_domain_suffix_match("company.com")
    .with_method(EapMethod::Peap)
    .with_phase2(Phase2::Mschapv2);
}

Security: Without certificate validation, your connection is vulnerable to evil-twin attacks. Always configure either system_ca_certs or ca_cert_path in production.

Common Configurations

Corporate PEAP/MSCHAPv2

The most common enterprise setup:

#![allow(unused)]
fn main() {
let eap = EapOptions::new("employee@corp.com", "password")
    .with_method(EapMethod::Peap)
    .with_phase2(Phase2::Mschapv2)
    .with_anonymous_identity("anonymous@corp.com")
    .with_domain_suffix_match("corp.com")
    .with_system_ca_certs(true);
}

University EAP-TTLS/PAP

Common at educational institutions using eduroam:

#![allow(unused)]
fn main() {
let eap = EapOptions::new("student@university.edu", "password")
    .with_method(EapMethod::Ttls)
    .with_phase2(Phase2::Pap)
    .with_ca_cert_path("file:///etc/ssl/certs/university-ca.pem")
    .with_domain_suffix_match("university.edu");
}

Full Example

use nmrs::{NetworkManager, WifiSecurity, EapOptions, EapMethod, Phase2};

#[tokio::main]
async fn main() -> nmrs::Result<()> {
    let nm = NetworkManager::new().await?;

    let eap = EapOptions::builder()
        .identity("user@company.com")
        .password(
            std::env::var("WIFI_PASSWORD")
                .expect("Set WIFI_PASSWORD env var"),
        )
        .method(EapMethod::Peap)
        .phase2(Phase2::Mschapv2)
        .anonymous_identity("anonymous@company.com")
        .domain_suffix_match("company.com")
        .system_ca_certs(true)
        .build();

    nm.connect("CorpNetwork", WifiSecurity::WpaEap {
        opts: eap,
    }).await?;

    if let Some(ssid) = nm.current_ssid().await {
        println!("Connected to: {}", ssid);
    }

    Ok(())
}

Troubleshooting

SymptomLikely Cause
AuthFailedWrong username/password, or server rejected credentials
SupplicantConfigFailedMisconfigured EAP method or phase2
SupplicantTimeoutServer not responding — check CA cert and domain
TimeoutAuthentication taking too long — try increasing timeout

For enterprise networks, the authentication process can take longer than standard WPA-PSK connections. Consider using custom timeouts:

#![allow(unused)]
fn main() {
use nmrs::{NetworkManager, TimeoutConfig};
use std::time::Duration;

let config = TimeoutConfig::new()
    .with_connection_timeout(Duration::from_secs(60));

let nm = NetworkManager::with_config(config).await?;
}

Next Steps