diff --git a/doc/esp8266wifi/bearssl-client-secure-class.rst b/doc/esp8266wifi/bearssl-client-secure-class.rst index 68c192217..4523759f7 100644 --- a/doc/esp8266wifi/bearssl-client-secure-class.rst +++ b/doc/esp8266wifi/bearssl-client-secure-class.rst @@ -20,7 +20,7 @@ BearSSL doesn't perform memory allocations at runtime, but it does require alloc . A per-application secondary stack . A per-connection TLS receive/transmit buffer plus overhead -The per-application secondary stack is approximately 5.6KB in size and is used for temporary variables during BearSSL processing. Only one stack is required, and it will be allocated whenever any `BearSSL::WiFiClientSecure` or `BearSSL::WiFiServerSecure` are instantiated. So, in the case of a global client or server, the memory will be allocated before `setup()` is called. +The per-application secondary stack is approximately 6KB in size and is used for temporary variables during BearSSL processing. Only one stack is required, and it will be allocated whenever any `BearSSL::WiFiClientSecure` or `BearSSL::WiFiServerSecure` are instantiated. So, in the case of a global client or server, the memory will be allocated before `setup()` is called. The per-connection buffers are approximately 22KB in size, but in certain circumstances it can be reduced dramatically by using MFLN or limiting message sizes. See the `MLFN section <#mfln-or-maximum-fragment-length-negotiation-saving-ram>`__ below for more information. @@ -219,3 +219,13 @@ setCiphersLessSecure() ^^^^^^^^^^^^^^^^^^^^^^ Helper function which essentially limits BearSSL to less secure ciphers than it would natively choose, but they may be helpful and faster if your server depended on specific crypto options. + +Limiting TLS(SSL) Versions +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +By default, BearSSL will connect with TLS 1.0, TLS 1.1, or TLS 1.2 protocols (depending on the request of the remote side). If you want to limit to a subset, use the following call: + +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. diff --git a/libraries/ESP8266WiFi/keywords.txt b/libraries/ESP8266WiFi/keywords.txt index d3c1bfa38..ccdef7680 100644 --- a/libraries/ESP8266WiFi/keywords.txt +++ b/libraries/ESP8266WiFi/keywords.txt @@ -156,6 +156,7 @@ loadCertificate KEYWORD2 loadPrivateKey KEYWORD2 loadCACert KEYWORD2 allowSelfSignedCerts KEYWORD2 +setSSLVersion KEYWORD2 #WiFiServer hasClient KEYWORD2 diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp index 9583d14b1..f72451d69 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp +++ b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp @@ -93,6 +93,8 @@ void WiFiClientSecureCtx::_clear() { _session = nullptr; _cipher_list = nullptr; _cipher_cnt = 0; + _tls_min = BR_TLS10; + _tls_max = BR_TLS12; } void WiFiClientSecureCtx::_clearAuthenticationSettings() { @@ -125,7 +127,7 @@ WiFiClientSecureCtx::~WiFiClientSecureCtx() { WiFiClientSecureCtx::WiFiClientSecureCtx(ClientContext* client, const X509List *chain, const PrivateKey *sk, int iobuf_in_size, int iobuf_out_size, ServerSessions *cache, - const X509List *client_CA_ta) { + const X509List *client_CA_ta, int tls_min, int tls_max) { _clear(); _clearAuthenticationSettings(); stack_thunk_add_ref(); @@ -133,6 +135,8 @@ WiFiClientSecureCtx::WiFiClientSecureCtx(ClientContext* client, _iobuf_out_size = iobuf_out_size; _client = client; _client->ref(); + _tls_min = tls_min; + _tls_max = tls_max; if (!_connectSSLServerRSA(chain, sk, cache, client_CA_ta)) { _client->unref(); _client = nullptr; @@ -144,7 +148,7 @@ WiFiClientSecureCtx::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) { + const X509List *client_CA_ta, int tls_min, int tls_max) { _clear(); _clearAuthenticationSettings(); stack_thunk_add_ref(); @@ -152,6 +156,8 @@ WiFiClientSecureCtx::WiFiClientSecureCtx(ClientContext *client, _iobuf_out_size = iobuf_out_size; _client = client; _client->ref(); + _tls_min = tls_min; + _tls_max = tls_max; if (!_connectSSLServerEC(chain, cert_issuer_key_type, sk, cache, client_CA_ta)) { _client->unref(); _client = nullptr; @@ -1005,6 +1011,17 @@ bool WiFiClientSecureCtx::setCiphers(const std::vector& list) { return setCiphers(&list[0], list.size()); } +bool WiFiClientSecureCtx::setSSLVersion(uint32_t min, uint32_t max) { + if ( ((min != BR_TLS10) && (min != BR_TLS11) && (min != BR_TLS12)) || + ((max != BR_TLS10) && (max != BR_TLS11) && (max != BR_TLS12)) || + (max < min) ) { + return false; // Invalid options + } + _tls_min = min; + _tls_max = max; + return true; +} + // Installs the appropriate X509 cert validation method for a client connection bool WiFiClientSecureCtx::_installClientX509Validator() { if (_use_insecure || _use_fingerprint || _use_self_signed) { @@ -1110,6 +1127,7 @@ bool WiFiClientSecureCtx::_connectSSL(const char* hostName) { return false; } br_ssl_engine_set_buffers_bidi(_eng, _iobuf_in.get(), _iobuf_in_size, _iobuf_out.get(), _iobuf_out_size); + br_ssl_engine_set_versions(_eng, _tls_min, _tls_max); // Apply any client certificates, if supplied. if (_sk && _sk->isRSA()) { @@ -1224,6 +1242,7 @@ bool WiFiClientSecureCtx::_connectSSLServerRSA(const X509List *chain, sk ? sk->getRSA() : nullptr, BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN, br_rsa_private_get_default(), br_rsa_pkcs1_sign_get_default()); br_ssl_engine_set_buffers_bidi(_eng, _iobuf_in.get(), _iobuf_in_size, _iobuf_out.get(), _iobuf_out_size); + br_ssl_engine_set_versions(_eng, _tls_min, _tls_max); if (cache != nullptr) br_ssl_server_set_cache(_sc_svr.get(), cache->getCache()); if (client_CA_ta && !_installServerX509Validator(client_CA_ta)) { @@ -1270,6 +1289,7 @@ bool WiFiClientSecureCtx::_connectSSLServerEC(const X509List *chain, sk ? sk->getEC() : nullptr, BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN, cert_issuer_key_type, br_ssl_engine_get_ec(_eng), br_ecdsa_i15_sign_asn1); br_ssl_engine_set_buffers_bidi(_eng, _iobuf_in.get(), _iobuf_in_size, _iobuf_out.get(), _iobuf_out_size); + br_ssl_engine_set_versions(_eng, _tls_min, _tls_max); if (cache != nullptr) br_ssl_server_set_cache(_sc_svr.get(), cache->getCache()); if (client_CA_ta && !_installServerX509Validator(client_CA_ta)) { diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h index 52dd87874..ee7fc8730 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h +++ b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h @@ -121,6 +121,10 @@ class WiFiClientSecureCtx : public WiFiClient { bool setCiphers(const std::vector& 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; } @@ -175,6 +179,10 @@ class WiFiClientSecureCtx : public WiFiClient { std::shared_ptr _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; @@ -194,10 +202,10 @@ class WiFiClientSecureCtx : public WiFiClient { 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); + 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); + const X509List *client_CA_ta, int tls_min, int tls_max); // RSA keyed server bool _connectSSLServerRSA(const X509List *chain, const PrivateKey *sk, @@ -321,14 +329,14 @@ class WiFiClientSecure : public WiFiClient { 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): - _ctx(new WiFiClientSecureCtx(client, chain, cert_issuer_key_type, sk, iobuf_in_size, iobuf_out_size, cache, client_CA_ta)) { + 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): - _ctx(new WiFiClientSecureCtx(client, chain, sk, iobuf_in_size, iobuf_out_size, cache, client_CA_ta)) { + 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 diff --git a/libraries/ESP8266WiFi/src/WiFiServerSecureBearSSL.cpp b/libraries/ESP8266WiFi/src/WiFiServerSecureBearSSL.cpp index 5f7bcac07..3dba2f6cd 100644 --- a/libraries/ESP8266WiFi/src/WiFiServerSecureBearSSL.cpp +++ b/libraries/ESP8266WiFi/src/WiFiServerSecureBearSSL.cpp @@ -79,13 +79,13 @@ WiFiClientSecure WiFiServerSecure::available(uint8_t* status) { (void) status; // Unused if (_unclaimed) { if (_sk && _sk->isRSA()) { - WiFiClientSecure result(_unclaimed, _chain, _sk, _iobuf_in_size, _iobuf_out_size, _cache, _client_CA_ta); + WiFiClientSecure result(_unclaimed, _chain, _sk, _iobuf_in_size, _iobuf_out_size, _cache, _client_CA_ta, _tls_min, _tls_max); _unclaimed = _unclaimed->next(); result.setNoDelay(_noDelay); DEBUGV("WS:av\r\n"); return result; } else if (_sk && _sk->isEC()) { - WiFiClientSecure result(_unclaimed, _chain, _cert_issuer_key_type, _sk, _iobuf_in_size, _iobuf_out_size, _cache, _client_CA_ta); + WiFiClientSecure result(_unclaimed, _chain, _cert_issuer_key_type, _sk, _iobuf_in_size, _iobuf_out_size, _cache, _client_CA_ta, _tls_min, _tls_max); _unclaimed = _unclaimed->next(); result.setNoDelay(_noDelay); DEBUGV("WS:av\r\n"); @@ -101,4 +101,15 @@ WiFiClientSecure WiFiServerSecure::available(uint8_t* status) { return WiFiClientSecure(); } +bool WiFiServerSecure::setSSLVersion(uint32_t min, uint32_t max) { + if ( ((min != BR_TLS10) && (min != BR_TLS11) && (min != BR_TLS12)) || + ((max != BR_TLS10) && (max != BR_TLS11) && (max != BR_TLS12)) || + (max < min) ) { + return false; // Invalid options + } + _tls_min = min; + _tls_max = max; + return true; +} + }; diff --git a/libraries/ESP8266WiFi/src/WiFiServerSecureBearSSL.h b/libraries/ESP8266WiFi/src/WiFiServerSecureBearSSL.h index 716f00eaa..fa89a1e69 100644 --- a/libraries/ESP8266WiFi/src/WiFiServerSecureBearSSL.h +++ b/libraries/ESP8266WiFi/src/WiFiServerSecureBearSSL.h @@ -60,6 +60,10 @@ class WiFiServerSecure : public WiFiServer { _client_CA_ta = client_CA_ta; } + // 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); + // If awaiting connection available and authenticated (i.e. client cert), return it. WiFiClientSecure available(uint8_t* status = NULL); @@ -76,6 +80,9 @@ class WiFiServerSecure : public WiFiServer { const X509List *_client_CA_ta = nullptr; ServerSessions *_cache = nullptr; + // TLS ciphers allowed + uint32_t _tls_min = BR_TLS10; + uint32_t _tls_max = BR_TLS12; }; };