Skip to content

Commit b1b66a4

Browse files
Add SSL Session capability to speed reconnections
SSL Sessions enable most of the SSL handshake to be skipped when both client and server agree to use them. Add a BearSSLSession class and an optional setting to the SSL client to enable this. Note that SSL sessions are unrelated to HTTP sessions. They are ephemeral and only relate to the SSL parameters, not anything at the HTTP protocol level. Fixes esp8266#4796
1 parent 1a44f79 commit b1b66a4

File tree

4 files changed

+188
-2
lines changed

4 files changed

+188
-2
lines changed
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
// Example of using SSL sessions to speed up SSL connection initiation
2+
//
3+
// September 2018 by Earle F. Philhower, III
4+
// Released to the public domain
5+
6+
#include <ESP8266WiFi.h>
7+
#include <time.h>
8+
9+
const char *ssid="....";
10+
const char *pass="....";
11+
12+
const char * host = "api.github.com";
13+
const uint16_t port = 443;
14+
const char * path = "/";
15+
16+
void setup() {
17+
Serial.begin(115200);
18+
Serial.println();
19+
Serial.println();
20+
21+
Serial.printf("Connecting to %s\n", ssid);
22+
WiFi.mode(WIFI_STA);
23+
WiFi.begin(ssid, pass);
24+
25+
while (WiFi.status() != WL_CONNECTED) {
26+
delay(500);
27+
Serial.print(".");
28+
}
29+
Serial.println("\nConnected");
30+
Serial.println("IP Address: ");
31+
Serial.println(WiFi.localIP());
32+
33+
// Set up time to allow for certificate validation
34+
configTime(3 * 3600, 0, "pool.ntp.org", "time.nist.gov");
35+
36+
Serial.print("Waiting for NTP time sync: ");
37+
time_t now = time(nullptr);
38+
while (now < 8 * 3600 * 2) {
39+
delay(500);
40+
Serial.print(".");
41+
now = time(nullptr);
42+
}
43+
Serial.println("");
44+
struct tm timeinfo;
45+
gmtime_r(&now, &timeinfo);
46+
Serial.print("Current time: ");
47+
Serial.print(asctime(&timeinfo));
48+
}
49+
50+
// Try and connect using a WiFiClientBearSSL to specified host:port and dump HTTP response
51+
void fetchURL(BearSSL::WiFiClientSecure *client, const char *host, const uint16_t port, const char *path) {
52+
if (!path) {
53+
path = "/";
54+
}
55+
56+
Serial.printf("Trying: %s:443...", host);
57+
client->connect(host, port);
58+
if (!client->connected()) {
59+
Serial.printf("*** Can't connect. ***\n-------\n");
60+
return;
61+
}
62+
Serial.printf("Connected!\n-------\n");
63+
client->write("GET ");
64+
client->write(path);
65+
client->write(" HTTP/1.0\r\nHost: ");
66+
client->write(host);
67+
client->write("\r\nUser-Agent: ESP8266\r\n");
68+
client->write("\r\n");
69+
uint32_t to = millis() + 5000;
70+
if (client->connected()) {
71+
do {
72+
char tmp[32];
73+
memset(tmp, 0, 32);
74+
int rlen = client->read((uint8_t*)tmp, sizeof(tmp) - 1);
75+
yield();
76+
if (rlen < 0) {
77+
break;
78+
}
79+
// Only print out first line up to \r, then abort connection
80+
char *nl = strchr(tmp, '\r');
81+
if (nl) {
82+
*nl = 0;
83+
Serial.print(tmp);
84+
break;
85+
}
86+
Serial.print(tmp);
87+
} while (millis() < to);
88+
}
89+
client->stop();
90+
Serial.printf("\n-------\n\n");
91+
}
92+
93+
94+
void loop() {
95+
static const char digicert[] PROGMEM = R"EOF(
96+
-----BEGIN CERTIFICATE-----
97+
MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs
98+
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
99+
d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
100+
ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL
101+
MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
102+
LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
103+
RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm
104+
+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW
105+
PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM
106+
xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB
107+
Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3
108+
hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg
109+
EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF
110+
MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA
111+
FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec
112+
nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z
113+
eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF
114+
hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2
115+
Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
116+
vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
117+
+OkuE6N36B9K
118+
-----END CERTIFICATE-----
119+
)EOF";
120+
uint32_t start, finish;
121+
BearSSL::WiFiClientSecure client;
122+
BearSSLX509List cert(digicert);
123+
client.setTrustAnchors(&cert);
124+
125+
Serial.printf("Connecting without sessions...");
126+
start = millis();
127+
fetchURL(&client, host, port, path);
128+
finish = millis();
129+
Serial.printf("Total time: %dms\n", finish - start);
130+
131+
BearSSLSession session;
132+
client.setSession(&session);
133+
Serial.printf("Connecting with an unitialized session...");
134+
start = millis();
135+
fetchURL(&client, host, port, path);
136+
finish = millis();
137+
Serial.printf("Total time: %dms\n", finish - start);
138+
139+
Serial.printf("Connecting with the just initialized session...");
140+
start = millis();
141+
fetchURL(&client, host, port, path);
142+
finish = millis();
143+
Serial.printf("Total time: %dms\n", finish - start);
144+
145+
Serial.printf("Connecting again with the initialized session...");
146+
start = millis();
147+
fetchURL(&client, host, port, path);
148+
finish = millis();
149+
Serial.printf("Total time: %dms\n", finish - start);
150+
151+
delay(10000); // Avoid DDOSing github
152+
}
153+

libraries/ESP8266WiFi/src/BearSSLHelpers.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,4 +119,21 @@ class BearSSLX509List {
119119
br_x509_trust_anchor *_ta;
120120
};
121121

122+
// Opaque object which wraps the BearSSL SSL session to make repeated connections
123+
// significantly faster. Completely optional.
124+
namespace BearSSL {
125+
class WiFiClientSecure;
126+
};
127+
128+
class BearSSLSession {
129+
friend class BearSSL::WiFiClientSecure;
130+
131+
public:
132+
BearSSLSession() { memset(&_session, 0, sizeof(_session)); }
133+
private:
134+
br_ssl_session_parameters *getSession() { return &_session; }
135+
// The actual BearSSL ession information
136+
br_ssl_session_parameters _session;
137+
};
138+
122139
#endif

libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ void WiFiClientSecure::_clear() {
7272
_recvapp_len = 0;
7373
_oom_err = false;
7474
_deleteChainKeyTA = false;
75+
_session = nullptr;
7576
}
7677

7778
void WiFiClientSecure::_clearAuthenticationSettings() {
@@ -179,8 +180,11 @@ void WiFiClientSecure::stop() {
179180
_client->abort();
180181
}
181182
WiFiClient::stop();
182-
// Only if we've already connected, clear the connection options
183+
// Only if we've already connected, istore session params and clear the connection options
183184
if (_handshake_done) {
185+
if (_session) {
186+
br_ssl_engine_get_session_parameters(_eng, _session->getSession());
187+
}
184188
_clearAuthenticationSettings();
185189
}
186190
_freeSSL();
@@ -805,7 +809,12 @@ bool WiFiClientSecure::_connectSSL(const char* hostName) {
805809
_cert_issuer_key_type, br_ec_get_default(), br_ecdsa_sign_asn1_get_default());
806810
}
807811

808-
if (!br_ssl_client_reset(_sc.get(), hostName, 0)) {
812+
// Restore session from the storage spot, if present
813+
if (_session) {
814+
br_ssl_engine_set_session_parameters(_eng, _session->getSession());
815+
}
816+
817+
if (!br_ssl_client_reset(_sc.get(), hostName, _session?1:0)) {
809818
_freeSSL();
810819
return false;
811820
}

libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ class WiFiClientSecure : public WiFiClient {
5757
void stop() override;
5858
void flush() override;
5959

60+
// Allow sessions to be saved/restored automatically to a memory area
61+
void setSession(BearSSLSession *session) { _session = session; }
62+
6063
// Don't validate the chain, just accept whatever is given. VERY INSECURE!
6164
void setInsecure() {
6265
_clearAuthenticationSettings();
@@ -163,6 +166,10 @@ class WiFiClientSecure : public WiFiClient {
163166
bool _handshake_done;
164167
bool _oom_err;
165168

169+
// Optional storage space pointer for session parameters
170+
// Will be used on connect and updated on close
171+
BearSSLSession *_session;
172+
166173
bool _use_insecure;
167174
bool _use_fingerprint;
168175
uint8_t _fingerprint[20];

0 commit comments

Comments
 (0)