Politician 1.0.0
WiFi Auditing Library for ESP32
Loading...
Searching...
No Matches
PoliticianStorage.h
Go to the documentation of this file.
1#pragma once
2
3#include <Arduino.h>
4#include <FS.h>
5#include <Preferences.h>
6#include "Politician.h"
7#include "PoliticianFormat.h"
8
9namespace politician {
10namespace storage {
11
12/**
13 * @brief Helper for writing HandshakeRecords to a standard PCAPNG file.
14 */
16public:
17 /**
18 * @brief Appends a HandshakeRecord to a file as PCAPNG.
19 * If the file is empty, it automatically writes the global PCAPNG Header Block first.
20 *
21 * @param fs The filesystem (e.g., SD, LittleFS)
22 * @param path The path to the file (e.g., "/captures.pcapng")
23 * @param rec The HandshakeRecord to write
24 * @return true if successful, false if file could not be opened
25 */
26 static bool append(fs::FS &fs, const char* path, const HandshakeRecord& rec) {
27 bool isNew = !fs.exists(path);
28 if (!isNew) {
29 fs::File check = fs.open(path, FILE_READ);
30 if (check) {
31 isNew = (check.size() == 0);
32 check.close();
33 }
34 }
35
36 fs::File file = fs.open(path, FILE_APPEND);
37 if (!file) return false;
38
39 if (isNew) {
40 uint8_t hdr[48];
41 size_t hl = format::writePcapngGlobalHeader(hdr);
42 file.write(hdr, hl);
43 }
44
45 uint8_t buf[512];
46 size_t len = format::writePcapngRecord(rec, buf, sizeof(buf));
47 if (len > 0) {
48 file.write(buf, len);
49 file.flush();
50 }
51 file.close();
52 return true;
53 }
54
55 /**
56 * @brief Appends a raw 802.11 sniffer frame to a PCAPNG file (used for intel gathering).
57 *
58 * @param fs The filesystem (e.g., SD, LittleFS)
59 * @param path The path to the file (e.g., "/intel.pcapng")
60 * @param payload Raw packet data
61 * @param len Packet length
62 * @param rssi Signal strength
63 * @param ts_usec Hardware microsecond timestamp
64 * @return true if successful, false otherwise
65 */
66 static bool appendPacket(fs::FS &fs, const char* path, const uint8_t* payload, uint16_t len, int8_t rssi, uint32_t ts_usec) {
67 bool isNew = !fs.exists(path);
68 if (!isNew) {
69 fs::File check = fs.open(path, FILE_READ);
70 if (check) {
71 isNew = (check.size() == 0);
72 check.close();
73 }
74 }
75
76 fs::File file = fs.open(path, FILE_APPEND);
77 if (!file) return false;
78
79 if (isNew) {
80 uint8_t hdr[48];
81 size_t hl = format::writePcapngGlobalHeader(hdr);
82 file.write(hdr, hl);
83 }
84
85 uint8_t buf[2500]; // Max 802.11 frame is 2346 bytes
86 size_t wlen = format::writePcapngPacket(payload, len, rssi, ts_usec, buf, sizeof(buf));
87 if (wlen > 0) {
88 file.write(buf, wlen);
89 file.flush();
90 }
91 file.close();
92 return true;
93 }
94};
95
96/**
97 * @brief Helper for writing HandshakeRecords to an HC22000 text file.
98 */
100public:
101 /**
102 * @brief Appends a HandshakeRecord to a file as an HC22000 string.
103 *
104 * @param fs The filesystem (e.g., SD, LittleFS)
105 * @param path The path to the file (e.g., "/captures.22000")
106 * @param rec The HandshakeRecord to write
107 * @return true if successful, false if file could not be opened
108 */
109 static bool append(fs::FS &fs, const char* path, const HandshakeRecord& rec) {
110 fs::File file = fs.open(path, FILE_APPEND);
111 if (!file) return false;
112
113 String str = format::toHC22000(rec);
114 if (str.length() > 0) {
115 file.println(str);
116 file.flush();
117 }
118 file.close();
119 return true;
120 }
121};
122
123/**
124 * @brief Helper for writing precise GPS location coordinates to a Wigle.net compatible CSV file.
125 *
126 * Wigle.net has a strict CSV format starting with a specific header:
127 * MAC,SSID,AuthMode,FirstSeen,Channel,RSSI,CurrentLatitude,CurrentLongitude,AltitudeMeters,AccuracyMeters,Type
128 */
130public:
131 /**
132 * @brief Appends a HandshakeRecord's details alongside GPS coordinates to a Wigle CSV.
133 *
134 * @param fs The filesystem (e.g., SD, LittleFS)
135 * @param path The path to the file (e.g., "/wardrive.csv")
136 * @param rec The captured HandshakeRecord
137 * @param lat Current GPS Latitude
138 * @param lon Current GPS Longitude
139 * @param alt (Optional) Current GPS Altitude in meters
140 * @param acc (Optional) GPS Accuracy radius in meters
141 * @return true if successful, false if file could not be opened
142 */
143 static bool append(fs::FS &fs, const char* path, const HandshakeRecord& rec,
144 float lat, float lon, float alt = 0.0, float acc = 10.0) {
145 bool isNew = !fs.exists(path);
146 if (!isNew) {
147 fs::File check = fs.open(path, FILE_READ);
148 if (check) {
149 isNew = (check.size() == 0);
150 check.close();
151 }
152 }
153
154 fs::File file = fs.open(path, FILE_APPEND);
155 if (!file) return false;
156
157 if (isNew) {
158 // Wigle.net standard header
159 file.println("WigleWifi-1.4,appRelease=1.0,model=Politician,release=1.0,device=ESP32,display=1.0,board=ESP32,brand=Espressif");
160 file.println("MAC,SSID,AuthMode,FirstSeen,Channel,RSSI,CurrentLatitude,CurrentLongitude,AltitudeMeters,AccuracyMeters,Type");
161 }
162
163 // Since this logger is fired specifically from the `onHandshake` callback,
164 // it is mathematically guaranteed that the network is at least WPA2/WPA3.
165 // Open and WEP networks do not generate 4-way EAPOL or PMKID frames.
166 const char* authStr = "[WPA2-PSK-CCMP][ESS]";
167
168 // We use a simplified FirstSeen format since we don't naturally have an RTC attached, but Wigle accepts standard SQL datetimes.
169 // E.g., "1970-01-01 00:00:00" - We leave time generalized.
170 char line[256];
171 snprintf(line, sizeof(line), "%02X:%02X:%02X:%02X:%02X:%02X,%s,%s,1970-01-01 00:00:00,%d,%d,%.6f,%.6f,%.1f,%.1f,WIFI",
172 rec.bssid[0], rec.bssid[1], rec.bssid[2], rec.bssid[3], rec.bssid[4], rec.bssid[5],
173 rec.ssid, authStr, rec.channel, rec.rssi, lat, lon, alt, acc);
174
175 file.println(line);
176 file.flush();
177 file.close();
178 return true;
179 }
180};
181
182/**
183 * @brief Helper for logging harvested 802.1X Enterprise Credentials.
184 * It writes a Clean CSV file containing BSSID, Client, and Plaintext Identity.
185 */
187public:
188 static bool append(fs::FS &fs, const char *path, const EapIdentityRecord &rec) {
189 bool isNew = !fs.exists(path);
190
191 fs::File file = fs.open(path, FILE_APPEND);
192 if (!file) return false;
193
194 if (isNew) {
195 file.println("Enterprise BSSID,Client MAC,Plaintext Identity,Channel,RSSI");
196 }
197
198 char line[128];
199 snprintf(line, sizeof(line), "%02X:%02X:%02X:%02X:%02X:%02X,%02X:%02X:%02X:%02X:%02X:%02X,%s,%d,%d",
200 rec.bssid[0], rec.bssid[1], rec.bssid[2], rec.bssid[3], rec.bssid[4], rec.bssid[5],
201 rec.client[0], rec.client[1], rec.client[2], rec.client[3], rec.client[4], rec.client[5],
202 rec.identity, rec.channel, rec.rssi);
203
204 file.println(line);
205 file.close();
206 return true;
207 }
208};
209
210/**
211 * @brief Helper for persistently storing captured BSSIDs in NVS memory.
212 * This ensures that previously captured networks aren't attacked again after a reboot.
213 */
215private:
216 Preferences _prefs;
217 String _ns;
218 static const int MAX_STORED = 128;
219 uint8_t _cache[MAX_STORED][6];
220 size_t _count;
221
222public:
223 NvsBssidCache(const char* ns = "wardrive") : _ns(ns), _count(0) {
224 memset(_cache, 0, sizeof(_cache));
225 }
226
227 /**
228 * @brief Initializes the NVS memory and loads the cached BSSIDs into RAM.
229 */
230 void begin() {
231 _prefs.begin(_ns.c_str(), false);
232 size_t bytes = _prefs.getBytes("bssids", _cache, sizeof(_cache));
233 _count = bytes / 6;
234 if (_count > MAX_STORED) _count = MAX_STORED; // Safety parameter
235 }
236
237 /**
238 * @brief Feeds the loaded BSSIDs into the Politician engine so it knows to ignore them.
239 * @param engine Reference to your active Politician instance
240 */
242 for (size_t i = 0; i < _count; i++) {
243 engine.markCaptured(_cache[i]);
244 }
245 }
246
247 /**
248 * @brief Adds a newly captured BSSID to the cache and saves it to NVS.
249 * @param bssid The 6-byte BSSID to save.
250 * @return true if added, false if it already exists or the cache is full.
251 */
252 bool add(const uint8_t* bssid) {
253 for (size_t i = 0; i < _count; i++) {
254 if (memcmp(_cache[i], bssid, 6) == 0) return false; // Already cached
255 }
256 if (_count >= MAX_STORED) return false; // Cache full
257
258 memcpy(_cache[_count], bssid, 6);
259 _count++;
260
261 // Save entire updated array to NVS
262 _prefs.putBytes("bssids", _cache, _count * 6);
263 return true;
264 }
265
266 /**
267 * @brief Clears the entire cache from NVS.
268 */
269 void clear() {
270 _count = 0;
271 _prefs.remove("bssids");
272 }
273};
274
275} // namespace storage
276} // namespace politician
The core WiFi handshake capturing engine.
Definition Politician.h:89
Helper for logging harvested 802.1X Enterprise Credentials.
static bool append(fs::FS &fs, const char *path, const EapIdentityRecord &rec)
Helper for writing HandshakeRecords to an HC22000 text file.
static bool append(fs::FS &fs, const char *path, const HandshakeRecord &rec)
Appends a HandshakeRecord to a file as an HC22000 string.
Helper for persistently storing captured BSSIDs in NVS memory.
NvsBssidCache(const char *ns="wardrive")
bool add(const uint8_t *bssid)
Adds a newly captured BSSID to the cache and saves it to NVS.
void loadInto(Politician &engine)
Feeds the loaded BSSIDs into the Politician engine so it knows to ignore them.
void begin()
Initializes the NVS memory and loads the cached BSSIDs into RAM.
void clear()
Clears the entire cache from NVS.
Helper for writing HandshakeRecords to a standard PCAPNG file.
static bool append(fs::FS &fs, const char *path, const HandshakeRecord &rec)
Appends a HandshakeRecord to a file as PCAPNG.
static bool appendPacket(fs::FS &fs, const char *path, const uint8_t *payload, uint16_t len, int8_t rssi, uint32_t ts_usec)
Appends a raw 802.11 sniffer frame to a PCAPNG file (used for intel gathering).
Helper for writing precise GPS location coordinates to a Wigle.net compatible CSV file.
static bool append(fs::FS &fs, const char *path, const HandshakeRecord &rec, float lat, float lon, float alt=0.0, float acc=10.0)
Appends a HandshakeRecord's details alongside GPS coordinates to a Wigle CSV.
Politician engine
Definition main.cpp:6
size_t writePcapngPacket(const uint8_t *payload, size_t payload_len, int8_t rssi, uint32_t ts_usec, uint8_t *buffer, size_t max_len)
Serializes a Raw 802.11 Frame into a PCAPNG Enhanced Packet Block.
String toHC22000(const HandshakeRecord &rec)
Converts a captured HandshakeRecord into the Hashcat 22000 (hc22000) text format.
size_t writePcapngRecord(const HandshakeRecord &rec, uint8_t *buffer, size_t max_len)
Serializes a HandshakeRecord into PCAPNG Enhanced Packet Blocks.
size_t writePcapngGlobalHeader(uint8_t *buffer)
Writes a PCAPNG Global Header (SHB + IDB).