npm package: @gibme/eapol
Description: A comprehensive, low-level EAPOL (Extensible Authentication Protocol over LAN) Key Frame parser for Wi-Fi security protocols. Parses and validates EAPOL-Key frames used in WPA/WPA2/WPA3 4-way handshakes, group key handshakes, and Fast Transition (802.11r)
Author: Brandon Lehmann
Latest version: 22.0.0

# @gibme/eapol

A comprehensive, low-level EAPOL (Extensible Authentication Protocol over LAN) Key Frame parser for Wi-Fi security protocols.

Parses and validates EAPOL-Key frames used in WPA/WPA2/WPA3 4-way handshakes, group key handshakes, and Fast Transition (802.11r) operations. Supports RSN/WPA IE parsing, all KDE types (GTK, IGTK, PMKID, etc.), and all standard cipher suites and AKM methods.

**Parser-only implementation** - does not perform cryptographic operations.

## Requirements

- Node.js >= 22

## Installation

```bash
npm install @gibme/eapol
```

or

```bash
yarn add @gibme/eapol
```

## Quick Start

```typescript
import { Frame, EAPOL } from '@gibme/eapol';

// Parse from hex string
const frame = Frame.from('0203005f02...', 'hex');

// Parse from buffer
const frame = Frame.from(buffer);

// Identify the handshake message
console.log(`Message ${frame.messageNumber} of ${frame.handshakeType} handshake`);

// Validate the frame
const result = frame.validate();
if (!result.valid) {
    console.error('Validation errors:', result.errors);
}
```

## Features

### Frame Parsing

- Parse complete EAPOL-Key frames from buffers or hex strings
- Automatic frame detection — scans up to 512 bytes past 802.11/LLC headers to locate the EAPOL header
- Supports EAPOL versions 1, 2, and 3 (802.1X-2001/2004/2010)
- Extracts all key descriptor fields (nonces, MIC, replay counter, IVs, RSC, etc.)
- Parses Key Information flags with full bit-field decoding

### Security Protocol Support

| Protocol | Standard | Description |
|---|---|---|
| WPA/TKIP | IEEE 802.11i | Legacy WPA with Microsoft OUI (00:50:F2:01) |
| WPA2/WPA3 | IEEE 802.11i/ac/ax | RSN Information Element (IE 0x30) parsing |
| Fast Transition | IEEE 802.11r | Full FT-IE and Mobility Domain parsing |
| PMF | IEEE 802.11w | Management Frame Protection with IGTK support |
| FILS | IEEE 802.11ai | Fast Initial Link Setup support |

### Key Data Encapsulation (KDE) Parsing

All standard KDE types are recognized and parsed:

- **GTK** - Group Temporal Key (key ID, Tx flag, key material)
- **IGTK** - Integrity Group Temporal Key with IPN (802.11w PMF)
- **PMKID** - Pairwise Master Key Identifier (16 bytes)
- **MAC Address** - TDLS peer identification
- **SMK** - Station-to-Station Master Key (TDLS)
- **Nonce** - Additional 32-byte nonce
- **Lifetime** - Key lifetime in seconds
- **Error** - Handshake error codes
- **Key ID** - Generic key identifier
- **Multiband GTK / Key ID** - Multi-band operation support

### Cipher Suites

Complete constants for all IEEE 802.11 cipher suites:

| Constant | Value | Usage |
|---|---|---|
| `CCMP_128` | 0x04 | WPA2/WPA3 standard |
| `GCMP_128` | 0x08 | WPA3 |
| `GCMP_256` | 0x09 | WPA3 192-bit |
| `CCMP_256` | 0x0a | WPA3 192-bit |
| `TKIP` | 0x02 | Legacy WPA |
| `WEP40` / `WEP_104` | 0x01 / 0x05 | Legacy (deprecated) |
| `BIP_CMAC_128` | 0x06 | PMF |
| `BIP_GMAC_128` / `BIP_GMAC_256` | 0x0b / 0x0c | PMF |
| `BIP_CMAC_256` | 0x0d | PMF |

Available via `EAPOL.PairwiseCipherSuite`, `EAPOL.GroupCipherSuite`, and `EAPOL.ManagementFrameProtectionSuite`.

### AKM Suites

All standard Authentication and Key Management methods:

| Constant | Value | Usage |
|---|---|---|
| `PSK` | 0x02 | WPA2-Personal |
| `EAP` | 0x01 | WPA2-Enterprise |
| `SAE` | 0x08 | WPA3-Personal |
| `OWE` | 0x12 | Opportunistic Wireless Encryption |
| `FT_PSK` / `FT_SAE` | 0x04 / 0x09 | Fast Transition variants |
| `EAP_SUITE_B_192` | 0x0c | WPA3-Enterprise 192-bit |
| `FILS_SHA256` / `FILS_SHA384` | 0x0e / 0x0f | Fast Initial Link Setup |
| `SAE_EXT_KEY` / `FT_SAE_EXT_KEY` | 0x18 / 0x19 | SAE with Extended Key |
| `PASN` | 0x13 | Pre-Association Security Negotiation |

Available via `EAPOL.AkmSuite`.

### Frame Validation

Comprehensive validation including:

- EAPOL version and descriptor type compatibility
- Key Information flag consistency per IEEE 802.11 spec
- Message-specific requirements (Messages 1-4 of the 4-way handshake)
- Replay counter validation
- RSN/WPA IE structure integrity (version, cipher counts, AKM counts)
- KDE structure validation (IGTK IPN length, MAC address length, PMKID length, nonce length)
- Nonce and MIC presence requirements
- Pairwise key length checks
- Encrypted key data flag consistency

### Handshake Message Detection

Automatic identification of 4-way handshake messages based on flag combinations:

| Message | Direction | Key Flags |
|---|---|---|
| Message 1 | AP -> STA | `hasAck`, no `hasMic`, no `hasInstall` |
| Message 2 | STA -> AP | `hasMic`, no `hasAck`, no `isSecure` |
| Message 3 | AP -> STA | `hasAck`, `hasInstall`, `hasMic` |
| Message 4 | STA -> AP | `hasMic`, `isSecure`, no `hasAck` |

Also detects group key handshake messages.

## Usage Examples

### Identify Handshake Messages

```typescript
import { Frame } from '@gibme/eapol';

const frame = Frame.from(buffer);

if (frame.isMessage1) {
    console.log('Message 1: AP -> STA (ANonce)');
    console.log('ANonce:', frame.keyNonce.toString('hex'));
}

if (frame.isMessage2) {
    console.log('Message 2: STA -> AP (SNonce, RSN IE)');
    console.log('SNonce:', frame.keyNonce.toString('hex'));
    console.log('RSN:', frame.rsn);
}

if (frame.isMessage3) {
    console.log('Message 3: AP -> STA (ANonce, GTK, Install)');
    console.log('GTK Key ID:', frame.gtk?.keyId);
    console.log('GTK:', frame.gtk?.gtk.toString('hex'));
}

if (frame.isMessage4) {
    console.log('Message 4: STA -> AP (Confirmation)');
}
```

### Extract Security Information

```typescript
const frame = Frame.from(buffer);

// RSN Information Element (WPA2/WPA3)
if (frame.rsn) {
    console.log('Version:', frame.rsn.version);
    console.log('Group Cipher:', frame.rsn.groupCipher.toString('hex'));
    console.log('Pairwise Ciphers:', frame.rsn.pairwiseCipherSuites.length);
    console.log('AKM Suites:', frame.rsn.akmSuites.length);
    console.log('MFP Required:', frame.rsn.capabilities.mfpRequired);
    console.log('MFP Capable:', frame.rsn.capabilities.mfpCapable);
    console.log('PMKIDs:', frame.rsn.pmkIds.length);
}

// Legacy WPA Information Element
if (frame.wpa) {
    console.log('WPA Version:', frame.wpa.version);
    console.log('Group Cipher:', frame.wpa.groupCipher.toString('hex'));
}

// Fast Transition (802.11r)
if (frame.ft) {
    console.log('Mobility Domain ID:', frame.mdid.toString('hex'));
    console.log('R0KH-ID:', frame.ft.r0KHId?.toString('hex'));
    console.log('R1KH-ID:', frame.ft.r1KHId?.toString('hex'));
}
```

### Extract Keys

```typescript
const frame = Frame.from(buffer);

// Group Temporal Key
if (frame.gtk) {
    console.log('GTK Key ID:', frame.gtk.keyId);
    console.log('GTK Tx Flag:', frame.gtk.txFlag);
    console.log('GTK:', frame.gtk.gtk.toString('hex'));
}

// Integrity Group Temporal Key (PMF)
if (frame.igtk) {
    console.log('IGTK Key ID:', frame.igtk.keyId);
    console.log('IGTK IPN:', frame.igtk.ipn.toString('hex'));
    console.log('IGTK:', frame.igtk.igtk.toString('hex'));
}

// PMKID (for fast roaming)
if (frame.pmkid) {
    console.log('PMKID:', frame.pmkid.pmkid.toString('hex'));
}
```

### Compare Cipher Suites

```typescript
import { Frame, EAPOL } from '@gibme/eapol';

const frame = Frame.from(buffer);

if (frame.rsn) {
    const supportsCCMP = frame.rsn.pairwiseCipherSuites.some(
        suite => suite.equals(EAPOL.PairwiseCipherSuite.CCMP_128)
    );

    const supportsSAE = frame.rsn.akmSuites.some(
        suite => suite.equals(EAPOL.AkmSuite.SAE)
    );

    console.log('Supports CCMP-128:', supportsCCMP);
    console.log('Supports WPA3-SAE:', supportsSAE);
}
```

### Validate a Frame

```typescript
const frame = Frame.from(buffer);
const validation = frame.validate();

if (!validation.valid) {
    console.error('Frame validation failed:');
    validation.errors.forEach(err => console.error(`  - ${err}`));
} else {
    console.log('Frame is valid');
    console.log('Key Descriptor Version valid:', frame.isValidKeyDescriptorVersion());
}
```

### MIC Verification Support

```typescript
const frame = Frame.from(buffer);

// Get the frame with MIC field zeroed for external MIC calculation
const zeroedFrame = frame.zeroedFrame;

// The MIC algorithm depends on the Key Descriptor Version
switch (frame.keyVersion) {
    case EAPOL.KeyVersion.HMAC_MD5_RC4:
        // HMAC-MD5 (WPA/TKIP)
        break;
    case EAPOL.KeyVersion.HMAC_SHA1_AES:
        // HMAC-SHA1-128 (WPA2/CCMP)
        break;
    case EAPOL.KeyVersion.AES_128_CMAC:
        // AES-128-CMAC (WPA3, PMF, FT)
        break;
}
```

## API Reference

### `Frame.from(blob, encoding?)`

Parses an EAPOL Key Frame from a buffer or string.

- **`blob`** - `Buffer` or `string` containing the EAPOL frame
- **`encoding`** - Optional `BufferEncoding` (default: `'hex'`)
- **Returns:** `Frame`
- **Throws:** `Error` if the blob does not contain a valid EAPOL Key Frame

### Frame Properties

| Property | Type | Description |
|---|---|---|
| `version` | `EAPOL.Version` | EAPOL protocol version (0x01-0x03) |
| `type` | `EAPOL.Type` | Packet type (0x03 for EAPOL-Key) |
| `descriptor` | `EAPOL.Descriptor` | Key descriptor type (RC4, RSN, FT, PMF, FILS, EHT) |
| `keyVersion` | `EAPOL.KeyVersion` | Crypto algorithm for MIC and key wrapping |
| `replayCounter` | `bigint` | 8-byte replay counter |
| `keyLength` | `number` | Pairwise key length in bytes |
| `keyNonce` | `Buffer` | 32-byte ANonce or SNonce |
| `keyIv` | `Buffer` | 16-byte initialization vector |
| `keyRsc` | `Buffer` | 8-byte receive sequence counter |
| `keyId` | `Buffer` | 8-byte key identifier (reserved) |
| `mic` | `Buffer` | Message Integrity Code |
| `zeroedFrame` | `Buffer` | Frame copy with MIC zeroed for verification |

### Key Information Flags

| Property | Type | Description |
|---|---|---|
| `keyInfo` | `EAPOL.KeyInfo` | Complete key info structure |
| `isPairwise` | `boolean` | Pairwise key (PTK) vs group key (GTK) |
| `hasInstall` | `boolean` | Key should be installed |
| `hasAck` | `boolean` | From authenticator (AP) |
| `hasMic` | `boolean` | MIC field is valid |
| `isSecure` | `boolean` | Secure association established |
| `hasError` | `boolean` | Error flag |
| `isRequest` | `boolean` | Supplicant requests new key |
| `isEncrypted` | `boolean` | Key data is encrypted |
| `isSmk` | `boolean` | TDLS SMK message |

### Handshake Detection

| Property | Type | Description |
|---|---|---|
| `isMessage1` | `boolean` | Message 1 of 4-way handshake |
| `isMessage2` | `boolean` | Message 2 of 4-way handshake |
| `isMessage3` | `boolean` | Message 3 of 4-way handshake |
| `isMessage4` | `boolean` | Message 4 of 4-way handshake |
| `messageNumber` | `1 \| 2 \| 3 \| 4 \| undefined` | Detected message number |
| `handshakeType` | `string` | `'four-way'`, `'group-key'`, or `'unknown'` |

### Parsed Information Elements

| Property | Type | Description |
|---|---|---|
| `rsn` | `EAPOL.Rsn \| undefined` | RSN IE (WPA2/WPA3) |
| `wpa` | `EAPOL.Wpa \| undefined` | Legacy WPA IE |
| `ft` | `Partial<EAPOL.Ft> \| undefined` | Fast Transition IE (802.11r) |
| `mobilityDomain` | `EAPOL.MobilityDomain \| undefined` | Mobility Domain IE |
| `ies` | `EAPOL.Ie[]` | All parsed information elements |

### Parsed KDEs

| Property | Type | Description |
|---|---|---|
| `gtk` | `EAPOL.GtkKde \| undefined` | Group Temporal Key |
| `igtk` | `EAPOL.IgtkKde \| undefined` | Integrity Group Temporal Key (PMF) |
| `pmkid` | `EAPOL.PmkidKde \| undefined` | Pairwise Master Key Identifier |
| `macAddr` | `EAPOL.MacAddrKde \| undefined` | MAC Address (TDLS) |
| `smk` | `EAPOL.SmkKde \| undefined` | Station-to-Station Master Key |
| `nonce` | `EAPOL.NonceKde \| undefined` | Additional nonce |
| `lifetime` | `EAPOL.LifetimeKde \| undefined` | Key lifetime |
| `error` | `EAPOL.ErrorKde \| undefined` | Error information |
| `keyIdKde` | `EAPOL.KeyIdKde \| undefined` | Generic key identifier |
| `multibandGtk` | `EAPOL.MultibandGtkKde \| undefined` | Multiband GTK |
| `multibandKeyId` | `EAPOL.MultibandKeyIdKde \| undefined` | Multiband key identifier |
| `kdes` | `EAPOL.Kde[]` | All parsed KDEs |
| `encryptedKeyData` | `Buffer` | Raw encrypted key data (if encrypted) |

### Frame Methods

#### `validate()`

Validates the frame structure and content against IEEE 802.11 requirements.

**Returns:** `{ valid: boolean; errors: string[] }`

#### `isValidKeyDescriptorVersion()`

Checks if the Key Descriptor Version is valid for the Descriptor Type.

**Returns:** `boolean`

## Non-Goals

This library **does not**:

- Capture packets from network interfaces
- Implement cryptographic operations (PMK/PTK derivation, MIC calculation)
- Perform active attacks or password cracking
- Implement EAP authentication protocols
- Act as a RADIUS server or supplicant

It is purely a **parser and validator** for already-captured EAPOL frames.

## Use Cases

- **Wireless security analysis** - Parse captured handshakes
- **DPSK implementations** - Validate handshake frames
- **Network monitoring** - Extract handshake parameters
- **Research & education** - Understand WPA/WPA2/WPA3 protocols
- **Testing & validation** - Verify frame structure and compliance
- **Forensics** - Analyze captured Wi-Fi traffic

## Supported Standards

- IEEE 802.11i - RSN (WPA2)
- IEEE 802.11r - Fast Transition (FT)
- IEEE 802.11w - Protected Management Frames (PMF)
- IEEE 802.11ai - Fast Initial Link Setup (FILS)
- IEEE 802.11ac/ax - WPA3, GCMP ciphers
- Wi-Fi Alliance - WPA, WPA2, WPA3

## License

MIT

## Contributing

Issues and pull requests welcome at [github.com/gibme-npm/eapol](https://github.com/gibme-npm/eapol)

## Author

Brandon Lehmann <brandonlehmann@gmail.com>