Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,26 @@ $ gzip -c esp32-fota-http-2.bin > esp32-fota-http-2.bin.gz

### Root Certificates

#### Certificate Bundles

Arduino 3.x now supports bundled root certificates, which means 99% of sites (including github.com) will work over https and you don't need to maintain a custom certificate on your firmware.

To enable this functionality, simply call this during your setup:

```C++
esp32FOTA.useBundledCerts();
```

If you are using Platformio / PIOArduino, the certificates are not automatically bundled and you will need to download them from [CURL](https://curl.se/docs/caextract.html).

Save that file to your project root directory and then add this line to your platformio.ini:

```board_build.embed_txtfiles=ca_cert_bundle```

Make sure it is named exactly ca_cert_bundle with no extension and located in the top level of your project.

#### Custom Certificates

Certificates and signatures can be stored in different places: any fs::FS filesystem or progmem as const char*.

Filesystems:
Expand Down
2 changes: 1 addition & 1 deletion library.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "esp32FOTA",
"version": "0.2.9",
"version": "0.3.0",
"keywords": "firmware, OTA, Over The Air Updates, ArduinoOTA",
"description": "Allows for firmware to be updated from a webserver, the device can check for updates at any time. Uses a simple JSON file to outline if a new firmware is avaiable.",
"examples": "examples/*/*.ino",
Expand Down
2 changes: 1 addition & 1 deletion library.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name=esp32FOTA
version=0.2.9
version=0.3.0
author=Chris Joyce
maintainer=Chris Joyce <chris@joyce.id.au>
sentence=A simple library for firmware OTA updates
Expand Down
86 changes: 68 additions & 18 deletions src/esp32FOTA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ void esp32FOTA::setConfig( FOTAConfig_t cfg )
_cfg.signature_len = cfg.signature_len;
_cfg.allow_reuse = cfg.allow_reuse;
_cfg.use_http10 = cfg.use_http10;
_cfg.use_bundled_certs = cfg.use_bundled_certs;
}


Expand Down Expand Up @@ -239,7 +240,15 @@ void esp32FOTA::setupCryptoAssets()
}



/* Enable ESP-IDF bundled certs */
void esp32FOTA::useBundledCerts(bool enable)
{
_cfg.use_bundled_certs = enable;
if (enable) {
_cfg.unsafe = false; // strict
_cfg.root_ca = nullptr; // disable custom CA
}
}
void esp32FOTA::handle()
{
if ( execHTTPcheck() ) {
Expand Down Expand Up @@ -370,34 +379,75 @@ bool esp32FOTA::setupHTTP( const char* url )
_http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
_http.setReuse(_cfg.allow_reuse);
_http.useHTTP10(_cfg.use_http10);


log_i("Connecting to: %s", url );
if( String(url).startsWith("https") ) {
if (!_cfg.unsafe) {
if( !_cfg.root_ca ) {
log_e("A strict security context has been set but no RootCA was provided");
return false;

bool is_https = String(url).startsWith("https");

if (is_https) {

bool https_initialized = false;

/* ================================
1. Bundled ESP-IDF CA certificate
================================ */
#if ESP_ARDUINO_VERSION_MAJOR >= 3
if (!https_initialized && _cfg.use_bundled_certs) {
__attribute__((weak)) extern const uint8_t ca_cert_bundle_start[] asm("_binary_ca_cert_bundle_start");
__attribute__((weak)) extern const uint8_t ca_cert_bundle_end[] asm("_binary_ca_cert_bundle_end");

if (ca_cert_bundle_start != nullptr && ca_cert_bundle_end != nullptr) {
size_t bundle_size = ca_cert_bundle_end - ca_cert_bundle_start;
log_i("Using built-in ESP-IDF certificate bundle (%u bytes)", bundle_size);

_client.setCACertBundle(ca_cert_bundle_start, bundle_size);
_http.begin(_client, url);
https_initialized = true;
} else {
log_w("Bundled certs requested, but CA bundle not linked. Falling back.");
}
if( _cfg.root_ca->size() == 0 ) {
log_e("A strict security context has been set but an empty RootCA was provided");
}
#endif

/* ================================
2. Custom root CA certificate
================================ */
if (!https_initialized && !_cfg.unsafe && _cfg.root_ca) {
if (_cfg.root_ca->size() == 0) {
log_e("Strict HTTPS but empty RootCA provided");
return false;
}
rootcastr = _cfg.root_ca->get();
if( !rootcastr ) {
log_e("Unable to get RootCA, aborting");
log_e("rootcastr=%s", rootcastr);
if (!rootcastr) {
log_e("Strict HTTPS but failed to get RootCA contents");
return false;
}
log_d("Loading root_ca.pem");
_client.setCACert( rootcastr );
} else {
// We're downloading from a secure URL, but we don't want to validate the root cert.
log_i("Using custom RootCA for TLS");
_client.setCACert(rootcastr);
_http.begin(_client, url);
https_initialized = true;
}

/* ================================
3. Insecure HTTPS
================================ */
if (!https_initialized && _cfg.unsafe) {
log_w("Insecure HTTPS enabled");
_client.setInsecure();
_http.begin(_client, url);
https_initialized = true;
}
_http.begin( _client, url );

/* ================================
4. No CA available (strict)
================================ */
if (!https_initialized) {
log_e("Strict HTTPS enabled but no CA source available (neither bundled nor custom)");
return false;
}

} else {
_http.begin( url );
_http.begin(url);
}

if( extraHTTPHeaders.size() > 0 ) {
Expand Down
5 changes: 5 additions & 0 deletions src/esp32FOTA.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ struct FOTAConfig_t
size_t signature_len {FW_SIGNATURE_LENGTH};
bool allow_reuse { true };
bool use_http10 { false }; // Use HTTP 1.0 (WARNING: setting to 'true' disables chunked transfers)
bool use_bundled_certs { false }; // use built-in ESP-IDF CA bundle
FOTAConfig_t() = default;
};

Expand Down Expand Up @@ -287,6 +288,10 @@ class esp32FOTA
void setCertFileSystem( fs::FS *cert_filesystem = nullptr );

// this is passed to Update.onProgress()

// enable bundled CA certificates
void useBundledCerts(bool enable = true);

typedef std::function<void(size_t,size_t)> ProgressCallback_cb; // size_t progress, size_t size
void setProgressCb(ProgressCallback_cb fn) { onOTAProgress = fn; } // callback setter

Expand Down