83 lines
3.3 KiB
C++
83 lines
3.3 KiB
C++
/*
|
|
* Wpa.hpp
|
|
* WPA2/WPA3-PSK supplicant: PMK derivation and the EAPOL-Key 4-way
|
|
* handshake that unlocks the link after association.
|
|
*
|
|
* The supplicant lives in the kernel because the handshake sits between
|
|
* association and the first IP packet: nothing above the driver can send or
|
|
* receive until the pairwise key is installed in the firmware. It drives
|
|
* the exchange but does not touch the hardware itself -- transmitting and
|
|
* key installation are provided by the MLME through the three hooks at the
|
|
* bottom of this header.
|
|
*
|
|
* Copyright (c) 2026 Daniel Hammer
|
|
*/
|
|
|
|
#pragma once
|
|
#include <cstdint>
|
|
|
|
namespace Drivers::Net::Wifi {
|
|
|
|
enum class WpaState : uint8_t {
|
|
Idle = 0,
|
|
WaitMsg1, // associated, waiting for the AP to start the exchange
|
|
WaitMsg3, // msg 2 sent, waiting for the GTK
|
|
Complete, // keys installed, link is usable
|
|
Failed,
|
|
};
|
|
|
|
struct WpaConfig {
|
|
uint8_t OwnMac[6];
|
|
uint8_t Bssid[6];
|
|
uint8_t Ssid[32];
|
|
uint8_t SsidLen;
|
|
char Passphrase[64];
|
|
uint8_t PassLen;
|
|
uint8_t Akm; // RSN_AKM_*
|
|
uint8_t PairwiseCipher; // RSN_CIPHER_*
|
|
uint8_t GroupCipher;
|
|
bool Mfp; // management frame protection negotiated
|
|
};
|
|
|
|
// Derive the PMK and arm the handshake. Returns false when the
|
|
// configuration names a cipher or AKM this supplicant cannot do.
|
|
bool WpaStart(const WpaConfig& cfg);
|
|
void WpaReset();
|
|
|
|
// Feed the 802.1X payload of an inbound EAPOL frame (everything after the
|
|
// LLC/SNAP header). Returns true when the frame was consumed.
|
|
bool WpaOnEapol(const uint8_t* data, uint32_t len);
|
|
|
|
// Re-send the last outbound message if the AP has gone quiet, and fail the
|
|
// handshake once it has been silent for too long. Called from the idle
|
|
// loop; `nowMs` is a monotonic millisecond clock.
|
|
void WpaService(uint64_t nowMs);
|
|
|
|
WpaState WpaGetState();
|
|
bool WpaIsComplete();
|
|
|
|
// Build the RSN information element advertising what WpaStart() was
|
|
// configured with. Returns the number of bytes written, 0 on error.
|
|
uint32_t WpaBuildRsnIe(uint8_t* out, uint32_t cap);
|
|
|
|
// Pick the pairwise/group cipher and AKM out of an AP's RSN IE. `ie`
|
|
// points at the element body (after id/len). Returns false when nothing
|
|
// in the IE is supported.
|
|
bool WpaParseApRsn(const uint8_t* ie, uint32_t len, WpaConfig& cfg);
|
|
|
|
// -------------------------------------------------------------------------
|
|
// Hooks implemented by the MLME (IwxConnect.cpp)
|
|
// -------------------------------------------------------------------------
|
|
|
|
// Transmit an EAPOL frame body (802.1X header included) to the AP.
|
|
bool WpaTxEapol(const uint8_t* body, uint32_t len);
|
|
|
|
// Install the pairwise temporal key. `cipher` is an RSN_CIPHER_* value.
|
|
bool WpaInstallPtk(const uint8_t* tk, uint32_t tkLen, uint8_t cipher);
|
|
|
|
// Install a group temporal key at `keyIdx`. `rsc` is the EAPOL key RSC
|
|
// field: 8 bytes, of which the low 6 are the AP's packet number.
|
|
bool WpaInstallGtk(const uint8_t* gtk, uint32_t gtkLen, uint8_t keyIdx,
|
|
uint8_t cipher, const uint8_t* rsc);
|
|
}
|