Skip to content

Commit 6314093

Browse files
Add SSL Session capability to speed reconnections (esp8266#5160)
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.
1 parent 8e11836 commit 6314093

File tree

4 files changed

+191
-2
lines changed

4 files changed

+191
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
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+
124+
Serial.printf("Connecting without sessions...");
125+
start = millis();
126+
client.setTrustAnchors(&cert);
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+
client.setTrustAnchors(&cert);
136+
fetchURL(&client, host, port, path);
137+
finish = millis();
138+
Serial.printf("Total time: %dms\n", finish - start);
139+
140+
Serial.printf("Connecting with the just initialized session...");
141+
start = millis();
142+
client.setTrustAnchors(&cert);
143+
fetchURL(&client, host, port, path);
144+
finish = millis();
145+
Serial.printf("Total time: %dms\n", finish - start);
146+
147+
Serial.printf("Connecting again with the initialized session...");
148+
start = millis();
149+
client.setTrustAnchors(&cert);
150+
fetchURL(&client, host, port, path);
151+
finish = millis();
152+
Serial.printf("Total time: %dms\n", finish - start);
153+
154+
delay(10000); // Avoid DDOSing github
155+
}
156+

libraries/ESP8266WiFi/src/BearSSLHelpers.h

+17
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

+11-2
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
_cipher_list = NULL;
7677
_cipher_cnt = 0;
7778
}
@@ -177,8 +178,11 @@ void WiFiClientSecure::setBufferSizes(int recv, int xmit) {
177178

178179
bool WiFiClientSecure::stop(unsigned int maxWaitMs) {
179180
bool ret = WiFiClient::stop(maxWaitMs); // calls our virtual flush()
180-
// Only if we've already connected, clear the connection options
181+
// Only if we've already connected, store session params and clear the connection options
181182
if (_handshake_done) {
183+
if (_session) {
184+
br_ssl_engine_get_session_parameters(_eng, _session->getSession());
185+
}
182186
_clearAuthenticationSettings();
183187
}
184188
_freeSSL();
@@ -865,7 +869,12 @@ bool WiFiClientSecure::_connectSSL(const char* hostName) {
865869
_cert_issuer_key_type, br_ec_get_default(), br_ecdsa_sign_asn1_get_default());
866870
}
867871

868-
if (!br_ssl_client_reset(_sc.get(), hostName, 0)) {
872+
// Restore session from the storage spot, if present
873+
if (_session) {
874+
br_ssl_engine_set_session_parameters(_eng, _session->getSession());
875+
}
876+
877+
if (!br_ssl_client_reset(_sc.get(), hostName, _session?1:0)) {
869878
_freeSSL();
870879
return false;
871880
}

libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h

+7
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ class WiFiClientSecure : public WiFiClient {
5858
bool flush(unsigned int maxWaitMs = 0) override;
5959
bool stop(unsigned int maxWaitMs = 0) override;
6060

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

176+
// Optional storage space pointer for session parameters
177+
// Will be used on connect and updated on close
178+
BearSSLSession *_session;
179+
173180
bool _use_insecure;
174181
bool _use_fingerprint;
175182
uint8_t _fingerprint[20];

0 commit comments

Comments
 (0)