Skip to content

Commit 794630e

Browse files
Remove dependency on SD/SPIFFS from CertStore (#4760)
Due to popular demand, remove the hardcoded dependency on SPIFFS or SD from the CertStore by factoring out the file interface into a new class (CertStoreFile) that the user will need to implement as a thin wrapper around either a SPIFFS.file or a SD.file Combine the downloaded certificates into a UNIX "ar" archive and parse that on-the-fly to allow easy inspection and creation of the Cert Store database. Examples updated with a new certificate downloader that creates the certs.ar archive and with a single sample that can be built for either SPIFFS or SD with a #define. Users can copy the implementation of the CertStoreFile they need to their own code as it is self-contained. Also move the CertStore to the BearSSL namespace and remove the suffix and separate SPIFFS/SD sources. Remove the "deep+" change from the CI build as well (no special options needed on any PIO or makefile build). We'll revisit the filesystem wrapper for 2.5.0, hopefully having a unified template for both filesystem usage at a global level. For current users, be aware the interface may change (simplify!) in release 2.5.0. Fixes #4740
1 parent c0cfe87 commit 794630e

10 files changed

+285
-458
lines changed

libraries/ESP8266WiFi/examples/BearSSL_CertStore/BearSSL_CertStore.ino

Lines changed: 91 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@
77
//
88
// Why would you need a CertStore?
99
//
10-
// If you know the exact serve being connected to, or you
10+
// If you know the exact server being connected to, or you
1111
// are generating your own self-signed certificates and aren't
1212
// allowing connections to HTTPS/TLS servers out of your
1313
// control, then you do NOT want a CertStore. Hardcode the
1414
// self-signing CA or the site's x.509 certificate directly.
1515
//
1616
// However, if you don't know what specific sites the system
1717
// will be required to connect to and verify, a
18-
// CertStore{SPIFFS,SD}BearSSL can allow you to select from
18+
// CertStore can allow you to select from among
1919
// 10s or 100s of CAs against which you can check the
2020
// target's X.509, without taking any more RAM than a single
2121
// certificate. This is the same way that standard browsers
@@ -31,7 +31,7 @@
3131
// Released to the public domain
3232

3333
#include <ESP8266WiFi.h>
34-
#include <CertStoreSPIFFSBearSSL.h>
34+
#include <CertStoreBearSSL.h>
3535
#include <time.h>
3636

3737
const char *ssid = "....";
@@ -40,7 +40,87 @@ const char *pass = "....";
4040
// A single, global CertStore which can be used by all
4141
// connections. Needs to stay live the entire time any of
4242
// the WiFiClientBearSSLs are present.
43-
CertStoreSPIFFSBearSSL certStore;
43+
BearSSL::CertStore certStore;
44+
45+
// Uncomment below to use the SD card to store the certs
46+
// #define USE_SDCARD 1
47+
48+
// NOTE: The CertStoreFile virtual class may migrate to a templated
49+
// model in a future release. Expect some changes to the interface,
50+
// no matter what, as the SD and SPIFFS filesystem get unified.
51+
52+
#ifdef USE_SDCARD
53+
54+
#include <SD.h>
55+
class SDCertStoreFile : public BearSSL::CertStoreFile {
56+
public:
57+
SDCertStoreFile(const char *name) {
58+
_name = name;
59+
};
60+
virtual ~SDCertStoreFile() override {};
61+
62+
// The main API
63+
virtual bool open(bool write = false) override {
64+
_file = SD.open(_name, write ? FILE_WRITE : FILE_READ);
65+
return _file;
66+
}
67+
virtual bool seek(size_t absolute_pos) override {
68+
return _file.seek(absolute_pos);
69+
}
70+
virtual ssize_t read(void *dest, size_t bytes) override {
71+
return _file.read(dest, bytes);
72+
}
73+
virtual ssize_t write(void *dest, size_t bytes) override {
74+
return _file.write((const uint8_t*)dest, bytes);
75+
}
76+
virtual void close() override {
77+
_file.close();
78+
}
79+
80+
private:
81+
File _file;
82+
const char *_name;
83+
};
84+
85+
SDCertStoreFile certs_idx("/certs.idx");
86+
SDCertStoreFile certs_ar("/certs.ar");
87+
88+
#else
89+
90+
#include <FS.h>
91+
class SPIFFSCertStoreFile : public BearSSL::CertStoreFile {
92+
public:
93+
SPIFFSCertStoreFile(const char *name) {
94+
_name = name;
95+
};
96+
virtual ~SPIFFSCertStoreFile() override {};
97+
98+
// The main API
99+
virtual bool open(bool write = false) override {
100+
_file = SPIFFS.open(_name, write ? "w" : "r");
101+
return _file;
102+
}
103+
virtual bool seek(size_t absolute_pos) override {
104+
return _file.seek(absolute_pos, SeekSet);
105+
}
106+
virtual ssize_t read(void *dest, size_t bytes) override {
107+
return _file.readBytes((char*)dest, bytes);
108+
}
109+
virtual ssize_t write(void *dest, size_t bytes) override {
110+
return _file.write((uint8_t*)dest, bytes);
111+
}
112+
virtual void close() override {
113+
_file.close();
114+
}
115+
116+
private:
117+
File _file;
118+
const char *_name;
119+
};
120+
121+
SPIFFSCertStoreFile certs_idx("/certs.idx");
122+
SPIFFSCertStoreFile certs_ar("/certs.ar");
123+
#endif
44124

45125
// Set time via NTP, as required for x.509 validation
46126
void setClock() {
@@ -108,6 +188,12 @@ void setup() {
108188
Serial.println();
109189
Serial.println();
110190

191+
#ifdef USE_SDCARD
192+
SD.begin();
193+
#else
194+
SPIFFS.begin();
195+
#endif
196+
111197
// We start by connecting to a WiFi network
112198
Serial.print("Connecting to ");
113199
Serial.println(ssid);
@@ -126,7 +212,7 @@ void setup() {
126212

127213
setClock(); // Required for X.509 validation
128214

129-
int numCerts = certStore.initCertStore();
215+
int numCerts = certStore.initCertStore(&certs_idx, &certs_ar);
130216
Serial.printf("Number of CA certs read: %d\n", numCerts);
131217
if (numCerts == 0) {
132218
Serial.printf("No certs found. Did you run certs-from-mozill.py and upload the SPIFFS directory before running?\n");

libraries/ESP8266WiFi/examples/BearSSL_CertStore/certs-from-mozilla.py

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
# Script by Earle F. Philhower, III. Released to the public domain.
1010

1111
import csv
12-
from os import mkdir
13-
from subprocess import Popen, PIPE
12+
import os
13+
from subprocess import Popen, PIPE, call
1414
import urllib2
1515
try:
1616
# for Python 2.x
@@ -40,12 +40,27 @@
4040
except:
4141
pass
4242

43+
derFiles = []
44+
idx = 0
4345
# Process the text PEM using openssl into DER files
4446
for i in range(0, len(pems)):
45-
certName = "data/ca_%03d.der" % (i);
47+
certName = "data/ca_%03d.der" % (idx);
4648
thisPem = pems[i].replace("'", "")
4749
print names[i] + " -> " + certName
48-
pipe = Popen(['openssl','x509','-inform','PEM','-outform','DER','-out', certName], shell = False, stdin = PIPE).stdin
50+
ssl = Popen(['openssl','x509','-inform','PEM','-outform','DER','-out', certName], shell = False, stdin = PIPE)
51+
pipe = ssl.stdin
4952
pipe.write(thisPem)
50-
pipe.close
53+
pipe.close()
54+
ssl.wait()
55+
if os.path.exists(certName):
56+
derFiles.append(certName)
57+
idx = idx + 1
5158

59+
if os.path.exists("data/certs.ar"):
60+
os.unlink("data/certs.ar");
61+
62+
arCmd = ['ar', 'mcs', 'data/certs.ar'] + derFiles;
63+
call( arCmd )
64+
65+
for der in derFiles:
66+
os.unlink(der)

0 commit comments

Comments
 (0)