Skip to content

wpa::eapol

The EAPOL-Key frame: the wire format of the 4-way handshake.

Every message of the handshake is an EAPOL-Key frame — an 802.1X header followed by a fixed 95-byte key body and a variable key-data trailer. This module reads a received frame, identifies which of the four messages it is from its key_info flags, verifies its MIC, and builds the station’s two replies. It is pure byte-handling over crypto; the state machine that decides what to do lives in [super::handshake].

Layout and flag values are transcribed from esp-idf’s wpa_common.h / wpa.c — the reference the blobs on the other end were built against — rather than from a reading of the standard, for the same reason [super]’s crypto matches its vectors: interoperation is the whole point.

struct EapolKey<'a> { /* private fields */ }

A read-only view over a received EAPOL-Key frame.

Borrows the buffer; the accessors read fields by offset. Construct with parse, which bounds-checks the length once so the accessors need not.

enum Message {
One,
Three,
Other,
}

Which of the four handshake messages a frame is.

Implements: Clone, Copy, Debug, Eq, PartialEq, StructuralPartialEq

fn build_reply(version: u16, kck: &[u8], reply_info: u16, replay_counter: &[u8; 8], nonce: &[u8; 32], key_data: &[u8], out: &mut [u8]) -> Option<usize>

Build the station’s reply (message 2 or 4) into out, returning its length.

reply_info is the key_info for the reply — the caller sets the flags per the message. nonce is the SNonce for message 2, ignored (send zeros) for message 4. key_data is the station’s RSN IE for message 2, empty for message 4. The MIC is computed last, over the finished frame.

fn compute_mic(version: u16, kck: &[u8], frame: &[u8]) -> Option<[u8; 16]>

Compute the MIC over frame under kck for descriptor version.

The MIC field in the frame is treated as zero for the computation, whatever it currently holds — so this works both to verify a received frame (its MIC is present) and to fill in an outgoing one (its MIC is still zero).

fn flag_version_sha1() -> u16

Descriptor version 2 — HMAC-SHA1 MIC, AES-key-wrapped key data. The version WPA2-CCMP uses, and the only one the handshake here handles.

fn info_is_secure(key_info: u16) -> bool

Whether a frame’s key_info has the SECURE bit — carried into message 4.

fn reply_info_msg2(version: u16) -> u16

The key_info for message 2: version, pairwise, MIC.

fn reply_info_msg4(version: u16, msg3_secure: bool) -> u16

The key_info for message 4: version, pairwise, MIC, and SECURE carried over from message 3.

const EAPOL_HDR_LEN: usize;

The 802.1X header: version, type, body length. Precedes the key body.

const EAPOL_TYPE_KEY: u8;

EAPOL packet type 3 — an EAPOL-Key frame.

const KEY_BODY_LEN: usize;

The fixed part of the EAPOL-Key body, before the variable key data.

const KEY_DESC_RSN: u8;

Key descriptor type 2 — RSN (WPA2).

const MIC_LEN: usize;

The MIC is 16 bytes for every descriptor version handled here.

const MIN_FRAME_LEN: usize;

The smallest a valid EAPOL-Key frame can be: header + fixed body.

const NONCE_LEN: usize;

A nonce is 32 bytes.