diff --git a/libraries/ESP8266WiFi/examples/BearSSL_Sessions/BearSSL_Sessions.ino b/libraries/ESP8266WiFi/examples/BearSSL_Sessions/BearSSL_Sessions.ino new file mode 100644 index 0000000000..55604a3f73 --- /dev/null +++ b/libraries/ESP8266WiFi/examples/BearSSL_Sessions/BearSSL_Sessions.ino @@ -0,0 +1,156 @@ +// Example of using SSL sessions to speed up SSL connection initiation +// +// September 2018 by Earle F. Philhower, III +// Released to the public domain + +#include +#include + +const char *ssid = "...."; +const char *pass = "...."; + +const char * host = "api.github.com"; +const uint16_t port = 443; +const char * path = "/"; + +void setup() { + Serial.begin(115200); + Serial.println(); + Serial.println(); + + Serial.printf("Connecting to %s\n", ssid); + WiFi.mode(WIFI_STA); + WiFi.begin(ssid, pass); + + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println("\nConnected"); + Serial.println("IP Address: "); + Serial.println(WiFi.localIP()); + + // Set up time to allow for certificate validation + configTime(3 * 3600, 0, "pool.ntp.org", "time.nist.gov"); + + Serial.print("Waiting for NTP time sync: "); + time_t now = time(nullptr); + while (now < 8 * 3600 * 2) { + delay(500); + Serial.print("."); + now = time(nullptr); + } + Serial.println(""); + struct tm timeinfo; + gmtime_r(&now, &timeinfo); + Serial.print("Current time: "); + Serial.print(asctime(&timeinfo)); +} + +// Try and connect using a WiFiClientBearSSL to specified host:port and dump HTTP response +void fetchURL(BearSSL::WiFiClientSecure *client, const char *host, const uint16_t port, const char *path) { + if (!path) { + path = "/"; + } + + Serial.printf("Trying: %s:443...", host); + client->connect(host, port); + if (!client->connected()) { + Serial.printf("*** Can't connect. ***\n-------\n"); + return; + } + Serial.printf("Connected!\n-------\n"); + client->write("GET "); + client->write(path); + client->write(" HTTP/1.0\r\nHost: "); + client->write(host); + client->write("\r\nUser-Agent: ESP8266\r\n"); + client->write("\r\n"); + uint32_t to = millis() + 5000; + if (client->connected()) { + do { + char tmp[32]; + memset(tmp, 0, 32); + int rlen = client->read((uint8_t*)tmp, sizeof(tmp) - 1); + yield(); + if (rlen < 0) { + break; + } + // Only print out first line up to \r, then abort connection + char *nl = strchr(tmp, '\r'); + if (nl) { + *nl = 0; + Serial.print(tmp); + break; + } + Serial.print(tmp); + } while (millis() < to); + } + client->stop(); + Serial.printf("\n-------\n\n"); +} + + +void loop() { + static const char digicert[] PROGMEM = R"EOF( +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j +ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 +LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug +RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm ++9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW +PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM +xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB +Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 +hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg +EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA +FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec +nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z +eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF +hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 +Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe +vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep ++OkuE6N36B9K +-----END CERTIFICATE----- +)EOF"; + uint32_t start, finish; + BearSSL::WiFiClientSecure client; + BearSSLX509List cert(digicert); + + Serial.printf("Connecting without sessions..."); + start = millis(); + client.setTrustAnchors(&cert); + fetchURL(&client, host, port, path); + finish = millis(); + Serial.printf("Total time: %dms\n", finish - start); + + BearSSLSession session; + client.setSession(&session); + Serial.printf("Connecting with an unitialized session..."); + start = millis(); + client.setTrustAnchors(&cert); + fetchURL(&client, host, port, path); + finish = millis(); + Serial.printf("Total time: %dms\n", finish - start); + + Serial.printf("Connecting with the just initialized session..."); + start = millis(); + client.setTrustAnchors(&cert); + fetchURL(&client, host, port, path); + finish = millis(); + Serial.printf("Total time: %dms\n", finish - start); + + Serial.printf("Connecting again with the initialized session..."); + start = millis(); + client.setTrustAnchors(&cert); + fetchURL(&client, host, port, path); + finish = millis(); + Serial.printf("Total time: %dms\n", finish - start); + + delay(10000); // Avoid DDOSing github +} + diff --git a/libraries/ESP8266WiFi/src/BearSSLHelpers.h b/libraries/ESP8266WiFi/src/BearSSLHelpers.h index 55b4970a42..6cfe4e4aec 100644 --- a/libraries/ESP8266WiFi/src/BearSSLHelpers.h +++ b/libraries/ESP8266WiFi/src/BearSSLHelpers.h @@ -119,4 +119,21 @@ class BearSSLX509List { br_x509_trust_anchor *_ta; }; +// Opaque object which wraps the BearSSL SSL session to make repeated connections +// significantly faster. Completely optional. +namespace BearSSL { + class WiFiClientSecure; +}; + +class BearSSLSession { + friend class BearSSL::WiFiClientSecure; + + public: + BearSSLSession() { memset(&_session, 0, sizeof(_session)); } + private: + br_ssl_session_parameters *getSession() { return &_session; } + // The actual BearSSL ession information + br_ssl_session_parameters _session; +}; + #endif diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp index 57bfc2ed29..276514ab91 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp +++ b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp @@ -72,6 +72,7 @@ void WiFiClientSecure::_clear() { _recvapp_len = 0; _oom_err = false; _deleteChainKeyTA = false; + _session = nullptr; _cipher_list = NULL; _cipher_cnt = 0; } @@ -177,8 +178,11 @@ void WiFiClientSecure::setBufferSizes(int recv, int xmit) { bool WiFiClientSecure::stop(unsigned int maxWaitMs) { bool ret = WiFiClient::stop(maxWaitMs); // calls our virtual flush() - // Only if we've already connected, clear the connection options + // Only if we've already connected, store session params and clear the connection options if (_handshake_done) { + if (_session) { + br_ssl_engine_get_session_parameters(_eng, _session->getSession()); + } _clearAuthenticationSettings(); } _freeSSL(); @@ -865,7 +869,12 @@ bool WiFiClientSecure::_connectSSL(const char* hostName) { _cert_issuer_key_type, br_ec_get_default(), br_ecdsa_sign_asn1_get_default()); } - if (!br_ssl_client_reset(_sc.get(), hostName, 0)) { + // Restore session from the storage spot, if present + if (_session) { + br_ssl_engine_set_session_parameters(_eng, _session->getSession()); + } + + if (!br_ssl_client_reset(_sc.get(), hostName, _session?1:0)) { _freeSSL(); return false; } diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h index 7d8d657ec7..af32a38ee1 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h +++ b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h @@ -58,6 +58,9 @@ class WiFiClientSecure : public WiFiClient { bool flush(unsigned int maxWaitMs = 0) override; bool stop(unsigned int maxWaitMs = 0) override; + // Allow sessions to be saved/restored automatically to a memory area + void setSession(BearSSLSession *session) { _session = session; } + // Don't validate the chain, just accept whatever is given. VERY INSECURE! void setInsecure() { _clearAuthenticationSettings(); @@ -170,6 +173,10 @@ class WiFiClientSecure : public WiFiClient { bool _handshake_done; bool _oom_err; + // Optional storage space pointer for session parameters + // Will be used on connect and updated on close + BearSSLSession *_session; + bool _use_insecure; bool _use_fingerprint; uint8_t _fingerprint[20];