Skip to content

Commit 8859b81

Browse files
Make CertStore natively use File interface (#6131)
__This is a breaking change, but the header and example did warn everyone that this API was in flux due to the incompatible SD and SPIFFS File implementations.__ BearSSL CertStores now simply need a filesystem and the names of the data (generated on-chip) and archive (uploaded by user) files on it. No more need to roll your own virtual CertStoreFile class. Update the library, examples, and device test.
1 parent 44bda41 commit 8859b81

File tree

7 files changed

+67
-222
lines changed

7 files changed

+67
-222
lines changed

libraries/ESP8266WiFi/examples/BearSSL_CertStore/BearSSL_CertStore.ino

+7-87
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include <ESP8266WiFi.h>
3737
#include <CertStoreBearSSL.h>
3838
#include <time.h>
39+
#include <FS.h>
3940

4041
#ifndef STASSID
4142
#define STASSID "your-ssid"
@@ -50,87 +51,6 @@ const char *pass = STAPSK;
5051
// the WiFiClientBearSSLs are present.
5152
BearSSL::CertStore certStore;
5253

53-
// Uncomment below to use the SD card to store the certs
54-
// #define USE_SDCARD 1
55-
56-
// NOTE: The CertStoreFile virtual class may migrate to a templated
57-
// model in a future release. Expect some changes to the interface,
58-
// no matter what, as the SD and SPIFFS filesystem get unified.
59-
60-
#ifdef USE_SDCARD
61-
62-
#include <SD.h>
63-
class SDCertStoreFile : public BearSSL::CertStoreFile {
64-
public:
65-
SDCertStoreFile(const char *name) {
66-
_name = name;
67-
};
68-
virtual ~SDCertStoreFile() override {};
69-
70-
// The main API
71-
virtual bool open(bool write = false) override {
72-
_file = SD.open(_name, write ? FILE_WRITE : FILE_READ);
73-
return _file;
74-
}
75-
virtual bool seek(size_t absolute_pos) override {
76-
return _file.seek(absolute_pos);
77-
}
78-
virtual ssize_t read(void *dest, size_t bytes) override {
79-
return _file.read(dest, bytes);
80-
}
81-
virtual ssize_t write(void *dest, size_t bytes) override {
82-
return _file.write((const uint8_t*)dest, bytes);
83-
}
84-
virtual void close() override {
85-
_file.close();
86-
}
87-
88-
private:
89-
File _file;
90-
const char *_name;
91-
};
92-
93-
SDCertStoreFile certs_idx("/certs.idx"); // Generated by the ESP8266
94-
SDCertStoreFile certs_ar("/certs.ar"); // Uploaded by the user
95-
96-
#else
97-
98-
#include <FS.h>
99-
class SPIFFSCertStoreFile : public BearSSL::CertStoreFile {
100-
public:
101-
SPIFFSCertStoreFile(const char *name) {
102-
_name = name;
103-
};
104-
virtual ~SPIFFSCertStoreFile() override {};
105-
106-
// The main API
107-
virtual bool open(bool write = false) override {
108-
_file = SPIFFS.open(_name, write ? "w" : "r");
109-
return _file;
110-
}
111-
virtual bool seek(size_t absolute_pos) override {
112-
return _file.seek(absolute_pos, SeekSet);
113-
}
114-
virtual ssize_t read(void *dest, size_t bytes) override {
115-
return _file.readBytes((char*)dest, bytes);
116-
}
117-
virtual ssize_t write(void *dest, size_t bytes) override {
118-
return _file.write((uint8_t*)dest, bytes);
119-
}
120-
virtual void close() override {
121-
_file.close();
122-
}
123-
124-
private:
125-
File _file;
126-
const char *_name;
127-
};
128-
129-
SPIFFSCertStoreFile certs_idx("/certs.idx"); // Generated by the ESP8266
130-
SPIFFSCertStoreFile certs_ar("/certs.ar"); // Uploaded by the user
131-
132-
#endif
133-
13454
// Set time via NTP, as required for x.509 validation
13555
void setClock() {
13656
configTime(3 * 3600, 0, "pool.ntp.org", "time.nist.gov");
@@ -197,11 +117,8 @@ void setup() {
197117
Serial.println();
198118
Serial.println();
199119

200-
#ifdef USE_SDCARD
201-
SD.begin();
202-
#else
203120
SPIFFS.begin();
204-
#endif
121+
// If using a SD card or LittleFS, call the appropriate ::begin instead
205122

206123
// We start by connecting to a WiFi network
207124
Serial.print("Connecting to ");
@@ -221,10 +138,10 @@ void setup() {
221138

222139
setClock(); // Required for X.509 validation
223140

224-
int numCerts = certStore.initCertStore(&certs_idx, &certs_ar);
141+
int numCerts = certStore.initCertStore(SPIFFS, PSTR("/certs.idx"), PSTR("/certs.ar"));
225142
Serial.printf("Number of CA certs read: %d\n", numCerts);
226143
if (numCerts == 0) {
227-
Serial.printf("No certs found. Did you run certs-from-mozill.py and upload the SPIFFS directory before running?\n");
144+
Serial.printf("No certs found. Did you run certs-from-mozilla.py and upload the SPIFFS directory before running?\n");
228145
return; // Can't connect to anything w/o certs!
229146
}
230147

@@ -242,6 +159,9 @@ void loop() {
242159
do {
243160
site = Serial.readString();
244161
} while (site == "");
162+
// Strip newline if present
163+
site.replace(String("\r"), emptyString);
164+
site.replace(String("\n"), emptyString);
245165
Serial.printf("https://%s/\n", site.c_str());
246166

247167
BearSSL::WiFiClientSecure *bear = new BearSSL::WiFiClientSecure();

libraries/ESP8266WiFi/src/CertStoreBearSSL.cpp

+46-30
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ extern "C" {
3737
}
3838
}
3939

40+
41+
CertStore::~CertStore() {
42+
free(_indexName);
43+
free(_dataName);
44+
}
45+
4046
CertStore::CertInfo CertStore::_preprocessCert(uint32_t length, uint32_t offset, const void *raw) {
4147
CertStore::CertInfo ci;
4248

@@ -70,62 +76,70 @@ CertStore::CertInfo CertStore::_preprocessCert(uint32_t length, uint32_t offset,
7076

7177
// The certs.ar file is a UNIX ar format file, concatenating all the
7278
// individual certificates into a single blob in a space-efficient way.
73-
int CertStore::initCertStore(CertStoreFile *index, CertStoreFile *data) {
79+
int CertStore::initCertStore(FS &fs, const char *indexFileName, const char *dataFileName) {
7480
int count = 0;
7581
uint32_t offset = 0;
7682

77-
_index = index;
78-
_data = data;
83+
_fs = &fs;
7984

80-
if (!_index || !data) {
85+
// No strdup_P, so manually do it
86+
_indexName = (char *)malloc(strlen_P(indexFileName) + 1);
87+
_dataName = (char *)malloc(strlen_P(dataFileName) + 1);
88+
if (!_indexName || !_dataName) {
89+
free(_indexName);
90+
free(_dataName);
8191
return 0;
8292
}
93+
memcpy_P(_indexName, indexFileName, strlen_P(indexFileName) + 1);
94+
memcpy_P(_dataName, dataFileName, strlen_P(dataFileName) + 1);
8395

84-
if (!_index->open(true)) {
96+
File index = _fs->open(_indexName, "w");
97+
if (!index) {
8598
return 0;
8699
}
87100

88-
if (!_data->open(false)) {
89-
_index->close();
101+
File data = _fs->open(_dataName, "r");
102+
if (!data) {
103+
index.close();
90104
return 0;
91105
}
92106

93-
char magic[8];
94-
if (_data->read(magic, sizeof(magic)) != sizeof(magic) ||
107+
uint8_t magic[8];
108+
if (data.read(magic, sizeof(magic)) != sizeof(magic) ||
95109
memcmp(magic, "!<arch>\n", sizeof(magic)) ) {
96-
_data->close();
97-
_index->close();
110+
data.close();
111+
index.close();
98112
return 0;
99113
}
100114
offset += sizeof(magic);
101115

102116
while (true) {
103-
char fileHeader[60];
117+
uint8_t fileHeader[60];
104118
// 0..15 = filename in ASCII
105119
// 48...57 = length in decimal ASCII
106120
uint32_t length;
107-
if (data->read(fileHeader, sizeof(fileHeader)) != sizeof(fileHeader)) {
121+
if (data.read(fileHeader, sizeof(fileHeader)) != sizeof(fileHeader)) {
108122
break;
109123
}
110124
offset += sizeof(fileHeader);
111125
fileHeader[58] = 0;
112-
if (1 != sscanf(fileHeader + 48, "%d", &length) || !length) {
126+
if (1 != sscanf((char *)(fileHeader + 48), "%d", &length) || !length) {
113127
break;
114128
}
115129

116130
void *raw = malloc(length);
117131
if (!raw) {
118132
break;
119133
}
120-
if (_data->read(raw, length) != (ssize_t)length) {
134+
if (data.read((uint8_t *)raw, length) != length) {
121135
free(raw);
122136
break;
123137
}
124138

125139
// If the filename starts with "//" then this is a rename file, skip it
126140
if (fileHeader[0] != '/' || fileHeader[1] != '/') {
127141
CertStore::CertInfo ci = _preprocessCert(length, offset, raw);
128-
if (_index->write(&ci, sizeof(ci)) != (ssize_t)sizeof(ci)) {
142+
if (index.write((uint8_t *)&ci, sizeof(ci)) != (ssize_t)sizeof(ci)) {
129143
free(raw);
130144
break;
131145
}
@@ -135,13 +149,13 @@ int CertStore::initCertStore(CertStoreFile *index, CertStoreFile *data) {
135149
offset += length;
136150
free(raw);
137151
if (offset & 1) {
138-
char x;
139-
_data->read(&x, 1);
152+
uint8_t x;
153+
data.read(&x, 1);
140154
offset++;
141155
}
142156
}
143-
_data->close();
144-
_index->close();
157+
data.close();
158+
index.close();
145159
return count;
146160
}
147161

@@ -153,35 +167,37 @@ const br_x509_trust_anchor *CertStore::findHashedTA(void *ctx, void *hashed_dn,
153167
CertStore *cs = static_cast<CertStore*>(ctx);
154168
CertStore::CertInfo ci;
155169

156-
if (!cs || len != sizeof(ci.sha256) || !cs->_index || !cs->_data) {
170+
if (!cs || len != sizeof(ci.sha256) || !cs->_indexName || !cs->_dataName || !cs->_fs) {
157171
return nullptr;
158172
}
159173

160-
if (!cs->_index->open(false)) {
174+
File index = cs->_fs->open(cs->_indexName, "r");
175+
if (!index) {
161176
return nullptr;
162177
}
163178

164-
while (cs->_index->read(&ci, sizeof(ci)) == sizeof(ci)) {
179+
while (index.read((uint8_t *)&ci, sizeof(ci)) == sizeof(ci)) {
165180
if (!memcmp(ci.sha256, hashed_dn, sizeof(ci.sha256))) {
166-
cs->_index->close();
181+
index.close();
167182
uint8_t *der = (uint8_t*)malloc(ci.length);
168183
if (!der) {
169184
return nullptr;
170185
}
171-
if (!cs->_data->open(false)) {
186+
File data = cs->_fs->open(cs->_dataName, "r");
187+
if (!data) {
172188
free(der);
173189
return nullptr;
174190
}
175-
if (!cs->_data->seek(ci.offset)) {
176-
cs->_data->close();
191+
if (!data.seek(ci.offset, SeekSet)) {
192+
data.close();
177193
free(der);
178194
return nullptr;
179195
}
180-
if (cs->_data->read(der, ci.length) != (ssize_t)ci.length) {
196+
if (data.read((uint8_t *)der, ci.length) != ci.length) {
181197
free(der);
182198
return nullptr;
183199
}
184-
cs->_data->close();
200+
data.close();
185201
cs->_x509 = new X509List(der, ci.length);
186202
free(der);
187203
if (!cs->_x509) {
@@ -196,7 +212,7 @@ const br_x509_trust_anchor *CertStore::findHashedTA(void *ctx, void *hashed_dn,
196212
return ta;
197213
}
198214
}
199-
cs->_index->close();
215+
index.close();
200216
return nullptr;
201217
}
202218

libraries/ESP8266WiFi/src/CertStoreBearSSL.h

+6-27
Original file line numberDiff line numberDiff line change
@@ -23,50 +23,29 @@
2323
#include <Arduino.h>
2424
#include <BearSSLHelpers.h>
2525
#include <bearssl/bearssl.h>
26+
#include <FS.h>
2627

2728
// Base class for the certificate stores, which allow use
2829
// of a large set of certificates stored on SPIFFS of SD card to
2930
// be dynamically used when validating a X509 certificate
3031

3132
namespace BearSSL {
3233

33-
// Subclass this and provide virtual functions appropriate for your storage.
34-
// Required because there are conflicting definitions for a "File" in the
35-
// Arduino setup, and there is no simple way to work around the minor
36-
// differences.
37-
// See the examples for implementations to use in your own code.
38-
//
39-
// NOTE: This virtual class may migrate to a templated model in a future
40-
// release. Expect some changes to the interface, no matter what, as the
41-
// SD and SPIFFS filesystem get unified.
42-
class CertStoreFile {
43-
public:
44-
CertStoreFile() {};
45-
virtual ~CertStoreFile() {};
46-
47-
// The main API
48-
virtual bool open(bool write=false) = 0;
49-
virtual bool seek(size_t absolute_pos) = 0;
50-
virtual ssize_t read(void *dest, size_t bytes) = 0;
51-
virtual ssize_t write(void *dest, size_t bytes) = 0;
52-
virtual void close() = 0;
53-
};
54-
55-
5634
class CertStore {
5735
public:
5836
CertStore() { };
59-
~CertStore() { };
37+
~CertStore();
6038

6139
// Set the file interface instances, do preprocessing
62-
int initCertStore(CertStoreFile *index, CertStoreFile *data);
40+
int initCertStore(FS &fs, const char *indexFileName, const char *dataFileName);
6341

6442
// Installs the cert store into the X509 decoder (normally via static function callbacks)
6543
void installCertStore(br_x509_minimal_context *ctx);
6644

6745
protected:
68-
CertStoreFile *_index = nullptr;
69-
CertStoreFile *_data = nullptr;
46+
FS *_fs = nullptr;
47+
char *_indexName = nullptr;
48+
char *_dataName = nullptr;
7049
X509List *_x509 = nullptr;
7150

7251
// These need to be static as they are callbacks from BearSSL C code

0 commit comments

Comments
 (0)