-
Notifications
You must be signed in to change notification settings - Fork 13.3k
/
WiFiClientSecureBearSSL.h
382 lines (309 loc) · 17.7 KB
/
WiFiClientSecureBearSSL.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
/*
WiFiClientBearSSL- SSL client/server for esp8266 using BearSSL libraries
- Mostly compatible with Arduino WiFi shield library and standard
WiFiClient/ServerSecure (except for certificate handling).
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 wificlientbearssl_h
#define wificlientbearssl_h
#include <vector>
#include "WiFiClient.h"
#include <bearssl/bearssl.h>
#include "BearSSLHelpers.h"
#include "CertStoreBearSSL.h"
namespace BearSSL {
class WiFiClientSecureCtx : public WiFiClient {
public:
WiFiClientSecureCtx();
WiFiClientSecureCtx(const WiFiClientSecureCtx &rhs) = delete;
~WiFiClientSecureCtx() override;
WiFiClientSecureCtx& operator=(const WiFiClientSecureCtx&) = delete;
// TODO: usage is invalid b/c of deleted copy, but this will only trigger an error when it is actually used by something
// TODO: don't remove just yet to avoid including the WiFiClient default implementation and unintentionally causing
// a 'slice' that this method tries to avoid in the first place
std::unique_ptr<WiFiClient> clone() const override {
return nullptr;
}
int connect(IPAddress ip, uint16_t port) override;
int connect(const String& host, uint16_t port) override;
int connect(const char* name, uint16_t port) override;
uint8_t connected() override;
size_t write(const uint8_t *buf, size_t size) override;
size_t write_P(PGM_P buf, size_t size) override;
size_t write(Stream& stream); // Note this is not virtual
int read(uint8_t *buf, size_t size) override;
int read(char *buf, size_t size) { return read((uint8_t*)buf, size); }
int available() override;
int read() override;
int peek() override;
size_t peekBytes(uint8_t *buffer, size_t length) override;
bool flush(unsigned int maxWaitMs);
bool stop(unsigned int maxWaitMs);
void flush() override { (void)flush(0); }
void stop() override { (void)stop(0); }
int availableForWrite() override;
// Allow sessions to be saved/restored automatically to a memory area
void setSession(Session *session) { _session = session; }
// Don't validate the chain, just accept whatever is given. VERY INSECURE!
void setInsecure() {
_clearAuthenticationSettings();
_use_insecure = true;
}
// Assume a given public key, don't validate or use cert info at all
void setKnownKey(const PublicKey *pk, unsigned usages = BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN) {
_clearAuthenticationSettings();
_knownkey = pk;
_knownkey_usages = usages;
}
// Only check SHA1 fingerprint of certificate
bool setFingerprint(const uint8_t fingerprint[20]) {
_clearAuthenticationSettings();
_use_fingerprint = true;
memcpy_P(_fingerprint, fingerprint, 20);
return true;
}
bool setFingerprint(const char *fpStr);
// Accept any certificate that's self-signed
void allowSelfSignedCerts() {
_clearAuthenticationSettings();
_use_self_signed = true;
}
// Install certificates of trusted CAs or specific site
void setTrustAnchors(const X509List *ta) {
_clearAuthenticationSettings();
_ta = ta;
}
// In cases when NTP is not used, app must set a time manually to check cert validity
void setX509Time(time_t now) {
_now = now;
}
// Install a client certificate for this connection, in case the server requires it (i.e. MQTT)
void setClientRSACert(const X509List *cert, const PrivateKey *sk);
void setClientECCert(const X509List *cert, const PrivateKey *sk,
unsigned allowed_usages, unsigned cert_issuer_key_type);
// Sets the requested buffer size for transmit and receive
void setBufferSizes(int recv, int xmit);
// Returns whether MFLN negotiation for the above buffer sizes succeeded (after connection)
int getMFLNStatus() {
return connected() && br_ssl_engine_get_mfln_negotiated(_eng);
}
// Return an error code and possibly a text string in a passed-in buffer with last SSL failure
int getLastSSLError(char *dest = NULL, size_t len = 0);
// Attach a preconfigured certificate store
void setCertStore(CertStoreBase *certStore) {
_certStore = certStore;
}
// Select specific ciphers (i.e. optimize for speed over security)
// These may be in PROGMEM or RAM, either will run properly
bool setCiphers(const uint16_t *cipherAry, int cipherCount);
bool setCiphers(const std::vector<uint16_t>& list);
bool setCiphersLessSecure(); // Only use the limited set of RSA ciphers without EC
// Limit the TLS versions BearSSL will connect with. Default is
// BR_TLS10...BR_TLS12
bool setSSLVersion(uint32_t min = BR_TLS10, uint32_t max = BR_TLS12);
// peek buffer API is present
virtual bool hasPeekBufferAPI () const override { return true; }
// return number of byte accessible by peekBuffer()
virtual size_t peekAvailable () override { return WiFiClientSecureCtx::available(); }
// return a pointer to available data buffer (size = peekAvailable())
// semantic forbids any kind of read() before calling peekConsume()
virtual const char* peekBuffer () override;
// consume bytes after use (see peekBuffer)
virtual void peekConsume (size_t consume) override;
protected:
bool _connectSSL(const char *hostName); // Do initial SSL handshake
private:
void _clear();
void _clearAuthenticationSettings();
// Only one of the following two should ever be != nullptr!
std::shared_ptr<br_ssl_client_context> _sc;
std::shared_ptr<br_ssl_server_context> _sc_svr;
inline bool ctx_present() {
return (_sc != nullptr) || (_sc_svr != nullptr);
}
br_ssl_engine_context *_eng; // &_sc->eng, to allow for client or server contexts
std::shared_ptr<br_x509_minimal_context> _x509_minimal;
std::shared_ptr<struct br_x509_insecure_context> _x509_insecure;
std::shared_ptr<br_x509_knownkey_context> _x509_knownkey;
std::shared_ptr<unsigned char> _iobuf_in;
std::shared_ptr<unsigned char> _iobuf_out;
time_t _now;
const X509List *_ta;
CertStoreBase *_certStore;
int _iobuf_in_size;
int _iobuf_out_size;
bool _handshake_done;
bool _oom_err;
// Optional storage space pointer for session parameters
// Will be used on connect and updated on close
Session *_session;
bool _use_insecure;
bool _use_fingerprint;
uint8_t _fingerprint[20];
bool _use_self_signed;
const PublicKey *_knownkey;
unsigned _knownkey_usages;
// Custom cipher list pointer or NULL if default
std::shared_ptr<uint16_t> _cipher_list;
uint8_t _cipher_cnt;
// TLS ciphers allowed
uint32_t _tls_min;
uint32_t _tls_max;
unsigned char *_recvapp_buf;
size_t _recvapp_len;
int _pollRecvBuffer(); // If there's a buffer with some pending data, return it's length
// If there's no buffer, poll the engine and store any received data there and return the length
// (which also may change the internal state, e.g. make us disconnected)
bool _clientConnected(); // Is the underlying socket alive?
bool _engineConnected(); // Are both socket and the bearssl engine alive?
std::shared_ptr<unsigned char> _alloc_iobuf(size_t sz);
void _freeSSL();
int _run_until(unsigned target, bool blocking = true);
size_t _write(const uint8_t *buf, size_t size, bool pmem);
bool _wait_for_handshake(); // Sets and return the _handshake_done after connecting
// Optional client certificate
const X509List *_chain;
const PrivateKey *_sk;
unsigned _allowed_usages;
unsigned _cert_issuer_key_type;
// Methods for handling server.available() call which returns a client connection.
friend class WiFiClientSecure; // access to private context constructors
WiFiClientSecureCtx(ClientContext *client, const X509List *chain, unsigned cert_issuer_key_type,
const PrivateKey *sk, int iobuf_in_size, int iobuf_out_size, ServerSessions *cache,
const X509List *client_CA_ta, int tls_min, int tls_max);
WiFiClientSecureCtx(ClientContext* client, const X509List *chain, const PrivateKey *sk,
int iobuf_in_size, int iobuf_out_size, ServerSessions *cache,
const X509List *client_CA_ta, int tls_min, int tls_max);
// RSA keyed server
bool _connectSSLServerRSA(const X509List *chain, const PrivateKey *sk,
ServerSessions *cache, const X509List *client_CA_ta);
// EC keyed server
bool _connectSSLServerEC(const X509List *chain, unsigned cert_issuer_key_type, const PrivateKey *sk,
ServerSessions *cache, const X509List *client_CA_ta);
// X.509 validators differ from server to client
bool _installClientX509Validator(); // Set up X509 validator for a client conn.
bool _installServerX509Validator(const X509List *client_CA_ta); // Setup X509 client cert validation, if supplied
uint8_t *_streamLoad(Stream& stream, size_t size);
}; // class WiFiClientSecureCtx
class WiFiClientSecure : public WiFiClient {
// WiFiClient's "ClientContext* _client" is always nullptr in this class.
// Instead, all virtual functions call their counterpart in "WiFiClientecureCtx* _ctx"
// which also derives from WiFiClient (this parent is the one which is eventually used)
// TODO: notice that this complicates the implementation by having two distinct ways the client connection is managed, consider:
// - implementing the secure connection details in the ClientContext
// (i.e. delegate the write & read functions there)
// - simplify the inheritance chain by implementing base wificlient class and inherit the original wificlient and wificlientsecure from it
// - abstract internals so it's possible to seamlessly =default copy and move with the instance *without* resorting to manual copy and initialization of each member
// TODO: prefer implementing virtual overrides in the .cpp (or, at least one of them)
public:
WiFiClientSecure():_ctx(new WiFiClientSecureCtx()) { _owned = _ctx.get(); }
WiFiClientSecure(const WiFiClientSecure &rhs): WiFiClient(), _ctx(rhs._ctx) { if (_ctx) _owned = _ctx.get(); }
~WiFiClientSecure() override { _ctx = nullptr; }
WiFiClientSecure& operator=(const WiFiClientSecure&) = default;
std::unique_ptr<WiFiClient> clone() const override { return std::unique_ptr<WiFiClient>(new WiFiClientSecure(*this)); }
uint8_t status() override { return _ctx->status(); }
int connect(IPAddress ip, uint16_t port) override { return _ctx->connect(ip, port); }
int connect(const String& host, uint16_t port) override { return _ctx->connect(host, port); }
int connect(const char* name, uint16_t port) override { return _ctx->connect(name, port); }
uint8_t connected() override { return _ctx->connected(); }
size_t write(const uint8_t *buf, size_t size) override { return _ctx->write(buf, size); }
size_t write_P(PGM_P buf, size_t size) override { return _ctx->write_P(buf, size); }
size_t write(const char *buf) { return write((const uint8_t*)buf, strlen(buf)); }
size_t write_P(const char *buf) { return write_P((PGM_P)buf, strlen_P(buf)); }
size_t write(Stream& stream) /* Note this is not virtual */ { return _ctx->write(stream); }
int read(uint8_t *buf, size_t size) override { return _ctx->read(buf, size); }
int available() override { return _ctx->available(); }
int availableForWrite() override { return _ctx->availableForWrite(); }
int read() override { return _ctx->read(); }
int peek() override { return _ctx->peek(); }
size_t peekBytes(uint8_t *buffer, size_t length) override { return _ctx->peekBytes(buffer, length); }
bool flush(unsigned int maxWaitMs) { return _ctx->flush(maxWaitMs); }
bool stop(unsigned int maxWaitMs) { return _ctx->stop(maxWaitMs); }
void flush() override { (void)flush(0); }
void stop() override { (void)stop(0); }
IPAddress remoteIP() override { return _ctx->remoteIP(); }
uint16_t remotePort() override { return _ctx->remotePort(); }
IPAddress localIP() override { return _ctx->localIP(); }
uint16_t localPort() override { return _ctx->localPort(); }
// Allow sessions to be saved/restored automatically to a memory area
void setSession(Session *session) { _ctx->setSession(session); }
// Don't validate the chain, just accept whatever is given. VERY INSECURE!
void setInsecure() { _ctx->setInsecure(); }
// Assume a given public key, don't validate or use cert info at all
void setKnownKey(const PublicKey *pk, unsigned usages = BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN) {
_ctx->setKnownKey(pk, usages);
}
// Only check SHA1 fingerprint of certificate
bool setFingerprint(const uint8_t fingerprint[20]) {
return _ctx->setFingerprint(fingerprint);
}
bool setFingerprint(const char *fpStr) { return _ctx->setFingerprint(fpStr); }
// Accept any certificate that's self-signed
void allowSelfSignedCerts() { _ctx->allowSelfSignedCerts(); }
// Install certificates of trusted CAs or specific site
void setTrustAnchors(const X509List *ta) { _ctx->setTrustAnchors(ta); }
// In cases when NTP is not used, app must set a time manually to check cert validity
void setX509Time(time_t now) { _ctx->setX509Time(now); }
// Install a client certificate for this connection, in case the server requires it (i.e. MQTT)
void setClientRSACert(const X509List *cert, const PrivateKey *sk) { _ctx->setClientRSACert(cert, sk); }
void setClientECCert(const X509List *cert, const PrivateKey *sk,
unsigned allowed_usages, unsigned cert_issuer_key_type) {
_ctx->setClientECCert(cert, sk, allowed_usages, cert_issuer_key_type);
}
// Sets the requested buffer size for transmit and receive
void setBufferSizes(int recv, int xmit) { _ctx->setBufferSizes(recv, xmit); }
// Returns whether MFLN negotiation for the above buffer sizes succeeded (after connection)
int getMFLNStatus() { return _ctx->getMFLNStatus(); }
// Return an error code and possibly a text string in a passed-in buffer with last SSL failure
int getLastSSLError(char *dest = NULL, size_t len = 0) { return _ctx->getLastSSLError(dest, len); }
// Attach a preconfigured certificate store
void setCertStore(CertStoreBase *certStore) { _ctx->setCertStore(certStore); }
// Select specific ciphers (i.e. optimize for speed over security)
// These may be in PROGMEM or RAM, either will run properly
bool setCiphers(const uint16_t *cipherAry, int cipherCount) { return _ctx->setCiphers(cipherAry, cipherCount); }
bool setCiphers(const std::vector<uint16_t> list) { return _ctx->setCiphers(list); }
bool setCiphersLessSecure() { return _ctx->setCiphersLessSecure(); } // Only use the limited set of RSA ciphers without EC
// Limit the TLS versions BearSSL will connect with. Default is
// BR_TLS10...BR_TLS12. Allowed values are: BR_TLS10, BR_TLS11, BR_TLS12
bool setSSLVersion(uint32_t min = BR_TLS10, uint32_t max = BR_TLS12) { return _ctx->setSSLVersion(min, max); };
// Check for Maximum Fragment Length support for given len before connection (possibly insecure)
static bool probeMaxFragmentLength(IPAddress ip, uint16_t port, uint16_t len);
static bool probeMaxFragmentLength(const char *hostname, uint16_t port, uint16_t len);
static bool probeMaxFragmentLength(const String& host, uint16_t port, uint16_t len);
// peek buffer API is present
virtual bool hasPeekBufferAPI () const override { return true; }
// return number of byte accessible by peekBuffer()
virtual size_t peekAvailable () override { return _ctx->available(); }
// return a pointer to available data buffer (size = peekAvailable())
// semantic forbids any kind of read() before calling peekConsume()
virtual const char* peekBuffer () override { return _ctx->peekBuffer(); }
// consume bytes after use (see peekBuffer)
virtual void peekConsume (size_t consume) override { return _ctx->peekConsume(consume); }
private:
std::shared_ptr<WiFiClientSecureCtx> _ctx;
// Methods for handling server.available() call which returns a client connection.
friend class WiFiServerSecure; // Server needs to access these constructors
WiFiClientSecure(ClientContext *client, const X509List *chain, unsigned cert_issuer_key_type,
const PrivateKey *sk, int iobuf_in_size, int iobuf_out_size, ServerSessions *cache,
const X509List *client_CA_ta, int tls_min, int tls_max):
_ctx(new WiFiClientSecureCtx(client, chain, cert_issuer_key_type, sk, iobuf_in_size, iobuf_out_size, cache, client_CA_ta, tls_min, tls_max)) {
}
WiFiClientSecure(ClientContext* client, const X509List *chain, const PrivateKey *sk,
int iobuf_in_size, int iobuf_out_size, ServerSessions *cache,
const X509List *client_CA_ta, int tls_min, int tls_max):
_ctx(new WiFiClientSecureCtx(client, chain, sk, iobuf_in_size, iobuf_out_size, cache, client_CA_ta, tls_min, tls_max)) {
}
}; // class WiFiClientSecure
}; // namespace BearSSL
#endif