Politician 1.0.0
WiFi Auditing Library for ESP32
Loading...
Searching...
No Matches
Politician.h
Go to the documentation of this file.
1#pragma once
2#include <Arduino.h>
3#include <esp_wifi.h>
4#include <esp_wifi_types.h>
5#include "PoliticianTypes.h"
6
7namespace politician {
8
9#ifndef POLITICIAN_MAX_AP_CACHE
10#define POLITICIAN_MAX_AP_CACHE 48
11#endif
12
13#ifndef POLITICIAN_MAX_SESSIONS
14#define POLITICIAN_MAX_SESSIONS 8
15#endif
16
17#ifndef POLITICIAN_MAX_CAPTURED
18#define POLITICIAN_MAX_CAPTURED 64
19#endif
20
21#ifndef POLITICIAN_MAX_CHANNELS
22#define POLITICIAN_MAX_CHANNELS 50
23#endif
24
25// ─── 802.11 Frame Structures ──────────────────────────────────────────────────
26
27typedef struct {
28 uint16_t frame_ctrl;
29 uint16_t duration;
30 uint8_t addr1[6];
31 uint8_t addr2[6];
32 uint8_t addr3[6];
33 uint16_t seq_ctrl;
34} __attribute__((packed)) ieee80211_hdr_t;
35
36typedef struct {
37 ieee80211_hdr_t hdr;
38 uint8_t payload[0];
39} __attribute__((packed)) ieee80211_frame_t;
40
41// ─── Frame Control Masks ──────────────────────────────────────────────────────
42#define FC_TYPE_MASK 0x000C
43#define FC_SUBTYPE_MASK 0x00F0
44#define FC_TODS_MASK 0x0100
45#define FC_FROMDS_MASK 0x0200
46#define FC_TYPE_MGMT 0x0000
47#define FC_TYPE_CTRL 0x0004
48#define FC_TYPE_DATA 0x0008
49#define FC_ORDER_MASK 0x8000
50
51#define MGMT_SUB_ASSOC_REQ 0x00
52#define MGMT_SUB_ASSOC_RESP 0x10
53#define MGMT_SUB_PROBE_REQ 0x40
54#define MGMT_SUB_PROBE_RESP 0x50
55#define MGMT_SUB_BEACON 0x80
56#define MGMT_SUB_DEAUTH 0xC0
57
58// ─── EAPOL ────────────────────────────────────────────────────────────────────
59#define EAPOL_LLC_OFFSET 0
60#define EAPOL_ETHERTYPE_HI 0x88
61#define EAPOL_ETHERTYPE_LO 0x8E
62#define EAPOL_LLC_SIZE 8
63#define EAPOL_MIN_FRAME_LEN (EAPOL_LLC_SIZE + 4)
64
65#define EAPOL_KEY_DESC_TYPE 0
66#define EAPOL_KEY_INFO 1
67#define EAPOL_KEY_LEN 3
68#define EAPOL_REPLAY_COUNTER 5
69#define EAPOL_KEY_NONCE 13
70#define EAPOL_KEY_IV 45
71#define EAPOL_KEY_RSC 61
72#define EAPOL_KEY_ID 69
73#define EAPOL_KEY_MIC 77
74#define EAPOL_KEY_DATA_LEN 93
75#define EAPOL_KEY_DATA 95
76
77#define KEYINFO_TYPE_MASK 0x0007
78#define KEYINFO_PAIRWISE 0x0008
79#define KEYINFO_ACK 0x0080
80#define KEYINFO_MIC 0x0100
81#define KEYINFO_SECURE 0x0200
82#define KEYINFO_INSTALL 0x0040
83
84// ─── Politician (The Handshaker) ──────────────────────────────────────────────
85
86/**
87 * @brief The core WiFi handshake capturing engine.
88 */
90public:
91 Politician();
92
93 /**
94 * @brief Initializes the WiFi driver in promiscuous mode.
95 * @param cfg Optional configuration struct.
96 * @return OK on success, or an error code.
97 */
98 Error begin(const Config &cfg = Config());
99
100 /**
101 * @brief Sets a custom logging callback to intercept library output.
102 */
103 void setLogger(LogCb cb) { _logCb = cb; }
104
105 /**
106 * @brief Manually adds a BSSID to the "already captured" list to skip it.
107 */
108 void markCaptured(const uint8_t *bssid);
109
110 /**
111 * @brief Clears the captured BSSID list.
112 */
113 void clearCapturedList();
114
115 /**
116 * @brief Sets a list of BSSIDs that should always be ignored by the engine.
117 */
118 void setIgnoreList(const uint8_t (*bssids)[6], uint8_t count);
119
120 /**
121 * @brief Enables or disables frame processing.
122 */
123 void setActive(bool active);
124
125 /**
126 * @brief Manually sets the WiFi radio to a specific channel.
127 * @param ch Channel number (2.4GHz: 1-14, 5GHz: 36-165)
128 * @return OK on success, ERR_INVALID_CH if ch is invalid.
129 */
130 Error setChannel(uint8_t ch);
131
132 /**
133 * @brief Starts autonomous channel hopping.
134 * @param dwellMs Time in milliseconds to stay on each channel (0 = use config).
135 */
136 void startHopping(uint16_t dwellMs = 0);
137
138 /**
139 * @brief Stops autonomous channel hopping and goes idle.
140 */
141 void stopHopping();
142
143 /**
144 * @brief Stops hopping and locks the radio to a specific channel.
145 * @return OK on success, or an error code.
146 */
147 Error lockChannel(uint8_t ch);
148
149 /**
150 * @brief Restricts hopping to a specific list of channels.
151 * @param channels Array of channel numbers (2.4GHz: 1-14, 5GHz: 36-165)
152 * @param count Number of channels in array
153 */
154 void setChannelList(const uint8_t *channels, uint8_t count);
155
156 /**
157 * @brief Main worker method. Must be called frequently from loop().
158 */
159 void tick();
160
161 /**
162 * @brief Configures which attack techniques are enabled.
163 */
164 void setAttackMask(uint8_t mask);
165
166 /**
167 * @brief Focuses the engine on a single BSSID.
168 * @return OK on success, ERR_ALREADY_CAPTURED if BSSID is on the captured/ignore list.
169 */
170 Error setTarget(const uint8_t *bssid, uint8_t channel);
171
172 /**
173 * @brief Clears the specific target and resumes autonomous wardriving.
174 */
175 void clearTarget();
176
177 /** @return True if currently focusing on a specific target BSSID. */
178 bool hasTarget() const { return _hasTarget; }
179
180 /** @return The current operating channel. */
181 uint8_t getChannel() const { return _channel; }
182
183 /** @return True if the engine is currently processing frames. */
184 bool isActive() const { return _active; }
185
186 /** @return Signal strength (RSSI) of the last received frame. */
187 int8_t getLastRssi() const { return _lastRssi; }
188
189 /** @return Reference to the internal statistics counter. */
190 Stats& getStats() { return _stats; }
191
192 /** @return Reference to the internal configuration struct for runtime mutations. */
193 Config& getConfig() { return _cfg; }
194
195 using EapolCb = void (*)(const HandshakeRecord &rec);
196 using ApFoundCb = void (*)(const ApRecord &ap);
197 using FilterCb = bool (*)(const ApRecord &ap);
198 using PacketCb = void (*)(const uint8_t *payload, uint16_t len, int8_t rssi, uint32_t ts_usec);
199 using IdentityCb = void (*)(const EapIdentityRecord &rec);
200
201 /**
202 * @brief Sets the callback for when a handshake (EAPOL or PMKID) is captured.
203 */
204 void setEapolCallback(EapolCb cb) { _eapolCb = cb; }
205
206 /**
207 * @brief Sets the callback for when a new Access Point is discovered.
208 */
209 void setApFoundCallback(ApFoundCb cb) { _apFoundCb = cb; }
210
211 /**
212 * @brief Sets an early filter callback. If it returns false, the AP is ignored completely.
213 */
214 void setTargetFilter(FilterCb cb) { _filterCb = cb; }
215
216 /**
217 * @brief Sets the callback for raw promiscuous mode packets.
218 */
219 void setPacketLogger(PacketCb cb) { _packetCb = cb; }
220
221 /**
222 * @brief Sets the callback for passive 802.1X Enterprise Identity harvesting.
223 */
224 void setIdentityCallback(IdentityCb cb) { _identityCb = cb; }
225
226private:
227 static void IRAM_ATTR _promiscuousCb(void *buf, wifi_promiscuous_pkt_type_t type);
228 static Politician *_instance;
229
230 void _handleFrame(const wifi_promiscuous_pkt_t *pkt, wifi_promiscuous_pkt_type_t type);
231 void _handleMgmt(const ieee80211_hdr_t *hdr, const uint8_t *payload, uint16_t len, int8_t rssi);
232 void _handleData(const ieee80211_hdr_t *hdr, const uint8_t *payload, uint16_t len, int8_t rssi);
233 bool _parseEapol(const uint8_t *bssid, const uint8_t *sta,
234 const uint8_t *eapol, uint16_t len, int8_t rssi);
235 void _parseEapIdentity(const uint8_t *bssid, const uint8_t *sta,
236 const uint8_t *eapol, uint16_t len, int8_t rssi);
237 void _parseSsid(const uint8_t *ie, uint16_t ie_len, char *out, uint8_t &out_len);
238 uint8_t _classifyEnc(const uint8_t *ie, uint16_t ie_len);
239
240 bool _active;
241 uint8_t _channel;
242 uint8_t _rxChannel;
243 bool _hopping;
244 uint32_t _lastHopMs;
245 int8_t _lastRssi;
246 uint8_t _hopIndex;
247 uint8_t _attackMask;
248
249 bool _hasTarget;
250 uint8_t _targetBssid[6];
251 uint8_t _targetChannel;
252
253 bool _m1Locked;
254 uint32_t _m1LockEndMs;
255
256 bool _probeLocked;
257 uint32_t _probeLockEndMs;
258
259 uint8_t _customChannels[POLITICIAN_MAX_CHANNELS];
260 uint8_t _customChannelCount;
261 Config _cfg;
262 Stats _stats;
263
264 LogCb _logCb = nullptr;
265 ApFoundCb _apFoundCb = nullptr;
266 FilterCb _filterCb = nullptr;
267 EapolCb _eapolCb = nullptr;
268 PacketCb _packetCb = nullptr;
269 IdentityCb _identityCb = nullptr;
270
271 void _log(const char *fmt, ...);
272
273 static const int MAX_IGNORE = 128;
274 uint8_t _ignoreList[MAX_IGNORE][6];
275 uint8_t _ignoreCount;
276
277 static const int MAX_AP_CACHE = POLITICIAN_MAX_AP_CACHE;
278 struct ApCacheEntry {
279 bool active;
280 uint8_t bssid[6];
281 char ssid[33];
282 uint8_t ssid_len;
283 uint8_t enc;
284 uint8_t channel;
285 uint32_t last_probe_ms;
286 uint32_t last_stimulate_ms;
287 bool has_active_clients;
288 bool is_wpa3_only; // True if PMF is required and no WPA2 AKM offered
289 };
290 ApCacheEntry _apCache[MAX_AP_CACHE];
291 int _apCacheCount;
292
293 void _cacheAp(const uint8_t *bssid, const char *ssid, uint8_t ssid_len,
294 uint8_t enc, uint8_t channel, bool is_wpa3_only = false);
295 bool _lookupSsid(const uint8_t *bssid, char *out_ssid, uint8_t &out_len);
296
297 enum FishState : uint8_t { FISH_IDLE = 0, FISH_CONNECTING = 1, FISH_CSA_WAIT = 2 };
298 FishState _fishState;
299 uint32_t _fishStartMs;
300 uint8_t _fishBssid[6];
301 uint8_t _fishRetry;
302 char _fishSsid[33];
303 uint8_t _fishSsidLen;
304 uint8_t _fishChannel;
305 uint8_t _ownStaMac[6];
306 bool _fishAuthLogged;
307 bool _fishAssocLogged;
308 bool _csaSecondBurstSent;
309
310 void _startFishing(const uint8_t *bssid, const char *ssid,
311 uint8_t ssid_len, uint8_t channel);
312 void _processFishing();
313 void _randomizeMac();
314 void _sendCsaBurst();
315 void _sendDeauthBurst();
316 void _markCapturedSsidGroup(const char *ssid, uint8_t ssid_len);
317 void _markCaptured(const uint8_t *bssid);
318
319 static const int MAX_SESSIONS = POLITICIAN_MAX_SESSIONS;
320 struct Session {
321 bool active;
322 uint8_t bssid[6];
323 uint8_t sta[6];
324 char ssid[33];
325 uint8_t ssid_len;
326 uint8_t channel;
327 int8_t rssi;
328 uint8_t anonce[32];
329 uint8_t mic[16];
330 uint8_t eapol_m2[256];
331 uint16_t eapol_m2_len;
332 bool has_m1;
333 bool has_m2;
334 uint32_t created_ms;
335 };
336 Session _sessions[MAX_SESSIONS];
337
338 Session* _findSession(const uint8_t *bssid, const uint8_t *sta);
339 Session* _createSession(const uint8_t *bssid, const uint8_t *sta);
340 void _expireSessions(uint32_t timeoutMs);
341
342 static const int MAX_CAPTURED = POLITICIAN_MAX_CAPTURED;
343 struct CapturedEntry {
344 bool active;
345 uint8_t bssid[6];
346 };
347 CapturedEntry _captured[MAX_CAPTURED];
348 int _capturedCount;
349
350 bool _isCaptured(const uint8_t *bssid) const;
351
352 static const uint8_t HOP_SEQ[];
353 static const uint8_t HOP_COUNT;
354};
355
356} // namespace politician
#define POLITICIAN_MAX_CHANNELS
Definition Politician.h:22
#define POLITICIAN_MAX_AP_CACHE
Definition Politician.h:10
#define POLITICIAN_MAX_SESSIONS
Definition Politician.h:14
#define POLITICIAN_MAX_CAPTURED
Definition Politician.h:18
The core WiFi handshake capturing engine.
Definition Politician.h:89
Error lockChannel(uint8_t ch)
Stops hopping and locks the radio to a specific channel.
void clearCapturedList()
Clears the captured BSSID list.
void markCaptured(const uint8_t *bssid)
Manually adds a BSSID to the "already captured" list to skip it.
void tick()
Main worker method.
int8_t getLastRssi() const
Definition Politician.h:187
void(*)(const uint8_t *payload, uint16_t len, int8_t rssi, uint32_t ts_usec) PacketCb
Definition Politician.h:198
bool(*)(const ApRecord &ap) FilterCb
Definition Politician.h:197
void setPacketLogger(PacketCb cb)
Sets the callback for raw promiscuous mode packets.
Definition Politician.h:219
void setLogger(LogCb cb)
Sets a custom logging callback to intercept library output.
Definition Politician.h:103
void(*)(const HandshakeRecord &rec) EapolCb
Definition Politician.h:195
void(*)(const ApRecord &ap) ApFoundCb
Definition Politician.h:196
void setActive(bool active)
Enables or disables frame processing.
void setApFoundCallback(ApFoundCb cb)
Sets the callback for when a new Access Point is discovered.
Definition Politician.h:209
uint8_t getChannel() const
Definition Politician.h:181
void(*)(const EapIdentityRecord &rec) IdentityCb
Definition Politician.h:199
void stopHopping()
Stops autonomous channel hopping and goes idle.
void setIgnoreList(const uint8_t(*bssids)[6], uint8_t count)
Sets a list of BSSIDs that should always be ignored by the engine.
void setEapolCallback(EapolCb cb)
Sets the callback for when a handshake (EAPOL or PMKID) is captured.
Definition Politician.h:204
Error setTarget(const uint8_t *bssid, uint8_t channel)
Focuses the engine on a single BSSID.
bool hasTarget() const
Definition Politician.h:178
void setIdentityCallback(IdentityCb cb)
Sets the callback for passive 802.1X Enterprise Identity harvesting.
Definition Politician.h:224
Error setChannel(uint8_t ch)
Manually sets the WiFi radio to a specific channel.
void clearTarget()
Clears the specific target and resumes autonomous wardriving.
Error begin(const Config &cfg=Config())
Initializes the WiFi driver in promiscuous mode.
bool isActive() const
Definition Politician.h:184
void startHopping(uint16_t dwellMs=0)
Starts autonomous channel hopping.
void setAttackMask(uint8_t mask)
Configures which attack techniques are enabled.
void setTargetFilter(FilterCb cb)
Sets an early filter callback.
Definition Politician.h:214
void setChannelList(const uint8_t *channels, uint8_t count)
Restricts hopping to a specific list of channels.
void(* LogCb)(const char *msg)
ieee80211_hdr_t hdr
Definition Politician.h:37
Configuration for the Politician engine.