From 10bcc65375c608897988e9cd54200d0e580c022b Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Wed, 23 May 2018 12:56:46 -0700 Subject: [PATCH 1/3] Remove dependency on SD/SPIFFS from CertStore 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. Fixes #4740 --- .../BearSSL_CertStore/BearSSL_CertStore.ino | 92 +++++++- .../BearSSL_CertStore/certs-from-mozilla.py | 25 ++- .../ESP8266WiFi/src/CertStoreBearSSL.cpp | 197 +++++++++++------- libraries/ESP8266WiFi/src/CertStoreBearSSL.h | 59 ++++-- .../ESP8266WiFi/src/CertStoreSDBearSSL.cpp | 141 ------------- .../ESP8266WiFi/src/CertStoreSDBearSSL.h | 47 ----- .../src/CertStoreSPIFFSBearSSL.cpp | 125 ----------- .../ESP8266WiFi/src/CertStoreSPIFFSBearSSL.h | 43 ---- .../ESP8266WiFi/src/WiFiClientSecureBearSSL.h | 4 +- 9 files changed, 276 insertions(+), 457 deletions(-) delete mode 100644 libraries/ESP8266WiFi/src/CertStoreSDBearSSL.cpp delete mode 100644 libraries/ESP8266WiFi/src/CertStoreSDBearSSL.h delete mode 100644 libraries/ESP8266WiFi/src/CertStoreSPIFFSBearSSL.cpp delete mode 100644 libraries/ESP8266WiFi/src/CertStoreSPIFFSBearSSL.h diff --git a/libraries/ESP8266WiFi/examples/BearSSL_CertStore/BearSSL_CertStore.ino b/libraries/ESP8266WiFi/examples/BearSSL_CertStore/BearSSL_CertStore.ino index 50246a9b31..19ef70ee55 100644 --- a/libraries/ESP8266WiFi/examples/BearSSL_CertStore/BearSSL_CertStore.ino +++ b/libraries/ESP8266WiFi/examples/BearSSL_CertStore/BearSSL_CertStore.ino @@ -7,7 +7,7 @@ // // Why would you need a CertStore? // -// If you know the exact serve being connected to, or you +// If you know the exact server being connected to, or you // are generating your own self-signed certificates and aren't // allowing connections to HTTPS/TLS servers out of your // control, then you do NOT want a CertStore. Hardcode the @@ -15,7 +15,7 @@ // // However, if you don't know what specific sites the system // will be required to connect to and verify, a -// CertStore{SPIFFS,SD}BearSSL can allow you to select from +// CertStore can allow you to select from among // 10s or 100s of CAs against which you can check the // target's X.509, without taking any more RAM than a single // certificate. This is the same way that standard browsers @@ -31,7 +31,7 @@ // Released to the public domain #include -#include +#include #include const char *ssid = "...."; @@ -40,7 +40,83 @@ const char *pass = "...."; // A single, global CertStore which can be used by all // connections. Needs to stay live the entire time any of // the WiFiClientBearSSLs are present. -CertStoreSPIFFSBearSSL certStore; +BearSSL::CertStore certStore; + +// Uncomment below to use the SD card to store the certs +// #define USE_SDCARD 1 + +#ifdef USE_SDCARD + +#include +class SDCertStoreFile : public BearSSL::CertStoreFile { + public: + SDCertStoreFile(const char *name) { + _name = name; + }; + virtual ~SDCertStoreFile() override {}; + + // The main API + virtual bool open(bool write = false) override { + _file = SD.open(_name, write ? FILE_WRITE : FILE_READ); + return _file; + } + virtual bool seek(size_t absolute_pos) override { + return _file.seek(absolute_pos); + } + virtual ssize_t read(void *dest, size_t bytes) override { + return _file.read(dest, bytes); + } + virtual ssize_t write(void *dest, size_t bytes) override { + return _file.write((const uint8_t*)dest, bytes); + } + virtual void close() override { + _file.close(); + } + + private: + File _file; + const char *_name; +}; + +SDCertStoreFile certs_idx("/certs.idx"); +SDCertStoreFile certs_ar("/certs.ar"); + +#else + +#include +class SPIFFSCertStoreFile : public BearSSL::CertStoreFile { + public: + SPIFFSCertStoreFile(const char *name) { + _name = name; + }; + virtual ~SPIFFSCertStoreFile() override {}; + + // The main API + virtual bool open(bool write = false) override { + _file = SPIFFS.open(_name, write ? "w" : "r"); + return _file; + } + virtual bool seek(size_t absolute_pos) override { + return _file.seek(absolute_pos, SeekSet); + } + virtual ssize_t read(void *dest, size_t bytes) override { + return _file.readBytes((char*)dest, bytes); + } + virtual ssize_t write(void *dest, size_t bytes) override { + return _file.write((uint8_t*)dest, bytes); + } + virtual void close() override { + _file.close(); + } + + private: + File _file; + const char *_name; +}; + +SPIFFSCertStoreFile certs_idx("/certs.idx"); +SPIFFSCertStoreFile certs_ar("/certs.ar"); +#endif // Set time via NTP, as required for x.509 validation void setClock() { @@ -108,6 +184,12 @@ void setup() { Serial.println(); Serial.println(); + #ifdef USE_SDCARD + SD.begin(); + #else + SPIFFS.begin(); + #endif + // We start by connecting to a WiFi network Serial.print("Connecting to "); Serial.println(ssid); @@ -126,7 +208,7 @@ void setup() { setClock(); // Required for X.509 validation - int numCerts = certStore.initCertStore(); + int numCerts = certStore.initCertStore(&certs_idx, &certs_ar); Serial.printf("Number of CA certs read: %d\n", numCerts); if (numCerts == 0) { Serial.printf("No certs found. Did you run certs-from-mozill.py and upload the SPIFFS directory before running?\n"); diff --git a/libraries/ESP8266WiFi/examples/BearSSL_CertStore/certs-from-mozilla.py b/libraries/ESP8266WiFi/examples/BearSSL_CertStore/certs-from-mozilla.py index cd0da9e327..15780ba73e 100755 --- a/libraries/ESP8266WiFi/examples/BearSSL_CertStore/certs-from-mozilla.py +++ b/libraries/ESP8266WiFi/examples/BearSSL_CertStore/certs-from-mozilla.py @@ -9,8 +9,8 @@ # Script by Earle F. Philhower, III. Released to the public domain. import csv -from os import mkdir -from subprocess import Popen, PIPE +import os +from subprocess import Popen, PIPE, call import urllib2 try: # for Python 2.x @@ -40,12 +40,27 @@ except: pass +derFiles = [] +idx = 0 # Process the text PEM using openssl into DER files for i in range(0, len(pems)): - certName = "data/ca_%03d.der" % (i); + certName = "data/ca_%03d.der" % (idx); thisPem = pems[i].replace("'", "") print names[i] + " -> " + certName - pipe = Popen(['openssl','x509','-inform','PEM','-outform','DER','-out', certName], shell = False, stdin = PIPE).stdin + ssl = Popen(['openssl','x509','-inform','PEM','-outform','DER','-out', certName], shell = False, stdin = PIPE) + pipe = ssl.stdin pipe.write(thisPem) - pipe.close + pipe.close() + ssl.wait() + if os.path.exists(certName): + derFiles.append(certName) + idx = idx + 1 +if os.path.exists("data/certs.ar"): + os.unlink("data/certs.ar"); + +arCmd = ['ar', 'mcs', 'data/certs.ar'] + derFiles; +call( arCmd ) + +for der in derFiles: + os.unlink(der) diff --git a/libraries/ESP8266WiFi/src/CertStoreBearSSL.cpp b/libraries/ESP8266WiFi/src/CertStoreBearSSL.cpp index b688f4c671..41f8e2d2bf 100644 --- a/libraries/ESP8266WiFi/src/CertStoreBearSSL.cpp +++ b/libraries/ESP8266WiFi/src/CertStoreBearSSL.cpp @@ -20,21 +20,18 @@ #include "CertStoreBearSSL.h" #include +namespace BearSSL { + extern "C" { - // Callbacks for the x509 decoder + // Callback for the x509 decoder static void dn_append(void *ctx, const void *buf, size_t len) { br_sha256_context *sha1 = (br_sha256_context*)ctx; br_sha256_update(sha1, buf, len); } - static void dn_append_null(void *ctx, const void *buf, size_t len) { - (void) ctx; - (void) buf; - (void) len; - } } -CertStoreBearSSL::CertInfo CertStoreBearSSL::preprocessCert(const char *fname, const void *raw, size_t sz) { - CertStoreBearSSL::CertInfo ci; +CertStore::CertInfo CertStore::_preprocessCert(uint32_t length, uint32_t offset, const void *raw) { + CertStore::CertInfo ci; // Clear the CertInfo memset(&ci, 0, sizeof(ci)); @@ -44,11 +41,12 @@ CertStoreBearSSL::CertInfo CertStoreBearSSL::preprocessCert(const char *fname, c br_sha256_context *sha256 = new br_sha256_context; br_sha256_init(sha256); br_x509_decoder_init(ctx, dn_append, sha256, nullptr, nullptr); - br_x509_decoder_push(ctx, (const void*)raw, sz); + br_x509_decoder_push(ctx, (const void*)raw, length); // Copy result to structure br_sha256_out(sha256, &ci.sha256); - strcpy(ci.fname, fname); + ci.length = length; + ci.offset = offset; // Clean up allocated memory delete sha256; @@ -58,84 +56,139 @@ CertStoreBearSSL::CertInfo CertStoreBearSSL::preprocessCert(const char *fname, c return ci; } -br_x509_trust_anchor *CertStoreBearSSL::makeTrustAnchor(const void *der, size_t der_len, const CertInfo *ci) { - // std::unique_ptr will free dc when we exit scope, automatically - std::unique_ptr dc(new br_x509_decoder_context); - br_x509_decoder_init(dc.get(), dn_append_null, nullptr, nullptr, nullptr); - br_x509_decoder_push(dc.get(), der, der_len); - br_x509_pkey *pk = br_x509_decoder_get_pkey(dc.get()); - if (!pk) { - return nullptr; +// The certs.ar file is a UNIX ar format file, concatenating all the +// individual certificates into a single blob in a space-efficient way. +int CertStore::initCertStore(CertStoreFile *index, CertStoreFile *data) { + int count = 0; + uint32_t offset = 0; + + _index = index; + _data = data; + + if (!_index || !data) { + return 0; } - br_x509_trust_anchor *ta = (br_x509_trust_anchor*)malloc(sizeof(br_x509_trust_anchor)); - if (!ta) { - return nullptr; + if (!_index->open(true)) { + return 0; + } + + if (!_data->open(false)) { + _index->close(); + return 0; + } + + char magic[8]; + if (_data->read(magic, sizeof(magic)) != sizeof(magic) || + memcmp(magic, "!\n", sizeof(magic)) ) { + _data->close(); + _index->close(); + return 0; + } + offset += sizeof(magic); + + while (true) { + char fileHeader[60]; + // 0..15 = filename in ASCII + // 48...57 = length in decimal ASCII + uint32_t length; + if (data->read(fileHeader, sizeof(fileHeader)) != sizeof(fileHeader)) { + break; + } + offset += sizeof(fileHeader); + fileHeader[58] = 0; + if (1 != sscanf(fileHeader + 48, "%d", &length) || !length) { + break; + } + + void *raw = malloc(length); + if (!raw) { + break; + } + if (_data->read(raw, length) != (ssize_t)length) { + free(raw); + break; + } + + // If the filename starts with "//" then this is a rename file, skip it + if (fileHeader[0] != '/' || fileHeader[1] != '/') { + CertStore::CertInfo ci = _preprocessCert(length, offset, raw); + if (_index->write(&ci, sizeof(ci)) != (ssize_t)sizeof(ci)) { + free(raw); + break; + } + count++; + } + + offset += length; + free(raw); + if (offset & 1) { + char x; + _data->read(&x, 1); + offset++; + } } - memset(ta, 0, sizeof(*ta)); - ta->dn.data = (uint8_t*)malloc(sizeof(ci->sha256)); - if (!ta->dn.data) { - free(ta); + _data->close(); + _index->close(); + return count; +} + +void CertStore::installCertStore(br_x509_minimal_context *ctx) { + br_x509_minimal_set_dynamic(ctx, (void*)this, findHashedTA, freeHashedTA); +} + +const br_x509_trust_anchor *CertStore::findHashedTA(void *ctx, void *hashed_dn, size_t len) { + CertStore *cs = static_cast(ctx); + CertStore::CertInfo ci; + + if (!cs || len != sizeof(ci.sha256) || !cs->_index || !cs->_data) { return nullptr; } - memcpy(ta->dn.data, ci->sha256, sizeof(ci->sha256)); - ta->dn.len = sizeof(ci->sha256); - ta->flags = 0; - if (br_x509_decoder_isCA(dc.get())) { - ta->flags |= BR_X509_TA_CA; + if (!cs->_index->open(false)) { + return nullptr; } - switch (pk->key_type) { - case BR_KEYTYPE_RSA: - ta->pkey.key_type = BR_KEYTYPE_RSA; - ta->pkey.key.rsa.n = (uint8_t*)malloc(pk->key.rsa.nlen); - if (!ta->pkey.key.rsa.n) { - free(ta->dn.data); - free(ta); + while (cs->_index->read(&ci, sizeof(ci)) == sizeof(ci)) { + if (!memcmp(ci.sha256, hashed_dn, sizeof(ci.sha256))) { + cs->_index->close(); + uint8_t *der = (uint8_t*)malloc(ci.length); + if (!der) { return nullptr; } - memcpy(ta->pkey.key.rsa.n, pk->key.rsa.n, pk->key.rsa.nlen); - ta->pkey.key.rsa.nlen = pk->key.rsa.nlen; - ta->pkey.key.rsa.e = (uint8_t*)malloc(pk->key.rsa.elen); - if (!ta->pkey.key.rsa.e) { - free(ta->pkey.key.rsa.n); - free(ta->dn.data); - free(ta); + if (!cs->_data->open(false)) { + free(der); return nullptr; } - memcpy(ta->pkey.key.rsa.e, pk->key.rsa.e, pk->key.rsa.elen); - ta->pkey.key.rsa.elen = pk->key.rsa.elen; - return ta; - case BR_KEYTYPE_EC: - ta->pkey.key_type = BR_KEYTYPE_EC; - ta->pkey.key.ec.curve = pk->key.ec.curve; - ta->pkey.key.ec.q = (uint8_t*)malloc(pk->key.ec.qlen); - if (!ta->pkey.key.ec.q) { - free(ta->dn.data); - free(ta); + if (!cs->_data->seek(ci.offset)) { + cs->_data->close(); + free(der); + return nullptr; + } + if (cs->_data->read(der, ci.length) != (ssize_t)ci.length) { + free(der); return nullptr; } - memcpy(ta->pkey.key.ec.q, pk->key.ec.q, pk->key.ec.qlen); - ta->pkey.key.ec.qlen = pk->key.ec.qlen; + cs->_data->close(); + cs->_x509 = new BearSSLX509List(der, ci.length); + free(der); + + br_x509_trust_anchor *ta = (br_x509_trust_anchor*)cs->_x509->getTrustAnchors(); + memcpy(ta->dn.data, ci.sha256, sizeof(ci.sha256)); + ta->dn.len = sizeof(ci.sha256); + return ta; - default: - free(ta->dn.data); - free(ta); - return nullptr; + } } + cs->_index->close(); + return nullptr; +} + +void CertStore::freeHashedTA(void *ctx, const br_x509_trust_anchor *ta) { + CertStore *cs = static_cast(ctx); + (void) ta; // Unused + delete cs->_x509; + cs->_x509 = nullptr; } -void CertStoreBearSSL::freeTrustAnchor(const br_x509_trust_anchor *ta) { - switch (ta->pkey.key_type) { - case BR_KEYTYPE_RSA: - free(ta->pkey.key.rsa.e); - free(ta->pkey.key.rsa.n); - break; - case BR_KEYTYPE_EC: - free(ta->pkey.key.ec.q); - break; - } - free(ta->dn.data); - free((void*)ta); } diff --git a/libraries/ESP8266WiFi/src/CertStoreBearSSL.h b/libraries/ESP8266WiFi/src/CertStoreBearSSL.h index bf669faed2..b5d16c0826 100644 --- a/libraries/ESP8266WiFi/src/CertStoreBearSSL.h +++ b/libraries/ESP8266WiFi/src/CertStoreBearSSL.h @@ -21,40 +21,65 @@ #define _CERTSTORE_BEARSSL_H #include +#include #include -// Virtual base class for the certificate stores, which allow use +// Base class for the certificate stores, which allow use // of a large set of certificates stored on SPIFFS of SD card to // be dynamically used when validating a X509 certificate -// Templates for child classes not possible due to the difference in SD -// and FS in terms of directory parsing and interating. Dir doesn't -// exist in SD, everything is a file (which might support get-next-entry() -// or not). +namespace BearSSL { -// This class should not be instantiated directly, only via its children. -class CertStoreBearSSL { +// Subclass this and provide virtual functions appropriate for your storage. +// Required because there are conflicting definitions for a "File" in the +// Arduino setup, and there is no simple way to work around the minor +// differences. +// See the examples for implementations to use in your own code. +class CertStoreFile { public: - CertStoreBearSSL() {} - virtual ~CertStoreBearSSL() {} + CertStoreFile() {}; + virtual ~CertStoreFile() {}; - // Preprocess the certs from the flash, returns number parsed - virtual int initCertStore(const char *dir) = 0; + // The main API + virtual bool open(bool write=false) = 0; + virtual bool seek(size_t absolute_pos) = 0; + virtual ssize_t read(void *dest, size_t bytes) = 0; + virtual ssize_t write(void *dest, size_t bytes) = 0; + virtual void close() = 0; +}; + + +class CertStore { + public: + CertStore() { }; + ~CertStore() { }; + + // Set the file interface instances, do preprocessing + int initCertStore(CertStoreFile *index, CertStoreFile *data); // Installs the cert store into the X509 decoder (normally via static function callbacks) - virtual void installCertStore(br_x509_minimal_context *ctx) = 0; + void installCertStore(br_x509_minimal_context *ctx); protected: - // The binary format of the pre-computed file + CertStoreFile *_index = nullptr; + CertStoreFile *_data = nullptr; + BearSSLX509List *_x509 = nullptr; + + // These need to be static as they are callbacks from BearSSL C code + static const br_x509_trust_anchor *findHashedTA(void *ctx, void *hashed_dn, size_t len); + static void freeHashedTA(void *ctx, const br_x509_trust_anchor *ta); + + // The binary format of the index file class CertInfo { public: uint8_t sha256[32]; - char fname[64]; + uint32_t offset; + uint32_t length; }; + static CertInfo _preprocessCert(uint32_t length, uint32_t offset, const void *raw); + +}; - CertInfo preprocessCert(const char *fname, const void *raw, size_t sz); - static br_x509_trust_anchor *makeTrustAnchor(const void *der, size_t der_len, const CertInfo *ci); - static void freeTrustAnchor(const br_x509_trust_anchor *ta); }; #endif diff --git a/libraries/ESP8266WiFi/src/CertStoreSDBearSSL.cpp b/libraries/ESP8266WiFi/src/CertStoreSDBearSSL.cpp deleted file mode 100644 index 7ec4873704..0000000000 --- a/libraries/ESP8266WiFi/src/CertStoreSDBearSSL.cpp +++ /dev/null @@ -1,141 +0,0 @@ -/* - CertStoreSDBearSSL.cpp - Library for Arduino ESP8266 - Copyright (c) 2018 Earle F. Philhower, III - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#include -#include "CertStoreSDBearSSL.h" - -CertStoreSDBearSSL::CertStoreSDBearSSL() : CertStoreBearSSL() { - path = ""; -} - -CertStoreSDBearSSL::~CertStoreSDBearSSL() { -} - -CertStoreBearSSL::CertInfo CertStoreSDBearSSL::preprocessCert(File *f) { - CertStoreBearSSL::CertInfo ci; - memset(&ci, 0, sizeof(ci)); - - // Load the DER into RAM temporarially - if (!f) { - return ci; - } - - int sz = f->size(); - uint8_t *buf = new uint8_t[sz]; - if (!buf) { - return ci; - } - f->read(buf, sz); - - ci = CertStoreBearSSL::preprocessCert(f->name(), buf, sz); - - delete buf; - - return ci; -} - -int CertStoreSDBearSSL::initCertStore(const char *subdir) { - int count = 0; - - // We want path to have a leading slash and a trailing one - path = subdir; - if (path[0] != '/') { - path = "/" + path; - } - if (!path.endsWith("/")) { - path += "/"; - } - - String tblName = path + "ca_tbl.bin"; - - File tbl = SD.open(tblName, FILE_WRITE); - if (!tbl) { - return 0; - } - File d = SD.open(path); - while (true) { - File nextFile = d.openNextFile(); - if (!nextFile) { - break; - } - if (!strstr(nextFile.name(), ".der")) { - continue; - } - CertStoreBearSSL::CertInfo ci = preprocessCert(&nextFile); - nextFile.close(); - tbl.write((uint8_t*)&ci, sizeof(ci)); - count++; - } - tbl.close(); - return count; -} - -void CertStoreSDBearSSL::installCertStore(br_x509_minimal_context *ctx) { - br_x509_minimal_set_dynamic(ctx, (void*)this, findHashedTA, freeHashedTA); -} - -const br_x509_trust_anchor *CertStoreSDBearSSL::findHashedTA(void *ctx, void *hashed_dn, size_t len) { - CertStoreSDBearSSL *cs = static_cast(ctx); - CertInfo ci; - - String tblName = cs->path + "ca_tbl.bin"; - - if (len != sizeof(ci.sha256) || !SD.exists(tblName)) { - return nullptr; - } - - File f = SD.open(tblName, FILE_READ); - if (!f) { - return nullptr; - } - while (f.read((uint8_t*)&ci, sizeof(ci)) == sizeof(ci)) { - if (!memcmp(ci.sha256, hashed_dn, sizeof(ci.sha256))) { - // This could be the one! - f.close(); - File d = SD.open(ci.fname, FILE_READ); - if (!d) { - return nullptr; - } - size_t der_len = d.size(); - uint8_t *der = (uint8_t*)malloc(der_len); - if (!der) { - d.close(); - return nullptr; - } - if (d.read(der, der_len) != (int)der_len) { - d.close(); - free(der); - return nullptr; - } - d.close(); - - br_x509_trust_anchor *ta = CertStoreBearSSL::makeTrustAnchor(der, der_len, &ci); - free(der); - - return ta; - } - } - f.close(); - return nullptr; -} - -void CertStoreSDBearSSL::freeHashedTA(void *ctx, const br_x509_trust_anchor *ta) { - (void) ctx; // not needed - CertStoreBearSSL::freeTrustAnchor(ta); -} diff --git a/libraries/ESP8266WiFi/src/CertStoreSDBearSSL.h b/libraries/ESP8266WiFi/src/CertStoreSDBearSSL.h deleted file mode 100644 index 7b3d462ea0..0000000000 --- a/libraries/ESP8266WiFi/src/CertStoreSDBearSSL.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - CertStoreSDBearSSL.h - Library for Arduino ESP8266 - Copyright (c) 2018 Earle F. Philhower, III - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#ifndef _CERTSTORESD_BEARSSL_H -#define _CERTSTORESD_BEARSSL_H - -#include "CertStoreBearSSL.h" - -class File; - -// SD cert store can be in a subdirectory as there are fewer limits -// Note that SD.begin() MUST be called before doing initCertStore because -// there are different options for the CS and other pins you need to -// specify it in your own code. -class CertStoreSDBearSSL : public CertStoreBearSSL { - public: - CertStoreSDBearSSL(); - virtual ~CertStoreSDBearSSL(); - - virtual int initCertStore(const char *dir = "/") override; - virtual void installCertStore(br_x509_minimal_context *ctx) override; - - private: - String path; - CertInfo preprocessCert(File *f); - // These need to be static as they are callbacks from BearSSL C code - static const br_x509_trust_anchor *findHashedTA(void *ctx, void *hashed_dn, size_t len); - static void freeHashedTA(void *ctx, const br_x509_trust_anchor *ta); -}; - -#endif diff --git a/libraries/ESP8266WiFi/src/CertStoreSPIFFSBearSSL.cpp b/libraries/ESP8266WiFi/src/CertStoreSPIFFSBearSSL.cpp deleted file mode 100644 index 00874d8f72..0000000000 --- a/libraries/ESP8266WiFi/src/CertStoreSPIFFSBearSSL.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/* - CertStoreSPIFFSBearSSL.cpp - Library for Arduino ESP8266 - Copyright (c) 2018 Earle F. Philhower, III - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#include "CertStoreSPIFFSBearSSL.h" -#include - -CertStoreSPIFFSBearSSL::CertStoreSPIFFSBearSSL() : CertStoreBearSSL() { -} - -CertStoreSPIFFSBearSSL::~CertStoreSPIFFSBearSSL() { -} - -CertStoreBearSSL::CertInfo CertStoreSPIFFSBearSSL::preprocessCert(const char *fname) { - CertStoreBearSSL::CertInfo ci; - memset(&ci, 0, sizeof(ci)); - - // Load the DER into RAM temporarially - File f = SPIFFS.open(fname, "r"); - if (!f) { - return ci; - } - int sz = f.size(); - uint8_t *buf = new uint8_t[sz]; - if (!buf) { - f.close(); - return ci; - } - f.read(buf, sz); - f.close(); - - ci = CertStoreBearSSL::preprocessCert(fname, buf, sz); - - delete[] buf; - - return ci; -} - -int CertStoreSPIFFSBearSSL::initCertStore(const char *subdir) { - (void) subdir; // ignored prefix, not enough space in filenames - int count = 0; - SPIFFS.begin(); - File tbl = SPIFFS.open("/ca_tbl.bin", "w"); - if (!tbl) { - return 0; - } - Dir d = SPIFFS.openDir(""); - while (d.next()) { - if (!strstr(d.fileName().c_str(), ".der")) { - continue; - } - CertStoreBearSSL::CertInfo ci = preprocessCert(d.fileName().c_str()); - tbl.write((uint8_t*)&ci, sizeof(ci)); - count++; - } - tbl.close(); - return count; -} - -void CertStoreSPIFFSBearSSL::installCertStore(br_x509_minimal_context *ctx) { - br_x509_minimal_set_dynamic(ctx, /* no context needed */nullptr, findHashedTA, freeHashedTA); -} - -const br_x509_trust_anchor *CertStoreSPIFFSBearSSL::findHashedTA(void *ctx, void *hashed_dn, size_t len) { - (void) ctx; // not needed - CertInfo ci; - - if (len != sizeof(ci.sha256) || !SPIFFS.exists("/ca_tbl.bin")) { - return nullptr; - } - - File f = SPIFFS.open("/ca_tbl.bin", "r"); - if (!f) { - return nullptr; - } - while (f.read((uint8_t*)&ci, sizeof(ci)) == sizeof(ci)) { - if (!memcmp(ci.sha256, hashed_dn, sizeof(ci.sha256))) { - // This could be the one! - f.close(); - File d = SPIFFS.open(ci.fname, "r"); - if (!d) { - return nullptr; - } - size_t der_len = d.size(); - uint8_t *der = (uint8_t*)malloc(der_len); - if (!der) { - d.close(); - return nullptr; - } - if (d.read(der, der_len) != der_len) { - d.close(); - free(der); - return nullptr; - } - d.close(); - - br_x509_trust_anchor *ta = CertStoreBearSSL::makeTrustAnchor(der, der_len, &ci); - free(der); - - return ta; - } - } - f.close(); - return nullptr; -} - -void CertStoreSPIFFSBearSSL::freeHashedTA(void *ctx, const br_x509_trust_anchor *ta) { - (void) ctx; // not needed - CertStoreBearSSL::freeTrustAnchor(ta); -} diff --git a/libraries/ESP8266WiFi/src/CertStoreSPIFFSBearSSL.h b/libraries/ESP8266WiFi/src/CertStoreSPIFFSBearSSL.h deleted file mode 100644 index 8cbb3795de..0000000000 --- a/libraries/ESP8266WiFi/src/CertStoreSPIFFSBearSSL.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - CertStoreSPIFFSBearSSL.h - Library for Arduino ESP8266 - Copyright (c) 2018 Earle F. Philhower, III - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#ifndef _CERTSTORESPIFFS_BEARSSL_H -#define _CERTSTORESPIFFS_BEARSSL_H - -#include "CertStoreBearSSL.h" -#include - -// SPIFFS cert stores stored in root directory due to filename length limits -class CertStoreSPIFFSBearSSL : public CertStoreBearSSL { - public: - CertStoreSPIFFSBearSSL(); - virtual ~CertStoreSPIFFSBearSSL(); - - virtual int initCertStore(const char *dir = "") override; // ignores dir - virtual void installCertStore(br_x509_minimal_context *ctx) override; - - private: - CertInfo preprocessCert(const char *fname); - // These need to be static as they are callbacks from BearSSL C code - static const br_x509_trust_anchor *findHashedTA(void *ctx, void *hashed_dn, size_t len); - static void freeHashedTA(void *ctx, const br_x509_trust_anchor *ta); -}; - -#endif - diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h index 9f23f5924e..23850fdba9 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h +++ b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h @@ -95,7 +95,7 @@ class WiFiClientSecure : public WiFiClient { int getLastSSLError(char *dest = NULL, size_t len = 0); // Attach a preconfigured certificate store - void setCertStore(CertStoreBearSSL *certStore) { + void setCertStore(CertStore *certStore) { _certStore = certStore; } @@ -152,7 +152,7 @@ class WiFiClientSecure : public WiFiClient { std::shared_ptr _iobuf_out; time_t _now; const BearSSLX509List *_ta; - CertStoreBearSSL *_certStore; + CertStore *_certStore; int _iobuf_in_size; int _iobuf_out_size; bool _handshake_done; From 4d3bcd1f142da7fe200b4b6dec0d15dcdc9fa467 Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Wed, 23 May 2018 22:54:56 -0700 Subject: [PATCH 2/3] Remove "deep+" LDF mode for PlatformIO --- tests/common.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/common.sh b/tests/common.sh index 0104c5841b..19f3bd5902 100755 --- a/tests/common.sh +++ b/tests/common.sh @@ -259,7 +259,7 @@ elif [ "$BUILD_TYPE" = "build_odd" ]; then elif [ "$BUILD_TYPE" = "platformio" ]; then # PlatformIO install_platformio - build_sketches_with_platformio $TRAVIS_BUILD_DIR/libraries "--board nodemcuv2 --project-option=lib_ldf_mode=deep+ --verbose" + build_sketches_with_platformio $TRAVIS_BUILD_DIR/libraries "--board nodemcuv2 --verbose" elif [ "$BUILD_TYPE" = "docs" ]; then # Build documentation using Sphinx cd $TRAVIS_BUILD_DIR/doc From e88ab73ae682a2f62f08eea8c3bb5a212443caa1 Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Tue, 12 Jun 2018 18:33:53 -0700 Subject: [PATCH 3/3] Add note that the CErtStoreFile may change soon --- .../examples/BearSSL_CertStore/BearSSL_CertStore.ino | 4 ++++ libraries/ESP8266WiFi/src/CertStoreBearSSL.h | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/libraries/ESP8266WiFi/examples/BearSSL_CertStore/BearSSL_CertStore.ino b/libraries/ESP8266WiFi/examples/BearSSL_CertStore/BearSSL_CertStore.ino index 19ef70ee55..220f59b228 100644 --- a/libraries/ESP8266WiFi/examples/BearSSL_CertStore/BearSSL_CertStore.ino +++ b/libraries/ESP8266WiFi/examples/BearSSL_CertStore/BearSSL_CertStore.ino @@ -45,6 +45,10 @@ BearSSL::CertStore certStore; // Uncomment below to use the SD card to store the certs // #define USE_SDCARD 1 +// NOTE: The CertStoreFile virtual class may migrate to a templated +// model in a future release. Expect some changes to the interface, +// no matter what, as the SD and SPIFFS filesystem get unified. + #ifdef USE_SDCARD #include diff --git a/libraries/ESP8266WiFi/src/CertStoreBearSSL.h b/libraries/ESP8266WiFi/src/CertStoreBearSSL.h index b5d16c0826..faa1c5b982 100644 --- a/libraries/ESP8266WiFi/src/CertStoreBearSSL.h +++ b/libraries/ESP8266WiFi/src/CertStoreBearSSL.h @@ -35,6 +35,10 @@ namespace BearSSL { // Arduino setup, and there is no simple way to work around the minor // differences. // See the examples for implementations to use in your own code. +// +// NOTE: This virtual class may migrate to a templated model in a future +// release. Expect some changes to the interface, no matter what, as the +// SD and SPIFFS filesystem get unified. class CertStoreFile { public: CertStoreFile() {};