8000 Add basic ESP32 WiFiClientSecure support by earlephilhower · Pull Request #704 · earlephilhower/arduino-pico · GitHub
[go: up one dir, main page]

Skip to content

Add basic ESP32 WiFiClientSecure support #704

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 23, 2022
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
29 changes: 28 additions & 1 deletion docs/bearssl-client-secure-class.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
:orphan:

WiFiClientSecure Class
----------------------
======================

`BearSSL::WiFiClientSecure` is the object which actually handles TLS encrypted WiFi connections to a remote server or client. It extends `WiFiClient` and so can be used with minimal changes to code that does unsecured communications.

Expand Down Expand Up @@ -117,3 +117,30 @@ setSSLVersion(uint32_t min, uint32_t max)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Valid values for min and max are `BR_TLS10`, `BR_TLS11`, `BR_TLS12`. Min and max may be set to the same value if only a single TLS version is desired.


ESP32 Compatibility
===================
Simple ESP32 ``WiFiClientSecure`` compatibility is built-in, allow for some sketches to run without any modification.
The following methods are implemented:

.. code :: cpp

void setCACert(const char *rootCA);
void setCertificate(const char *client_ca);
void setPrivateKey(const char *private_key);
bool loadCACert(Stream& stream, size_t size);
bool loadCertificate(Stream& stream, size_t size);
bool loadPrivateKey(Stream& stream, size_t size);
int connect(IPAddress ip, uint16_t port, int32_t timeout);
int connect(const char *host, uint16_t port, int32_t timeout);
int connect(IPAddress ip, uint16_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key);
int connect(const char *host, uint16_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key);

Note that the SSL backend is very different between Arduino-Pico and ESP32-Arduino (BearSSL vs. mbedTLS). This means
that, for instance, the SSL connection will check valid dates of certificates (and hence require system time to be
set on the Pico, which is automatically done in this case).

TLS-Pre Shared Keys (PSK) is not supported by BearSSL, and hence not implemented here. Neither is ALPN.

For more advanced control, it is recommended to port to the native Pico calls which allows much more flexibility and control.
17 changes: 17 additions & 0 deletions docs/wifintp.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,20 @@ returns a sane value before continuing a sketch:
Serial.print("Current time: ");
Serial.print(asctime(&timeinfo));
}

bool NTP.waitSet(uint32_t timeout)
----------------------------------
This call will wait up to timeout milliseconds for the time to be set, and returns
success or failure. It will also begin NTP with a default "pool.ntp.org" server if
it is not already running. Using this method, the above code becomes:

.. code :: cpp

void setClock() {
NTP.begin("pool.ntp.org", "time.nist.gov");
NTP.waitSet();
struct tm timeinfo;
gmtime_r(&now, &timeinfo);
Serial.print("Current time: ");
Serial.print(asctime(&timeinfo));
}
1 change: 1 addition & 0 deletions 8000 libraries/WiFi/keywords.txt
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ noLowPowerMode KEYWORD2
ping KEYWORD2
beginMulticast KEYWORD2
setTimeout KEYWORD2
waitSet KEYWORD2


#######################################
Expand Down
1 change: 1 addition & 0 deletions libraries/WiFi/src/WiFiClientSecure.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 10000 MA 02110-1301 USA

*/
#include "WiFi.h"
#include "WiFiClient.h"
#include "WiFiClientSecureBearSSL.h"

Expand Down
47 changes: 41 additions & 6 deletions libraries/WiFi/src/WiFiClientSecureBearSSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,11 @@
#include <list>
#include <errno.h>
#include <algorithm>
//#include <Esp.h>



//#include "debug.h"
#include "WiFi.h"
//#include "PolledTimeout.h"
#include "WiFiClient.h"
#include "WiFiClientSecureBearSSL.h"
#include "WiFiNTP.h"
#include "StackThunk.h"
#include "lwip/opt.h"
#include "lwip/ip.h"
Expand Down Expand Up @@ -95,6 +91,10 @@ void WiFiClientSecureCtx::_clear() {
_cipher_cnt = 0;
_tls_min = BR_TLS10;
_tls_max = BR_TLS12;
if (_esp32_ta) {
delete _esp32_ta;
_esp32_ta = nullptr;
}
}

void WiFiClientSecureCtx::_clearAuthenticationSettings() {
Expand All @@ -103,6 +103,10 @@ void WiFiClientSecureCtx::_clearAuthenticationSettings() {
_use_self_signed = false;
_knownkey = nullptr;
_ta = nullptr;
if (_esp32_ta) {
delete _esp32_ta;
_esp32_ta = nullptr;
}
}


Expand Down Expand Up @@ -166,12 +170,28 @@ WiFiClientSecureCtx::WiFiClientSecureCtx(ClientContext *client,
}

void WiFiClientSecureCtx::setClientRSACert(const X509List *chain, const PrivateKey *sk) {
if (_esp32_chain) {
delete _esp32_chain;
_esp32_chain = nullptr;
}
if (_esp32_sk) {
delete _esp32_sk;
_esp32_sk = nullptr;
}
_chain = chain;
_sk = sk;
}

void WiFiClientSecureCtx::setClientECCert(const X509List *chain,
const PrivateKey *sk, unsigned allowed_usages, unsigned cert_issuer_key_type) {
if (_esp32_chain) {
delete _esp32_chain;
_esp32_chain = nullptr;
}
if (_esp32_sk) {
delete _esp32_sk;
_esp32_sk = nullptr;
}
_chain = chain;
_sk = sk;
_allowed_usages = allowed_usages;
Expand Down Expand Up @@ -1071,7 +1091,11 @@ bool WiFiClientSecureCtx::_installClientX509Validator() {
DEBUG_BSSL("_installClientX509Validator: OOM for _x509_minimal\n");
return false;
}
br_x509_minimal_init(_x509_minimal.get(), &br_sha256_vtable, _ta ? _ta->getTrustAnchors() : nullptr, _ta ? _ta->getCount() : 0);
if (_esp32_ta) {
br_x509_minimal_init(_x509_minimal.get(), &br_sha256_vtable, _esp32_ta->getTrustAnchors(), _esp32_ta->getCount());
} else {
br_x509_minimal_init(_x509_minimal.get(), &br_sha256_vtable, _ta ? _ta->getTrustAnchors() : nullptr, _ta ? _ta->getCount() : 0);
}
br_x509_minimal_set_rsa(_x509_minimal.get(), br_ssl_engine_get_rsavrfy(_eng));
#ifndef BEARSSL_SSL_BASIC
br_x509_minimal_set_ecdsa(_x509_minimal.get(), br_ssl_engine_get_ec(_eng), br_ssl_engine_get_ecdsa(_eng));
Expand Down Expand Up @@ -1107,6 +1131,14 @@ bool WiFiClientSecureCtx::_connectSSL(const char* hostName) {
_freeSSL();
_oom_err = false;

// The ESP32 doesn't check cert time, but we do. So enable NTP silently
if (_esp32_ta || _esp32_chain || _esp32_sk) {
if (!NTP.running()) {
NTP.begin("pool.ntp.org");
NTP.waitSet();
}
}

#ifdef DEBUG_ESP_SSL
// BearSSL will reject all connections unless an authentication option is set, warn in DEBUG builds
if (!_use_insecure && !_use_fingerprint && !_use_self_signed && !_knownkey && !_certStore && !_ta) {
Expand Down Expand Up @@ -1160,6 +1192,9 @@ bool WiFiClientSecureCtx::_connectSSL(const char* hostName) {
DEBUG_BSSL("_connectSSL: Attempting to use EC cert in minimal cipher mode (no EC)\n");
return false;
#endif
} else if (_esp32_sk && _esp32_chain) {
br_ssl_client_set_single_rsa(_sc.get(), _esp32_chain->getX509Certs(), _esp32_chain->getCount(),
_esp32_sk->getRSA(), br_rsa_pkcs1_sign_get_default());
}

// Restore session from the storage spot, if present
Expand Down
134 changes: 134 additions & 0 deletions libraries/WiFi/src/WiFiClientSecureBearSSL.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,101 @@ class WiFiClientSecureCtx : public WiFiClient {
// consume bytes after use (see peekBuffer)
virtual void peekConsume(size_t consume) override;
#endif

// ESP32 compatibility
void setCACert(const char *rootCA) {
if (_esp32_ta) {
delete _esp32_ta;
}
_esp32_ta = new X509List(rootCA);
}
void setCertificate(const char *client_ca) {
if (_esp32_chain) {
delete _esp32_chain;
}
_esp32_chain = new X509List(client_ca);
}
void setPrivateKey(const char *private_key) {
if (_esp32_sk) {
delete _esp32_sk;
}
_esp32_sk = new PrivateKey(private_key);
}
bool loadCACert(Stream& stream, size_t size) {
bool ret = false;
auto buff = new char[size];
if (size == stream.readBytes(buff, size)) {
setCACert(buff);
ret = true;
}
delete[] buff;
return ret;
}
bool loadCertificate(Stream& stream, size_t size) {
bool ret = false;
auto buff = new char[size];
if (size == stream.readBytes(buff, size)) {
setCertificate(buff);
ret = true;
}
delete[] buff;
return ret;
}
bool loadPrivateKey(Stream& stream, size_t size) {
bool ret = false;
auto buff = new char[size];
if (size == stream.readBytes(buff, size)) {
setPrivateKey(buff);
ret = true;
}
delete[] buff;
return ret;
}
int connect(IPAddress ip, uint16_t port, int32_t timeout) {
auto save = _timeout;
_timeout = timeout * 1000; // timeout is in secs, _timeout in milliseconds
auto ret = connect(ip, port);
_timeout = save;
return ret;
}
int connect(const char *host, uint16_t port, int32_t timeout) {
auto save = _timeout;
_timeout = timeout * 1000; // timeout is in secs, _timeout in milliseconds
auto ret = connect(host, port);
_timeout = save;
return ret;
}
int connect(IPAddress ip, uint16_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key) {
if (_esp32_ta) {
delete _esp32_ta;
_esp32_ta = nullptr;
}
if (_esp32_chain) {
delete _esp32_chain;
_esp32_chain = nullptr;
}
if (_esp32_sk) {
delete _esp32_sk;
_esp32_sk = nullptr;
}
if (rootCABuff) {
setCertificate(rootCABuff);
}
if (cli_cert && cli_key) {
setCertificate(cli_cert);
setPrivateKey(cli_key);
}
return connect(ip, port);
}
int connect(const char *host, uint16_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key) {
IPAddress ip;
if (WiFi.hostByName(host, ip, _timeout)) {
return connect(ip, port, rootCABuff, cli_cert, cli_key);
} else {
return 0;
}
}

protected:
bool _connectSSL(const char *hostName); // Do initial SSL handshake

Expand Down Expand Up @@ -241,6 +336,11 @@ class WiFiClientSecureCtx : public WiFiClient {
bool _installServerX509Validator(const X509List *client_CA_ta); // Setup X509 client cert validation, if supplied

uint8_t *_streamLoad(Stream& stream, size_t size);

// ESP32 compatibility
X509List *_esp32_ta = nullptr;
X509List *_esp32_chain = nullptr;
PrivateKey *_esp32_sk = nullptr;
}; // class WiFiClientSecureCtx


Expand Down Expand Up @@ -443,6 +543,40 @@ class WiFiClientSecure : public WiFiClient {
return _ctx->peekConsume(consume);
}
#endif

// ESP32 compatibility
void setCACert(const char *rootCA) {
return _ctx->setCACert(rootCA);
}
void setCertificate(const char *client_ca) {
return _ctx->setCertificate(client_ca);
}
void setPrivateKey(const char *private_key) {
return _ctx->setPrivateKey(private_key);
}
bool loadCACert(Stream& stream, size_t size) {
return _ctx->loadCACert(stream, size);
}
bool loadCertificate(Stream& stream, size_t size) {
return _ctx->loadCertificate(stream, size);
}
bool loadPrivateKey(Stream& stream, size_t size) {
return _ctx->loadPrivateKey(stream, size);
}

int connect(IPAddress ip, uint16_t port, int32_t timeout) {
return _ctx->connect(ip, port, timeout);
}
int connect(const char *host, uint16_t port, int32_t timeout) {
return _ctx->connect(host, port, timeout);
}
int connect(IPAddress ip, uint16_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key) {
return _ctx->connect(ip, port, rootCABuff, cli_cert, cli_key);
}
int connect(const char *host, uint16_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key) {
return _ctx->connect(host, port, rootCABuff, cli_cert, cli_key);
}

private:
std::shared_ptr<WiFiClientSecureCtx> _ctx;

Expand Down
25 changes: 24 additions & 1 deletion libraries/WiFi/src/WiFiNTP.h
F365
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#pragma once

#include <Arduino.h>
#include <time.h>
#include <lwip/apps/sntp.h>

class NTPClass {
Expand All @@ -37,6 +38,7 @@ class NTPClass {
sntp_setserver(0, server);
sntp_setoperatingmode(SNTP_OPMODE_POLL);
sntp_init();
_running = true;
}
}

Expand All @@ -51,15 +53,17 @@ class NTPClass {
}
sntp_setoperatingmode(SNTP_OPMODE_POLL);
sntp_init();
_running = true;
}


void begin(const char *server, int timeout = 3600) {
IPAddress addr;
if (WiFi.hostByName(server, addr)) {
begin(addr, timeout);
}
_running = true;
}

void begin(const char *s1, const char *s2, int timeout = 3600) {
IPAddress a1, a2;
if (WiFi.hostByName(s1, a1)) {
Expand All @@ -69,7 +73,26 @@ class NTPClass {
begin(a1, timeout);
}
}
_running = true;
}

bool waitSet(uint32_t timeout = 10000) {
if (!running()) {
begin("pool.ntp.org");
}
uint32_t start = millis();
while ((time(nullptr) < 10000000) && (millis() - start < timeout)) {
delay(10);
}
return time(nullptr) < 10000000;
}

bool running() {
return _running;
}

private:
bool _running = false;
};

// ESP8266 compat
Expand Down
Loading
0