From aa476cf412ddf24c4a37af35be1a42bc04852054 Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Tue, 18 Sep 2018 21:04:08 -0700 Subject: [PATCH 1/3] Allow cipher specification for BearSSL BearSSL has many more ciphers than axTLS, but they are more compute intensive and slower. Add an option to use only the same, limited security, axTLS ciphers as well as allow users to specify any suite of ciphers they want using standard BearSSL formats. Fixes #5110 --- .../BearSSL_Validation/BearSSL_Validation.ino | 20 ++++++++++++++ .../src/WiFiClientSecureBearSSL.cpp | 27 ++++++++++++++++--- .../ESP8266WiFi/src/WiFiClientSecureBearSSL.h | 11 +++++++- 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino b/libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino index 18f72f14da..7935564f21 100644 --- a/libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino +++ b/libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino @@ -193,6 +193,25 @@ BearSSL does verify the notValidBefore/After fields. fetchURL(&client, host, port, path); } +void fetchAxTLS() { + Serial.printf(R"EOF( +The ciphers used to set up the SSL connection can be configured to +be the same as axTLS. They are faster, but less secure, so if you care +about security you won't want to do this. +)EOF"); + BearSSL::WiFiClientSecure client; + client.setInsecure(); + uint32_t now = millis(); + fetchURL(&client, host, port, path); + uint32_t delta = millis() - now; + now = millis(); + client.setInsecure(); + client.setAxTLSCiphers(); + fetchURL(&client, host, port, path); + uint32_t delta2 = millis() - now; + Serial.printf("Using more secure: %dms\nUsiing axTLS ciphers: %dms\n", delta, delta2); +} + void setup() { Serial.begin(115200); Serial.println(); @@ -220,6 +239,7 @@ void setup() { fetchSelfSigned(); fetchKnownKey(); fetchCertAuthority(); + fetchAxTLS(); } diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp index 45b7eec62c..9d95311353 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp +++ b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp @@ -85,6 +85,8 @@ void WiFiClientSecure::_clearAuthenticationSettings() { WiFiClientSecure::WiFiClientSecure() : WiFiClient() { + _cipher_list = NULL; + _cipher_cnt = 0; _clear(); _clearAuthenticationSettings(); _certStore = nullptr; // Don't want to remove cert store on a clear, should be long lived @@ -685,6 +687,13 @@ extern "C" { BR_TLS_RSA_WITH_3DES_EDE_CBC_SHA }; + // For apps which want to use less secure but faster axTLS ciphers, only + static const uint16_t axtls_suites_P[] PROGMEM = { + BR_TLS_RSA_WITH_AES_256_CBC_SHA256, + BR_TLS_RSA_WITH_AES_128_CBC_SHA256, + BR_TLS_RSA_WITH_AES_256_CBC_SHA, + BR_TLS_RSA_WITH_AES_128_CBC_SHA }; + // Install hashes into the SSL engine static void br_ssl_client_install_hashes(br_ssl_engine_context *eng) { br_ssl_engine_set_hash(eng, br_md5_ID, &br_md5_vtable); @@ -705,9 +714,9 @@ extern "C" { } // Default initializion for our SSL clients - static void br_ssl_client_base_init(br_ssl_client_context *cc) { - uint16_t suites[sizeof(suites_P) / sizeof(uint16_t)]; - memcpy_P(suites, suites_P, sizeof(suites_P)); + static void br_ssl_client_base_init(br_ssl_client_context *cc, const uint16_t *cipher_list, int cipher_cnt) { + uint16_t suites[cipher_cnt]; + memcpy_P(suites, cipher_list, cipher_cnt * sizeof(cipher_list[0])); br_ssl_client_zero(cc); br_ssl_engine_set_versions(&cc->eng, BR_TLS10, BR_TLS12); br_ssl_engine_set_suites(&cc->eng, suites, (sizeof suites) / (sizeof suites[0])); @@ -726,6 +735,12 @@ extern "C" { } +// Set the AXTLS ciphers as the only ones allowed +void WiFiClientSecure::setAxTLSCiphers() +{ + setCiphers(axtls_suites_P, sizeof(axtls_suites_P)/sizeof(axtls_suites_P[0])); +} + // Installs the appropriate X509 cert validation method for a client connection bool WiFiClientSecure::_installClientX509Validator() { if (_use_insecure || _use_fingerprint || _use_self_signed) { @@ -787,7 +802,11 @@ bool WiFiClientSecure::_connectSSL(const char* hostName) { return false; } - br_ssl_client_base_init(_sc.get()); + // If no cipher list yet set, use defaults + if (_cipher_list == NULL) { + setCiphers(suites_P, sizeof(suites_P) / sizeof(uint16_t)); + } + br_ssl_client_base_init(_sc.get(), _cipher_list, _cipher_cnt); // Only failure possible in the installation is OOM if (!_installClientX509Validator()) { _freeSSL(); diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h index 365cb9bd01..ef97ccaeb3 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h +++ b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h @@ -104,12 +104,17 @@ class WiFiClientSecure : public WiFiClient { _certStore = certStore; } + // Select specific ciphers (i.e. optimize for speed over security) + // These may be in PROGMEM or RAM, either will run properly + void setCiphers(const uint16_t *cipherAry, int cipherCount) { _cipher_list = cipherAry; _cipher_cnt = cipherCount; } + void setAxTLSCiphers(); // Only use the limited set of axTLS ciphers + // Check for Maximum Fragment Length support for given len 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); - // AXTLS compatbile wrappers + // AXTLS compatible wrappers bool verify(const char* fingerprint, const char* domain_name) { (void) fingerprint; (void) domain_name; return false; } // Can't handle this case, need app code changes bool verifyCertChain(const char* domain_name) { (void)domain_name; return connected(); } // If we're connected, the cert passed validation during handshake @@ -170,6 +175,10 @@ class WiFiClientSecure : public WiFiClient { const BearSSLPublicKey *_knownkey; unsigned _knownkey_usages; + // Custom cipher list pointer or NULL if default + const uint16_t *_cipher_list; + uint16_t _cipher_cnt; + unsigned char *_recvapp_buf; size_t _recvapp_len; From 4be10f8bae0393d8eedc9b20bcb6e5731f5bfcbc Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Wed, 19 Sep 2018 19:25:52 -0700 Subject: [PATCH 2/3] Rename methods to avoid axtls references. --- .../BearSSL_Validation/BearSSL_Validation.ino | 13 +++++++------ .../ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp | 10 +++++----- libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h | 2 +- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino b/libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino index 7935564f21..8f9674e3cb 100644 --- a/libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino +++ b/libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino @@ -193,11 +193,12 @@ BearSSL does verify the notValidBefore/After fields. fetchURL(&client, host, port, path); } -void fetchAxTLS() { +void fetchFaster() { Serial.printf(R"EOF( The ciphers used to set up the SSL connection can be configured to -be the same as axTLS. They are faster, but less secure, so if you care -about security you won't want to do this. +only support faster but less secure ciphers. If you care about security +you won't want to do this. If you need to maximize battery life, these +may make sense )EOF"); BearSSL::WiFiClientSecure client; client.setInsecure(); @@ -206,10 +207,10 @@ about security you won't want to do this. uint32_t delta = millis() - now; now = millis(); client.setInsecure(); - client.setAxTLSCiphers(); + client.setCiphersLessSecure(); fetchURL(&client, host, port, path); uint32_t delta2 = millis() - now; - Serial.printf("Using more secure: %dms\nUsiing axTLS ciphers: %dms\n", delta, delta2); + Serial.printf("Using more secure: %dms\nUsing less secure ciphers: %dms\n", delta, delta2); } void setup() { @@ -239,7 +240,7 @@ void setup() { fetchSelfSigned(); fetchKnownKey(); fetchCertAuthority(); - fetchAxTLS(); + fetchFaster(); } diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp index 9d95311353..c38c81e48d 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp +++ b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp @@ -687,8 +687,8 @@ extern "C" { BR_TLS_RSA_WITH_3DES_EDE_CBC_SHA }; - // For apps which want to use less secure but faster axTLS ciphers, only - static const uint16_t axtls_suites_P[] PROGMEM = { + // For apps which want to use less secure but faster ciphers, only + static const uint16_t faster_suites_P[] PROGMEM = { BR_TLS_RSA_WITH_AES_256_CBC_SHA256, BR_TLS_RSA_WITH_AES_128_CBC_SHA256, BR_TLS_RSA_WITH_AES_256_CBC_SHA, @@ -735,10 +735,10 @@ extern "C" { } -// Set the AXTLS ciphers as the only ones allowed -void WiFiClientSecure::setAxTLSCiphers() +// Set the faster ciphers as the only ones allowed +void WiFiClientSecure::setCiphersLessSecure() { - setCiphers(axtls_suites_P, sizeof(axtls_suites_P)/sizeof(axtls_suites_P[0])); + setCiphers(faster_suites_P, sizeof(faster_suites_P)/sizeof(faster_suites_P[0])); } // Installs the appropriate X509 cert validation method for a client connection diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h index ef97ccaeb3..6e4ddcb685 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h +++ b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h @@ -107,7 +107,7 @@ class WiFiClientSecure : public WiFiClient { // Select specific ciphers (i.e. optimize for speed over security) // These may be in PROGMEM or RAM, either will run properly void setCiphers(const uint16_t *cipherAry, int cipherCount) { _cipher_list = cipherAry; _cipher_cnt = cipherCount; } - void setAxTLSCiphers(); // Only use the limited set of axTLS ciphers + void setCiphersLessSecure(); // Only use the limited set of RSA ciphers without EC // Check for Maximum Fragment Length support for given len static bool probeMaxFragmentLength(IPAddress ip, uint16_t port, uint16_t len); From af699f458099b0612bdaa55381805cb99d3f91ca Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Wed, 19 Sep 2018 20:06:45 -0700 Subject: [PATCH 3/3] Allow std::vector to set a list of allowed ciphers For C++ afficionados, allow std::vectors to be passed in to the setCipher() routine. The BearSSL object will now keep a copy of any set ciphers and free on object destruction. These custom lists should normally only be 1-4 entries long, so it is not expected to be a memory hog having this extra copy. --- .../BearSSL_Validation/BearSSL_Validation.ino | 10 +++++-- .../src/WiFiClientSecureBearSSL.cpp | 28 +++++++++++++++---- .../ESP8266WiFi/src/WiFiClientSecureBearSSL.h | 10 ++++--- 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino b/libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino index 8f9674e3cb..6d016f702e 100644 --- a/libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino +++ b/libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino @@ -205,12 +205,18 @@ may make sense uint32_t now = millis(); fetchURL(&client, host, port, path); uint32_t delta = millis() - now; - now = millis(); client.setInsecure(); client.setCiphersLessSecure(); + now = millis(); fetchURL(&client, host, port, path); uint32_t delta2 = millis() - now; - Serial.printf("Using more secure: %dms\nUsing less secure ciphers: %dms\n", delta, delta2); + std::vector myCustomList = { BR_TLS_RSA_WITH_AES_256_CBC_SHA256, BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, BR_TLS_RSA_WITH_3DES_EDE_CBC_SHA }; + client.setInsecure(); + client.setCiphers(myCustomList); + now = millis(); + fetchURL(&client, host, port, path); + uint32_t delta3 = millis() - now; + Serial.printf("Using more secure: %dms\nUsing less secure ciphers: %dms\nUsing custom cipher list: %dms\n", delta, delta2, delta3); } void setup() { diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp index c38c81e48d..44471d640c 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp +++ b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp @@ -103,6 +103,7 @@ WiFiClientSecure::~WiFiClientSecure() { _client->unref(); _client = nullptr; } + free(_cipher_list); _freeSSL(); _local_bearssl_stack = nullptr; // Potentially delete it if we're the last SSL object if (_deleteChainKeyTA) { @@ -735,10 +736,24 @@ extern "C" { } -// Set the faster ciphers as the only ones allowed -void WiFiClientSecure::setCiphersLessSecure() -{ - setCiphers(faster_suites_P, sizeof(faster_suites_P)/sizeof(faster_suites_P[0])); +// Set custom list of ciphers +bool WiFiClientSecure::setCiphers(const uint16_t *cipherAry, int cipherCount) { + free(_cipher_list); + _cipher_list = (uint16_t *)malloc(cipherCount * sizeof(uint16_t)); + if (!_cipher_list) { + return false; + } + memcpy_P(_cipher_list, cipherAry, cipherCount * sizeof(uint16_t)); + _cipher_cnt = cipherCount; + return true; +} + +bool WiFiClientSecure::setCiphersLessSecure() { + return setCiphers(faster_suites_P, sizeof(faster_suites_P)/sizeof(faster_suites_P[0])); +} + +bool WiFiClientSecure::setCiphers(std::vector list) { + return setCiphers(&list[0], list.size()); } // Installs the appropriate X509 cert validation method for a client connection @@ -804,9 +819,10 @@ bool WiFiClientSecure::_connectSSL(const char* hostName) { // If no cipher list yet set, use defaults if (_cipher_list == NULL) { - setCiphers(suites_P, sizeof(suites_P) / sizeof(uint16_t)); + br_ssl_client_base_init(_sc.get(), suites_P, sizeof(suites_P) / sizeof(uint16_t)); + } else { + br_ssl_client_base_init(_sc.get(), _cipher_list, _cipher_cnt); } - br_ssl_client_base_init(_sc.get(), _cipher_list, _cipher_cnt); // Only failure possible in the installation is OOM if (!_installClientX509Validator()) { _freeSSL(); diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h index 6e4ddcb685..aff2935c80 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h +++ b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h @@ -23,6 +23,7 @@ #ifndef wificlientbearssl_h #define wificlientbearssl_h +#include #include "WiFiClient.h" #include #include "BearSSLHelpers.h" @@ -106,8 +107,9 @@ class WiFiClientSecure : public WiFiClient { // Select specific ciphers (i.e. optimize for speed over security) // These may be in PROGMEM or RAM, either will run properly - void setCiphers(const uint16_t *cipherAry, int cipherCount) { _cipher_list = cipherAry; _cipher_cnt = cipherCount; } - void setCiphersLessSecure(); // Only use the limited set of RSA ciphers without EC + bool setCiphers(const uint16_t *cipherAry, int cipherCount); + bool setCiphers(std::vector list); + bool setCiphersLessSecure(); // Only use the limited set of RSA ciphers without EC // Check for Maximum Fragment Length support for given len static bool probeMaxFragmentLength(IPAddress ip, uint16_t port, uint16_t len); @@ -176,8 +178,8 @@ class WiFiClientSecure : public WiFiClient { unsigned _knownkey_usages; // Custom cipher list pointer or NULL if default - const uint16_t *_cipher_list; - uint16_t _cipher_cnt; + uint16_t *_cipher_list; + uint8_t _cipher_cnt; unsigned char *_recvapp_buf; size_t _recvapp_len;