8000 Add basic ESP32 WiFiClientSecure support · earlephilhower/arduino-pico@0e28664 · GitHub
[go: up one dir, main page]

Skip to content

Commit 0e28664

Browse files
Add basic ESP32 WiFiClientSecure support
Simple sketches should work without modification, but some modes (listed in the docs) are not possible to support on the Pico W with BearSSL. Fixes #691
1 parent 4a17410 commit 0e28664

File tree

8 files changed

+250
-8
lines changed

8 files changed

+250
-8
lines changed

docs/bearssl-client-secure-class.rst

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
:orphan:
22

33
WiFiClientSecure Class
4-
----------------------
4+
======================
55

66
`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.
77

@@ -117,3 +117,30 @@ setSSLVersion(uint32_t min, uint32_t max)
117117
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
118118

119119
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.
120+
121+
122+
ESP32 Compatibility
123+
===================
124+
Simple ESP32 ``WiFiClientSecure`` compatibility is built-in, allow for some sketches to run without any modification.
125+
The following methods are implemented:
126+
127+
.. code :: cpp
128+
129+
void setCACert(const char *rootCA);
130+
void setCertificate(const char *client_ca);
131+
void setPrivateKey(const char *private_key);
132+
bool loadCACert(Stream& stream, size_t size);
133+
bool loadCertificate(Stream& stream, size_t size);
134+
bool loadPrivateKey(Stream& stream, size_t size);
135+
int connect(IPAddress ip, uint16_t port, int32_t timeout);
136+
int connect(const char *host, uint16_t port, int32_t timeout);
137+
int connect(IPAddress ip, uint16_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key);
138+
int connect(const char *host, uint16_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key);
139+
140+
Note that the SSL backend is very different between Arduino-Pico and ESP32-Arduino (BearSSL vs. mbedTLS). Ths means
141+
that, for instance, the SSL connection will check valid dates of certificates (and hence require system time to be
142+
set on the Pico, which is automatically done in this case).
143+
144+
TLS-Pre Shared Keys (PSK) is not supported by BearSSL, and hence not implemented here. Neither is ALPN.
145+
146+
For more advanced control, it is recommended to port to the native Pico calls which allows much more flexibility and control.

docs/wifintp.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,20 @@ returns a sane value before continuing a sketch:
3939
Serial.print("Current time: ");
4040
Serial.print(asctime(&timeinfo));
4141
}
42+
43+
bool NTP.waitSet(uint32_t timeout)
44+
----------------------------------
45+
This call will wait up to timout milliseconds for the time to be set, and returns
46+
success or failure. It will also begin NTP with a default "pool.ntp.org" server if
47+
it is not already running. Using this method, the above code becomes:
48+
49+
.. code :: cpp
50+
51+
void setClock() {
52+
NTP.begin("pool.ntp.org", "time.nist.gov");
53+
NTP.waitSet();
54+
struct tm timeinfo;
55+
gmtime_r(&now, &timeinfo);
56+
Serial.print("Current time: ");
57+
Serial.print(asctime(&timeinfo));
58+
}

libraries/WiFi/keywords.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ noLowPowerMode KEYWORD2
5959
ping KEYWORD2
6060
beginMulticast KEYWORD2
6161
setTimeout KEYWORD2
62+
waitSet KEYWORD2
6263

6364

6465
#######################################

libraries/WiFi/src/WiFiClientSecure.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
2020
2121
*/
22+
#include "WiFi.h"
2223
#include "WiFiClient.h"
2324
#include "WiFiClientSecureBearSSL.h"
2425

libraries/WiFi/src/WiFiClientSecureBearSSL.cpp

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,11 @@
2323
#include <list>
2424
#include <errno.h>
2525
#include <algorithm>
26-
//#include <Esp.h>
2726

28-
29-
30-
//#include "debug.h"
3127
#include "WiFi.h"
32-
//#include "PolledTimeout.h"
3328
#include "WiFiClient.h"
3429
#include "WiFiClientSecureBearSSL.h"
30+
#include "WiFiNTP.h"
3531
#include "StackThunk.h"
3632
#include "lwip/opt.h"
3733
#include "lwip/ip.h"
@@ -95,6 +91,10 @@ void WiFiClientSecureCtx::_clear() {
9591
_cipher_cnt = 0;
9692
_tls_min = BR_TLS10;
9793
_tls_max = BR_TLS12;
94+
if (_esp32_ta) {
95+
delete _esp32_ta;
96+
_esp32_ta = nullptr;
97+
}
9898
}
9999

100100
void WiFiClientSecureCtx::_clearAuthenticationSettings() {
@@ -103,6 +103,10 @@ void WiFiClientSecureCtx::_clearAuthenticationSettings() {
103103
_use_self_signed = false;
104104
_knownkey = nullptr;
105105
_ta = nullptr;
106+
if (_esp32_ta) {
107+
delete _esp32_ta;
108+
_esp32_ta = nullptr;
109+
}
106110
}
107111

108112

@@ -166,12 +170,28 @@ WiFiClientSecureCtx::WiFiClientSecureCtx(ClientContext *client,
166170
}
167171

168172
void WiFiClientSecureCtx::setClientRSACert(const X509List *chain, const PrivateKey *sk) {
173+
if (_esp32_chain) {
174+
delete _esp32_chain;
175+
_esp32_chain = nullptr;
176+
}
177+
if (_esp32_sk) {
178+
delete _esp32_sk;
179+
_esp32_sk = nullptr;
180+
}
169181
_chain = chain;
170182
_sk = sk;
171183
}
172184

173185
void WiFiClientSecureCtx::setClientECCert(const X509List *chain,
174186
const PrivateKey *sk, unsigned allowed_usages, unsigned cert_issuer_key_type) {
187+
if (_esp32_chain) {
188+
delete _esp32_chain;
189+
_esp32_chain = nullptr;
190+
}
191+
if (_esp32_sk) {
192+
delete _esp32_sk;
193+
_esp32_sk = nullptr;
194+
}
175195
_chain = chain;
176196
_sk = sk;
177197
_allowed_usages = allowed_usages;
@@ -1071,7 +1091,11 @@ bool WiFiClientSecureCtx::_installClientX509Validator() {
10711091
DEBUG_BSSL("_installClientX509Validator: OOM for _x509_minimal\n");
10721092
return false;
10731093
}
1074-
br_x509_minimal_init(_x509_minimal.get(), &br_sha256_vtable, _ta ? _ta->getTrustAnchors() : nullptr, _ta ? _ta->getCount() : 0);
1094+
if (_esp32_ta) {
1095+
br_x509_minimal_init(_x509_minimal.get(), &br_sha256_vtable, _esp32_ta->getTrustAnchors(), _esp32_ta->getCount());
1096+
} else {
1097+
br_x509_minimal_init(_x509_minimal.get(), &br_sha256_vtable, _ta ? _ta->getTrustAnchors() : nullptr, _ta ? _ta->getCount() : 0);
1098+
}
10751099
br_x509_minimal_set_rsa(_x509_minimal.get(), br_ssl_engine_get_rsavrfy(_eng));
10761100
#ifndef BEARSSL_SSL_BASIC
10771101
br_x509_minimal_set_ecdsa(_x509_minimal.get(), br_ssl_engine_get_ec(_eng), br_ssl_engine_get_ecdsa(_eng));
@@ -1107,6 +1131,14 @@ bool WiFiClientSecureCtx::_connectSSL(const char* hostName) {
11071131
_freeSSL();
11081132
_oom_err = false;
11091133

1134+
// The ESP32 doesn't check cert time, but we do. So enable NTP silently
1135+
if (_esp32_ta || _esp32_chain || _esp32_sk) {
1136+
if (!NTP.running()) {
1137+
NTP.begin("pool.ntp.org");
1138+
NTP.waitSet();
1139+
}
1140+
}
1141+
11101142
#ifdef DEBUG_ESP_SSL
11111143
// BearSSL will reject all connections unless an authentication option is set, warn in DEBUG builds
11121144
if (!_use_insecure && !_use_fingerprint && !_use_self_signed && !_knownkey && !_certStore && !_ta) {
@@ -1160,6 +1192,9 @@ bool WiFiClientSecureCtx::_connectSSL(const char* hostName) {
11601192
DEBUG_BSSL("_connectSSL: Attempting to use EC cert in minimal cipher mode (no EC)\n");
11611193
return false;
11621194
#endif
1195+
} else if (_esp32_sk && _esp32_chain) {
1196+
br_ssl_client_set_single_rsa(_sc.get(), _esp32_chain->getX509Certs(), _esp32_chain->getCount(),
1197+
_esp32_sk->getRSA(), br_rsa_pkcs1_sign_get_default());
11631198
}
11641199

11651200
// Restore session from the storage spot, if present

libraries/WiFi/src/WiFiClientSecureBearSSL.h

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,101 @@ class WiFiClientSecureCtx : public WiFiClient {
159159
// consume bytes after use (see peekBuffer)
160160
virtual void peekConsume(size_t consume) override;
161161
#endif
162+
163+
// ESP32 compatibility
164+
void setCACert(const char *rootCA) {
165+
if (_esp32_ta) {
166+
delete _esp32_ta;
167+
}
168+
_esp32_ta = new X509List(rootCA);
169+
}
170+
void setCertificate(const char *client_ca) {
171+
if (_esp32_chain) {
172+
delete _esp32_chain;
173+
}
174+
_esp32_chain = new X509List(client_ca);
175+
}
176+
void setPrivateKey(const char *private_key) {
177+
if (_esp32_sk) {
178+
delete _esp32_sk;
179+
}
180+
_esp32_sk = new PrivateKey(private_key);
181+
}
182+
bool loadCACert(Stream& stream, size_t size) {
183+
bool ret = false;
184+
auto buff = new char[size];
185+
if (size == stream.readBytes(buff, size)) {
186+
setCACert(buff);
187+
ret = true;
188+
}
189+
delete[] buff;
190+
return ret;
191+
}
192+
bool loadCertificate(Stream& stream, size_t size) {
193+
bool ret = false;
194+
auto buff = new char[size];
195+
if (size == stream.readBytes(buff, size)) {
196+
setCertificate(buff);
197+
ret = true;
198+
}
199+
delete[] buff;
200+
return ret;
201+
}
202+
bool loadPrivateKey(Stream& stream, size_t size) {
203+
bool ret = false;
204+
auto buff = new char[size];
205+
if (size == stream.readBytes(buff, size)) {
206+
setPrivateKey(buff);
207+
ret = true;
208+
}
209+
delete[] buff;
210+
return ret;
211+
}
212+
int connect(IPAddress ip, uint16_t port, int32_t timeout) {
213+
auto save = _timeout;
214+
_timeout = timeout * 1000; // timeout is in secs, _timeout in milliseconds
215+
auto ret = connect(ip, port);
216+
_timeout = save;
217+
return ret;
218+
}
219+
int connect(const char *host, uint16_t port, int32_t timeout) {
220+
auto save = _timeout;
221+
_timeout = timeout * 1000; // timeout is in secs, _timeout in milliseconds
222+
auto ret = connect(host, port);
223+
_timeout = save;
224+
return ret;
225+
}
226+
int connect(IPAddress ip, uint16_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key) {
227+
if (_esp32_ta) {
228+
delete _esp32_ta;
229+
_esp32_ta = nullptr;
230+
}
231+
if (_esp32_chain) {
232+
delete _esp32_chain;
233+
_esp32_chain = nullptr;
234+
}
235+
if (_esp32_sk) {
236+
delete _esp32_sk;
237+
_esp32_sk = nullptr;
238+
}
239+
if (rootCABuff) {
240+
setCertificate(rootCABuff);
241+
}
242+
if (cli_cert && cli_key) {
243+
setCertificate(cli_cert);
244+
setPrivateKey(cli_key);
245+
}
246+
return connect(ip, port);
247+
}
248+
int connect(const char *host, uint16_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key) {
249+
IPAddress ip;
250+
if (WiFi.hostByName(host, ip, _timeout)) {
251+
return connect(ip, port, rootCABuff, cli_cert, cli_key);
252+
} else {
253+
return 0;
254+
}
255+
}
256+
162257
protected:
163258
bool _connectSSL(const char *hostName); // Do initial SSL handshake
164259

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

243338
uint8_t *_streamLoad(Stream& stream, size_t size);
339+
340+
// ESP32 compatibility
341+
X509List *_esp32_ta = nullptr;
342+
X509List *_esp32_chain = nullptr;
343+
PrivateKey *_esp32_sk = nullptr;
244344
}; // class WiFiClientSecureCtx
245345

246346

@@ -443,6 +543,40 @@ class WiFiClientSecure : public WiFiClient {
443543
return _ctx->peekConsume(consume);
444544
}
445545
#endif
546+
547+
// ESP32 compatibility
548+
void setCACert(const char *rootCA) {
549+
return _ctx->setCACert(rootCA);
550+
}
551+
void setCertificate(const char *client_ca) {
552+
return _ctx->setCertificate(client_ca);
553+
}
554+
void setPrivateKey(const char *private_key) {
555+
return _ctx->setPrivateKey(private_key);
556+
}
557+
bool loadCACert(Stream& stream, size_t size) {
558+
return _ctx->loadCACert(stream, size);
559+
}
560+
bool loadCertificate(Stream& stream, size_t size) {
561+
return _ctx->loadCertificate(stream, size);
562+
}
563+
bool loadPrivateKey(Stream& stream, size_t size) {
564+
return _ctx->loadPrivateKey(stream, size);
565+
}
566+
567+
int connect(IPAddress ip, uint16_t port, int32_t timeout) {
568+
return _ctx->connect(ip, port, timeout);
569+
}
570+
int connect(const char *host, uint16_t port, int32_t timeout) {
571+
return _ctx->connect(host, port, timeout);
572+
}
573+
int connect(IPAddress ip, uint16_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key) {
574+
return _ctx->connect(ip, port, rootCABuff, cli_cert, cli_key);
575+
}
576+
int connect(const char *host, uint16_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key) {
577+
return _ctx->connect(host, port, rootCABuff, cli_cert, cli_key);
578+
}
579+
446580
private:
447581
std::shared_ptr<WiFiClientSecureCtx> _ctx;
448582

libraries/WiFi/src/WiFiNTP.h

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#pragma once
2121

2222
#include <Arduino.h>
23+
#include <time.h>
2324
#include <lwip/apps/sntp.h>
2425

2526
class NTPClass {
@@ -37,6 +38,7 @@ class NTPClass {
3738
sntp_setserver(0, server);
3839
sntp_setoperatingmode(SNTP_OPMODE_POLL);
3940
sntp_init();
41+
_running = true;
4042
}
4143
}
4244

@@ -51,15 +53,17 @@ class NTPClass {
5153
}
5254
sntp_setoperatingmode(SNTP_OPMODE_POLL);
5355
sntp_init();
56+
_running = true;
5457
}
5558

56-
5759
void begin(const char *server, int timeout = 3600) {
5860
IPAddress addr;
5961
if (WiFi.hostByName(server, addr)) {
6062
begin(addr, timeout);
6163
}
64+
_running = true;
6265
}
66+
6367
void begin(const char *s1, const char *s2, int timeout = 3600) {
6468
IPAddress a1, a2;
6569
if (WiFi.hostByName(s1, a1)) {
@@ -69,7 +73,26 @@ class NTPClass {
6973
begin(a1, timeout);
7074
}
7175
}
76+
_running = true;
7277
}
78+
79+
bool waitSet(uint32_t timeout = 10000) {
80+
if (!running()) {
81+
begin("pool.ntp.org");
82+
}
83+
uint32_t start = millis();
84+
while ((time(nullptr) < 10000000) && (millis() - start < timeout)) {
85+
delay(10);
86+
}
87+
return time(nullptr) < 10000000;
88+
}
89+
90+
bool running() {
91+
return _running;
92+
}
93+
94+
private:
95+
bool _running = false;
7396
};
7497

7598
// ESP8266 compat

0 commit comments

Comments
 (0)
0