Skip to content

Commit

Permalink
Add setSSLVersion call to SSL object (#7920)
Browse files Browse the repository at this point in the history
* Add setSSLVersion call to SSL object

Allow users to only allow specific TLS versions for connections with an
additional call in their app, similar to the setCiphers call.

Fixes #7918

* Add SSL level options to WiFiServerSecure
  • Loading branch information
earlephilhower committed Mar 15, 2021
1 parent dcdd431 commit 7475ba7
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 11 deletions.
12 changes: 11 additions & 1 deletion doc/esp8266wifi/bearssl-client-secure-class.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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.
1 change: 1 addition & 0 deletions libraries/ESP8266WiFi/keywords.txt
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ loadCertificate KEYWORD2
loadPrivateKey KEYWORD2
loadCACert KEYWORD2
allowSelfSignedCerts KEYWORD2
setSSLVersion KEYWORD2

#WiFiServer
hasClient KEYWORD2
Expand Down
24 changes: 22 additions & 2 deletions libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -125,14 +127,16 @@ 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();
_iobuf_in_size = iobuf_in_size;
_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;
Expand All @@ -144,14 +148,16 @@ 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();
_iobuf_in_size = iobuf_in_size;
_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;
Expand Down Expand Up @@ -1005,6 +1011,17 @@ bool WiFiClientSecureCtx::setCiphers(const std::vector<uint16_t>& 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) {
Expand Down Expand Up @@ -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()) {
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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)) {
Expand Down
20 changes: 14 additions & 6 deletions libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@ class WiFiClientSecureCtx : public WiFiClient {
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; }

Expand Down Expand Up @@ -175,6 +179,10 @@ class WiFiClientSecureCtx : public WiFiClient {
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;

Expand All @@ -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,
Expand Down Expand Up @@ -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
Expand Down
15 changes: 13 additions & 2 deletions libraries/ESP8266WiFi/src/WiFiServerSecureBearSSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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;
}

};
7 changes: 7 additions & 0 deletions libraries/ESP8266WiFi/src/WiFiServerSecureBearSSL.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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;
};

};
Expand Down

0 comments on commit 7475ba7

Please sign in to comment.