Subversion Repositories ESP8266_P1_Meter

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 raymond 1
#ifndef CREDENTIAL_MANAGER_H
2
#define CREDENTIAL_MANAGER_H
3
 
4
#include <Arduino.h>
5
#include <IPAddress.h>
6
#include <vector>
7
#include <cstring>
8
 
9
#if defined(ESP32)
10
    #include <mbedtls/aes.h>
11
    #include "esp_efuse.h"
12
    #include "nvs_flash.h"
13
#elif defined(ESP8266)
14
    // Use local compatibility shim for AES on ESP8266 (BearSSL-backed)
15
    #include "compat/mbedtls_aes.h"
16
    #include "LittleFS.h"
17
#endif
18
 
19
/**
20
 * @brief Default encryption key (32 bytes for AES-256)
21
 * 
22
 * IMPORTANT: Modify this with your own 32-byte key!
23
 * ESP32: This is used as fallback if BLOCK_KEY0 is not programmed
24
 * ESP8266: This is always used (no eFuse available)
25
 * 
26
 * Example generation:
27
 * $ python3 -c "import os; key = os.urandom(32); print('0x' + ',0x'.join(f'{b:02x}' for b in key))"
28
 */
29
#ifndef CREDENTIAL_MANAGER_ENCRYPTION_KEY
30
#define CREDENTIAL_MANAGER_ENCRYPTION_KEY \
31
    0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, \
32
    0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99
33
#endif
34
 
35
/**
36
 * @class CredentialManager
37
 * @brief Manages WiFi credentials with transparent AES-256 encryption
38
 * 
39
 * Features:
40
 * - Automatic encryption with AES-256-CBC
41
 * - ESP32: Support for BLOCK_KEY0 (eFuse) with secure fallback
42
 * - ESP8266: User-defined encryption key (no eFuse available)
43
 * - Persistent storage in NVS (ESP32) or SPIFFS (ESP8266)
44
 * - Multiple SSID/password management
45
 * - Transparent: works seamlessly across platforms
46
 * - Passwords always encrypted in memory and storage
47
 */
48
 
49
struct WiFiCredential {
50
    char ssid[33];                  // Max 32 char SSID + null
51
    uint8_t pwd_encrypted[64];      // Encrypted password
52
    uint16_t pwd_len;               // Length of encrypted data
53
    IPAddress gateway;              // Optional static IP config
54
    IPAddress subnet;               // Optional static IP config    
55
    IPAddress local_ip;             // Optional static IP config
56
    IPAddress dns1;                 // Optional primary DNS (per SSID)
57
    IPAddress dns2;                 // Optional secondary DNS (per SSID)
58
};
59
 
60
class CredentialManager {
61
public:
62
    friend class FSWebServer;
63
    static const uint8_t MAX_CREDENTIALS = 5;
64
    static const uint8_t ENCRYPTION_KEY_SIZE = 32; // 256-bit AES
65
    static const uint16_t AES_BLOCK_SIZE = 16;
66
 
67
    /**
68
     * @brief Constructor - initializes the manager
69
     */
70
    CredentialManager();
71
 
72
    /**
73
     * @brief Destructor - frees resources
74
     */
75
    ~CredentialManager();
76
 
77
    /**
78
     * @brief Initialize encryption key from eFuse (ESP32) or predefined constant (ESP8266)
79
     * @return true if successful, false on error
80
     */
81
    bool begin();
82
 
83
    /**
84
     * @brief Return encryption security status
85
     * @return true if encryption is properly configured
86
     */
87
    bool isSecure() const { return m_efuse_initialized; }
88
 
89
    /**
90
     * @brief Return status description
91
     * @return String with security status and platform info
92
     */
93
    String getStatus() const;
94
 
95
    /**
96
     * @brief Add a WiFi credential (automatically encrypted)
97
     * @param credential WiFiCredential struct containing SSID and optional IP config
98
     * @param plaintext_password Plaintext password (will be encrypted internally)
99
     * @return true if successfully added
100
     */
101
    bool addCredential(const WiFiCredential& credential, const char* plaintext_password);
102
 
103
    /**
104
     * @brief Update a WiFi credential (automatically encrypted)
105
     * @param credential WiFiCredential struct with IP config and SSID to identify
106
     * @param plaintext_password Plaintext password (will be encrypted internally)
107
     * @return true if successfully updated
108
     */
109
    bool updateCredential(const WiFiCredential& credential, const char* plaintext_password);
110
 
111
    /**
112
     * @brief Get number of stored credentials
113
     * @return Credential count
114
     */
115
    uint8_t getCredentialCount() const { return m_credentials.size(); }
116
 
117
    /**
118
     * @brief Get SSID of a credential
119
     * @param index Credential index
120
     * @return SSID (nullptr if index invalid)
121
     */
122
    const char* getSSID(uint8_t index) const;
123
 
124
    /**
125
     * @brief Get decrypted password of a credential
126
     * @param index Credential index
127
     * @return Password in plaintext (automatic removal from memory after use)
128
     */
129
    String getPassword(uint8_t index);
130
 
131
    String getPassword(const char* ssid) {
132
        for (size_t i = 0; i < m_credentials.size(); i++) {
133
            if (strcmp(m_credentials[i].ssid, ssid) == 0) {
134
                return getPassword(i);
135
            }
136
        }
137
        return "";
138
    }
139
 
140
 
141
    /**
142
     * @brief Set static IP configuration for a credential
143
     * @param index Credential index
144
     * @param ip Local IP address (0.0.0.0 for DHCP)
145
     * @param gateway Gateway IP address
146
     * @param subnet Subnet mask
147
     * @return true if successfully set
148
     */
149
    bool setIPConfiguration(uint8_t index, IPAddress ip, IPAddress gateway, IPAddress subnet);
150
 
151
    /**
152
     * @brief Get static IP configuration for a credential
153
     * @param index Credential index
154
     * @param ip Output: Local IP address
155
     * @param gateway Output: Gateway IP address
156
     * @param subnet Output: Subnet mask
157
     * @return true if valid configuration exists
158
     */
159
    bool getIPConfiguration(uint8_t index, IPAddress& ip, IPAddress& gateway, IPAddress& subnet) const;
160
 
161
    /**
162
     * @brief Clear all credentials from memory
163
     */
164
    void clearAll();
165
 
166
    /**
167
     * @brief Remove a single credential by index
168
     * @param index Credential index
169
     * @return true if removed
170
     */
171
    bool removeCredential(uint8_t index);
172
 
173
    /**
174
     * @brief Remove a single credential by SSID
175
     * @param ssid Network SSID
176
     * @return true if removed
177
     */
178
    bool removeCredential(const char* ssid);
179
 
180
    #if defined(ESP8266)
181
    /**
182
     * @brief Set filesystem reference for persistence (ESP8266/ESP32)
183
     * @param fs Pointer to FS object (LittleFS or SPIFFS)
184
     * @note Required for saveToFS() and loadFromFS() to work on ESP8266
185
     */
186
    void setFilesystem(fs::FS* fs);
187
 
188
 
189
    /**
190
     * @brief Save credentials to filesystem (ESP8266/SPIFFS)
191
     * @param filepath Path to save file
192
     * @return true if successful
193
     */
194
    bool saveToFS(const char* filepath = "/credentials.bin");
195
 
196
    /**
197
     * @brief Load credentials from filesystem (ESP8266/SPIFFS)
198
     * @param filepath Path to load file from
199
     * @return true if at least one credential loaded
200
     */
201
    bool loadFromFS(const char* filepath = "/credentials.bin");
202
    #endif
203
 
204
    #if defined(ESP32)
205
    /**
206
     * @brief Save credentials to NVS (ESP32 only)
207
     * @param nvs_namespace NVS namespace to use
208
     * @return true if successful
209
     */
210
    bool saveToNVS(const char* nvs_namespace = "wifi_creds");
211
 
212
    /**
213
     * @brief Load credentials from NVS (ESP32 only)
214
     * @param nvs_namespace NVS namespace to use
215
     * @return true if at least one credential loaded
216
     */
217
    bool loadFromNVS(const char* nvs_namespace = "wifi_creds");
218
    #endif
219
 
220
    /**
221
     * @brief Get BLOCK_KEY0 status details
222
     * @return Technical status details
223
     */
224
    String getDebugInfo() const;
225
 
226
    std::vector<WiFiCredential>* getCredentials() {
227
        return &m_credentials;
228
    }
229
 
230
    bool checkSSIDExists(const char* ssid) const {
231
        for (const auto& cred : m_credentials) {
232
            if (strcmp(cred.ssid, ssid) == 0) {
233
                return true;
234
            }
235
        }
236
        return false;
237
    }
238
 
239
    /**
240
     * @brief Set HTTP webserver hostname (shared across libraries)
241
     */
242
    void setHostname(const char* hostname);
243
 
244
    /**
245
     * @brief Get HTTP webserver hostname (shared across libraries)
246
     */
247
    String getHostname() const;
248
 
249
    /**
250
     * @brief Set HTTP webserver port (shared across libraries)
251
     */
252
    // Port is fixed at construction time in AsyncFsWebServer, so it is not
253
    // exposed as a configurable shared option here.
254
 
255
protected:
256
    std::vector<WiFiCredential> m_credentials;
257
    uint8_t m_encryption_key[ENCRYPTION_KEY_SIZE];
258
    bool m_efuse_initialized;
259
 
260
    // Global options shared across libraries (hostname only)
261
    char m_hostname[33];            // HTTP server hostname (max 32 chars + null)
262
 
263
    #if defined(ESP8266)
264
    fs::FS* m_filesystem;
265
    #endif
266
 
267
    /**
268
     * @brief Load key from eFuse (ESP32) or predefined constant (ESP8266)
269
     */
270
    void initializeEncryptionKey();
271
 
272
    /**
273
     * @brief Encrypt password with AES-256-CBC
274
     * @param plaintext Password in plaintext
275
     * @param ciphertext Output encrypted buffer
276
     * @param cipher_len Output: length of encrypted data
277
     * @return true if successful
278
     */
279
    bool encryptPassword(const char* plaintext, uint8_t* ciphertext, uint16_t& cipher_len);
280
 
281
    /**
282
     * @brief Decrypt password with AES-256-CBC
283
     * @param ciphertext Encrypted data
284
     * @param cipher_len Length of encrypted data
285
     * @param plaintext Output password buffer
286
     * @param max_len Maximum buffer size
287
     * @return true if successful
288
     */
289
    bool decryptPassword(const uint8_t* ciphertext, uint16_t cipher_len,
290
                        char* plaintext, uint16_t max_len);
291
 
292
    /**
293
     * @brief Apply PKCS7 padding
294
     * @param data Input buffer
295
     * @param data_len Length of original data
296
     * @param padded Output buffer with padding
297
     * @param padded_len Output: total length with padding
298
     */
299
    void applyPKCS7Padding(const uint8_t* data, uint16_t data_len,
300
                           uint8_t* padded, uint16_t& padded_len);
301
 
302
    /**
303
     * @brief Remove PKCS7 padding
304
     * @param data Input buffer
305
     * @param data_len Buffer length
306
     * @return Length of data without padding (0 if error)
307
     */
308
    uint16_t removePKCS7Padding(uint8_t* data, uint16_t data_len);
309
};
310
 
311
#endif // CREDENTIAL_MANAGER_H