EmlaLockSafe
UpdateCertificatesView.h
Go to the documentation of this file.
1
5#pragma once
6
7#include "../configuration/HardwareConfiguration.h"
8
9#include <ESP8266WiFi.h>
10#include <LittleFS.h>
11#include <ViewBase.h>
12
13namespace views {
17class UpdateCertificatesView : public lcd::ViewBase {
18protected:
22 WiFiClientSecure& sslClient;
23
24protected:
28 BearSSL::CertStore certStore;
29
31
32protected:
34
35public:
43 UpdateCertificatesView(LiquidCrystal_PCF8574* display, WiFiClientSecure& sslClient, BearSSL::CertStore& certStore)
44 : lcd::ViewBase(display)
47
48public:
53
54public:
59
60public:
61 int begin() {
62 numberOfLoadedCertificates = certStore.initCertStore(LittleFS, PSTR("/certs.idx"), PSTR("/certs.ar"));
64 // no SSL certificates available
65 Serial.println("No SSL Certificates found in Flash.");
66 sslClient.setInsecure();
67 }
68 else {
69 Serial.print(numberOfLoadedCertificates);
70 Serial.println(" SSL Certificates found in Flash.");
71 sslClient.setCertStore(&certStore);
72 }
73
75 }
76
77protected:
81 virtual void activate() {
82 begin();
83 display->clear();
84 display->setCursor(0, 0);
85 display->print("Updating SSL Certs");
86
87 // download the csv file to the file-system
89 Serial.println(lastErrorMessage);
90 return;
91 }
92 sslClient.stopAll();
93
94 Serial.print("Free Heap: ");
95 Serial.print(ESP.getFreeHeap());
96 Serial.print(" Free Stack: ");
97 Serial.println(ESP.getFreeContStack());
98
99 // First iteration through csv file: Count certificates
101 File certsCsv = LittleFS.open("/certs.csv", "r");
102 int numberOfCertificates = 0;
103 while (true) {
104 // jump to the start of the next certificate
105 if (!goToStartOfNextCertificate(certsCsv)) {
106 break;
107 }
108 numberOfCertificates++;
109 }
110
111 // check if it makes sense to regenerate the archive
112 if (numberOfCertificates == 0) {
113 lastErrorMessage = "No certificates found in downloaded CSV file";
114 return;
115 }
116
117 Serial.print("Free Heap: ");
118 Serial.print(ESP.getFreeHeap());
119 Serial.print(" Free Stack: ");
120 Serial.println(ESP.getFreeContStack());
121 // go to the beginning of the CSV file to start parsing
122 certsCsv.seek(0);
123
124 // Second iteration through csv file: Convert to DER and save as archive
126 display->clear();
127 display->setCursor(0, 0);
128 display->print("Updating SSL Certs");
129 display->setCursor(0, 1);
130 display->print("Converting:");
131 display->setCursor(0, 2);
132 display->print("Certificate 0/");
133 display->print(numberOfCertificates);
134
135 File certsAr = LittleFS.open("/certs.tmp", "w");
136 certsAr.write("!<arch>\n");
137
138 for (int certificateCounter = 1; certificateCounter <= numberOfCertificates; certificateCounter++) {
139 display->setCursor(12, 2);
141 display->print("/");
142 display->print(numberOfCertificates);
143
144 if (!goToStartOfNextCertificate(certsCsv)) {
145 break;
146 }
147 if (!convertCertificate(certsCsv, certsAr)) {
148 break;
149 }
150 yield();
151 }
152
153 certsAr.close();
154 certsCsv.close();
155 Serial.print("Free Heap: ");
156 Serial.print(ESP.getFreeHeap());
157 Serial.print(" Free Stack: ");
158 Serial.println(ESP.getFreeContStack());
159
160 display->clear();
161 display->setCursor(0, 0);
162 display->print("Updating SSL Certs");
163 display->setCursor(0, 1);
164 display->print("Verifying...");
165
166 Serial.print("Free Heap: ");
167 Serial.print(ESP.getFreeHeap());
168 Serial.print(" Free Stack: ");
169 Serial.println(ESP.getFreeContStack());
170 auto numberOfLoadedCertificates = certStore.initCertStore(LittleFS, PSTR("/certst.idx"), PSTR("/certs.tmp"));
172 if (LittleFS.exists("/certs.ar")) {
173 LittleFS.remove("/certs.ar");
174 }
175 if (LittleFS.exists("/certs.idx")) {
176 LittleFS.remove("/certs.idx");
177 }
178 if (LittleFS.exists("/certst.idx")) {
179 LittleFS.remove("/certst.idx");
180 }
181 LittleFS.rename("/certs.tmp", "/certs.ar");
182 }
183
184 display->setCursor(0, 2);
185 display->print("OK. Going to Reboot");
186
187 delay(2000);
188 ESP.restart();
189 }
190
191protected:
198 void waitAnimation(std::function<bool()> stillActive) {
199 while (stillActive()) {
200 for (int i = 0; (i < 20) && stillActive(); i++) {
201 if (i == 0) {
202 display->setCursor(19, 3);
203 display->print(' ');
204 display->setCursor(i, 3);
205 display->print('*');
206 }
207 else {
208 display->setCursor(i - 1, 3);
209 display->print(" *");
210 }
211
212 delay(100);
213 }
214 }
215 }
216
217protected:
218 bool initializeConnection(const String& host, const String& url) {
219 // try to connect to the server
220 if (!sslClient.connect(host, 443)) {
221 lastErrorMessage = "Connection to https://" + host + "/" + url + " failed.";
222 return false;
223 }
224 yield();
225
226 // send get request
227 sslClient.print("GET /" + url +
228 " HTTP/1.1\r\n"
229 "Host: " +
230 host +
231 "\r\n"
232 "User-Agent: ESP8266\r\n"
233 "Connection: Keep-Alive\r\n\r\n");
234 yield();
235
236 // wait until the header is received.
237 String result = "Invalid Header received.";
238 while (sslClient.connected()) {
239 String line = sslClient.readStringUntil('\n');
240
241 if ((line.length() > 8) && (line.substring(0, 8) == "HTTP/1.1")) {
242 result = line.substring(9);
243 }
244
245 if (line == "\r") {
246 break;
247 }
248 }
249 yield();
250
251 // check result
252 result.trim();
253 if (result != "200 OK") {
254 lastErrorMessage = "Error Getting https://" + host + "/" + url + ": " + result;
255 sslClient.stopAll();
256 }
257 yield();
258 return true;
259 }
260
262 Serial.print("Free Heap: ");
263 Serial.print(ESP.getFreeHeap());
264 Serial.print(" Free Stack: ");
265 Serial.println(ESP.getFreeContStack());
266 display->setCursor(0, 1);
267 display->print("Connecting...");
268 if (!initializeConnection("ccadb-public.secure.force.com", "mozilla/IncludedCACertificateReportPEMCSV")) {
269 return false;
270 }
271 Serial.print("Free Heap: ");
272 Serial.print(ESP.getFreeHeap());
273 Serial.print(" Free Stack: ");
274 Serial.println(ESP.getFreeContStack());
275
276 File certsCsv = LittleFS.open("/certs.csv", "w");
277 char buf[100];
278 int blockCounter = 0;
279
280 display->setCursor(0, 1);
281 display->print("Downloading 0kb ");
282 while (true) {
283 size_t numBytesRead = sslClient.readBytes(buf, 100);
284 if (numBytesRead == 0) {
285 break;
286 }
287 certsCsv.write(buf, numBytesRead);
288
289 blockCounter++;
290 if (blockCounter % 100 == 0) {
291 display->setCursor(12, 1);
292 display->print(blockCounter / 10);
293 display->print("kb");
294 }
295
296 yield();
297 }
298 display->setCursor(0, 1);
299 display->print("CSV downloaded. ");
300 display->setCursor(0, 2);
301 display->print("Saving file.");
302 certsCsv.close();
303 Serial.print("Free Heap: ");
304 Serial.print(ESP.getFreeHeap());
305 Serial.print(" Free Stack: ");
306 Serial.println(ESP.getFreeContStack());
307
308 // cleanup client
309 sslClient.stopAll();
310 Serial.print("Free Heap: ");
311 Serial.print(ESP.getFreeHeap());
312 Serial.print(" Free Stack: ");
313 Serial.println(ESP.getFreeContStack());
314
315 return true;
316 }
317
318protected:
319 bool goToStartOfNextCertificate(File& certsCsv) {
320 const char* searchString = "-----BEGIN CERTIFICATE-----";
321 const char* searchChar = searchString;
322
323 while (searchChar != searchString + strlen(searchString)) {
324 uint8_t chr;
325 if (certsCsv.read(&chr, 1) != 1) {
326 return false;
327 }
328 // check if it is part of the search string
329 if (chr == *searchChar) {
330 searchChar++;
331 }
332 else {
333 searchChar = searchString;
334 }
335 }
336
337 return true;
338 }
339
340protected:
341 uint16_t certificateCounter = 0;
342
343 bool convertCertificate(File& certsCsv, File& certsAr) {
344 static const uint8_t base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
345 uint8_t chr;
346
347 // write header of file
348 char headerBuf[17];
349 strcpy(headerBuf, "ca_000.der/ ");
350 headerBuf[3] = (certificateCounter / 100) % 10 + 48;
351 headerBuf[4] = (certificateCounter / 10) % 10 + 48;
352 headerBuf[5] = certificateCounter % 10 + 48;
354 certsAr.write(headerBuf, 16);
355 // file modification date
356 certsAr.write("1596291962 ", 12);
357 // owner id
358 certsAr.write("0 ", 6);
359 // group ID
360 certsAr.write("0 ", 6);
361 // File mode (type and permission)
362 certsAr.write("100666 ", 8);
363 auto sizeStart = certsAr.position();
364 // File size in bytes
365 certsAr.write(" ", 10);
366 // Ending characters
367 headerBuf[0] = 0x60;
368 headerBuf[1] = 0x0A;
369 certsAr.write(headerBuf, 2);
370
371 // Parse, Convert and Write the actual certificate
372 uint16_t fileSize = 0;
373 while (true) {
374 // buffer for the converted bytes
375 uint8_t buf[3] = {0, 0, 0};
376 uint8_t parserInCounter = 0;
377 uint8_t parserOutCounter = 0;
378
379 // Try to get 4 base64 characters which convert into 3 binary characters
380 while (parserInCounter != 4) {
381 // read next character
382 if (certsCsv.read(&chr, 1) != 1) {
383 return false;
384 }
385
386 // check if this is part of the end certificate string
387 if (chr == '-') {
388 break;
389 }
390
391 // Is it a alignment character?
392 if (chr == '=') {
393 parserInCounter++;
394 continue;
395 }
396
397 // First part of conversion
398 uint8_t i;
399 for (i = 0; i < 64 && chr != base64[i]; i++) {
400 }
401
402 // Shift the bits of the conversion to the correct position
403 if (i != 64) {
404 switch (parserInCounter) {
405 case 0:
406 buf[0] = i << 2;
407 parserOutCounter = 1;
408 break;
409 case 1:
410 buf[0] += i >> 4;
411 buf[1] = i << 4;
412 if (buf[1] != 0) {
413 parserOutCounter = 2;
414 }
415 break;
416 case 2:
417 buf[1] += i >> 2;
418 buf[2] = i << 6;
419 parserOutCounter = 2;
420 if (buf[2] != 0) {
421 parserOutCounter = 3;
422 }
423 break;
424 case 3:
425 buf[2] += i;
426 parserOutCounter = 3;
427 break;
428 }
429 parserInCounter++;
430 }
431 }
432
433 // output the generated data
434 certsAr.write(buf, parserOutCounter);
435 fileSize += parserOutCounter;
436
437 // Check if the certificate was fully read.
438 if (chr == '-') {
439 break;
440 }
441 }
442
443 // check if the number of bytes is odd:
444 if (fileSize % 2 != 0) {
445 headerBuf[0] = 0x0A;
446 certsAr.write(headerBuf, 1);
447 }
448
449 // Save the file Size
450 certsAr.seek(sizeStart);
451 certsAr.print(fileSize);
452 certsAr.seek(0, fs::SeekEnd);
453
454 return true;
455 }
456
457public:
463 virtual void tick(const bool& forceRedraw) {}
464}; // namespace views
465} // namespace views
View displayed while updating the certificates.
Definition: UpdateCertificatesView.h:17
virtual void activate()
called as soon as the view becomes active
Definition: UpdateCertificatesView.h:81
BearSSL::CertStore certStore
Reference of the certificate store.
Definition: UpdateCertificatesView.h:28
bool goToStartOfNextCertificate(File &certsCsv)
Definition: UpdateCertificatesView.h:319
UpdateCertificatesView(LiquidCrystal_PCF8574 *display, WiFiClientSecure &sslClient, BearSSL::CertStore &certStore)
Construct a vie object.
Definition: UpdateCertificatesView.h:43
int numberOfLoadedCertificates
Definition: UpdateCertificatesView.h:33
WiFiClientSecure & sslClient
Reference of the ssl client used for the communication.
Definition: UpdateCertificatesView.h:22
UpdateCertificatesView(const UpdateCertificatesView &other)=delete
Copy constructor - not available.
bool downloadCertificatesAsCsv()
Definition: UpdateCertificatesView.h:261
bool convertCertificate(File &certsCsv, File &certsAr)
Definition: UpdateCertificatesView.h:343
uint16_t certificateCounter
Definition: UpdateCertificatesView.h:341
virtual void tick(const bool &forceRedraw)
called during the loop function
Definition: UpdateCertificatesView.h:463
UpdateCertificatesView(UpdateCertificatesView &&other) noexcept=delete
Move constructor - not available.
String lastErrorMessage
Definition: UpdateCertificatesView.h:30
void waitAnimation(std::function< bool()> stillActive)
Shows the animation while waiting until the callback returns false.
Definition: UpdateCertificatesView.h:198
bool initializeConnection(const String &host, const String &url)
Definition: UpdateCertificatesView.h:218
int begin()
Definition: UpdateCertificatesView.h:61
views::ConfigurationServerView configurationServerView & display
Definition: main.cpp:57
Definition: ConfigurationServerView.h:10